Repository navigation
Opt-in typed model attributes with real PHP properties (#[Column]) #62325
Unanswered
clementbirkle
asked this question in
Ideas
Replies: 1 comment 3 replies
|
This looks like what Doctrine ORM does and is fundamentally what Eloquent does, so I doubt, it will be adopted in this form. However, I also always felt, that this wasn't ideal.
Following this example, the attributes are declared on the class, not the property, so if it would be implemented, it would probably look more like this: #[Column(name: 'name', fillable: true, …)]
class User extends Model
{
}So, this could be utilized, but it won't result in auto-completion and such. |
3 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Eloquent attributes are invisible to the language. A model's columns and their types live in
@propertydocblocks, written by hand or generated by tools likelaravel-ide-helper. Docblocks drift from the schema, need a generation step, and are the only type information IDEs, PHPStan and AI coding agents get about a model. Agents in particular read the class file and little else, so what is missing from the class is missing for them.I would like to propose an opt-in way to declare attributes as real typed properties, without changing how Eloquent works:
How it works. Eloquent already calls
initialize{Trait}()on every instantiation. The traitunset()s the properties marked with#[Column]. PHP then routes access to those properties back to__get/__set, so casts, accessors, mutators and dirty tracking behave exactly as before. It uses plainunset(), no PHP 8.4 features (I only tested on 8.5). The whole trait is small:What I checked (Pest, against Laravel 13):
isDirty()andgetChanges()are correctfill(),isset(),toArray(),create/find/saveagainst a real databaseclone,replicate(),serialize/unserializeMissingAttributeException$invoice->id = 'abc'Cost. Measured on 20,000 hydrated models (PHP 8.5, median of 7 runs,
newFromBuilderonly, no SQL): about +0.3 µs per model (+6%) with 6 declared columns, +0.4 µs with 25. Attribute reads and writes take the same path as today, and memory per model is unchanged.Limits. There is no runtime
TypeErrorwhen writing a wrong type; static analysis catches it, the framework does not. The declared type also has to match what the cast returns, which is up to the developer.Compatibility. Models that do not use the trait are untouched, so this could ship in a minor release.
Tooling. Today this information is rebuilt outside the class:
laravel-ide-helperwrites@propertydocblocks (or a_ide_helper_models.phpstub file), and editor tooling inspects the schema at runtime. With declared properties the class itself is the source of truth, so these tools could read it directly. They would remain needed for models that have not opted in, and for relations, scopes and facades.This also fits the direction of the recent attributes (
#[Fillable],#[Hidden],#[Table]). Related requests (#32459, laravel/ideas#2240) asked for native properties onModelitself, which is a much bigger change. This is a narrower and additive take on the same need.I would be glad to send a PR against
13.xif there is interest. Open questions I would value input on: whether this belongs in the framework or in a package, whether#[Column]is the right name, and whether an artisan command to generate the declarations from the schema would be welcome.All reactions