Laravel - Using a nested relationship with the query builder - php

I am working on an API but its starting to get a bit slow now that the data is increasing. I am moving some of the queries so that they use the DB query builder.
I have my last one which has a nested query:
$artists = Artist::with('performances', 'performances.stage')->get();
I have got so far:
$artists = \DB::table('artists')
->leftJoin('performances', 'artists.id', '=', 'performances.artist_id')
->get();
But now need to do the second relationship which in the Performance model is:
public function stage()
{
return $this->hasOne('App\Models\Stage', 'id', 'stage_id');
}
Any help on how I do this?

yes you can use eloquent relationship with query builder like this
$artists = Artist::join('performances', 'artists.id', '=', 'performances.artist_id')
->all();
foreach($artists as $artist){
$data = $artist->stage()->first();
}

It is very well covered in the official documentation, please, refer to this section of Documentation
I think that you want to achieve something like this:
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
And also, please, read carefully this section

Related

Laravel OrderBy Nested Collection

I'm using a Roles package (similar to entrust). I'm trying to sort my User::all() query on roles.id or roles.name
The following is all working
User::with('roles');
This returns a Collection, with a Roles relation that also is a collection.. Like this:
I'm trying to get all users, but ordered by their role ID.
I tried the following without success
maybe because 'roles' returns a collection? And not the first role?
return App\User::with(['roles' => function($query) {
$query->orderBy('roles.id', 'asc');
}])->get();
And this
return App\User::with('roles')->orderBy('roles.id','DESC')->get();
None of them are working. I'm stuck! Can someone point me in the right direction please?
You can take the help of joins like this:
App\User::join('roles', 'users.role_id', '=', 'roles.id')
->orderBy('roles.id', 'desc')
->get();
Hope this helps!
You can make accessor which contains role id or name that you want to sort by.
Assume that the accessor name is roleCode. Then App\User::all()->sortBy('roleCode') will work.
Here's the dirty trick using collections. There might be a better way to achieve this(using Paginator class, I guess). This solution is definitely a disaster for huge tables.
$roles = Role::with('users')->orderBy('id', 'DESC')->get();
$sortedByRoleId = collect();
$roles->each(function ($role) use($sorted) {
$sortedByRoleId->push($role->users);
});
$sortedByRoleId = $sortedByRoleId->flatten()->keyBy('id');
You can sort your relations by using the query builder:
notice the difference with your own example: I don't set roles.id but just id
$users = App\User::with(['roles' => function ($query) {
$query->orderBy('id', 'desc');
}])->get();
See the Official Laravel Docs on Constraining Eager Loading
f you want to order the result based on nested relation column, you must use a chain of joins:
$values = User::query()->leftJoin('model_has_roles', function ($join)
{
$join>on('model_has_roles.model_id', '=', 'users.id')
->where('model_has_roles.model_type', '=', 'app\Models\User');})
->leftJoin('roles', 'roles.id', '=', 'model_has_roles.role_id')
->orderBy('roles.id')->get();
please note that if you want to order by multiple columns you could add 'orderBy' clause as much as you want:
->orderBy('roles.name', 'DESC')->orderby('teams.roles', 'ASC') //... ext
check my answer here:
https://stackoverflow.com/a/61194625/10573560

Sorting users through relation in laravel

i want to sort the users through voornaam(firstname). but im getting the data via a relation.
How do i make my query so that, the relation users are sorted by firstname by alphabet
my function:
public function sortfirstname($id) {
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get()->all();
//This query ^^
$toernooi = Toernooi::findOrFail($id);
dd($ingeschrevenspelers);
return view('adminfeatures.generatespelerslijst', compact('ingeschrevenspelers', 'toernooi'));
}
What i want to sort
any help is appreciated
thanks in advance
Writing code in your own language doesn't make it very easy for other developers to understand your code.
That being said, you can try the orderBy() method on your relationship
In your model where you define the relationship:
public function relationship()
{
return $this->belongsTo(SomeClass::class)->orderBy('name', 'DESC');
}
Don't fire all() function at the end thus obtaining a Collection instance of result
//query without the all function
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get();
//
$ingeschrevenspelers = $ingeschrevenspelers->sortBy('users.firstname');
An alternative to Jordy Groote's answer if you do not want to modify the Model class itself, you can query it with a closure.
$ingeschrevenspelers = UserToernooi::with(['users' => function($q) {
$q->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();
Reference: https://laravel.com/docs/5.3/eloquent-relationships#constraining-eager-loads
Sidenote: I don't think you need a ->all() when you already did a ->get()
$ingeschrevenspelers = UserToernooi::with(['users' => function($query){
$query->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();

Laravel ajax search with relations

I wanted to search the records after getting the relations. I tried with Laravel colletions. didnt work tough.
public function search($key){
$products = Product::with('unit', 'category', 'brand')->get();
$allproducts = collect($products);
$result = $allproducts->search($key);
Here I want the search to be done also based on category and brand.
If I cant you collection then how to do it the standard way.
I think using a query is the best approach since you dont need to load all products-categories-etc from the database and then try to find the matching ones.
Example query
Product::where('product_name', 'LIKE', "%$keyword%")
->orWhereHas('category', function ($q) use ($keyword) {
$q->where('category_name', 'LIKE', "%$keyword%");
//everything else you need to check
})->get();

Laravel 5 Model::with() behavior

I'm trying to get a simple list of products with a given category, using Laravel 5's (L5) Model::with() method. But it seems that L5 ignores the category where clause.
The relation in my Product model:
public function categories(){
return $this->belongsToMany('App\Category', 'categories_products');
}
In my Controller:
public function getByCategory($slug){
$return = Product::with(array('categories' => function($query) use ($slug){
$query->where('slug', 'like', $slug);
}))->paginate(60);
dd($return);
}
The result is a list of every product in my database, instead of just a list of those with the given category slug.
I'v tried to hardcode in some different where clauses, but all seems to be ignored. Am I missing something?
Eloquent doesn't use joins to query related data when using with(), but instead uses separate queries. In your example, it first fetches products and then fetches related categories.
You need to use has() or whereHas() to return only those products that have categories (slugs?).
public function getByCategory($slug){
$return = Product::has('categories')->with(array('categories' => function($query) use ($slug){
$query->where('slug', 'like', $slug);
}))->paginate(60);
dd($return);
}
Or:
public function getByCategory($slug){
$return = Product::whereHas('categories', function($query) use ($slug){
$query->where('slug', 'like', $slug);
})->paginate(60);
dd($return);
}
whereHas() adds a subquery that counts the number of relations. You should use DB::getQueryLog() to see the SQL that Eloquent produces. Makes it a lot easier to figure out what's going on!
Do you need to have an advanced subquery? If slug is defined as a column in product you can do this:
$return = Product::where('slug', 'like', $slug)->paginate(60);
dd($return);
Or if using the relations your query would look like this:
$return = Product::where('slug', 'like', $slug)->categories->paginate(60);
dd($return);

Query relationship Eloquent

I have News model, and News has many comments, so I did this in News model:
public function comments(){
$this->hasMany('Comment', 'news_id');
}
But I also have field trashed in comments table, and I only want to select comments that are not trashed. So trashed <> 1. So I wonder is there a way to do something like this:
$news = News::find(123);
$news->comments->where('trashed', '<>', 1); //some sort of pseudo-code
Is there a way to use above method or should I just write something like this:
$comments = Comment::where('trashed', '<>', 1)
->where('news_id', '=', $news->id)
->get();
Any of these should work for you, pick the one you like the most:
Eager-loading.
$comments = News::find(123)->with(['comments' => function ($query) {
$query->where('trashed', '<>', 1);
}])->get();
You can inject the parameter to query function by use($param) method, that allows you to use dynemic query value at runtime.
Lazy-loading
$news = News::find(123);
$comments = $news->comments()->where('trashed', '<>', 1)->get();
I couldn't help but notice, though, that what you're probably trying to do is handle soft deleting, and that Laravel has built-in functionality to help you with that: http://laravel.com/docs/eloquent#soft-deleting
You can do simply in your eloquent model file.
do like this :
public function comments_with_deleted()
{
return $this->belongsTo('Comments', 'id')->where('deleted', 1);
}
public function comments()
{
return $this->belongsTo('Comments', 'id');
}
call like this :
// for show comments with deleted
$comments = News::find(123)->with('comments_with_deleted');
// for show comments without deleted
$comments = News::find(123)->with('comments');
rmobis's answer was what I needed, but it throws an error in current Laravel 5. You have to use it as an associatve array now:
$comments = News::find(123)->with(
['comments' => function ($query) {$query->where('trashed', '<>', 1);}]
);
Took me some time to figure it out, hope this will help others.
Read more in Laravel's Docs (5.6): https://laravel.com/docs/5.6/eloquent-relationships#querying-relations

Categories