How to do pagination in Many to Many in Eloquent? - php

I have two tables Posts and Tags with many to many relation. I need to retrieve all posts by some tag and paginate it. Is it possible to do with paginate method or I need to create a new instance of Paginator and do everything by hand?
P.S.
Need something like:
$tags = Tags::where('name', '=', $tag)->with('posts')->first();
$posts = $tags->posts->paginate(5);
return view('blog/posts')->with('posts', $posts);

You should probably use:
$posts = $tags->posts()->paginate(5);
and you should also rename $tags to $tag if you are getting only one record to not make variable name misleading.

You have to use a join statement. It should look something like this:
DB::table('tags')
->join('posts', 'tags.id', '=', 'posts.tag_id')
->where('tags.name', '=', $tag)
->paginate(5);

Related

Get filtered child from parent

I have a one to many relationship. So I can use this code to display all posts.
$tag = Tag::where('slug', $slug)->first();
$posts = $tag->posts;
It works correctly but I want to filter child to display. For example:
$posts = $tag::whereHas('posts', function($query){
$query->where('accept', 1)
})->get();
But it gets tags not posts. Any idea how I can solve my problem?
In Post model you have to define relation to tag like this
public function tags(){
return $this->hasMany(Tag::class);
}
and this is how you can get posts from specific tag
$slug = "my-slug";
$posts = Post::whereHas('tags', function($query) use ($slug){
$query->where('slug', $slug)
})->where('accept', 1)->get();
As mentioned by the documentation:
Since all relationships also serve as query builders, you can add further constraints to which comments are retrieved by calling the comments method and continuing to chain conditions onto the query:
$comment = App\Post::find(1)->comments()->where('title', 'foo')->first();
You can change your code to:
$posts = $tag->posts()->where('accept', 1)->get()
You can directly query the relationship.
You can use
$tag = Tag::where('slug', $slug)
->with(['posts' => function($q) {
$q->where('accept', 1);
}])->first();
$posts = $tag->posts;

Laravel 5 query with variable number of where clauses

I'm try to make a tag-based search system similar to the one here on stackoverflow using Laravel and jQuery Token-Input plugin. The contents of the "tags" are then to be used as the query itself, drawn from a list from a different table.
Using Eloquent, I want to build a query based on a variable number of tags in the search bar (limited only by the number of possible tags there can be). It would look something like this if not done as a loop:
$query = Model::whereHas('attribute', 'name', '=', 'tag1')
->whereHas('attribute', 'name', '=', 'tag2')
->whereHas('attribute', 'name', '=', 'tag3')
// Repeat until...
->get();
The 'attribute' in question is actually something drawn from a pivot table.
Obviously, I want it to be in the form of a loop since we're dealing with a variable number of tags. How would I go about doing this?
I suppose you can so something like this:
$query = Model::whereHas('attribute', 'name', '=', 'tag1');
foreach ($other_tags as $tag) {
$query = $query->whereHas('attribute', 'name', '=', $tag);
}
print_r($query->get());
The only hint here is that you need to use first tag to init Model::whereHas. Other tags can be iterated over and each one will be added to $query.
If you have tags in array like this
$tags = [ 'tag1', 'tag2', 'tag3', 'tag4', ...];
then you can simply use whereIn
$results = Model::whereHas('attribute', function($query) use ($tags) {
$query->whereIn('name', $tags);
})->get();

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

Laravel - Using a nested relationship with the query builder

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

Selecting and ordering data from a query using Laravel Eloquent

I am trying to use Laravel and eloquent to return results based on the following query.
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->orderBy('created_at', 'desc')
->paginate(5);
Ok, so that is fine, it return exactly what it should, all the blog posts with associated relations to other tables.
What I now want to do is return only the $blogPosts where a tag is clicked by the user. So let's say there is a tag "PHP", so I pass in that value as $tag to the method. I then have something like this.
public function tag_search($tag)
{
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->where('tags', $tag)
->orderBy('created_at', 'desc')
->paginate(5);
$categories = BlogCategories::with('blog_posts')->get();
$data = array('blogPosts' => $blogPosts, 'categories' => $categories,
);
return view('blog.index')->with($data);
}
Now my issue is actually relatively simple I guess, if the where clause was a column in the BlogPosts table it would work, I know this because I tried that.
However the above won't work as is, I can only use;
->where('x', y)
Where x is a field in the BlogPosts table. I want to return a set of values where the submitted $tag is the same as one associated to the tags attached to the blog posts.
Make sense? I think I am over thinking it the point I am just not thinking now :)
Add the column field 'tags' to your table and then these queries:
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->where('tags', '=', $tag)
->orderBy('created_at', 'desc')
->paginate(5);
$categories = BlogCategories::with('blog_posts')->get();
return view('blog.index', compact ('categories', 'blogposts'));
Ok so the answer was to use whereHas like this.
$blogPosts = BlogPosts::whereHas('tags', function ($query) use ($tag) {
$query->where('name', $tag);
})->orderBy('created_at', 'desc')
->paginate(5);
That returns a search based on the associated elements.

Categories