I know that you can append variables to model arrays and json representations by using the protected $appends = ["your", "vars", "here"];
array. But imagine the following situation:
Our use case would be a fictional game or similiar:
Imagine that we have a User
model that holds simple information about an (human) user, like the full name, address and so on.
Now, we also have a Faction
model that represents the faction/origin/guild/... of this user.
The Faction
model is eager-loaded when retrieving users, because the Faction name is wanted almost every time when displaying the user information.
A User
also has DailyStatistics
, which holds some information about their daily scores (simple points would be enough).
Because I want to know the points of the a faction, which is the sum of the user points, I thought about appending a new variable totalPoints
.
The getTotalPointsAttribute
function would look like this:
function getTotalPointsAttribute(){
return $this->users->sum->getTotalPoints();
}
Everytime when we retrieve a user now, the eager-loaded faction would also want to calculate the totalPoints
attribute. That means, that we have a lot of overhead per user.
Is there a way to avoid situations like this? Can I "conditionally" append variables? Are properties calculated when they are hidden?
I tried to wrap the totalPoints
variable in a simple function, instead of an accessor instead. The problem is, that Frontend-Frameworks like VueJS would need access to the totalPoints
variable (or to an endpoint to retrieve that value, but this solution is the least favorable).
I met this problem as I wanted to Appends on the fly but don't want this to auto-appends on any other Controller/Models (The other way is produce 2 Models for the same Table, which is difficult to maintain).
Currently I'm maintaining a Laravel 5.4 (Since they refuse to upgrade PHP5.6 to PHP7)
For Laravel 5.4 and below
Just add a closure after completed the query builder get()
->each(function ($items) {
$items->append('TotalPoints');
);
Source of original solutions: laravel-how-to-ignore-an-accessor
$openOrders = Order::open()->has('contents')
->get(['id','date','tableName'])
->each(function ($items) {
$items->append('TotalPoints');
);
Your model still contains the
public function getTotalPointsAttribute()
{
return $this->users->sum->getTotalPoints();
}
Now you can remove/comment out the the appends in your models :
protected $appends = [
'TotalPoints',
];
Alternatively, if you're on Laravel 5.5 and above, you could use the collection magic like so:
$openOrders->each->setAppends(['TotalPoints']);
Laravel 5.5 and above now have a Laravel 5.6 #Appending At Run Time