-
Notifications
You must be signed in to change notification settings - Fork 11.4k
[7.x] Custom Cast Types + Object / Value Object Casts #31035
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
Conversation
In my opinion, its implementation requires refinement: 7befbb6#diff-b50af0acefc58e2c0d89569159ab38d5R33-R36 @taylorotwell, Why should a developer keep different key names in mind? This is not obvious.
The solution proposed in #30958 PR covers this problem. |
it's only that way because that's how it was defined in the Custom Caster. you can use whatever names you'd like. |
@browner12, ok, see: 33d7f2f#diff-b50af0acefc58e2c0d89569159ab38d5R45-R48 $model->address = null;
$this->assertNull($model->toArray()['address_line_one']);
$this->assertNull($model->toArray()['address_line_two']); I don’t understand where the relationship between the various attributes is: But what if I need to store different values in a model? For example:
? With the current code, this will cause an error. |
In general, I have a lot of questions about the functionality of this code. |
I'm sure the functionality will be fully documented by the release of version 7. |
Documented but not working ;) And I will prove it! We take the class DatabaseEloquentModelCustomCastingTest extends DatabaseTestCase
{
public function testStringCastable()
{
$this->migrate();
$this->createModel();
}
protected function migrate()
{
Schema::create('test_model', function (Blueprint $table) {
$table->increments('id');
$table->string('field_1')->nullable();
$table->decimal('field_2')->nullable();
$table->json('field_3')->nullable();
});
}
protected function createModel()
{
return TestModel::create([
'field_1' => ['f', 'o', 'o', 'b', 'a', 'r'],
'field_2' => 20,
'field_3' => [
'line_one' => 'Address Line 1',
'line_two' => 'Address Line 2',
],
]);
}
}
class TestModel extends Model
{
public $table = 'test_model';
public $timestamps = false;
public $casts = [
'field_1' => StringCast::class,
'field_2' => NumberCast::class,
'field_6' => AddressCast::class,
];
protected $guarded = ['id'];
}
class StringCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
return str_split($value);
}
public function set($model, string $key, $value, array $attributes)
{
return implode('', $value);
}
}
class NumberCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
return $value / 100;
}
public function set($model, string $key, $value, array $attributes)
{
return $value;
}
}
class AddressCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
return new AddressCustom($value);
}
public function set($model, string $key, $value, array $attributes)
{
return $value;
}
}
class AddressCustom
{
public function __construct($value = null)
{
if (! is_array($value) && ! is_object($value)) {
return;
}
foreach ($value as $key => $val) {
$this->$key = $val;
}
}
public function toArray()
{
return get_object_vars($this);
}
} And I get an error when running the test:
|
@browner12, @devcircus, I am surprised that a more workable version has already been created and proposed (#30958), but for some reason, Taylor chose to return to the incomplete worker, but his code was three years old (#13706). This is upsetting. And after this, less and less I want to try to make something new. I agree, Taylor knows best what to do with his product. But in our country this is called "invent a bicycle". |
As we mentioned in that PR, there have been many many other attempts at this feature in the past, so it's not surprising that Taylor picked his own attempt over selecting one of the others. This feature has a lot of caveats, and that's the reason it's been put off so many times in the past. Let's be happy this feature got in, and work on making it better as we go and learn. |
@browner12, Man, a work tool for this has already been created. There he is - #30958 |
didn't you "invent a bicycle", since Taylor had done it already? 🤔 |
@browner12, you are mistaken. A bicycle can be called a working analogue code which already exists. In the case of Taylor, his code is not working. See my easy test above. In doing so, my code covers this functionality. What Taylor did is called the invention of the bicycle, since its original code is not working. And now he copy-pasted it. |
@browner12, Further, Taylor used the Spoiler: it is not clear which method is responsible for writing to the database, and which is for reading from it. And also which method is responsible for setting the attribute. |
As this is a brand new feature on an unreleased branch, any bugs or weird behavior will be worked out before it is released. Also, as we've seen my attempts at this over the years, I have to assume there is a reason they went this direction, and like the rest of this amazing framework, all will work well when it is released. There's no reason, at this time to be stressed about something that isn't clear or that doesn't work how you expect. It will be worked out in the coming weeks and fully documented. |
@devcircus, anyway, I don’t understand why to invent and modify something at a time when there is already a proposed working solution (#30958)?.. |
@andrey-helldar of course your test doesn't work because you didn't even use the Cast properly. Your address cast should be returning: return ['field_3' => $value]; You're becoming toxic in this thread. I didn't like your code. I didn't merge it. I didn't think it was very good. I merged my code which I liked better. The end. |
This adds the ability to write your own custom casts. These can cast to simple primitive values like strings (like an encrypted string) or to objects. When casting to objects, you have the opportunity to sync the object's values back into the model before saving.
Objects that cast attributes must implement the
Illuminate\Contracts\Database\Eloquent\CastsAttributes
contract orCastsInboundAttributes
. ThisCastsAttributes
interface defines aget
andset
method.For example, a JSON caster that replicates the built-in JSON casting offered by Laravel would look like the following:
A caster that casts to a value object with multiple properties may look like the following:
When defined this way, you may update the address object properties and then save the model. The object's properties will be synced back to the model before saving: