diff --git a/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php b/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php index db9a21b461ba..d6a20d42c827 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php @@ -19,7 +19,9 @@ public static function castUsing(array $arguments) { public function get($model, $key, $value, $attributes) { - return isset($attributes[$key]) ? new ArrayObject(json_decode($attributes[$key], true)) : null; + $data = json_decode($attributes[$key], true); + + return is_array($data) ? new ArrayObject($data) : null; } public function set($model, $key, $value, $attributes) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsCollection.php index 585b6cfc7012..82bf945af744 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsCollection.php @@ -20,7 +20,9 @@ public static function castUsing(array $arguments) { public function get($model, $key, $value, $attributes) { - return isset($attributes[$key]) ? new Collection(json_decode($attributes[$key], true)) : null; + $data = json_decode($attributes[$key], true); + + return is_array($data) ? new Collection($data) : null; } public function set($model, $key, $value, $attributes) diff --git a/tests/Integration/Database/DatabaseCustomCastsTest.php b/tests/Integration/Database/DatabaseCustomCastsTest.php index ec1a73d7fe1d..aa050f7db538 100644 --- a/tests/Integration/Database/DatabaseCustomCastsTest.php +++ b/tests/Integration/Database/DatabaseCustomCastsTest.php @@ -17,10 +17,20 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() Schema::create('test_eloquent_model_with_custom_casts', function (Blueprint $table) { $table->increments('id'); $table->text('array_object'); + $table->json('array_object_json'); $table->text('collection'); $table->string('stringable'); $table->timestamps(); }); + + Schema::create('test_eloquent_model_with_custom_casts_nullables', function (Blueprint $table) { + $table->increments('id'); + $table->text('array_object')->nullable(); + $table->json('array_object_json')->nullable(); + $table->text('collection')->nullable(); + $table->string('stringable')->nullable(); + $table->timestamps(); + }); } public function test_custom_casting() @@ -28,6 +38,7 @@ public function test_custom_casting() $model = new TestEloquentModelWithCustomCasts; $model->array_object = ['name' => 'Taylor']; + $model->array_object_json = ['name' => 'Taylor']; $model->collection = collect(['name' => 'Taylor']); $model->stringable = Str::of('Taylor'); @@ -36,21 +47,84 @@ public function test_custom_casting() $model = $model->fresh(); $this->assertEquals(['name' => 'Taylor'], $model->array_object->toArray()); + $this->assertEquals(['name' => 'Taylor'], $model->array_object_json->toArray()); $this->assertEquals(['name' => 'Taylor'], $model->collection->toArray()); $this->assertSame('Taylor', (string) $model->stringable); $model->array_object['age'] = 34; $model->array_object['meta']['title'] = 'Developer'; + $model->array_object_json['age'] = 34; + $model->array_object_json['meta']['title'] = 'Developer'; + + $model->save(); + + $model = $model->fresh(); + + $this->assertEquals( + [ + 'name' => 'Taylor', + 'age' => 34, + 'meta' => ['title' => 'Developer'], + ], + $model->array_object->toArray() + ); + + $this->assertEquals( + [ + 'name' => 'Taylor', + 'age' => 34, + 'meta' => ['title' => 'Developer'], + ], + $model->array_object_json->toArray() + ); + } + + public function test_custom_casting_nullable_values() + { + $model = new TestEloquentModelWithCustomCastsNullable(); + + $model->array_object = null; + $model->array_object_json = null; + $model->collection = collect(); + $model->stringable = null; + + $model->save(); + + $model = $model->fresh(); + + $this->assertEmpty($model->array_object); + $this->assertEmpty($model->array_object_json); + $this->assertEmpty($model->collection); + $this->assertSame('', (string) $model->stringable); + + $model->array_object = ['name' => 'John']; + $model->array_object['name'] = 'Taylor'; + $model->array_object['meta']['title'] = 'Developer'; + + $model->array_object_json = ['name' => 'John']; + $model->array_object_json['name'] = 'Taylor'; + $model->array_object_json['meta']['title'] = 'Developer'; + $model->save(); $model = $model->fresh(); - $this->assertEquals([ - 'name' => 'Taylor', - 'age' => 34, - 'meta' => ['title' => 'Developer'], - ], $model->array_object->toArray()); + $this->assertEquals( + [ + 'name' => 'Taylor', + 'meta' => ['title' => 'Developer'], + ], + $model->array_object->toArray() + ); + + $this->assertEquals( + [ + 'name' => 'Taylor', + 'meta' => ['title' => 'Developer'], + ], + $model->array_object_json->toArray() + ); } } @@ -70,6 +144,29 @@ class TestEloquentModelWithCustomCasts extends Model */ protected $casts = [ 'array_object' => AsArrayObject::class, + 'array_object_json' => AsArrayObject::class, + 'collection' => AsCollection::class, + 'stringable' => AsStringable::class, + ]; +} + +class TestEloquentModelWithCustomCastsNullable extends Model +{ + /** + * The attributes that aren't mass assignable. + * + * @var string[] + */ + protected $guarded = []; + + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'array_object' => AsArrayObject::class, + 'array_object_json' => AsArrayObject::class, 'collection' => AsCollection::class, 'stringable' => AsStringable::class, ];