I want to add a field to an Eloquent model instance. This field data comes from a query, not a constant or a value.
Yes, it could be done adding a field to $appends and adding an Attribute, but the problem with this approach is that it’s not really added to the object as a field but as something like a function call, which makes that every time it’s accessed it causes a query to the database, which if the data won’t change will be time consuming needlessly.
In this case I could create a PDO and populate it, but I wonder if this could be done with Eloquent’s ways.
I tried adding a Scope or something within the boot method, but nothing that worked.
I have searched but haven’t found anything, so here I am asking if could it be done with a Scope, or the boot method or in any other Laravel/Eloquent way. Thanks for your time.
2
Answers
So to make some things clear here. Using accessors on a model is something like computed fields, where you are able to call an attribute on a model that doesn’t map to a field in the table, something like:
===
A note on Laravel 9 documentation
To make things more clearly, this was the only way to use accessors with a model prior to Laravel 9. Since the release of Laravel 9, this approach isn’t mentioned anymore in the documentation and the use of
Attribute
is introduced.To my opinion, this isn’t making any improvement at all. This just narrows the models scope for using function names where it will accidentally hit a magic method call unwillingly due to all the magicness happening in Eloquents models already.
===
The
$appends
attribute is used to append any none existing field from the table or relation to a processed output like JSON usingUser::toJson()
orUser::toArray()
. You can also use the$appends
field to save the state of any accessor field you define. Now you can use the combination of the two and if you would like to use a query to fill the attribute but this query should be executed once, just check if the attribute exists and if so, skip it.As yhiamdan pointed out in his answer, the getter method
getMyFieldAttribute
first parameter will be filled with the computed attributes value if set. On first call this value will benull
, on any subsequent call it will be filled with the computed value if set withsetAttribute
or$this->attributes
.You cannot mix eloquents attributes with a defined property on the class
User
, this will never reach the magic methods__get
or__set
used to magically call the functionsgetMyFieldAttribute
orsetMyFieldAttribute
.Just remind yourself that this is only the getter method of an accessor field. So the attribute
my_field
should have agetMyFieldAttribute
andsetMyFieldAttribute
function. In the above setup, thegetMyFieldAttribute
is used as both where the value is variable and static when assigned.Yes, it could be done adding a field to $appends and adding an Attribute, but the problem with this approach is that it’s not really added to the object but something like a function call, and every time it’s accessed it causes a query to the database.
Yes whenever you have an attribute defined on a model and you try to access it, the accessor function will be called every time. Its your job to decide whether to query the database or return the original result during the first query.
Lets say you have an attribute fullname on a user model and for some reasons computing the fullname is expensive and you do not want to recompute everytime the the attribute is accessed, you can set the result after the initial computation to your model’s attributes using $this->setAttribute("fullname",$result). This way, whenever you try to access the attribute, laravel will pass the value you assigned/set to the attribute to the accessor function, and you can check and decide whether to recompute or simply return the previous result.
This is an example
However what you need to consider is situations where the fullname attribute could be stale.
For instance when you retrieve a user model, you retrieve the fullname for the first time, then you change eg. the firstname attribute and then you try to retrieve the fullname attribute again, you will get the original fullname even if its dependent of the firstname attribute.
I hope this helps.