Conditional query based on aggregation fields - laravel

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

Related

How Can I Understand Eloquent orderBy() Works?

Please help me understand how orderBy works. Look at the following code.
$posts = Post::orderBy('title','asc')->get();
When I use orderBy('title','asc') does it mean I receive all of the Post records and put them into
$posts and then order them by title ascending? I'm confused with orderBy(). I remember
when we want to receive all the records we should type "all" after Post so how does orderBy() do that?
Exactly, orderBy method allows you to sort the result of the query by a given column.
If using orderBy your query should look like:
$posts = Post::orderBy('title','asc')->get();
When using all() you query would be:
$posts = Post::all();
Yes, it's exactly what you said.
The orderBy method allows you to sort the result of the query by a given column. The first argument to the orderBy method should be the column you wish to sort by, while the second argument controls the direction of the sort and may be either asc or desc:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
Take a look at the docs to see more info about this

Laravel i will get integer in DB query

after returning $query from this:
$query = DB::table('pets')->select('id')->where('id', '=', $pet->id)->where('user_id', '=', Auth::id())->get();
for example i get this result : [{"id":"66"}]
how can i get only 66 as integer?
Thanks!
Instead of get() which returns the entire collection of selected data, use value('id') to get the first value of the id field. You also wouldn't need select('id') if you use this method.

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

Laravel Eloquent orWherePivot Not Returning Expected Result

I have an eloquent query where I am not getting the expected results and I was hoping someone could explain to me what the correct way to write the query.
I have three tables:
records (belongsToMany users)
users (belongsToMany records)
record_user (pivot)
The record_user table also has a column for role.
I attempt to get all the records where the user has the role of either singer or songwriter:
$results = User::find(Auth::user()->id)
->records()
->wherePivot('role', 'singer')
->orWherePivot('role', 'songwriter')
->get();
Below is how the SQL syntax is generated:
select `records`.*, `record_user`.`user_id` as `pivot_user_id`,
`record_user`.`record_id` as `pivot_record_id` from `records`
inner join `record_user` on `records`.`id` = `record_user`.`property_id`
where
`record_user`.`user_id` = '1' and `record_user`.`role` = 'singer' or
`record_user`.`role` = 'songwriter'
The results for singer role are what is expected: All records where the user is the singer. The problem is the results for the songwriter: I am getting ALL songwriters and the query is not constrained by the user_id. For some reason I was expecting the songwriter role to also be constrained by the user_id - what is the correct way to write this using the eloquent syntax?
Hmm..I think you need to use an advanced where clause.
$results = Auth::user()
->records()
->where(function($query) {
$query->where('record_user.role', '=', 'singer')
->orWhere('record_user.role', '=', 'songwriter');
})
->get();
It is Laravel issue. In my case I solve it like this:
$results = Auth::user()
->records()
->wherePivot('role', 'singer')
->orWherePivot('role', 'songwriter')
->where('user_id', Auth::id())
->get();
Or use advance where clause
If your trying to get records I would do something similar to this:
User::find(Auth::user()->id)
->records()
->where(function($q){
$q->where('records.role', 'singer')
->orWhere('records.role', 'songwriter');
})->get();

Laravel Eloquent: Ordering results of all()

I'm stuck on a simple task.
I just need to order results coming from this call
$results = Project::all();
Where Project is a model. I've tried this
$results = Project::all()->orderBy("name");
But it didn't work. Which is the better way to obtain all data from a table and get them ordered?
You can actually do this within the query.
$results = Project::orderBy('name')->get();
This will return all results with the proper order.
You could still use sortBy (at the collection level) instead of orderBy (at the query level) if you still want to use all() since it returns a collection of objects.
Ascending Order
$results = Project::all()->sortBy("name");
Descending Order
$results = Project::all()->sortByDesc("name");
Check out the documentation about Collections for more details.
https://laravel.com/docs/5.1/collections
In addition, just to buttress the former answers, it could be sorted as well either in descending desc or ascending asc orders by adding either as the second parameter.
$results = Project::orderBy('created_at', 'desc')->get();
DO THIS:
$results = Project::orderBy('name')->get();
Why?
Because it's fast! The ordering is done in the database.
DON'T DO THIS:
$results = Project::all()->sortBy('name');
Why?
Because it's slow. First, the the rows are loaded from the database, then loaded into Laravel's Collection class, and finally, ordered in memory.
2017 update
Laravel 5.4 added orderByDesc() methods to query builder:
$results = Project::orderByDesc('name')->get();
While you need result for date as desc
$results = Project::latest('created_at')->get();
In Laravel Eloquent you have to create like the query below it will get all the data from the DB, your query is not correct:
$results = Project::all()->orderBy("name");
You have to use it in this way:
$results = Project::orderBy('name')->get();
By default, your data is in ascending order, but you can also use orderBy in the following ways:
//---Ascending Order
$results = Project::orderBy('name', 'asc')->get();
//---Descending Order
$results = Project::orderBy('name', 'desc')->get();
Check out the sortBy method for Eloquent: http://laravel.com/docs/eloquent
Note, you can do:
$results = Project::select('name')->orderBy('name')->get();
This generate a query like:
"SELECT name FROM proyect ORDER BY 'name' ASC"
In some apps when the DB is not optimized and the query is more complex, and you need prevent generate a ORDER BY in the finish SQL, you can do:
$result = Project::select('name')->get();
$result = $result->sortBy('name');
$result = $result->values()->all();
Now is php who order the result.
You instruction require call to get, because is it bring the records and orderBy the catalog
$results = Project::orderBy('name')
->get();
Example:
$results = Result::where ('id', '>=', '20')
->orderBy('id', 'desc')
->get();
In the example the data is filtered by "where" and bring records greater than 20 and orderBy catalog by order from high to low.
Try this:
$categories = Category::all()->sortByDesc("created_at");
One interesting thing is multiple order by:
according to laravel docs:
DB::table('users')
->orderBy('priority', 'desc')
->orderBy('email', 'asc')
->get();
this means laravel will sort result based on priority attribute. when it's done, it will order result with same priority based on email internally.
EDIT:
As #HedayatullahSarwary said, it's recommended to prefer Eloquent over QueryBuilder. off course i didn't encourage using QueryBuilder and we all know that each has own usecases.
Any way so why i wrote an answer with QueryBuilder? As we see in eloquent documents:
You can think of each Eloquent model as a powerful query builder allowing you to fluently query the database table associated with the model.
BTWS the above code with eloquent should be something like this:
Project::orderBy('priority', 'desc')
->orderBy('email', 'asc')
->get();

Resources