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

Laravel Metable Laravel Package

mmanos/laravel-metable

Add metadata to Eloquent models with a simple key-value interface. Attach arbitrary attributes to any model without schema changes, query them efficiently, and keep your tables clean.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install. composer require mmanos/laravel-metable. Because the package targets Laravel 4, expect the constraint to fight with a modern illuminate/* or laravel/framework requirement. In a Laravel 4 app it installs cleanly; on anything newer you will likely need to fork it, drop it in packages/, or point Composer at a VCS repository with a relaxed constraint.

  2. Register the service provider. In app/config/app.php add 'Mmanos\Metable\MetableServiceProvider' to providers. (Laravel 4 style — no package discovery.)

  3. Create the meta table. The package ships a migration; publish/copy it and run it. The canonical schema is roughly:

    Schema::create('meta', function ($table) {
        $table->increments('id');
        $table->integer('metable_id')->unsigned();
        $table->string('metable_type');
        $table->string('key');
        $table->string('value');
        $table->timestamps();
    });
    
  4. Add the trait to a model.

    class Post extends Eloquent {
        use \Mmanos\Metable\MetaTrait;
    }
    
  5. First use case — arbitrary attributes on a Post:

    $post = Post::find(1);
    $post->meta->seo_title = 'My Post';
    $post->meta->canonical = 'https://example.com/my-post';
    $post->meta->save();
    
    echo $post->meta->seo_title; // "My Post"
    

Look first at MetaTrait, the MetaCollection class (where save(), get(), attach()/detach() live), and the migration — those three files define the entire surface area you'll use day to day.

Implementation Patterns

Read/write through the meta collection. Treat $model->meta like a magic key-value bag: assign properties to write, call ->save() on the collection (not the model), and read properties back. This is the pattern you'll use for settings pages, SEO fields, feature flags, and per-record config.

$post->meta->og_image = $url;
$post->meta->save();

Use attach/detach for explicit lifecycle. When you don't want to load the whole collection, the relation methods let you add or remove single keys:

$post->meta()->attach('theme', 'dark');
$post->meta()->detach('theme');

Detaching is cleaner than unset($post->meta->theme); $post->meta->save(); when you only care about one key.

Eager-load on index pages. meta is a morphMany relation; every model you loop over without eager loading fires a query.

$posts = Post::with('meta')->paginate(25);

Query by meta value. Because meta is a relation you can use whereHas for filtered listings (e.g., "all posts flagged as featured"):

Post::whereHas('meta', function ($q) {
    $q->where('key', 'featured')->where('value', '1');
})->get();

Wrap access in your own accessors. The package exposes raw strings only. If you need typed values, add helpers on the model:

public function getFeaturedAttribute()
{
    return filter_var($this->meta->featured, FILTER_VALIDATE_BOOLEAN);
}

public function setFeaturedAttribute($value)
{
    $this->meta->featured = $value ? '1' : '0';
}

Hydrate a settings array from a form. A common workflow is a settings form with dynamic fields. Loop over the request keys, assign to $model->meta, then ->save() once at the end — one write per key, but a single logical operation.

foreach ($request->only(['seo_title', 'seo_description']) as $k => $v) {
    $model->meta->{$k} = $v;
}
$model->meta->save();

Cache or memoize per request. The collection is loaded lazily and doesn't cache across calls, so on hot paths (rendering layouts, API serialization) memoize your own copy on the model or in a request-scoped repository.

Gotchas and Tips

  • Laravel 4 only. The last release is 2015 and the Composer constraints reflect that. On Laravel 5+ either fork and relax the constraint, or migrate to a maintained alternative: plank/laravel-metable (direct successor) or spatie/laravel-schemaless-attributes. Budget for this before committing.
  • value is string(255). Anything longer — JSON, HTML, long descriptions — silently truncates. Change the column to text in your migration before you write real data.
  • Everything is a string. Booleans come back as "1"/"0" (or null), numbers as strings. Cast in accessors; don't trust $post->meta->price > 100.
  • Model must be saved first. Meta attaches via metable_id, so $post->meta->x = 'y' on an unsaved model fails or attaches to id = 0. Save the parent first, then attach meta.
  • save() belongs to the collection. $post->save() won't persist meta, and $post->meta->save() won't touch the parent model's updated_at. If you need timestamp bumps, do both explicitly.
  • No uniqueness constraint by default. Duplicate keys are possible if you use attach carelessly. Add unique(['metable_id', 'metable_type', 'key']) to the migration, and normalize key names.
  • metable_type is a short string. Namespaced model classes (App\Models\Post) eat into the 255-char budget fast; if you use long namespaces, widen it or store a morph map alias.
  • N+1 is easy to hit. Any foreach over models that reads ->meta is a query per row. Always with('meta') in listings, especially for JSON/API responses.
  • Deletes don't cascade. Deleting the parent model leaves orphaned meta rows unless you add an observer/hook or foreign-key cascade. Add a deleting observer if you care.
  • Debugging: dump $post->meta->toArray() (or the underlying relation) when a key "isn't saving" — usually it's a truncation, a case mismatch on the key, or a missing save().
  • Extension points: subclass Meta if you need casts or a custom table; override metaTrait methods in your model to add defaults, filtering, or audit logging. There are no events or config knobs in the package itself — anything beyond the basic CRUD you build yourself.
  • Testing: use SQLite :memory: and load the migration in your base test case; the package has no test helpers of its own, so seed meta directly in fixtures rather than relying on factories.
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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus