How to have the sum of a related models column in Eloquent - laravel-5

I have a hasMany relation on my user model
public function timesheet()
{
return $this->hasMany('App\TimeTable', 'employee');
}
and I would like to have the sum of a column
$users = User::select('firstname','lastname')->with('timesheet')->get();
foreach($users as $user) {
//sume of $user->timesheet
//tried $user->timesheet->sum('minutes') which return on each iteration 0
}
What is the best approach to get the right result?
the problem lays on the issue that I use select fileds on User object. On the other hand the sum is really expensive and I get Allowed memory size of 1073741824 bytes exhausted

For the eager loading to work you have to select the users.id column:
$users = User::select('id', 'firstname', 'lastname')->with('timesheet')->get();
If you only fetch the timesheet models for the minutes, you can use a modified withCount():
User::withCount(['timesheet as minutes' => function($query) {
$query->select(DB::raw('sum(minutes)'));
}])->get();
foreach($users as $user) {
$user->minutes
}

Try something along these lines:
$users_with_time = $users->keyBy('id')->map(function($user) {
return $user->sum('timesheets.minutes');
});
It is taking your collection of users, keys them by the user's id and them maps the resultant collection by the sum of the minutes property of the timesheets Eloquent Collection.
You should be left with a Support Collection of total time spent per user id.
I have not had time to test this but it should be a good place to start from.

Related

Sum of Items field in collection of collections

I'm trying to see if it's possible, but I would like to get the sum of a field in an item in a collection of collections.
I have the following in my controller:
$prefiltered_contacts = Contact::with(['donations' => function ($query) use ($request) {
$query->whereMonth('date_received', $request->month)
->whereYear('date_received', $request->year);
}])->get();
$contacts = $prefiltered_contacts ->filter(function ($contact) {
return $contact->donations->isNotEmpty();
});
My donation class has the following:
public function monetary_donations(){
return $this->hasMany('App\Payments_Distribution', 'donation_id','id');
}
Now the last part of this is that in the Payments_Distribution class, there is a field titled amount.
If I was coming directly from the Donation model, I would access the sum of the monetary donations as $donation->monetary_donations->sum('amount'); and I would receive the sum. But how would I go about doing this from the Contact model? Or is that even possible given that it would need to go through a collection of donations to get to the collection of monetary_donations? I'm trying to get a report of all Contacts donations (and monetary donations) and output a subtotal of the monetary_donations for that specific period.
Sum accepts a closure as the argument. So you could do something like this:
$sum = $donations->sum(function ($donation) {
return $donation->monetary_donations->sum('amount');
});
Or 1 level higher (from $contacts):
$sum = $contacts->sum(function ($contact) {
return $contact->donations->sum(function ($donation) {
return $donation->monetary_donations->sum('amount');
});
});
Edit:
I would also recommend eager loading your relationships and filtering out contacts without donations with SQL rather than collections:
$contacts = Contact::with(['donations' => function ($query) use ($request) {
$query
->with('monetary_donations')
->whereMonth('date_received', $request->month)
->whereYear('date_received', $request->year);
}])
->whereHas('donations') // Filter out those without donations with SQL
->get();

Eloquent select with() based on foreign key

I have a table with user data (users) and a table with prices (prices).
My prices table can contain multiple prices pr. user since I want to keep historical data.
I've defined my relation as a one-to-one
$this->hasOne("App\Model\Price","userid","id")->orderBy("id","desc")->take(1);
to allow me to see the users current price.
What I want to do now, is to select every user that has a current price of 100, but how do I do this? I know I could go for a left join, but as I read the documentation, it should be possible without a left join.
I've built a pseudo-query to explain what I'm after;
User::with("price")->where("prices.price","100")->get();
I've read through the documentation (Eloquent: Querying relationships), but that doesn't seem to be useful to my question.
I've also read several questions here on SO but unfortunately to no avail.
You may try this:
$currentPrice = 100;
$users = User::whereHas('price', function($query) use ($currentPrice) {
$query->where('price', $currentPrice); // price is the field name
})
->with("price")->get();
Since you have more than a single price for per user then you may also declare another relationship method to get all the price models instead of one and you may do it using something like this:
// In User model
public function prices()
{
return $this->hasMany("App\Model\Price", "userid", "id");
}
In this case, with::price will give you the last single record and with::prices will give you all the related prices. So, if you want then you may write something like the following to get all users with their all related prices who has the (latest/current) price of 100:
$currentPrice = 100;
$users = User::whereHas('price', function($query) use($currentPrice) {
$query->where('price', $currentPrice); // price is the field name
})
->with("prices") // with all prices
->get();
You can use the combination of whereHas() and with() as:
$users = User::whereHas("price", function($q) use ($currentPrice) {
$q->where("price", $currentPrice);
})
->with(["price" => function ($q) {
$query->where("price", $currentPrice);
})
->get();

Ordering data from a table with the help of other table by using relationships with laravel?

I have the controller with the query:
$Comments = Comment::orderBy('id_parent', 'asc')->get();
And I have the Comment model:
class comment extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function votes()
{
return $this->hasMany('App\Vote', 'comment_id', 'id_comment');
}
}
I want to retrieve the comments data sorted in a specific way, every comment has multiply votes voted by different users, so the count('vote') is the number of votes for each comment. The problem is that I am stuck with how to call the specific votes function in the model so that it can count the column vote and order it either asc or desc.
That in the end I can have the $Comments sorted also by the total number of votes.
You can try as:
$Comments = Comment::withCount('votes')->orderBy('votes_count', 'asc')->get();
withCount() method is used when you want to count the number of results from a relationship without actually loading them, which will place a {relation}_count column on your resulting models.
Using sortBy:
$comments=Comment::with('votes')->get()->sortBydesc('votes');
foreach($comments as $comment)
{
echo $comment->votes->count('vote');
}

Best way to do this listing laravel with relationship

What is the best way to do this listing?
I would not want to do it that way "ugly".
/**
* Get user indicateds
* #return array|null
*/
static public function indicateds()
{
$users = ModelUser::all();
foreach( $users as $user ) {
if( $user->financial->status_payment ) {
$newArray[] = $user;
}
}
return (isset($newArray) ? $newArray : null);
}
Thanks
You can use the collection's filter method:
return ModelUser::with('financial')
->get()
->filter(function($user) {
return $user->financial->status_payment;
});
I'm supposing you have defined the financial relation and you should eager load it as I did to improve the performance.
One of the benefits to relationships is that you can use them to modify your queries, as well. So, instead of getting all users into a Collection, and then filtering that Collection, you can use the relationship to modify the query so that you only get the desired records in the first place. This will reduce the number of records returned from the database, as well as the number of model instances that get created. This will save you time and memory.
$users = ModelUser::with('financial')
->whereHas('financial', function($q) {
// $q is the query for the financial relationship;
return $q->where('status_payment', true);
}
->get();
The with() is not required, but if you'll be accessing the financial relationship on the returned users, it is a good idea to eager load it.
The whereHas() is where the magic happens. It modifies the query so that it will only return users that have a related financial record that matches the conditions added by the closure used in the second parameter.
You can read more about it in the documentation here.

Laravel Get Collection for a Collection

I have 2 models, a User model and a Post model. I am trying to get a collection of Users that have posted between a certain date range. I am just lost on how to tie the 2 models together. There is a One-to-Many relationship between the two.
User Model:
public function posts()
{
return $this->hasMany('App\Models\Post', 'user_id');
}
So, I want a collection of users where posts are between date 1 & date 2 AND if a user has posts between those dates, I want a collection of those posts. I know I can use whereBetween for a query builder, but can I use that for eloquent? Also, how do I sort the entire User model by the ones that have those posts? How about the filter() method?
$users = Users::all();
$users = $users->filter(function() {
foreach ($users as $user)
{
if ($user->posts->whereBetween(date 1, date 2))
{
return true; //something like that
}
}
});
It's better to eager load your relation model as you wanted and then work with the result. no need to use the if checks.
$users = Users::with(['posts' => function($query) {
$query->whereBetween(date 1, date 2);
}])->get();
More Information: Eager Loading

Resources