phplaravelvuejs2laravel-5.4

Laravel 5 - Conditionally append variables


A few words before

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:

The 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).

The Clue

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();
}

The problem

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.

The question

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).


Solution

  • 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