Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Metapi Laravel Package

acidjazz/metapi

Metapi is a Laravel package for managing application metadata through a simple API. Define and organize meta information—such as page details, SEO tags, or custom model metadata—and expose it cleanly in your views, controllers, or JSON responses.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require fumeapp/metapi. As of v2.4.0 the package supports Laravel 12.x, so on a fresh L12 app you can pull it in without any constraint gymnastics (PHP 8.2+ is required by Laravel 12 itself).
  • Publish the package assets: php artisan vendor:publish --tag=metapi (config + migration). Run php artisan migrate before touching any model — the trait will fail silently or throw "no such table" errors otherwise.
  • Open the README quickstart first, then the published config/metapi.php. The config is where table names, morph aliases, and any caching behaviour are defined; read it before you write your first saveMeta() call.
  • Typical first use case: add the meta trait to an Eloquent model (e.g. Page, Post, Product) and attach SEO/Open Graph data to it, then render it in Blade for public pages or expose it through an API resource for a headless frontend.
// app/Models/Page.php
use FumeApp\Metapi\Traits\HasMeta; // verify namespace against your version's README

class Page extends Model
{
    use HasMeta;
}

$page->saveMeta([
    'title'       => 'Spring Sale',
    'description' => 'Up to 40% off everything.',
    'og_image'    => '/img/sale.jpg',
]);

echo $page->meta('title'); // or $page->meta->title, depending on version

Implementation Patterns

Trait-per-model, not per-controller. Attach the trait only to models that actually need metadata (Page, Post, Product). Keeping it off User and other high-churn tables keeps the extra relation and joins out of unrelated queries.

Eager load everything you list. Metadata is a relation (typically a morph relation). Any index listing that shows meta titles needs Page::with('meta')->paginate(), otherwise you get an N+1 per row — very easy to miss because the page still renders fine in development.

Write through a form request, not raw arrays. Validate the meta payload in a MetaRequest (max lengths on title/description, URL validation on images) and pass $request->validated('meta') to the save method. This keeps the JSON/column payload predictable and avoids storing null keys that break later ?? fallbacks.

Fallbacks in an accessor. Rather than sprinkling ?? $page->title through Blade, add a small accessor or a MetaPresenter that resolves meta.title ?: $page->name and a default OG image from config. One place to change when marketing asks for new defaults.

Expose via a dedicated API resource. Wrap meta in its own MetaResource and merge it conditionally ($this->mergeWhen($this->meta, ...)) so endpoints that haven't been backfilled don't emit meta: null.

// app/Http/Resources/PageResource.php
public function toArray($request): array
{
    return [
        'id'    => $this->id,
        'title' => $this->title,
        'meta'  => new MetaResource($this->whenLoaded('meta')),
    ];
}

Pair with a sitemap/og Blade partial. Render the same meta record through <x-meta-tags :model="$page" /> for server-rendered pages, and through the resource for the API — one source of truth for both surfaces.

Seed meta in factories for tests. A HasMeta factory state (->hasMeta(['title' => 'Test'])) makes feature tests for the meta endpoint deterministic and avoids depending on migration fixtures.

Treat meta as content, not schema. If you need per-locale metadata, store it keyed by locale inside the meta payload and resolve in the accessor, rather than adding a locale column you later regret.

Gotchas and Tips

  • v2.4.0 is a compatibility-only release. The only change is Laravel 12 support (PR #9, first contribution by @laravel-shift). There are no new features, no deprecations, and no breaking changes — upgrading from v2.3.0 is a composer update fumeapp/metapi away. Confirm the constraint is "fumeapp/metapi": "^2.4" before running your L12 upgrade so Composer doesn't resolve the older, L12-incompatible release.
  • Clear caches after the framework bump. php artisan config:clear && php artisan cache:clear after upgrading to Laravel 12; a stale cached config from L11 pointing at an old package provider is the most common "it installed but nothing works" symptom.
  • Run the migrations before the trait. Missing published migration is the number one first-run error — the model loads fine, then fails on the first meta query. Always vendor:publish → migrate → attach trait.
  • JSON columns behave differently across drivers. SQLite in tests handles JSON casts but won't support JSON path queries the way MySQL 8 does. If you filter on meta values, test against the same driver you deploy on.
  • Morph relations and deletions. Metadata rows tied to a model won't necessarily be removed by a hard delete unless the migration uses cascadeOnDelete or the package registers a cleanup. Verify this before deleting content in production; orphan rows accumulate quietly.
  • No mass-assignment on meta. If you're used to $model->update($request->all()), remember that metadata is a separate save path — it won't be filled by fillable on the parent model.
  • Cache the rendered meta, not the model. If you cache page HTML or meta arrays, key on $page->id . $page->updated_at or bust the cache in an observer, otherwise edited SEO titles stay stale.
  • Debug with toArray()/toJson() on the meta object directly. Dumping the parent model buries the relation; dumping $page->meta (or the result of the getter) shows exactly what was persisted versus what the accessor returns — the usual source of "my title isn't showing" confusion.
  • Extension points: the published config is the supported way to swap the meta model or table, and a custom meta model is the cleanest place to add casts, mutators, or an updated_at-based cache key. Prefer that over subclassing the trait.
  • Pin the major version in CI. The trait/method names have been stable across 2.x, but always check the README's quickstart against your installed version — the namespace and helper names are the things most likely to drift between minor releases.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource