Inconsistent accessor attribute name conversion
Laravel Version
11.42.0
PHP Version
8.4.3
Database Driver & Version
No response
Description
Define a custom attribute with a convoluted name using the Attribute syntax:
Example:
protected function foo1Bar(): Attribute
{
return Attribute::make(
get: fn () => 'yay',
);
}
Accessing a custom attribute is internally done by a snake-to-camel case conversion. So this means both foo1_bar and foo_1_bar will yield the expected value.
However, some features in Eloquent do the inverse by calling upon the attribute cache ($getAttributeMutatorCache): a camel-to-snake case conversion. This means that foo1Bar now only translates to foo1_bar, and not anymore to foo_1_bar even though its value would get returned when retrieving it.
echo $model->foo1_bar; // "yay"
$model->append('foo1_bar');
$model->toArray(); // Works fine.
echo $model->foo_1_bar; // "yay"
$model->append('foo_1_bar');
$model->toArray(); // Call to undefined method Model::getFoo1BarAttribute()
On the contrary, when resorting back to the good old getFoo1BarAttribute() everything works as expected for both foo1_bar and foo_1_bar.
protected function getFoo1BarAttribute()
{
return 'yay';
}
echo $model->foo1_bar; // "yay"
$model->append('foo1_bar');
$model->toArray(); // Works fine.
echo $model->foo_1_bar; // "yay"
$model->append('foo_1_bar');
$model->toArray(); // Works fine.
So, because of internal two-way case conversions to support the newer attribute syntax some unexpected and disfunctional ambiguity gets introduced.
I think both syntaxes (Attribute vs getXXXAttribute()) should behave equally. But I'm not quite sure how to proceed with this. Should foo_1_bar get blocked for access to prevent this kind of expectations further down the line? Or should it also resolve properly when doing the camel-to-snake conversion? Or...?
Steps To Reproduce
protected function foo1Bar(): Attribute
{
return Attribute::make(
get: fn () => 'yay',
);
}
$model->append('foo_1_bar');
$model->toArray();
Because it actually gets appended, I'd expect it to work when you do ->toArray();
it looks like its down to static::$getAttributeMutatorCache
The key you enter into the append method won't match because it does Str::snake(foo1Bar) internally which gives foo1_bar which then doesn't match the key you've entered.
Will see if I can PR a fix :)
@Propaganistas @jackbayliss the issue was reolved in below PR
https://github.com/laravel/framework/pull/54578
thanks!
@pandiselvamm Nice, beat me to it! 👍🏻
Thank you for reporting this issue!
As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.
If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.
Thank you!
Hey, can you check this PR whether this approach solves your problem or not? My PR Description might have been unclear, but as you already know the context, please do check the PR if it helps to solve your issue:
PR: https://github.com/laravel/framework/pull/54793
@ laravel maintainers: some community members have provided PRs for this, but they got rejected.
Please could you direct us on how to tackle this issue instead?
Maybe my specific case is a different issue, but for me
protected function fooBar(): Attribute
{
return Attribute::make(
get: fn () => 'yay',
);
}
$model->append('foo_bar');
$model->toArray(); // Call to undefined method Model::getFooBarAttribute()
$model->append('fooBar');
$model->toArray(); // works, but now the appended attribute is in camel case, and other model attributes are in snake case.
Laravel 12.12.0