Laravel Version
13.22.0
PHP Version
8.5.6
Database Driver & Version
No response
Description
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((new OrderAlphaFirst)->getTable()); // "from_alpha"
dump((new OrderBetaFirst)->getTable()); // "from_beta"
Laravel Version
13.22.0
PHP Version
8.5.6
Database Driver & Version
No response
Description
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