Laravel-Metable

repository·master·Indexed 43 days ago

https://github.com/plank/laravel-metable

A Laravel package that allows developers to attach arbitrary metadata to Eloquent models using a one-to-many polymorphic relationship. It provides methods for attaching, retrieving, and querying metadata via setMeta, getMeta, and whereMeta, avoiding the need for schema changes. It supports various scalar and composite data types, including Eloquent models, collections, and Enums, and includes a SignedSerializeHandler for secure object storage.

Tokens
11.7K
Snippets
41
Records
57
Agent score
54%

What's inside Laravel-Metable

  1. Use Meta as Model Attributes with MetableAttributes

    master

    By adding the MetableAttributes trait to your model, you can access and manipulate meta values as if they were standard model attributes by prefixing them with meta_.

    Key Features:

    • Access/Set: $model->meta_key = 'value' is equivalent to setMeta('key', 'value').
    • Mass Assignment: $model->fill(['meta_key' => 'value']) works for setting multiple meta values.
    • Deletion: unset($model->meta_key) is equivalent to removeMeta('key').
    • Array Conversion: toArray() includes meta attributes by default. Use makeMetaHidden() to hide them.
    • IDE Support: Use PHPDoc @property annotations to enable autocomplete and static analysis for meta attributes.

    Note: The standard getAttributes() method does not include meta attributes. Use getMetaAttributes() to retrieve them.

    namespace App;
    
    use Plank\Metable\Metable;
    use Plank\Metable\MetableAttributes;
    use Illuminate\Database\Eloquent\Model;
    
    /**
     * @property bool $meta_approved
     * @property int $meta_likes
     */
    class Page extends Model
    {
        use Metable, MetableAttributes;
    
        protected $metaCasts = [
            'approved' => 'boolean',
            'likes' => 'integer',
        ];
    
        protected $defaultMetaValues = [
            'approved' => false,
            'likes' => 0,
        ];
    }
    
    $page = new Page();
    $page->meta_likes = 5; // Sets meta 'likes'
    $page->fill(['meta_approved' => true]); // Sets meta 'approved'
    
    if ($page->meta_likes > 0) { /* ... */ }
    
    unset($page->meta_likes); // Removes meta 'likes'
  2. Implement MetableInterface for generic model handling

    master
    If you need a generic way to reference different models that use the Metable functionality (for example, in a service or repository that handles multiple types of metable models), you can apply the Plank\Metable\MetableInterface to your model classes in addition to using the Metable trait.
  3. Store Complex Data with SignedSerializeHandler

    master

    To store objects and arrays, use the \Plank\Metable\DataType\SignedSerializeHandler. This handler uses PHP's serialize() function and cryptographically signs the value with an HMAC using your application's APP_KEY to prevent PHP object injection attacks.

    Security Configuration

    You can restrict which classes are allowed to be unserialized by specifying an array of class names in the metable.SignedSerializeHandlerAllowedClasses configuration key within config/metable.php.

    Important: Handler Ordering

    SignedSerializeHandler should generally be the last entry in your datatypes array in config/metable.php. Because it accepts data of any type, placing it earlier will cause it to intercept values intended for more specific handlers below it. However, handlers defined below it will still be used for unserializing existing data during transitions.

    // Storing an array
    $metable->setMeta('data', ['key' => 'value']);
    
    // Storing a value object
    $metable->setMeta('data', new MyValueObject(123));
  4. How Laravel-Metable works

    master

    Laravel-Metable uses a one-to-many polymorphic relationship to attach arbitrary data to Eloquent models. This allows you to store metadata without modifying your existing database schema.

    It includes a type conversion system that supports various scalar and object types for storage, querying, and retrieval.

  5. Attach, query, and retrieve metadata

    master

    Once the Plank\Metable\Metable trait is added to your model, you can use the following methods to manage metadata:

    Set metadata

    Use setMeta(key, value) to attach data to a model instance.

    Retrieve metadata

    Use getMeta(key) to fetch a specific value.

    Query by metadata

    Use the whereMeta(key, value) scope to filter models based on their attached metadata.

    // Attach metadata
    $post->setMeta('color', 'blue');
    
    // Retrieve metadata
    $value = $post->getMeta('color');
    
    // Query models by metadata
    $posts = Post::whereMeta('color', 'blue')->get();
  6. Attach, retrieve, and query metadata using Metable

    master

    Once the Plank\Metable\Metable trait is added to your Eloquent model, you can manage arbitrary metadata using the following methods:

    Attaching Metadata

    Use setMeta(key, value) to attach data to a model instance.

    Retrieving Metadata

    Use getMeta(key) to retrieve a stored value.

    Querying by Metadata

    Use the whereMeta(key, value) scope to filter models based on their metadata values.

    Note: The package uses a type conversion system to handle various scalar and object types automatically.

    // Attach metadata
    $post = Post::create($data);
    $post->setMeta('color', 'blue');
    
    // Retrieve metadata
    $value = $post->getMeta('color');
    
    // Query models by metadata
    $posts = Post::whereMeta('color', 'blue')->get();
  7. Customize the Meta model

    master

    You can customize the behavior of the Meta model by extending the base Meta class. There are two ways to apply these custom models to your entities:

    1. Global Customization: To use the same custom Meta subclass for every model using the Metable trait, register the fully-qualified class name in the metable.model configuration key.

    2. Entity-Specific Customization: To use different Meta subclasses for different entities (for example, to store metadata in separate tables), override the getMetaClassName() method within your model to return the specific class name you wish to use.

    class UserMeta extends Meta
    {
        protected $table = 'user_meta';
    }
    
    class ProductMeta extends Meta
    {
        protected $table = 'product_meta';
    }
    
    class User extends Model
    {
        use Metable;
    
        protected function getMetaClassName(): string
        {
            return UserMeta::class;
        }
    }
    
    class Product extends Model
    {
        use Metable;
    
        protected function getMetaClassName(): string
        {
            return ProductMeta::class;
        }
    }
  8. Eager Loading Meta to Avoid N+1 Issues

    master

    Since meta is stored in a separate relationship, you should eager load it when working with collections of models to prevent N+1 query problems.

    • Query Builder: Use with('meta').
    • Eloquent Collection: Use $models->load('meta').
    • Always Eager Load: Add 'meta' to the model's $with property to ensure it is always loaded.
    // Eager load from query
    $models = MyModel::with('meta')->get();
    
    // Lazy eager load from collection
    $models->load('meta');
    
    // Always eager load in the model definition
    class MyModel extends Model {
        use Metable;
        protected $with = ['meta'];
    }
  9. Enable Metable in an Eloquent Model

    master

    To use meta capabilities, you must add the Metable trait to your Eloquent model. This trait adds a meta() relationship to the model and enables specialized methods for managing metadata.

    Note: Do not modify the meta() relationship directly. Instead, use the provided setMeta, getMeta, and other management methods to ensure metadata is correctly indexed and cached.

    <?php
    
    namespace App;
    
    use Plank\
    Metable\\Metable;
    use Illuminate\\Database\\Eloquent\\Model;
    
    class Page extends Model
    {
        use Metable;
    
        // ...
    }
  10. Modify the Metable database schema

    master

    To customize the database schema used by the package, copy the base migration files provided by the package into your application's database/migration folder. If the filenames match exactly, your local migrations will take precedence and the package's default migrations will be ignored.

    If your customizations involve structural changes that might conflict with future package updates (such as renaming tables), disable automatic migrations by setting the metable.applyMigrations configuration key to false. Note that if you disable this, you will be responsible for manually applying any new migrations introduced in future major versions of the package.

    // In your config/metable.php
    'applyMigrations' => false,
  11. Upgrade from 3.X to 4.X

    master

    In version 4.X, migration files are served directly from the package. To upgrade:

    1. Locate your local migration file (e.g., XXXX_XX_XX_XXXXXX_create_meta_table.php) in database/migrations.
    2. Rename the entry in your migrations database table to 2017_01_01_000000_create_meta_table.php.
    3. Delete your local copy of the migration file from database/migrations.

    Note: If you made custom changes to the table structure in your local migration, you must implement those changes as separate ALTER table migrations.