-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[8.x] ArrayObject + Collection Custom Casts #36245
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use ArrayObject as BaseArrayObject; | ||
use Illuminate\Contracts\Support\Arrayable; | ||
use JsonSerializable; | ||
|
||
class ArrayObject extends BaseArrayObject implements Arrayable, JsonSerializable | ||
{ | ||
/** | ||
* Get a collection containing the underlying array. | ||
* | ||
* @return \Illuminate\Support\Collection | ||
*/ | ||
public function collect() | ||
{ | ||
return collect($this->getArrayCopy()); | ||
} | ||
|
||
/** | ||
* Get the instance as an array. | ||
* | ||
* @return array | ||
*/ | ||
public function toArray() | ||
{ | ||
return $this->getArrayCopy(); | ||
} | ||
|
||
/** | ||
* Get the array that should be JSON serialized. | ||
* | ||
* @return array | ||
*/ | ||
public function jsonSerialize() | ||
{ | ||
return $this->getArrayCopy(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
|
||
class AsArrayObject 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 implements CastsAttributes { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This anonymous class makes me feel warm and fuzzy |
||
public function get($model, $key, $value, $attributes) | ||
{ | ||
return new ArrayObject(json_decode($attributes[$key], true)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(also worth noting https://www.php.net/manual/en/function.json-decode.php#refsect1-function.json-decode-returnvalues There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then PHP will throw an error if it is not an error - which is what I would want to happen in this case. The column assigned this cast should always contain something that can be decoded to an array. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is bad. Throws an exception web DB column is nullable (and item is null). |
||
} | ||
|
||
public function set($model, $key, $value, $attributes) | ||
{ | ||
return [$key => json_encode($value)]; | ||
} | ||
|
||
public function serialize($model, string $key, $value, array $attributes) | ||
{ | ||
return $value->getArrayCopy(); | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Illuminate\Support\Collection; | ||
|
||
class AsCollection 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 implements CastsAttributes { | ||
public function get($model, $key, $value, $attributes) | ||
{ | ||
return new Collection(json_decode($attributes[$key], true)); | ||
} | ||
|
||
public function set($model, $key, $value, $attributes) | ||
{ | ||
return [$key => json_encode($value)]; | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Illuminate\Support\Facades\Crypt; | ||
|
||
class AsEncryptedArrayObject 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 implements CastsAttributes { | ||
public function get($model, $key, $value, $attributes) | ||
{ | ||
return new ArrayObject(json_decode(Crypt::decryptString($attributes[$key]), true)); | ||
} | ||
|
||
public function set($model, $key, $value, $attributes) | ||
{ | ||
return [$key => Crypt::encryptString(json_encode($value))]; | ||
} | ||
|
||
public function serialize($model, string $key, $value, array $attributes) | ||
{ | ||
return $value->getArrayCopy(); | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace Illuminate\Database\Eloquent\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\Crypt; | ||
|
||
class AsEncryptedCollection 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 implements CastsAttributes { | ||
public function get($model, $key, $value, $attributes) | ||
{ | ||
return new Collection(json_decode(Crypt::decryptString($attributes[$key]), true)); | ||
} | ||
|
||
public function set($model, $key, $value, $attributes) | ||
{ | ||
return [$key => Crypt::encryptString(json_encode($value))]; | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Integration\Database; | ||
|
||
use Illuminate\Database\Eloquent\Casts\AsArrayObject; | ||
use Illuminate\Database\Eloquent\Casts\AsCollection; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Schema\Blueprint; | ||
use Illuminate\Support\Facades\Schema; | ||
|
||
/** | ||
* @group integration | ||
*/ | ||
class DatabaseArrayObjectAndCollectionCustomCastTest extends DatabaseTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
Schema::create('test_eloquent_model_with_custom_array_object_casts', function (Blueprint $table) { | ||
$table->increments('id'); | ||
$table->text('array_object'); | ||
$table->text('collection'); | ||
$table->timestamps(); | ||
}); | ||
} | ||
|
||
public function test_array_object_and_collection_casting() | ||
{ | ||
$model = new TestEloquentModelWithCustomArrayObjectCast; | ||
|
||
$model->array_object = ['name' => 'Taylor']; | ||
$model->collection = collect(['name' => 'Taylor']); | ||
|
||
$model->save(); | ||
|
||
$model = $model->fresh(); | ||
|
||
$this->assertEquals(['name' => 'Taylor'], $model->array_object->toArray()); | ||
$this->assertEquals(['name' => 'Taylor'], $model->collection->toArray()); | ||
|
||
$model->array_object['age'] = 34; | ||
$model->array_object['meta']['title'] = 'Developer'; | ||
|
||
$model->save(); | ||
|
||
$model = $model->fresh(); | ||
|
||
$this->assertEquals([ | ||
'name' => 'Taylor', | ||
'age' => 34, | ||
'meta' => ['title' => 'Developer'], | ||
], $model->array_object->toArray()); | ||
} | ||
} | ||
|
||
class TestEloquentModelWithCustomArrayObjectCast 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, | ||
'collection' => AsCollection::class, | ||
]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the
ArrayObject
is alreadyTraversable
, you don't need to get an->getArrayCopy()
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe one of my project is exactly waiting for that