From 6d9550c10446172d81d257353d45f6f95a0811ee Mon Sep 17 00:00:00 2001 From: Andrew Bashtannik Date: Sat, 2 Apr 2022 17:47:20 +0300 Subject: [PATCH 1/3] Fix support of nullable type support for AsArrayObject/AsCollection --- .../Database/Eloquent/Casts/AsArrayObject.php | 4 +- .../Database/Eloquent/Casts/AsCollection.php | 4 +- .../Database/DatabaseCustomCastsTest.php | 107 +++++++++++++++++- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php b/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php index db9a21b461ba..504c02aff7b2 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) : new ArrayObject(); } 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..9b98b980307c 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) : new Collection(); } public function set($model, $key, $value, $attributes) diff --git a/tests/Integration/Database/DatabaseCustomCastsTest.php b/tests/Integration/Database/DatabaseCustomCastsTest.php index ec1a73d7fe1d..2ad3961b23ad 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,82 @@ 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); + $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'] = 'Taylor'; + $model->array_object['meta']['title'] = 'Developer'; + + $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 +142,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, ]; From 89c093f86ba0916724713208ee3b21194fba3e78 Mon Sep 17 00:00:00 2001 From: Andrew Bashtannik Date: Sat, 2 Apr 2022 18:02:53 +0300 Subject: [PATCH 2/3] Apply fixes from StyleCI --- tests/Integration/Database/DatabaseCustomCastsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Integration/Database/DatabaseCustomCastsTest.php b/tests/Integration/Database/DatabaseCustomCastsTest.php index 2ad3961b23ad..16551bb5da97 100644 --- a/tests/Integration/Database/DatabaseCustomCastsTest.php +++ b/tests/Integration/Database/DatabaseCustomCastsTest.php @@ -49,7 +49,7 @@ public function test_custom_casting() $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); + $this->assertSame('Taylor', (string) $model->stringable); $model->array_object['age'] = 34; $model->array_object['meta']['title'] = 'Developer'; @@ -85,7 +85,7 @@ public function test_custom_casting_nullable_values() $model = new TestEloquentModelWithCustomCastsNullable(); $model->array_object = null; - $model->array_object_json = null; + $model->array_object_json = null; $model->collection = collect(); $model->stringable = null; @@ -96,7 +96,7 @@ public function test_custom_casting_nullable_values() $this->assertEmpty($model->array_object); $this->assertEmpty($model->array_object_json); $this->assertEmpty($model->collection); - $this->assertSame('', (string)$model->stringable); + $this->assertSame('', (string) $model->stringable); $model->array_object['name'] = 'Taylor'; $model->array_object['meta']['title'] = 'Developer'; From 156773c878cc79bee99c66dc95424786ac8ee790 Mon Sep 17 00:00:00 2001 From: Andrew Bashtannik Date: Mon, 4 Apr 2022 09:12:29 +0300 Subject: [PATCH 3/3] Return null instead of empty array object or collection for non-array --- src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php | 2 +- src/Illuminate/Database/Eloquent/Casts/AsCollection.php | 2 +- tests/Integration/Database/DatabaseCustomCastsTest.php | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php b/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php index 504c02aff7b2..d6a20d42c827 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsArrayObject.php @@ -21,7 +21,7 @@ public function get($model, $key, $value, $attributes) { $data = json_decode($attributes[$key], true); - return is_array($data) ? new ArrayObject($data) : new ArrayObject(); + 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 9b98b980307c..82bf945af744 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsCollection.php @@ -22,7 +22,7 @@ public function get($model, $key, $value, $attributes) { $data = json_decode($attributes[$key], true); - return is_array($data) ? new Collection($data) : new Collection(); + 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 16551bb5da97..aa050f7db538 100644 --- a/tests/Integration/Database/DatabaseCustomCastsTest.php +++ b/tests/Integration/Database/DatabaseCustomCastsTest.php @@ -98,9 +98,11 @@ public function test_custom_casting_nullable_values() $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';