Laravel Query Builder max function and relationships - laravel

this code
$maxsub = Invoice::with(['userinvoicesubaccount'])->where([['person_id',$id]])->get();
#foreach($maxsub as $max)
{{$max->userinvoicesubaccount->subaccount_name}} </font></div>
#endforeach
Get me all subaccount's names related to my list of Invoices with a predefined ID.
What i need is to get the only subaccount name related to my list of invoices with the highest amount (that existe in my invoice table) with a predefined ID.
Any help ? thank you

First calculate the invoice with the highest amount like so:
$maxInvoice = Invoice::with('userinvoicesubaccount')->where('person_id',$id)
->orderBy('amount', 'desc')->first();
Then get the related sub accounts for that invoice like so:
$maxInvoice->userinvoicesubaccount

You can add a subquery to your with function.
$invoices = Invoice::with(['userinvoicesubaccount' => function($query) {
$query->max('amount'); // Max function gives you highest value of a given column
}])
->where([['person_id', $id]])
->get();
#foreach($invoices as $invoice)
// You will still need to call first because you will have a collection
{{$invoice->first()->userinvoicesubaccount->subaccount_name}} </font></div>
#endforeach
Take a look at https://laravel.com/docs/5.7/eloquent-relationships#eager-loading

Related

Laravel sort by average of property of relationship

I'm wondering if what I'd like to achieve is possible with Eloquent. Say you have a Post model with a Comments model relationship, and the Comment model has a votes property.
I know you can get a Post collection with the Comments count with:
$posts = Post::withCount('comments')->get();
And that this is how to apply a votes filter on the relationship:
$posts = Post::whereHas('comments', function (Builder $q, $vote_count) {
$q->where('votes', '>=', $vote_count);
});
But what I would like to do, ideally in one query, is filter (using where()) and sort (using orderBy()) the posts collection on an average vote score.
To get that average for one Post I would first get the sum with sum('votes') and divide it by the withCount('comments') to get the average.
But I'm wondering if I can make a filter for a collection, all in one query, that filters on the average vote score.
You can get the Post comments votes average passing an array with the relationship name as key and an anonymous function as value. Inside the function you can perform a selectRaw querying the votes column average. Then you will have available that "column" (an alias actually) to do the where and the orderBy:
$value = 5;
Post::withCount(['comments as comments_avg_votes' => function($query) {
$query->select(DB::raw('avg(votes)'));
}])
->where('comments_avg_votes', '>', $value)
->orderByDesc('comments_avg_votes')
->get();

Conditional query based on aggregation fields

Given I have this simple query:
$builder = User::select(DB::raw('users.age as age, users.restrict as restrict'))
->whereBetween("user.id",$id)
->get();
Is there any way to get users where age is lower than restrict column ?
Since both age and restrict are columns, use the whereColumn() method. Also, it looks like you want to get only records with IDs that are in $ids array. So, use whereIn():
User::whereColumn('age', '<', 'restrict')->whereIn('id', $ids)->get();
This ->where("user_id", $id) returns only one user! does not it ?
$users = DB::table('users')->whereBetween("id",$ids)
->where("age",'<','restrict')->get();
Or
$users = User::where("age",'<','restrict')->whereBetween("id",$ids)->get();

Order by relationship column

I have the following query:
$items = UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->get();
I need to order it by item.type so I tried:
$items = UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy('item.type')
->get();
but I get Unknown column 'item.type' in 'order clause'
What I am missing?
join() worked fine thanks to #rypskar comment
$items = UserItems
::where('user_id','=',$this->id)
->where('quantity','>',0)
->join('items', 'items.id', '=', 'user_items.item_id')
->orderBy('items.type')
->select('user_items.*') //see PS:
->get();
PS: To avoid the id attribute (or any shared name attribute between the two tables) to overlap and resulting in the wrong value, you should specify the select limit with select('user_items.*').
Well, your eager loading is probably not building the query you're expecting, and you can check it by enabling the query log.
But I would probably just use a collection filter:
$items = UserItems::where('user_id','=',$this->id)
->where('quantity','>',0)
->get()
->sortBy(function($useritem, $key) {
return $useritem->item->type;
});
You can use withAggregate function to solve your problem
UserItems::withAggregate('item','type')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy('item_type')
->get();
I know it's an old question, but you can still use an
"orderByRaw" without a join.
$items = UserItems
::where('user_id','=',$this->id)
->where('quantity','>',0)
->orderByRaw('(SELECT type FROM items WHERE items.id = user_items.item_id)')
->get();
For a one to many relationship, there is an easier way. Let's say an order has many payments and we want to sort orders by the latest payment date. Payments table has a field called order_id which is FK.
We can write it like below
$orders = Order->orderByDesc(Payment::select('payments.date')->whereColumn('payments.order_id', 'orders.id')->latest()->take(1))->get()
SQL Equivalent of this code:
select * from orders order by (
select date
from payments
where order_id = payments.id
order by date desc
limit 1
) desc
You can adapt it according to your example. If I understood right, order's equivalent is user and payment's equivalent is item in your situation.
Further reading
https://reinink.ca/articles/ordering-database-queries-by-relationship-columns-in-laravel
I found another way of sorting a dataset using a field from a related model, you can get a function in the model that gets a unique relation to the related table(ex: table room related to room category, and the room is related to a category by category id, you can have a function like 'room_category' which returns the related category based on the category id of the Room Model) and after that the code will be the following:
Room::with('room_category')->all()->sortBy('room_category.name',SORT_REGULAR,false);
This will get you the rooms sorted by category name
I did this on a project where i had a DataTable with Server side processing and i had a case where it was required to sort by a field of a related entity, i did it like this and it works. More easier, more proper to MVC standards.
In your case it will be in a similar fashion:
User::with('item')->where('quantity','>',0)->get()->sortBy('item.type',SORT_REGULAR,false);
$users
->whereRole($role)
->join('address', 'users.id', '=', 'address.user_id')
->orderByRaw("address.email $sortType")
->select('users.*')
you can simply do it by
UserItems::with('item')
->where('user_id','=',$this->id)
->where('quantity','>',0)
->orderBy(
Item::select('type')
->whereColumn('items.useritem_id','useritems.id')
->take(1),'desc'
)
->get();

Join query in laravel with data manipulation send to view

I am having 2 tables, users and profiledetails
I am able to run Join query and access the data and send to view.
But when I am manipulation the field 'dob' (date format) in profiledetails table. No Success, Please check the code below ,
webpagesConroller:
$users = DB::table('users')
->join('profiledetails', 'users.id', '=', 'profiledetails.user_id')
->select('users.*', 'profiledetails.dob')
->get();
$age = $users->dob->diffInYears(Carbon::now());
return view('webpages.index',compact('users'))->with($age);
View:
<li class="cate_head">Age : {{ $age}}</li>
Error:
Trying to get property of non-object
I have model Profiledetails added the mutators as below,
public function getAge(){
return $this->dob->diffInYears(Carbon::now());
}
public function getDOB(){
return $this->dob->format('d-m-Y');
}
Can I not use this method on another controller for Ex- webpagesController, If yes How.
Since you are using a raw query, the data returned is not an object but an array with the data.
Also you did not limit the query, meaning it could return multiple rows.
You'll probably need to get the data from the $users as:
//with the possibily of multiple rows. Index is the row which has to be used
$users[index]['dob']
//when you are sure it only returns one row
$users[0]['dob'] // or $users['dob'] not sure as of the moment
You want to check the differences with Carbon.
So you will probably need to make a Carbon object of the dob result and check it against the Carbon::now.
$age = Carbon::createFromFormat('Y-m-d', $users[0]['dob'])->diffForHumans();
// diffForHumans() uses the local time
Note that this only gets the age for one row (the index or the first because of [0]).
If you want to do it for every row use a for or foreach where you set the $users->age for every $user. But as these are not object/models but raw data, this will not work.
Hope this helps your question a bit

Eloquent ORM select all with limit and column name

Hi how can I select limited number of all the item from model and the selection with certain names.
eg:
$product = Product::all()->limit(4)->select('id','name');
Most of example start with Product::find(1) but my case, I don't have id. Thanks
Use the other methods first, then call get at the end:
$products = Product::limit(4)->select('id', 'name')->get();
Limit Query Results
To limit the number of results returned from the query, or to skip a given number of results in the query, you may use the skip() and take() methods:
Example of User model which extends Eloquent:
$users = User::take(5)->skip(10)->get();
Product::all() will be used when get all data without any condition. You need to use get method.
you need to
$products = Product::select('id', 'name')->limit(4)->get();

Resources