Skip to content

Instantly share code, notes, and snippets.

@DominikStyp
Last active January 5, 2022 10:49
Show Gist options
  • Save DominikStyp/e13a15a0a0a9fd693b0fed92db7f9bc5 to your computer and use it in GitHub Desktop.
Save DominikStyp/e13a15a0a0a9fd693b0fed92db7f9bc5 to your computer and use it in GitHub Desktop.
Laravel 8: Database JSON fields (migration, casts, feature tests, update JSON attributes in DB)
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddColumnOptionsToSomethingTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('something', function (Blueprint $table) {
$table->json('options');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('something', function (Blueprint $table) {
$table->dropColumn('options');
});
}
}
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Something extends Model {
// if JSON field contains object use 'object' type
// if JSON field is array -> use 'array' type
protected $casts = [
'stats' => 'object'
];
// IMPORTANT: default JSON attributes cannot be null or empty string
protected $attributes = [
'stats' => '{}'
];
}
<?php
declare(strict_types=1);
use Tests\TestCase;
use App\Models\Something;
class TestMe extends TestCase {
public function testMe() {
$options = new \stdClass();
$options->nested1 = new \stdClass();
$options->nested1->myStr = "myStr";
$options->nested2 = [1, 5, 10];
$model = Something::find(1);
$model->options = $options; // do not encode to JSON here if we already have a cast in model
$model->save();
$model->refresh();
$this->assertEquals(10, $model->options->nested2[2]);
$this->assertEquals("myStr", $model->options->nested1->myStr);
}
/**
* WARNING! DO NOT MUTATE JSON PROPERTY LIKE YOU WOULD MUTATE MODEL ATTRIBUTE
*
*/
public function testMutateJSONAttributes()
{
/** @var Listing $listing */
$model = Something::find(1);
$oneChange = new \stdClass;
$oneChange->statusChange = "changed status 123";
if (empty($model->stats) || !is_object($model->stats)) {
$stats = new \stdClass;
$stats->statusChanges = [
$oneChange
];
// TO UPDATE JSON FIELD ON MODEL USE forceFill
// due to: https://github.com/laravel/framework/issues/15433
$model->forceFill(['stats' => $stats]);
} else {
// WARNING: following mutation WON'T WORK
// $model->stats->statusChanges[] = $oneChange
// you have to mutate the JSON attributes by copy
$changes = $model->stats->statusChanges;
$changes[] = $oneChange;
$model->forceFill(['stats->statusChanges' => $changes]);
}
$this->assertTrue($model->save());
$model->refresh();
dump($model->stats->statusChanges); // you should see the reflected changes in JSON
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment