You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Model attribute resolution is silently order-dependent when multiple traits declare the same attribute #60953
Since #60566 (and #60519 for queue attributes), class-level attributes such as #[Table], #[Connection], etc. are also resolved from traits used by the model.
When two or more traits on the same class declare the same attribute and the class itself does not, the conflict is resolved silently: the attribute from the first trait in the use statement wins, because resolveClassAttribute() iterates ReflectionClass::getTraits() and returns on the first match.
This means that the class behaviour can be altered by a non-semantic change (like a different linting rule, a renaming, etc.).
I'm not sure what the correct solution would be, other than avoiding the short-circuiting on the first match, since every order-based heuristic seems shaky to me.
/cc @jackbayliss as the author of both PRs, in case you have thoughts on this
Steps To Reproduce
<?php
#[\Illuminate\Database\Eloquent\Attributes\Table(name: 'from_alpha')]
trait CarriesAlphaTable
{
}
#[\Illuminate\Database\Eloquent\Attributes\Table(name: 'from_beta')]
trait CarriesBetaTable
{
}
class OrderAlphaFirst extends \Illuminate\Database\Eloquent\Model
{
use CarriesAlphaTable, CarriesBetaTable;
}
class OrderBetaFirst extends \Illuminate\Database\Eloquent\Model
{
use CarriesBetaTable, CarriesAlphaTable;
}
dump((newOrderAlphaFirst)->getTable()); // "from_alpha"dump((newOrderBetaFirst)->getTable()); // "from_beta"
I guess, though they could also come from different vendors, in which case neither trait author can know about the conflict. I was thinking about an exception as well, although it implies always iterating over all traits instead of short-circuiting on the first match (and sooner or later recursively for traits included via trait composition as well). Not a serious performance concern, but still.
Btw this issue was mainly meant to open a discussion on how to handle this, more than a push for an immediate fix
Activity
[13.x] Throw when traits declare conflicting class attributes
jackbayliss commentedon Jul 31, 2026
@Carnicero90 Thanks for the ping, I guess it should probably throw some kind of exception in this case. Similar to https://3v4l.org/nBWcS
But, I expect most people that use attrs on traits would know not to do this kind of thing. Should be addressed I imagine though..
Carnicero90 commentedon Jul 31, 2026
I guess, though they could also come from different vendors, in which case neither trait author can know about the conflict. I was thinking about an exception as well, although it implies always iterating over all traits instead of short-circuiting on the first match (and sooner or later recursively for traits included via trait composition as well). Not a serious performance concern, but still.
Btw this issue was mainly meant to open a discussion on how to handle this, more than a push for an immediate fix