Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 475
manniL Avatar asked Jun 24 '17 22:06

manniL


People also ask

How to use where condition in Laravel 5?

How to Use Where Condition in Laravel 5. The Where clause is used to extract only those records which fulfill a condition stated. The most simple call to where three arguments are required. The first statement is the column name. The second argument is an operator that can be supported by any of the operators in the database.

How to retrieve values from variables in Laravel configuration files?

However, you may use the env helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: The second value passed to the env function is the "default value".

What is the default value of the env function in Laravel?

In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: The second value passed to the env function is the "default value". This value will be returned if no environment variable exists for the given key.

What happened to environment-dependent loading in Laravel 5?

Since Laravel 5 flattened a lot of the environment-specific structures, much of the configuration that was once stored in different config directories for each environment has now moved into .env files. But one that can't just live in .env is the environment-dependent loading of service providers.


Video Answer


1 Answers

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

like image 148
John Avatar answered Oct 29 '22 00:10

John