centrex / laravel-model-data
Add virtual columns in any model of laravel
v1.1.1
2026-04-11 11:07 UTC
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^2.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.5
- orchestra/testbench: ^9.5
- pestphp/pest: ^3.4
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- rector/rector: ^1.2
- spatie/laravel-ray: ^1.26
This package is auto-updated.
Last update: 2026-04-22 05:58:48 UTC
README
Serialize arbitrary attributes that don’t have dedicated columns into a JSON data field stored in a polymorphic model_datas table.
This package allows you to:
- Store flexible, schema-less data per model
- Access virtual attributes like native Eloquent properties
- Keep your core tables clean while supporting dynamic fields
- Use deterministic 32-character keys for efficient indexing
✨ Features
- Transparent virtual attributes ($model->color)
- External storage via polymorphic model_datas table
- Deterministic 32-char hashed keys (md5)
- Supports multiple data namespaces via data_type
- Fully compatible with Eloquent casting
- Query support for JSON attributes
📦 Installation
composer require centrex/laravel-model-data
php artisan vendor:publish --tag="model-data-migrations"
php artisan migrate
Usage
1. Add the trait to your model
use Centrex\ModelData\Concerns\HasModelData; class Product extends Model { use HasModelData; }
2. Virtual Attributes (Transparent Mode)
$product = Product::create(['name' => 'Widget']); // Assign attributes that don't exist as columns $product->color = 'red'; $product->weight = 1.5; $product->is_featured = true; $product->save();
// Retrieve — fully transparent, works like regular attributes
$product = Product::find(1); echo $product->color; // red echo $product->weight; // 1.5 echo $product->is_featured; // true
3. Structured Data (Explicit Mode)
You can also store grouped data explicitly:
$product->putData('settings', [ 'currency' => 'USD', 'stock_alert' => true, ]); $product->putData('seo', [ 'title' => 'Best Product', 'description' => 'Top quality item', ]);
Retrieve
$settings = $product->getData('settings'); $currency = $product->getDataValue('settings', 'currency');
4. Check / Delete
$product->hasData('settings'); // true $product->forgetData('settings'); // deletes record
5. Query Virtual Attributes
$column = (new Product())->getColumnForQuery('color'); Product::whereRaw("{$column} = ?", ['red'])->get();
6. Casting Support
Works seamlessly with Laravel casts:
protected $casts = [ 'is_featured' => 'boolean', 'tags' => 'array', ];
⚙️ How It Works
Storage Model
Each record in model_datas represents:
model_type + model_id + data_type → unique key (md5)
Example:
Product|15|settings → md5 → 32-char key
Data Structure
{
"color": "red",
"weight": 1.5,
"is_featured": true
}
🧠 Design Modes
1. Transparent Mode (default)
- Works like native attributes
- Best for dynamic fields
$product->color = 'red';
2. Explicit Mode (recommended for structure)
- Namespaced data storage
- Better for large systems (ERP, SaaS)
$product->putData('settings', [...]);
🏗️ Advanced Usage
Multiple Data Types
$product->putData('pricing', [...]); $product->putData('inventory', [...]); $product->putData('analytics', [...]);
Each stored separately but linked to the same model.
Access Nested Values
$product->getDataValue('settings', 'notifications.email');
⚡ Performance Notes
- Primary key is fixed 32-char string
- Indexed for fast lookup
- No joins required for simple access
- JSON queries supported natively (MySQL / PostgreSQL)
⚠️ Best Practices
- Prefer explicit mode for complex systems
- Avoid overloading with deeply nested JSON
- Keep frequently queried fields as real columns
🧪 Testing
composer test # full suite composer test:unit # pest only composer test:types # phpstan composer lint # pint
🔄 Migration Strategy (from inline JSON)
If you're migrating from a data column:
- Move JSON to model_datas
- Drop old column
- Add trait
- Done
Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
License
The MIT License (MIT). Please see License File for more information.