Search code examples
laravel

Laravel issue with extras field


I'm having some issues with the extras field in my laravel model, this seems to be preventing the model for getting saved.

Basically anything that I put in the extras field (which is a json array) it automatically creates a fake attribute for each item in the array. Generally I don't mind this however on save it complains about not being able to find these fake attributes when I save the model.

See example model here:

#attributes: array:13 [▼
    "id" => 3
    "created_at" => "2023-06-09 04:20:07"
    "updated_at" => "2023-06-10 06:01:10"
    "name" => "TEST123"
    "description" => null
    "active_flag" => 1
    "read_only_flag" => 0
    "extras" => "{"test": "thing", "last_sync": {"time": "Sat, 10 Jun 2023 06:01:09 +0000", "count": 59, "result": "success"}}"
    "options" => "{"group_type": "StudentController", "import_users": "1", "student_import_StudentHouse": null, "student_import_StudentCampus": null, "student_import_StudentBoarder": null, "student_import_StudentYearLevel": ["9"], "student_import_StudentStatusDescription": null} ◀"
    "integration_id" => null
    "test" => "thing"
    "last_sync" => array:3 [▼
      "time" => "Sat, 10 Jun 2023 06:01:09 +0000"
      "count" => 59
      "result" => "success"
    ]
]

This is the code I'm using to save the model and the relevant model code.

The fields 'test' and 'last_sync' are just items in array and not actual DB columns.

Other than populating the array I don't think I did anything different to my other json items.

I read somewhere that "extras" was a special type of column but I don't remember where I saw this...

class Group extends Model
{
    use \Backpack\CRUD\app\Models\Traits\CrudTrait;
    use HasFactory, HasRoles;
    protected $guard_name = 'web';

    protected $fillable = ['name', 'description', 'updated_at', 'created_at', 'active_flag', 'extras', 'options','controller'];
    protected $casts = [
        'extras' => 'array',
        'options' => 'array'
    ];
$object->extras =  array_merge($object->extras ?? [], ['last_sync' => $report]);
                
            $object->save();
        Schema::create('groups', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name', 50);
            $table->string('description', 255)->nullable();
            $table->boolean('active_flag')->default(1);
            $table->boolean('read_only_flag')->default(0);
            $table->json('extras')->nullable();
            $table->json('options')->nullable();
            $table->string('controller')->nullable();
            $table->string('integration_id')->nullable();
        });

Has anyone come across this before?

Thanks!


Solution

  • This is a feature of Backpack - a field named extras is handled in a special way. From the documentation:

    Optional - Fake Field Attributes (stores fake attributes as JSON in the database)

    In case you want to store information for an entry that doesn't need a separate database column, you can add any number of Fake Fields, and their information will be stored inside a column in the db, as JSON. By default, an extras column is used and assumed on the database table, but you can change that.

    Step 1. Use the fake attribute on your field:

    [
        'name'     => 'name', // JSON variable name
        'label'    => "Tag Name", // human-readable label for the input
    
        'fake'     => true, // show the field, but don't store it in the database column above
        'store_in' => 'extras' // [optional] the database column name where you want the fake fields to ACTUALLY be stored as a JSON array 
    ],
    

    Step 2. On your model, make sure the db columns where you store the JSONs (by default only extras):

    • are in your $fillable property;
    • are on a new $fakeColumns property (create it now);
    • are cast as array in $casts;

    The simplest thing you can do is either change the name of Backpack's automated extras field (the store_in value in the snippet above), or change the column name from extras to something else.