From da40aa89db4c1ca2115941333979a5c6cd122617 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:04:49 +0100 Subject: [PATCH 01/22] Implement enum array casts --- .../Eloquent/Concerns/HasAttributes.php | 96 ++++++++++++++++++- .../Database/EloquentModelEnumCastingTest.php | 50 ++++++++++ tests/Integration/Database/Enums.php | 2 + 3 files changed, 145 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index d672f9ebb6fc..f6cc5c775318 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -314,6 +314,10 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($attributes[$key]) : null; } + if ($this->isEnumArrayCastable($key)) { + $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumArrayValue($attributes[$key]) : null; + } + if ($attributes[$key] instanceof Arrayable) { $attributes[$key] = $attributes[$key]->toArray(); } @@ -789,6 +793,10 @@ protected function castAttribute($key, $value) return $this->getEnumCastableAttributeValue($key, $value); } + if ($this->isEnumArrayCastable($key)) { + return $this->getEnumArrayCastableAttributeValue($key, $value); + } + if ($this->isClassCastable($key)) { return $this->getClassCastableAttributeValue($key, $value); } @@ -846,6 +854,26 @@ protected function getEnumCastableAttributeValue($key, $value) return $this->getEnumCaseFromValue($castType, $value); } + /** + * Cast the given attribute to an array of enums. + * + * @param string $key + * @param mixed $value + * @return mixed + */ + protected function getEnumArrayCastableAttributeValue($key, $value) + { + if (is_null($value)) { + return; + } + + $castType = Str::before($this->getCasts()[$key], ':array'); + + $value = is_array($value) ? $value : $this->fromJson($value); + + return array_map(fn (string $value) => $this->getEnumCaseFromValue($castType, $value), $value); + } + /** * Get the type of cast for a model attribute. * @@ -968,6 +996,12 @@ public function setAttribute($key, $value) return $this; } + if ($this->isEnumArrayCastable($key)) { + $this->setEnumArrayCastableAttribute($key, $value); + + return $this; + } + if ($this->isClassCastable($key)) { $this->setClassCastableAttribute($key, $value); @@ -1155,6 +1189,25 @@ protected function setEnumCastableAttribute($key, $value) } else { $this->attributes[$key] = $this->getStorableEnumValue( $this->getEnumCaseFromValue($enumClass, $value) + + /** + * Set the value of an enum array castable attribute. + * + * @param string $key + * @param \UnitEnum|string|int $value + * @return void + */ + protected function setEnumArrayCastableAttribute($key, $value) + { + $enumClass = Str::before($this->getCasts()[$key], ':array'); + + if (! isset($value)) { + $this->attributes[$key] = null; + } else { + $value = is_array($value) ? $value : $this->fromJson($value); + + $this->attributes[$key] = $this->getStorableEnumArrayValue( + array_map(fn ($value) => is_object($value) ? $value : $this->getEnumCaseFromValue($enumClass, $value), $value) ); } } @@ -1184,6 +1237,15 @@ protected function getStorableEnumValue($value) return $value instanceof BackedEnum ? $value->value : $value->name; + /** + * Get the storable value from the given enum. + * + * @param array $value + * @return string + */ + protected function getStorableEnumArrayValue($value) + { + return json_encode(array_map(fn ($value) => $this->getStorableEnumValue($value), $value)); } /** @@ -1620,6 +1682,33 @@ protected function isEnumCastable($key) } } + /** + * Determine if the given key is cast using an array of enums. + * + * @param string $key + * @return bool + */ + protected function isEnumArrayCastable($key) + { + $casts = $this->getCasts(); + + if (! array_key_exists($key, $casts)) { + return false; + } + + $castType = $casts[$key]; + + if (in_array($castType, static::$primitiveCastTypes)) { + return false; + } + + if (! Str::endsWith($castType, ':array')) { + return false; + } + + return function_exists('enum_exists') && enum_exists(Str::before($castType, ':array')); + } + /** * Determine if the key is deviable using a custom class. * @@ -1649,9 +1738,10 @@ protected function isClassDeviable($key) */ protected function isClassSerializable($key) { - return ! $this->isEnumCastable($key) && - $this->isClassCastable($key) && - method_exists($this->resolveCasterClass($key), 'serialize'); + return ! $this->isEnumCastable($key) + && ! $this->isEnumArrayCastable($key) + && $this->isClassCastable($key) + && method_exists($this->resolveCasterClass($key), 'serialize'); } /** diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 3eb72aad6828..a3faf4afab63 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -21,7 +21,9 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() Schema::create('enum_casts', function (Blueprint $table) { $table->increments('id'); $table->string('string_status', 100)->nullable(); + $table->json('string_status_array', 100)->nullable(); $table->integer('integer_status')->nullable(); + $table->json('integer_status_array')->nullable(); $table->string('arrayable_status')->nullable(); }); } @@ -30,14 +32,18 @@ public function testEnumsAreCastable() { DB::table('enum_casts')->insert([ 'string_status' => 'pending', + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ]); $model = EloquentModelEnumCastingTestModel::first(); $this->assertEquals(StringStatus::pending, $model->string_status); + $this->assertEquals([StringStatus::pending, StringStatus::done], $model->string_status_array); $this->assertEquals(IntegerStatus::pending, $model->integer_status); + $this->assertEquals([IntegerStatus::pending, IntegerStatus::done], $model->integer_status_array); $this->assertEquals(ArrayableStatus::pending, $model->arrayable_status); } @@ -45,14 +51,18 @@ public function testEnumsReturnNullWhenNull() { DB::table('enum_casts')->insert([ 'string_status' => null, + 'string_status_array' => null, 'integer_status' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); $model = EloquentModelEnumCastingTestModel::first(); $this->assertEquals(null, $model->string_status); + $this->assertEquals(null, $model->string_status_array); $this->assertEquals(null, $model->integer_status); + $this->assertEquals(null, $model->integer_status_array); $this->assertEquals(null, $model->arrayable_status); } @@ -60,13 +70,17 @@ public function testEnumsAreCastableToArray() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, + 'string_status_array' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, + 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); $this->assertEquals([ 'string_status' => 'pending', + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => [ 'name' => 'pending', 'value' => 'pending', @@ -79,13 +93,17 @@ public function testEnumsAreCastableToArrayWhenNull() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, + 'string_status_array' => null, 'integer_status' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); $this->assertEquals([ 'string_status' => null, + 'string_status_array' => null, 'integer_status' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ], $model->toArray()); } @@ -94,7 +112,9 @@ public function testEnumsAreConvertedOnSave() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, + 'string_status_array' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, + 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); @@ -103,7 +123,31 @@ public function testEnumsAreConvertedOnSave() $this->assertEquals((object) [ 'id' => $model->id, 'string_status' => 'pending', + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, + 'integer_status_array' => json_encode([1, 2]), + 'arrayable_status' => 'pending', + ], DB::table('enum_casts')->where('id', $model->id)->first()); + } + + public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() + { + $model = new EloquentModelEnumCastingTestModel([ + 'string_status' => 'pending', + 'string_status_array' => ['pending', 'done'], + 'integer_status' => 1, + 'integer_status_array' => [1, 2], + 'arrayable_status' => 'pending', + ]); + + $model->save(); + + $this->assertEquals((object) [ + 'id' => $model->id, + 'string_status' => 'pending', + 'string_status_array' => json_encode(['pending', 'done']), + 'integer_status' => 1, + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ], DB::table('enum_casts')->where('id', $model->id)->first()); } @@ -112,7 +156,9 @@ public function testEnumsAcceptNullOnSave() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, + 'string_status_array' => null, 'integer_status' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); @@ -121,7 +167,9 @@ public function testEnumsAcceptNullOnSave() $this->assertEquals((object) [ 'id' => $model->id, 'string_status' => null, + 'string_status_array' => null, 'integer_status' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ], DB::table('enum_casts')->where('id', $model->id)->first()); } @@ -195,7 +243,9 @@ class EloquentModelEnumCastingTestModel extends Model public $casts = [ 'string_status' => StringStatus::class, + 'string_status_array' => StringStatus::class.':array', 'integer_status' => IntegerStatus::class, + 'integer_status_array' => IntegerStatus::class.':array', 'arrayable_status' => ArrayableStatus::class, ]; } diff --git a/tests/Integration/Database/Enums.php b/tests/Integration/Database/Enums.php index fc466716533a..f3bf199a16dc 100644 --- a/tests/Integration/Database/Enums.php +++ b/tests/Integration/Database/Enums.php @@ -6,12 +6,14 @@ enum StringStatus: string { + case draft = 'draft'; case pending = 'pending'; case done = 'done'; } enum IntegerStatus: int { + case draft = 0; case pending = 1; case done = 2; } From 24feb8800fc5d0b977d0b6fe16bd0839de3e13e8 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:07:54 +0100 Subject: [PATCH 02/22] Update typehint, fix merge error --- .../Eloquent/Concerns/HasAttributes.php | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index f6cc5c775318..35f4e54b80bb 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1180,21 +1180,22 @@ protected function setClassCastableAttribute($key, $value) */ protected function setEnumCastableAttribute($key, $value) { - $enumClass = $this->getCasts()[$key]; - - if (! isset($value)) { - $this->attributes[$key] = null; - } elseif (is_object($value)) { - $this->attributes[$key] = $this->getStorableEnumValue($value); - } else { - $this->attributes[$key] = $this->getStorableEnumValue( - $this->getEnumCaseFromValue($enumClass, $value) + $enumClass = $this->getCasts()[$key]; + + if ( !isset($value) ) { + $this->attributes[$key] = null; + } elseif ( is_object($value) ) { + $this->attributes[$key] = $this->getStorableEnumValue($value); + } else { + $this->attributes[$key] = $this->getStorableEnumValue($this->getEnumCaseFromValue($enumClass, $value)); + } + } /** * Set the value of an enum array castable attribute. * * @param string $key - * @param \UnitEnum|string|int $value + * @param array|string $value * @return void */ protected function setEnumArrayCastableAttribute($key, $value) @@ -1234,9 +1235,9 @@ protected function getEnumCaseFromValue($enumClass, $value) */ protected function getStorableEnumValue($value) { - return $value instanceof BackedEnum - ? $value->value - : $value->name; + return $value instanceof BackedEnum ? $value->value : $value->name; + } + /** * Get the storable value from the given enum. * From bf614fedaaf8cad4a922b484a890620c96f39106 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:08:48 +0100 Subject: [PATCH 03/22] Style --- .../Eloquent/Concerns/HasAttributes.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 35f4e54b80bb..3334568ee2cc 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1180,15 +1180,15 @@ protected function setClassCastableAttribute($key, $value) */ protected function setEnumCastableAttribute($key, $value) { - $enumClass = $this->getCasts()[$key]; - - if ( !isset($value) ) { - $this->attributes[$key] = null; - } elseif ( is_object($value) ) { - $this->attributes[$key] = $this->getStorableEnumValue($value); - } else { - $this->attributes[$key] = $this->getStorableEnumValue($this->getEnumCaseFromValue($enumClass, $value)); - } + $enumClass = $this->getCasts()[$key]; + + if (! isset($value)) { + $this->attributes[$key] = null; + } elseif (is_object($value)) { + $this->attributes[$key] = $this->getStorableEnumValue($value); + } else { + $this->attributes[$key] = $this->getStorableEnumValue($this->getEnumCaseFromValue($enumClass, $value)); + } } /** @@ -1235,9 +1235,9 @@ protected function getEnumCaseFromValue($enumClass, $value) */ protected function getStorableEnumValue($value) { - return $value instanceof BackedEnum ? $value->value : $value->name; + return $value instanceof BackedEnum ? $value->value : $value->name; } - + /** * Get the storable value from the given enum. * @@ -1740,9 +1740,9 @@ protected function isClassDeviable($key) protected function isClassSerializable($key) { return ! $this->isEnumCastable($key) - && ! $this->isEnumArrayCastable($key) - && $this->isClassCastable($key) - && method_exists($this->resolveCasterClass($key), 'serialize'); + && ! $this->isEnumArrayCastable($key) + && $this->isClassCastable($key) + && method_exists($this->resolveCasterClass($key), 'serialize'); } /** From c3f1e7786f8973ee8ed8956de966d2709cf1ffcb Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:09:48 +0100 Subject: [PATCH 04/22] Update HasAttributes.php --- src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 3334568ee2cc..02ef7b634385 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1187,7 +1187,9 @@ protected function setEnumCastableAttribute($key, $value) } elseif (is_object($value)) { $this->attributes[$key] = $this->getStorableEnumValue($value); } else { - $this->attributes[$key] = $this->getStorableEnumValue($this->getEnumCaseFromValue($enumClass, $value)); + $this->attributes[$key] = $this->getStorableEnumValue( + $this->getEnumCaseFromValue($enumClass, $value) + ); } } From f5859eb3acc8180967b10f447f09713ed41b5d4c Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:10:31 +0100 Subject: [PATCH 05/22] Style --- .../Database/Eloquent/Concerns/HasAttributes.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 02ef7b634385..e31464007595 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1237,7 +1237,9 @@ protected function getEnumCaseFromValue($enumClass, $value) */ protected function getStorableEnumValue($value) { - return $value instanceof BackedEnum ? $value->value : $value->name; + return $value instanceof BackedEnum + ? $value->value + : $value->name; } /** @@ -1248,7 +1250,9 @@ protected function getStorableEnumValue($value) */ protected function getStorableEnumArrayValue($value) { - return json_encode(array_map(fn ($value) => $this->getStorableEnumValue($value), $value)); + return json_encode( + array_map(fn ($value) => $this->getStorableEnumValue($value), $value) + ); } /** From 0ea419eb6e123698ed7bcee471369a96c1f63d51 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 12 Jan 2023 14:11:02 +0100 Subject: [PATCH 06/22] Style --- .../Database/Eloquent/Concerns/HasAttributes.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index e31464007595..7a9f8822d2f9 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1188,7 +1188,7 @@ protected function setEnumCastableAttribute($key, $value) $this->attributes[$key] = $this->getStorableEnumValue($value); } else { $this->attributes[$key] = $this->getStorableEnumValue( - $this->getEnumCaseFromValue($enumClass, $value) + $this->getEnumCaseFromValue($enumClass, $value) ); } } @@ -1238,8 +1238,8 @@ protected function getEnumCaseFromValue($enumClass, $value) protected function getStorableEnumValue($value) { return $value instanceof BackedEnum - ? $value->value - : $value->name; + ? $value->value + : $value->name; } /** @@ -1251,7 +1251,7 @@ protected function getStorableEnumValue($value) protected function getStorableEnumArrayValue($value) { return json_encode( - array_map(fn ($value) => $this->getStorableEnumValue($value), $value) + array_map(fn ($value) => $this->getStorableEnumValue($value), $value) ); } From d5a7aebe9ef6bf2c6ef4f141b8f9ebdd30f534de Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Sat, 14 Jan 2023 13:59:13 +0100 Subject: [PATCH 07/22] Remove unnecessary param --- tests/Integration/Database/EloquentModelEnumCastingTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index a3faf4afab63..2ab8efd3f996 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -21,7 +21,7 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() Schema::create('enum_casts', function (Blueprint $table) { $table->increments('id'); $table->string('string_status', 100)->nullable(); - $table->json('string_status_array', 100)->nullable(); + $table->json('string_status_array')->nullable(); $table->integer('integer_status')->nullable(); $table->json('integer_status_array')->nullable(); $table->string('arrayable_status')->nullable(); From c53c2ff296863dca1bb6116b6f065f569085df3d Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Mon, 16 Jan 2023 12:55:10 +0100 Subject: [PATCH 08/22] Remove space --- tests/Integration/Database/EloquentModelEnumCastingTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 2ab8efd3f996..c3b91ae1af48 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -32,9 +32,9 @@ public function testEnumsAreCastable() { DB::table('enum_casts')->insert([ 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending', 'done']), + 'string_status_array' => json_encode(['pending','done']), 'integer_status' => 1, - 'integer_status_array' => json_encode([1, 2]), + 'integer_status_array' => json_encode([1,2]), 'arrayable_status' => 'pending', ]); From 2a494e71268721e869fca68cbefe3a96728f4041 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Mon, 16 Jan 2023 12:57:40 +0100 Subject: [PATCH 09/22] Revert "Remove space" This reverts commit c53c2ff296863dca1bb6116b6f065f569085df3d. --- tests/Integration/Database/EloquentModelEnumCastingTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index c3b91ae1af48..2ab8efd3f996 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -32,9 +32,9 @@ public function testEnumsAreCastable() { DB::table('enum_casts')->insert([ 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending','done']), + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, - 'integer_status_array' => json_encode([1,2]), + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ]); From a3a24d9a7d2918b1ab02d18dcccf402fdacee0ec Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 17 Jan 2023 15:45:17 +0100 Subject: [PATCH 10/22] Try re-arranging commas --- .../Database/EloquentModelEnumCastingTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 2ab8efd3f996..718db44e5409 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -6,6 +6,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Str; if (PHP_VERSION_ID >= 80100) { include 'Enums.php'; @@ -127,7 +128,9 @@ public function testEnumsAreConvertedOnSave() 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], DB::table('enum_casts')->where('id', $model->id)->first()); + ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + return str_replace(',', ', ', str_replace(', ', ',', $value)); + })->all()); } public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() @@ -149,7 +152,9 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], DB::table('enum_casts')->where('id', $model->id)->first()); + ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + return str_replace(',', ', ', str_replace(', ', ',', $value)); + })->all()); } public function testEnumsAcceptNullOnSave() From c38797a511422dc3935dd791acca344863d00053 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 17 Jan 2023 15:55:01 +0100 Subject: [PATCH 11/22] wip --- tests/Integration/Database/EloquentModelEnumCastingTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 718db44e5409..4c03530e70cb 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -128,7 +128,7 @@ public function testEnumsAreConvertedOnSave() 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + ], (object) collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(',', ', ', str_replace(', ', ',', $value)); })->all()); } @@ -152,7 +152,7 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + ], (object) collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(',', ', ', str_replace(', ', ',', $value)); })->all()); } From 18244cd752d78f12f4948a03155bf97d8f94f856 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 17 Jan 2023 15:58:59 +0100 Subject: [PATCH 12/22] wip --- .../Database/EloquentModelEnumCastingTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 4c03530e70cb..254688b8fd2d 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -121,15 +121,15 @@ public function testEnumsAreConvertedOnSave() $model->save(); - $this->assertEquals((object) [ + $this->assertEquals([ 'id' => $model->id, 'string_status' => 'pending', 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], (object) collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { - return str_replace(',', ', ', str_replace(', ', ',', $value)); + ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + return str_replace(', ', ',', $value); })->all()); } @@ -145,15 +145,15 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() $model->save(); - $this->assertEquals((object) [ + $this->assertEquals([ 'id' => $model->id, 'string_status' => 'pending', 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', - ], (object) collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { - return str_replace(',', ', ', str_replace(', ', ',', $value)); + ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { + return str_replace(', ', ',', $value); })->all()); } From 29d15a87e25cc4b5374da0f2fa207ed446091897 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 17 Jan 2023 16:01:30 +0100 Subject: [PATCH 13/22] wip --- tests/Integration/Database/EloquentModelEnumCastingTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 254688b8fd2d..71a9fe9e36fe 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; -use Illuminate\Support\Str; if (PHP_VERSION_ID >= 80100) { include 'Enums.php'; From 1990cbdd2c3b7107f85824b32b0e4361cb54d198 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:41:53 +0100 Subject: [PATCH 14/22] Refactor enum array cast to AsEnumCollection --- .../Eloquent/Casts/AsEnumCollection.php | 82 +++++++++++++++++++ tests/Database/DatabaseEloquentModelTest.php | 30 +++++++ .../Database/EloquentModelEnumCastingTest.php | 66 +++++++-------- 3 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php new file mode 100644 index 000000000000..7b9cd58601cb --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php @@ -0,0 +1,82 @@ +arguments = $arguments; + } + + public function get($model, $key, $value, $attributes) + { + if ( !isset($attributes[$key]) || is_null($attributes[$key]) ) { + return; + } + + $data = json_decode($attributes[$key], true); + + if ( !is_array($data) ) { + return; + } + + $enumClass = $this->arguments[0]; + + return (new Collection($data))->map(function ($value) use ($enumClass) { + return is_subclass_of($enumClass, + BackedEnum::class) ? $enumClass::from($value) : constant($enumClass . '::' . $value); + }); + } + + public function set($model, $key, $value, $attributes) + { + $value = $value !== null + ? (new Collection($value))->map(function ($enum) { + return $this->getStorableAnumValue($enum); + })->toJson() + : null; + + return [$key => $value]; + } + + public function serialize($model, string $key, $value, array $attributes) + { + return (new Collection($value))->map(function ($enum) { + return $this->getStorableAnumValue($enum); + })->toArray(); + } + + /** + * @param \UnitEnum|int|string $enum + * @return string|int + */ + protected function getStorableAnumValue($enum) + { + // Enum is already the backed value, no need to convert it again. + if ( is_string($enum) || is_int($enum) ) { + return $enum; + } + + return $enum instanceof BackedEnum ? $enum->value : $enum->name; + } + }; + } +} diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index 75fe19c41aac..61898aad3b6e 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -19,6 +19,7 @@ use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Casts\AsEncryptedArrayObject; use Illuminate\Database\Eloquent\Casts\AsEncryptedCollection; +use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Casts\AsStringable; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\JsonEncodingException; @@ -300,6 +301,25 @@ public function testDirtyOnCastedEncryptedArrayObject() $model->asEncryptedArrayObjectAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('asEncryptedArrayObjectAttribute')); } + + + public function testDirtyOnEnumCollectionObject() + { + $model = new EloquentModelCastingStub; + $model->setRawAttributes([ + 'asEnumCollectionAttribute' => json_encode(['draft', 'pending']), + ]); + $model->syncOriginal(); + + $this->assertInstanceOf(BaseCollection::class, $model->asEnumCollectionAttribute); + $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); + + $model->asEnumCollectionAttribute = ['draft', 'pending']; + $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); + + $model->asEnumCollectionAttribute = ['draft', 'done']; + $this->assertTrue($model->isDirty('asEnumCollectionAttribute')); + } public function testCleanAttributes() { @@ -2990,6 +3010,7 @@ class EloquentModelCastingStub extends Model 'asStringableAttribute' => AsStringable::class, 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, + 'asEnumCollectionAttribute' => AsEnumCollection::class . ':' . StringStatus::class, ]; public function jsonAttributeValue() @@ -3078,3 +3099,12 @@ public function set($model, string $key, $value, array $attributes) return is_string($value) ? strtoupper($value) : $value; } } + +if (PHP_VERSION_ID >= 80100) { + enum StringStatus: string + { + case draft = 'draft'; + case pending = 'pending'; + case done = 'done'; + } +} diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 71a9fe9e36fe..9d0fd3d23c2b 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -2,6 +2,8 @@ namespace Illuminate\Tests\Integration\Database; +use Illuminate\Database\Eloquent\Casts\AsCollection; +use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; @@ -21,9 +23,9 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() Schema::create('enum_casts', function (Blueprint $table) { $table->increments('id'); $table->string('string_status', 100)->nullable(); - $table->json('string_status_array')->nullable(); + $table->json('string_status_collection')->nullable(); $table->integer('integer_status')->nullable(); - $table->json('integer_status_array')->nullable(); + $table->json('integer_status_collection')->nullable(); $table->string('arrayable_status')->nullable(); }); } @@ -32,18 +34,18 @@ public function testEnumsAreCastable() { DB::table('enum_casts')->insert([ 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending', 'done']), + 'string_status_collection' => json_encode(['pending', 'done']), 'integer_status' => 1, - 'integer_status_array' => json_encode([1, 2]), + 'integer_status_collection' => json_encode([1, 2]), 'arrayable_status' => 'pending', ]); $model = EloquentModelEnumCastingTestModel::first(); $this->assertEquals(StringStatus::pending, $model->string_status); - $this->assertEquals([StringStatus::pending, StringStatus::done], $model->string_status_array); + $this->assertEquals([StringStatus::pending, StringStatus::done], $model->string_status_collection->all()); $this->assertEquals(IntegerStatus::pending, $model->integer_status); - $this->assertEquals([IntegerStatus::pending, IntegerStatus::done], $model->integer_status_array); + $this->assertEquals([IntegerStatus::pending, IntegerStatus::done], $model->integer_status_collection->all()); $this->assertEquals(ArrayableStatus::pending, $model->arrayable_status); } @@ -51,18 +53,18 @@ public function testEnumsReturnNullWhenNull() { DB::table('enum_casts')->insert([ 'string_status' => null, - 'string_status_array' => null, + 'string_status_collection' => null, 'integer_status' => null, - 'integer_status_array' => null, + 'integer_status_collection' => null, 'arrayable_status' => null, ]); $model = EloquentModelEnumCastingTestModel::first(); $this->assertEquals(null, $model->string_status); - $this->assertEquals(null, $model->string_status_array); + $this->assertEquals(null, $model->string_status_collection); $this->assertEquals(null, $model->integer_status); - $this->assertEquals(null, $model->integer_status_array); + $this->assertEquals(null, $model->integer_status_collection); $this->assertEquals(null, $model->arrayable_status); } @@ -70,17 +72,17 @@ public function testEnumsAreCastableToArray() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, - 'string_status_array' => [StringStatus::pending, StringStatus::done], + 'string_status_collection' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, - 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], + 'integer_status_collection' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); $this->assertEquals([ 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending', 'done']), + 'string_status_collection' => ['pending', 'done'], 'integer_status' => 1, - 'integer_status_array' => json_encode([1, 2]), + 'integer_status_collection' => [1, 2], 'arrayable_status' => [ 'name' => 'pending', 'value' => 'pending', @@ -93,17 +95,17 @@ public function testEnumsAreCastableToArrayWhenNull() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, - 'string_status_array' => null, + 'string_status_collection' => null, 'integer_status' => null, - 'integer_status_array' => null, + 'integer_status_collection' => null, 'arrayable_status' => null, ]); $this->assertEquals([ 'string_status' => null, - 'string_status_array' => null, + 'string_status_collection' => null, 'integer_status' => null, - 'integer_status_array' => null, + 'integer_status_collection' => null, 'arrayable_status' => null, ], $model->toArray()); } @@ -112,9 +114,9 @@ public function testEnumsAreConvertedOnSave() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, - 'string_status_array' => [StringStatus::pending, StringStatus::done], + 'string_status_collection' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, - 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], + 'integer_status_collection' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); @@ -123,9 +125,9 @@ public function testEnumsAreConvertedOnSave() $this->assertEquals([ 'id' => $model->id, 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending', 'done']), + 'string_status_collection' => json_encode(['pending', 'done']), 'integer_status' => 1, - 'integer_status_array' => json_encode([1, 2]), + 'integer_status_collection' => json_encode([1, 2]), 'arrayable_status' => 'pending', ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(', ', ',', $value); @@ -136,9 +138,9 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => 'pending', - 'string_status_array' => ['pending', 'done'], + 'string_status_collection' => ['pending', 'done'], 'integer_status' => 1, - 'integer_status_array' => [1, 2], + 'integer_status_collection' => [1, 2], 'arrayable_status' => 'pending', ]); @@ -147,9 +149,9 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() $this->assertEquals([ 'id' => $model->id, 'string_status' => 'pending', - 'string_status_array' => json_encode(['pending', 'done']), + 'string_status_collection' => json_encode(['pending', 'done']), 'integer_status' => 1, - 'integer_status_array' => json_encode([1, 2]), + 'integer_status_collection' => json_encode([1, 2]), 'arrayable_status' => 'pending', ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(', ', ',', $value); @@ -160,9 +162,9 @@ public function testEnumsAcceptNullOnSave() { $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, - 'string_status_array' => null, + 'string_status_collection' => null, 'integer_status' => null, - 'integer_status_array' => null, + 'integer_status_collection' => null, 'arrayable_status' => null, ]); @@ -171,9 +173,9 @@ public function testEnumsAcceptNullOnSave() $this->assertEquals((object) [ 'id' => $model->id, 'string_status' => null, - 'string_status_array' => null, + 'string_status_collection' => null, 'integer_status' => null, - 'integer_status_array' => null, + 'integer_status_collection' => null, 'arrayable_status' => null, ], DB::table('enum_casts')->where('id', $model->id)->first()); } @@ -247,9 +249,9 @@ class EloquentModelEnumCastingTestModel extends Model public $casts = [ 'string_status' => StringStatus::class, - 'string_status_array' => StringStatus::class.':array', + 'string_status_collection' => AsEnumCollection::class . ':' . StringStatus::class, 'integer_status' => IntegerStatus::class, - 'integer_status_array' => IntegerStatus::class.':array', + 'integer_status_collection' => AsEnumCollection::class . ':' . IntegerStatus::class, 'arrayable_status' => ArrayableStatus::class, ]; } From 487d14e5ad1d5a66a5a67ae0c5029ded96e78871 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:42:46 +0100 Subject: [PATCH 15/22] Reset HasAttributes trait --- .../Eloquent/Concerns/HasAttributes.php | 107 +----------------- 1 file changed, 5 insertions(+), 102 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 7a9f8822d2f9..d672f9ebb6fc 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -314,10 +314,6 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($attributes[$key]) : null; } - if ($this->isEnumArrayCastable($key)) { - $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumArrayValue($attributes[$key]) : null; - } - if ($attributes[$key] instanceof Arrayable) { $attributes[$key] = $attributes[$key]->toArray(); } @@ -793,10 +789,6 @@ protected function castAttribute($key, $value) return $this->getEnumCastableAttributeValue($key, $value); } - if ($this->isEnumArrayCastable($key)) { - return $this->getEnumArrayCastableAttributeValue($key, $value); - } - if ($this->isClassCastable($key)) { return $this->getClassCastableAttributeValue($key, $value); } @@ -854,26 +846,6 @@ protected function getEnumCastableAttributeValue($key, $value) return $this->getEnumCaseFromValue($castType, $value); } - /** - * Cast the given attribute to an array of enums. - * - * @param string $key - * @param mixed $value - * @return mixed - */ - protected function getEnumArrayCastableAttributeValue($key, $value) - { - if (is_null($value)) { - return; - } - - $castType = Str::before($this->getCasts()[$key], ':array'); - - $value = is_array($value) ? $value : $this->fromJson($value); - - return array_map(fn (string $value) => $this->getEnumCaseFromValue($castType, $value), $value); - } - /** * Get the type of cast for a model attribute. * @@ -996,12 +968,6 @@ public function setAttribute($key, $value) return $this; } - if ($this->isEnumArrayCastable($key)) { - $this->setEnumArrayCastableAttribute($key, $value); - - return $this; - } - if ($this->isClassCastable($key)) { $this->setClassCastableAttribute($key, $value); @@ -1193,28 +1159,6 @@ protected function setEnumCastableAttribute($key, $value) } } - /** - * Set the value of an enum array castable attribute. - * - * @param string $key - * @param array|string $value - * @return void - */ - protected function setEnumArrayCastableAttribute($key, $value) - { - $enumClass = Str::before($this->getCasts()[$key], ':array'); - - if (! isset($value)) { - $this->attributes[$key] = null; - } else { - $value = is_array($value) ? $value : $this->fromJson($value); - - $this->attributes[$key] = $this->getStorableEnumArrayValue( - array_map(fn ($value) => is_object($value) ? $value : $this->getEnumCaseFromValue($enumClass, $value), $value) - ); - } - } - /** * Get an enum case instance from a given class and value. * @@ -1238,21 +1182,8 @@ protected function getEnumCaseFromValue($enumClass, $value) protected function getStorableEnumValue($value) { return $value instanceof BackedEnum - ? $value->value - : $value->name; - } - - /** - * Get the storable value from the given enum. - * - * @param array $value - * @return string - */ - protected function getStorableEnumArrayValue($value) - { - return json_encode( - array_map(fn ($value) => $this->getStorableEnumValue($value), $value) - ); + ? $value->value + : $value->name; } /** @@ -1689,33 +1620,6 @@ protected function isEnumCastable($key) } } - /** - * Determine if the given key is cast using an array of enums. - * - * @param string $key - * @return bool - */ - protected function isEnumArrayCastable($key) - { - $casts = $this->getCasts(); - - if (! array_key_exists($key, $casts)) { - return false; - } - - $castType = $casts[$key]; - - if (in_array($castType, static::$primitiveCastTypes)) { - return false; - } - - if (! Str::endsWith($castType, ':array')) { - return false; - } - - return function_exists('enum_exists') && enum_exists(Str::before($castType, ':array')); - } - /** * Determine if the key is deviable using a custom class. * @@ -1745,10 +1649,9 @@ protected function isClassDeviable($key) */ protected function isClassSerializable($key) { - return ! $this->isEnumCastable($key) - && ! $this->isEnumArrayCastable($key) - && $this->isClassCastable($key) - && method_exists($this->resolveCasterClass($key), 'serialize'); + return ! $this->isEnumCastable($key) && + $this->isClassCastable($key) && + method_exists($this->resolveCasterClass($key), 'serialize'); } /** From d7e3b56289bf4d0de2e4b1c12c9be88121504212 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:43:13 +0100 Subject: [PATCH 16/22] Update AsEnumCollection.php --- src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php index 7b9cd58601cb..923fc209dc29 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php @@ -51,7 +51,7 @@ public function set($model, $key, $value, $attributes) $value = $value !== null ? (new Collection($value))->map(function ($enum) { return $this->getStorableAnumValue($enum); - })->toJson() + })->toJson() : null; return [$key => $value]; From c08eb24dac6efea836071fb60955cb946c1366b8 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:44:02 +0100 Subject: [PATCH 17/22] Style --- .../Eloquent/Casts/AsEnumCollection.php | 140 +++++++++--------- tests/Database/DatabaseEloquentModelTest.php | 51 ++++--- .../Database/EloquentModelEnumCastingTest.php | 5 +- 3 files changed, 97 insertions(+), 99 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php index 923fc209dc29..4cb80942005c 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php @@ -9,74 +9,74 @@ class AsEnumCollection implements Castable { - /** - * Get the caster class to use when casting from / to this cast target. - * - * @param array $arguments - * @return object|string - */ - public static function castUsing(array $arguments) - { - return new class($arguments) implements CastsAttributes - { - protected $arguments; - - public function __construct(array $arguments) - { - $this->arguments = $arguments; - } - - public function get($model, $key, $value, $attributes) - { - if ( !isset($attributes[$key]) || is_null($attributes[$key]) ) { - return; - } - - $data = json_decode($attributes[$key], true); - - if ( !is_array($data) ) { - return; - } - - $enumClass = $this->arguments[0]; - - return (new Collection($data))->map(function ($value) use ($enumClass) { - return is_subclass_of($enumClass, - BackedEnum::class) ? $enumClass::from($value) : constant($enumClass . '::' . $value); - }); - } - - public function set($model, $key, $value, $attributes) - { - $value = $value !== null - ? (new Collection($value))->map(function ($enum) { - return $this->getStorableAnumValue($enum); - })->toJson() - : null; - - return [$key => $value]; - } - - public function serialize($model, string $key, $value, array $attributes) - { - return (new Collection($value))->map(function ($enum) { - return $this->getStorableAnumValue($enum); - })->toArray(); - } - - /** - * @param \UnitEnum|int|string $enum - * @return string|int - */ - protected function getStorableAnumValue($enum) - { - // Enum is already the backed value, no need to convert it again. - if ( is_string($enum) || is_int($enum) ) { - return $enum; - } - - return $enum instanceof BackedEnum ? $enum->value : $enum->name; - } - }; - } + /** + * Get the caster class to use when casting from / to this cast target. + * + * @param array $arguments + * @return object|string + */ + public static function castUsing(array $arguments) + { + return new class($arguments) implements CastsAttributes + { + protected $arguments; + + public function __construct(array $arguments) + { + $this->arguments = $arguments; + } + + public function get($model, $key, $value, $attributes) + { + if (! isset($attributes[$key]) || is_null($attributes[$key])) { + return; + } + + $data = json_decode($attributes[$key], true); + + if (! is_array($data)) { + return; + } + + $enumClass = $this->arguments[0]; + + return (new Collection($data))->map(function ($value) use ($enumClass) { + return is_subclass_of($enumClass, + BackedEnum::class) ? $enumClass::from($value) : constant($enumClass.'::'.$value); + }); + } + + public function set($model, $key, $value, $attributes) + { + $value = $value !== null + ? (new Collection($value))->map(function ($enum) { + return $this->getStorableAnumValue($enum); + })->toJson() + : null; + + return [$key => $value]; + } + + public function serialize($model, string $key, $value, array $attributes) + { + return (new Collection($value))->map(function ($enum) { + return $this->getStorableAnumValue($enum); + })->toArray(); + } + + /** + * @param \UnitEnum|int|string $enum + * @return string|int + */ + protected function getStorableAnumValue($enum) + { + // Enum is already the backed value, no need to convert it again. + if (is_string($enum) || is_int($enum)) { + return $enum; + } + + return $enum instanceof BackedEnum ? $enum->value : $enum->name; + } + }; + } } diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index 61898aad3b6e..e73988b40d69 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -301,25 +301,24 @@ public function testDirtyOnCastedEncryptedArrayObject() $model->asEncryptedArrayObjectAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('asEncryptedArrayObjectAttribute')); } - - - public function testDirtyOnEnumCollectionObject() - { - $model = new EloquentModelCastingStub; - $model->setRawAttributes([ - 'asEnumCollectionAttribute' => json_encode(['draft', 'pending']), - ]); - $model->syncOriginal(); - - $this->assertInstanceOf(BaseCollection::class, $model->asEnumCollectionAttribute); - $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); - - $model->asEnumCollectionAttribute = ['draft', 'pending']; - $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); - - $model->asEnumCollectionAttribute = ['draft', 'done']; - $this->assertTrue($model->isDirty('asEnumCollectionAttribute')); - } + + public function testDirtyOnEnumCollectionObject() + { + $model = new EloquentModelCastingStub; + $model->setRawAttributes([ + 'asEnumCollectionAttribute' => json_encode(['draft', 'pending']), + ]); + $model->syncOriginal(); + + $this->assertInstanceOf(BaseCollection::class, $model->asEnumCollectionAttribute); + $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); + + $model->asEnumCollectionAttribute = ['draft', 'pending']; + $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); + + $model->asEnumCollectionAttribute = ['draft', 'done']; + $this->assertTrue($model->isDirty('asEnumCollectionAttribute')); + } public function testCleanAttributes() { @@ -3010,7 +3009,7 @@ class EloquentModelCastingStub extends Model 'asStringableAttribute' => AsStringable::class, 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, - 'asEnumCollectionAttribute' => AsEnumCollection::class . ':' . StringStatus::class, + 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.StringStatus::class, ]; public function jsonAttributeValue() @@ -3101,10 +3100,10 @@ public function set($model, string $key, $value, array $attributes) } if (PHP_VERSION_ID >= 80100) { - enum StringStatus: string - { - case draft = 'draft'; - case pending = 'pending'; - case done = 'done'; - } + enum StringStatus : string + { + case draft = 'draft'; + case pending = 'pending'; + case done = 'done'; + } } diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index 9d0fd3d23c2b..ef4ca6f4fe58 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Integration\Database; -use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Schema\Blueprint; @@ -249,9 +248,9 @@ class EloquentModelEnumCastingTestModel extends Model public $casts = [ 'string_status' => StringStatus::class, - 'string_status_collection' => AsEnumCollection::class . ':' . StringStatus::class, + 'string_status_collection' => AsEnumCollection::class.':'.StringStatus::class, 'integer_status' => IntegerStatus::class, - 'integer_status_collection' => AsEnumCollection::class . ':' . IntegerStatus::class, + 'integer_status_collection' => AsEnumCollection::class.':'.IntegerStatus::class, 'arrayable_status' => ArrayableStatus::class, ]; } From 3cbc712f821333835431c0be8da92a610acbceb0 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:47:33 +0100 Subject: [PATCH 18/22] Update DatabaseEloquentModelTest.php --- tests/Database/DatabaseEloquentModelTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index e73988b40d69..df4eafc56d39 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -3009,7 +3009,7 @@ class EloquentModelCastingStub extends Model 'asStringableAttribute' => AsStringable::class, 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, - 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.StringStatus::class, + 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.'StringStatus', ]; public function jsonAttributeValue() From 66063a0f1141896ced926e209a5e5d80d9dda9e4 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:54:56 +0100 Subject: [PATCH 19/22] Fix tests --- tests/Database/DatabaseEloquentModelTest.php | 22 +++++----- tests/Database/Enums.php | 42 ++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 tests/Database/Enums.php diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index df4eafc56d39..fd15e7960f22 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -44,6 +44,10 @@ use ReflectionClass; use stdClass; +if (PHP_VERSION_ID >= 80100) { + include 'Enums.php'; +} + class DatabaseEloquentModelTest extends TestCase { use InteractsWithTime; @@ -301,7 +305,10 @@ public function testDirtyOnCastedEncryptedArrayObject() $model->asEncryptedArrayObjectAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('asEncryptedArrayObjectAttribute')); } - + + /** + * @requires PHP >= 8.1 + */ public function testDirtyOnEnumCollectionObject() { $model = new EloquentModelCastingStub; @@ -3009,7 +3016,7 @@ class EloquentModelCastingStub extends Model 'asStringableAttribute' => AsStringable::class, 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, - 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.'StringStatus', + 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.StringStatus::class, ]; public function jsonAttributeValue() @@ -3097,13 +3104,4 @@ public function set($model, string $key, $value, array $attributes) { return is_string($value) ? strtoupper($value) : $value; } -} - -if (PHP_VERSION_ID >= 80100) { - enum StringStatus : string - { - case draft = 'draft'; - case pending = 'pending'; - case done = 'done'; - } -} +} \ No newline at end of file diff --git a/tests/Database/Enums.php b/tests/Database/Enums.php new file mode 100644 index 000000000000..40735cb90366 --- /dev/null +++ b/tests/Database/Enums.php @@ -0,0 +1,42 @@ + 'pending status description', + self::done => 'done status description' + }; + } + + public function toArray() + { + return [ + 'name' => $this->name, + 'value' => $this->value, + 'description' => $this->description(), + ]; + } +} From 90d1f017acaa99681fceeffd12b97a5a5261fa65 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 08:58:47 +0100 Subject: [PATCH 20/22] Style --- tests/Database/DatabaseEloquentModelTest.php | 12 ++--- tests/Database/Enums.php | 50 ++++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index fd15e7960f22..8f76e466b1f1 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -45,7 +45,7 @@ use stdClass; if (PHP_VERSION_ID >= 80100) { - include 'Enums.php'; + include 'Enums.php'; } class DatabaseEloquentModelTest extends TestCase @@ -305,10 +305,10 @@ public function testDirtyOnCastedEncryptedArrayObject() $model->asEncryptedArrayObjectAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('asEncryptedArrayObjectAttribute')); } - - /** - * @requires PHP >= 8.1 - */ + + /** + * @requires PHP >= 8.1 + */ public function testDirtyOnEnumCollectionObject() { $model = new EloquentModelCastingStub; @@ -3104,4 +3104,4 @@ public function set($model, string $key, $value, array $attributes) { return is_string($value) ? strtoupper($value) : $value; } -} \ No newline at end of file +} diff --git a/tests/Database/Enums.php b/tests/Database/Enums.php index 40735cb90366..2dbcb98d776d 100644 --- a/tests/Database/Enums.php +++ b/tests/Database/Enums.php @@ -6,37 +6,37 @@ enum StringStatus: string { - case draft = 'draft'; - case pending = 'pending'; - case done = 'done'; + case draft = 'draft'; + case pending = 'pending'; + case done = 'done'; } enum IntegerStatus: int { - case draft = 0; - case pending = 1; - case done = 2; + case draft = 0; + case pending = 1; + case done = 2; } enum ArrayableStatus: string implements Arrayable { - case pending = 'pending'; - case done = 'done'; - - public function description(): string - { - return match ($this) { - self::pending => 'pending status description', - self::done => 'done status description' - }; - } - - public function toArray() - { - return [ - 'name' => $this->name, - 'value' => $this->value, - 'description' => $this->description(), - ]; - } + case pending = 'pending'; + case done = 'done'; + + public function description(): string + { + return match ($this) { + self::pending => 'pending status description', + self::done => 'done status description' + }; + } + + public function toArray() + { + return [ + 'name' => $this->name, + 'value' => $this->value, + 'description' => $this->description(), + ]; + } } From 757b7b3d38627b78b2c009c3a45fddebfe729bf7 Mon Sep 17 00:00:00 2001 From: "Ralph J. Smit" <59207045+ralphjsmit@users.noreply.github.com> Date: Thu, 19 Jan 2023 10:49:03 +0100 Subject: [PATCH 21/22] Fix typo --- src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php index 4cb80942005c..971c61778544 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php @@ -50,7 +50,7 @@ public function set($model, $key, $value, $attributes) { $value = $value !== null ? (new Collection($value))->map(function ($enum) { - return $this->getStorableAnumValue($enum); + return $this->getStorableEnumValue($enum); })->toJson() : null; @@ -60,7 +60,7 @@ public function set($model, $key, $value, $attributes) public function serialize($model, string $key, $value, array $attributes) { return (new Collection($value))->map(function ($enum) { - return $this->getStorableAnumValue($enum); + return $this->getStorableEnumValue($enum); })->toArray(); } @@ -68,7 +68,7 @@ public function serialize($model, string $key, $value, array $attributes) * @param \UnitEnum|int|string $enum * @return string|int */ - protected function getStorableAnumValue($enum) + protected function getStorableEnumValue($enum) { // Enum is already the backed value, no need to convert it again. if (is_string($enum) || is_int($enum)) { From 4aab6f5e91d921c3e6167ada402b1a36e3985304 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 19 Jan 2023 16:25:15 -0600 Subject: [PATCH 22/22] add support for enum array object --- .../Eloquent/Casts/AsEnumArrayObject.php | 82 +++++++++++++++++++ .../Eloquent/Casts/AsEnumCollection.php | 13 +-- tests/Database/DatabaseEloquentModelTest.php | 25 +++++- .../Database/EloquentModelEnumCastingTest.php | 33 ++++++++ 4 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 src/Illuminate/Database/Eloquent/Casts/AsEnumArrayObject.php diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumArrayObject.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumArrayObject.php new file mode 100644 index 000000000000..3ba60a951aa9 --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumArrayObject.php @@ -0,0 +1,82 @@ +arguments = $arguments; + } + + public function get($model, $key, $value, $attributes) + { + if (! isset($attributes[$key]) || is_null($attributes[$key])) { + return; + } + + $data = json_decode($attributes[$key], true); + + if (! is_array($data)) { + return; + } + + $enumClass = $this->arguments[0]; + + return new ArrayObject((new Collection($data))->map(function ($value) use ($enumClass) { + return is_subclass_of($enumClass, BackedEnum::class) + ? $enumClass::from($value) + : constant($enumClass.'::'.$value); + })->toArray()); + } + + public function set($model, $key, $value, $attributes) + { + if ($value === null) { + return [$key => null]; + } + + $storable = []; + + foreach ($value as $enum) { + $storable[] = $this->getStorableEnumValue($enum); + } + + return [$key => json_encode($storable)]; + } + + public function serialize($model, string $key, $value, array $attributes) + { + return (new Collection($value->getArrayCopy()))->map(function ($enum) { + return $this->getStorableEnumValue($enum); + })->toArray(); + } + + protected function getStorableEnumValue($enum) + { + if (is_string($enum) || is_int($enum)) { + return $enum; + } + + return $enum instanceof BackedEnum ? $enum->value : $enum->name; + } + }; + } +} diff --git a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php index 971c61778544..0d49606e585b 100644 --- a/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php +++ b/src/Illuminate/Database/Eloquent/Casts/AsEnumCollection.php @@ -17,8 +17,7 @@ class AsEnumCollection implements Castable */ public static function castUsing(array $arguments) { - return new class($arguments) implements CastsAttributes - { + return new class($arguments) implements CastsAttributes { protected $arguments; public function __construct(array $arguments) @@ -41,8 +40,9 @@ public function get($model, $key, $value, $attributes) $enumClass = $this->arguments[0]; return (new Collection($data))->map(function ($value) use ($enumClass) { - return is_subclass_of($enumClass, - BackedEnum::class) ? $enumClass::from($value) : constant($enumClass.'::'.$value); + return is_subclass_of($enumClass, BackedEnum::class) + ? $enumClass::from($value) + : constant($enumClass.'::'.$value); }); } @@ -64,13 +64,8 @@ public function serialize($model, string $key, $value, array $attributes) })->toArray(); } - /** - * @param \UnitEnum|int|string $enum - * @return string|int - */ protected function getStorableEnumValue($enum) { - // Enum is already the backed value, no need to convert it again. if (is_string($enum) || is_int($enum)) { return $enum; } diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index 8f76e466b1f1..16bf86ac32a9 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -11,14 +11,15 @@ use Illuminate\Contracts\Encryption\Encrypter; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\Connection; -use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\ConnectionResolverInterface as Resolver; +use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\ArrayObject; use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Casts\AsEncryptedArrayObject; use Illuminate\Database\Eloquent\Casts\AsEncryptedCollection; +use Illuminate\Database\Eloquent\Casts\AsEnumArrayObject; use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Casts\AsStringable; use Illuminate\Database\Eloquent\Collection; @@ -327,6 +328,27 @@ public function testDirtyOnEnumCollectionObject() $this->assertTrue($model->isDirty('asEnumCollectionAttribute')); } + /** + * @requires PHP >= 8.1 + */ + public function testDirtyOnEnumArrayObject() + { + $model = new EloquentModelCastingStub; + $model->setRawAttributes([ + 'asEnumArrayObjectAttribute' => json_encode(['draft', 'pending']), + ]); + $model->syncOriginal(); + + $this->assertInstanceOf(ArrayObject::class, $model->asEnumArrayObjectAttribute); + $this->assertFalse($model->isDirty('asEnumArrayObjectAttribute')); + + $model->asEnumArrayObjectAttribute = ['draft', 'pending']; + $this->assertFalse($model->isDirty('asEnumArrayObjectAttribute')); + + $model->asEnumArrayObjectAttribute = ['draft', 'done']; + $this->assertTrue($model->isDirty('asEnumArrayObjectAttribute')); + } + public function testCleanAttributes() { $model = new EloquentModelStub(['foo' => '1', 'bar' => 2, 'baz' => 3]); @@ -3017,6 +3039,7 @@ class EloquentModelCastingStub extends Model 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.StringStatus::class, + 'asEnumArrayObjectAttribute' => AsEnumArrayObject::class.':'.StringStatus::class, ]; public function jsonAttributeValue() diff --git a/tests/Integration/Database/EloquentModelEnumCastingTest.php b/tests/Integration/Database/EloquentModelEnumCastingTest.php index ef4ca6f4fe58..e342e09fef34 100644 --- a/tests/Integration/Database/EloquentModelEnumCastingTest.php +++ b/tests/Integration/Database/EloquentModelEnumCastingTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Integration\Database; +use Illuminate\Database\Eloquent\Casts\AsEnumArrayObject; use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Schema\Blueprint; @@ -23,8 +24,10 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() $table->increments('id'); $table->string('string_status', 100)->nullable(); $table->json('string_status_collection')->nullable(); + $table->json('string_status_array')->nullable(); $table->integer('integer_status')->nullable(); $table->json('integer_status_collection')->nullable(); + $table->json('integer_status_array')->nullable(); $table->string('arrayable_status')->nullable(); }); } @@ -34,8 +37,10 @@ public function testEnumsAreCastable() DB::table('enum_casts')->insert([ 'string_status' => 'pending', 'string_status_collection' => json_encode(['pending', 'done']), + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, 'integer_status_collection' => json_encode([1, 2]), + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ]); @@ -43,8 +48,10 @@ public function testEnumsAreCastable() $this->assertEquals(StringStatus::pending, $model->string_status); $this->assertEquals([StringStatus::pending, StringStatus::done], $model->string_status_collection->all()); + $this->assertEquals([StringStatus::pending, StringStatus::done], $model->string_status_array->toArray()); $this->assertEquals(IntegerStatus::pending, $model->integer_status); $this->assertEquals([IntegerStatus::pending, IntegerStatus::done], $model->integer_status_collection->all()); + $this->assertEquals([IntegerStatus::pending, IntegerStatus::done], $model->integer_status_array->toArray()); $this->assertEquals(ArrayableStatus::pending, $model->arrayable_status); } @@ -53,8 +60,10 @@ public function testEnumsReturnNullWhenNull() DB::table('enum_casts')->insert([ 'string_status' => null, 'string_status_collection' => null, + 'string_status_array' => null, 'integer_status' => null, 'integer_status_collection' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); @@ -62,8 +71,10 @@ public function testEnumsReturnNullWhenNull() $this->assertEquals(null, $model->string_status); $this->assertEquals(null, $model->string_status_collection); + $this->assertEquals(null, $model->string_status_array); $this->assertEquals(null, $model->integer_status); $this->assertEquals(null, $model->integer_status_collection); + $this->assertEquals(null, $model->integer_status_array); $this->assertEquals(null, $model->arrayable_status); } @@ -72,16 +83,20 @@ public function testEnumsAreCastableToArray() $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, 'string_status_collection' => [StringStatus::pending, StringStatus::done], + 'string_status_array' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, 'integer_status_collection' => [IntegerStatus::pending, IntegerStatus::done], + 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); $this->assertEquals([ 'string_status' => 'pending', 'string_status_collection' => ['pending', 'done'], + 'string_status_array' => ['pending', 'done'], 'integer_status' => 1, 'integer_status_collection' => [1, 2], + 'integer_status_array' => [1, 2], 'arrayable_status' => [ 'name' => 'pending', 'value' => 'pending', @@ -95,16 +110,20 @@ public function testEnumsAreCastableToArrayWhenNull() $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, 'string_status_collection' => null, + 'string_status_array' => null, 'integer_status' => null, 'integer_status_collection' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); $this->assertEquals([ 'string_status' => null, 'string_status_collection' => null, + 'string_status_array' => null, 'integer_status' => null, 'integer_status_collection' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ], $model->toArray()); } @@ -114,8 +133,10 @@ public function testEnumsAreConvertedOnSave() $model = new EloquentModelEnumCastingTestModel([ 'string_status' => StringStatus::pending, 'string_status_collection' => [StringStatus::pending, StringStatus::done], + 'string_status_array' => [StringStatus::pending, StringStatus::done], 'integer_status' => IntegerStatus::pending, 'integer_status_collection' => [IntegerStatus::pending, IntegerStatus::done], + 'integer_status_array' => [IntegerStatus::pending, IntegerStatus::done], 'arrayable_status' => ArrayableStatus::pending, ]); @@ -125,8 +146,10 @@ public function testEnumsAreConvertedOnSave() 'id' => $model->id, 'string_status' => 'pending', 'string_status_collection' => json_encode(['pending', 'done']), + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, 'integer_status_collection' => json_encode([1, 2]), + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(', ', ',', $value); @@ -138,8 +161,10 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() $model = new EloquentModelEnumCastingTestModel([ 'string_status' => 'pending', 'string_status_collection' => ['pending', 'done'], + 'string_status_array' => ['pending', 'done'], 'integer_status' => 1, 'integer_status_collection' => [1, 2], + 'integer_status_array' => [1, 2], 'arrayable_status' => 'pending', ]); @@ -149,8 +174,10 @@ public function testEnumsAreNotConvertedOnSaveWhenAlreadyCorrect() 'id' => $model->id, 'string_status' => 'pending', 'string_status_collection' => json_encode(['pending', 'done']), + 'string_status_array' => json_encode(['pending', 'done']), 'integer_status' => 1, 'integer_status_collection' => json_encode([1, 2]), + 'integer_status_array' => json_encode([1, 2]), 'arrayable_status' => 'pending', ], collect(DB::table('enum_casts')->where('id', $model->id)->first())->map(function ($value) { return str_replace(', ', ',', $value); @@ -162,8 +189,10 @@ public function testEnumsAcceptNullOnSave() $model = new EloquentModelEnumCastingTestModel([ 'string_status' => null, 'string_status_collection' => null, + 'string_status_array' => null, 'integer_status' => null, 'integer_status_collection' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ]); @@ -173,8 +202,10 @@ public function testEnumsAcceptNullOnSave() 'id' => $model->id, 'string_status' => null, 'string_status_collection' => null, + 'string_status_array' => null, 'integer_status' => null, 'integer_status_collection' => null, + 'integer_status_array' => null, 'arrayable_status' => null, ], DB::table('enum_casts')->where('id', $model->id)->first()); } @@ -249,8 +280,10 @@ class EloquentModelEnumCastingTestModel extends Model public $casts = [ 'string_status' => StringStatus::class, 'string_status_collection' => AsEnumCollection::class.':'.StringStatus::class, + 'string_status_array' => AsEnumArrayObject::class.':'.StringStatus::class, 'integer_status' => IntegerStatus::class, 'integer_status_collection' => AsEnumCollection::class.':'.IntegerStatus::class, + 'integer_status_array' => AsEnumArrayObject::class.':'.IntegerStatus::class, 'arrayable_status' => ArrayableStatus::class, ]; }