Store strictly typed PHP objects in Eloquent JSON columns. Data class changes never invalidate stored data.
composer require raveren/eloquent-data-objectYour model column becomes a strictly typed object!
Any changes to your data classes in the future don't invalidate stored data!
Just extend EloquentDataObject (or see below if you can't):
<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Raveren\EloquentDataObject\EloquentDataObject;
class MyData extends EloquentDataObject ##### <---- Step 1/3: extend EloquentDataObject ######
{
public string $name;
/** @var MyData[] */
public array $children;
}
class MyModel extends Model
{
protected function casts(): array
{
return [
'json_data' => MyData::class ##### <---- Step 2/3: add Eloquent cast ######
];
}
}
Schema::table('my_table', function (Blueprint $table) {
$table->json('json_data')->nullable(); ##### <---- Step 3/3: create DB column ######
});🎉 Done!
Objects are stored as JSON inside the database.
In PHP $myModel->json_data is always an instance of MyData.
Thus, if you receive an associative-array representation, you must convert it to MyData before saving:
$myModel->json_data = MyData::from($array); // helper method comes in the box- No data is ever lost, and you get no errors (see 5. below).
- If you rename or remove a class attribute, existing data will be preserved as a Dynamic property with the old name on the class instance (no data loss).
- To fix drifted names inside the database, define a
renamedDataObjectColumns():
class MyData extends EloquentDataObject
{
public string $newName;
public static function renamedDataObjectColumns(): array // <----- This
{
return [
'name' => 'newName',
'children' => null, // Set new name to null to drop the value from storage on saving.
];
}
}- It's enough to load each model once and save it to fix the data inside the DB permanently, so you don't have to keep the
renamedDataObjectColumns()method if you do that. - Attributes that appear in database JSON, but are missing in class definitions issue
Log::error()with detailed information. You can provide your own error handler instead, example:
// app/Providers/AppServiceProvider.php
EloquentDataObject::setMissingAttributeHandler(
static fn(string $targetClassname, string $currentClassname, string $missingKey) => report(
new Exception(
sprintf(
'EloquentDataObject cast for %s has unrecognized property %s->%s. To solve, extend `renamedDataObjectColumns(): array` in your data class.',
$targetClassname,
$targetObject::class,
$key,
),
),
);
);- Add
use EloquentDataObjectTrait; - Add
implements Castable - Annotate with
#[AllowDynamicProperties](only if you renamed class attributes)
#[AllowDynamicProperties]
class MyData implements Castable
{
use EloquentDataObjectTrait;
// ...
}Hierarchical object conversion is handled by netresearch/jsonmapper (sole project dependency).
Unlicence, do whatever you want with it.
