Paginate a relationship? - laravel

Im trying to find a user and paginate their posts, I've tried:
User::find($id)->posts()->paginate();
This paginates the posts, but I lose the users information. I also need to get the users information out along with their posts.
How can I do this?

This can be done by passing a function when eager loading:
User::find($id)->with(['posts' => function($query) use ($limit) {
$query->paginate($limit);
}])->get();

Just inverse it
$posts = Post::where('user_id',$user_id)->with('user')->paginate();
Access user
foreach ($posts as $post){
$post->user;
}

I have not tested this but I believe it could work something like this
$user = User::find($id)
Send that to the view and then you can access the User as
{{ $user->property }}
and for his posts
#foreach ($user->posts()->paginate(15) as $post)
// do something
#endforeach
{{ $user->posts()->links() }}
UPDATE
However this is not the best practice.
You could also send both $user and $user->posts()->paginate() to the view.

Related

Laravel eloquent "with" the easy way

I have posted a related question a few hours ago but I can not understand why Laravel eloquent is so complicated... once again I read many posts on the subject and not one gave me the solution to a very simple request.
Here is the simple example where a post belongs to one article and one user only. So the relations are defined as below in the Post model:
public function article()
{
return $this->belongsTo(Article::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
I am quite new to Laravel eloquent... so I simply want to display a list of all the posts with their id, comment and related user name and article title as per relation defined in the model above. I believe that is quite simple and I just learned the "with" today.
So in the PostController, I have:
$posts = Post::orderBy($sortField,$sortOrder)
->with(['article','user'])
->get();
This gives me a collection with all the posts.
First question, do I have to manipulate the 2 related fields in such a way that they become easily accessible in the list view below? Or can I avoid this in controller in anyway by putting it in the query?
foreach ($posts as $post) {
$post->article = $post->article()->pluck('article');
$post->user = $post->user()->pluck('name');
}
In the above, the field $post->article lists all the article titles in an array (article field).
If I add [0] at the end, I get the first element of the array.
If I use [post->article_id] I get the wrong related title.
So nothing works the way I want... why is it so complicated???
Ultimately, here is what I need to do in the list view.
#foreach ($posts as $post)
<p>{{ $post->id }} : {{ $post->comment }} by {{ $post->name }} from article {{ $post->article }}</p>
#endforeach
By doing the query in query builder instead, I get what I want immediately... so how to do the same in eloquent?
$users = DB::table('posts')
->join('articles', 'articles.id', '=', 'posts.user_id')
->join('users', 'users.id', '=', 'posts.user_id')
->select('users.*', 'articles.article', 'users.name')
->get();
It is not clear what is making you misunderstand it, so here is an explanation.
First of all, you have two belongsTo relations user & article in your post model.
When you run this
$posts = Post::orderBy($sortField,$sortOrder)
->with(['article','user'])
->get();
You get a collection of Posts and with the article and the user in the attributes $post->article & $post->user respectively. That queryBuild will launch 3 queries, one for the posts, one for the articles and one final for the users.
Now, in your blade, you want to access the attribute article in your Article model wich is inside the Post model so you do it like this:
#foreach ($posts as $post)
.... from article {{ $post->article->article }}</p>
#endforeach
Notes: When you call $post->article() you get a query builder, so you follow it with a
first() => instance of a model / null
get() => Collection of instance of a model or empty collection
pluck() => array of the attribute plucked
value() => the value of the first element as if you used pluck
... (there are other ways to get results)
When you call $post->article without parenthesis, it will run :
first() if the relation is a belongsTo
get() if the relation is belongsToMany or HasMany or....
OR
It will give you the attribute if already fetched (either a collection or a model instance).
So, since you used with('article') in the first query (already fetched), $post->article will return an instance of the model Article and no new query will be run.

Pagination in relations using 'with'

I have some trouble with pagination. I need to take only one category with movies and paginate it. Now I write some code, but I don't think it's optimized.
$category = Category::with(['movies' => function ($query) {
$query->orderBy('id', 'desc')->paginate(18);
}])->where('slug', $slug)->first();
$catMoviesPaginate = $category->movies()->paginate(18);
You can do it with lazy loading as here I mention the code which can help to you.
$category = Category::where('slug', $slug)->first();
$movies= $category->movies()->paginate(18); //lazy loding.
return view('example', compact('category', 'movies'));
You can render the pagination in view file as well.
#foreach ($movies as $movie)
{{ $movie->id }}
#endforeach
{!! $movies->render() !!}
The most efficient way would be to create an inversed relation in movies for categories. Then you query the movies in X category by slug. You should end up with something like this
Movies::whereHas('categories', function($q) use($slug) {
$q->where('slug', $slug)
})->paginate(18);
You can take it a step further and create a scope in the Movies model to simplify your code. With the proper scope it can be as simple as Movies::inCategory($slug)->paginate(18)

Laravel Factories - How to save nested relationships

I was working with laravel factories and stuck with nested relation ships.
For example a user may have many posts and a post has many comments.
i was able to save posts with user by each() method but was not able to save comments with posts
$users = factory(App\User::class, 3)->create()
->each(function ($user) {
$user->posts()->saveMany(factory(App\Post::class, 5)->make());
});
as save() method accepts array so i must have to use make() method with posts but now i can't attach comments with the posts.
after hour of searching i could not fond the solution but now i have solved it.
I am posting it in answers for the fellows who are looking for the solution and please update me if there have more decent solution to this.
Thanks
Here is how i solve this:
$users = factory(App\User::class, 3)->create()
->each(function ($user) {
$user->posts()->saveMany(factory(App\Post::class, 5)->make());
});
foreach ($users as $user){
foreach ($user->posts as $post){
$post->comments()->saveMany(factory(App\Comment::class, 5)->make());
}
}

Attain many has-many-through

I need to attain latest comments from array of users through posts.
Users model
public function comments()
{
return $this->hasManyThrough('App\Comment', 'App\Post');
}
has-many-through
$user = users::where('id',5)->first();
$user->comments()->get()
many has-many-through
$user = users::where('role','member')->get();
How can I achieve this.
You might looking for the method with() in ORM to get your expected results.
eg. `User::where('field','SOMECONDTION')->with('comments');
so in your foreach on your view you simply get the comments data.
#foreach($users as $index => $user)
#foreach($user->comments as $j => $comment)
{{$comment->some_field}}
#endforeach
#endforeach

Retrieving data from belongsToMany relationship in Laravel

I have some problems with getting data from my relationship. I need the tags of some domains.
$domains = Domains::where('customer_id', Auth::user()->customers_id)->get();
There are all the domains I need. On my Domains model I have this belongsToMany relation to my pivot table.
public function tags() {
return $this->belongsToMany('App\Models\Tags_Domains', 'domain_tag', 'domains_id', 'tags_id');
}
I was able to get all the datas from my relation with this:
dd($domains[0]->tags);
That gave me all the data I wanted but only for the very first domain. But I want this for every domain, to pass this new array to my Blade template. I tried many things out but couldn't make it work. ( $collection error, trying to get properly of non-object ... )
Can someone help me there?
Controller Code:
$domains = Domains::where('customer_id', Auth::user()->customers_id)->get();
return view('defaultLayout.domains.tagsdelete', [
'domains' => $domains
]);
This is because you use $domains[0] and you get the first domain.
You must loop through them:
foreach($domains as $domain) {
foreach($domain->tags as $tag) {
var_dump($tag);
}
}
Edit:
If you need the tags in your view here is how:
#foreach($domains as $domain)
<p>{{ $domain->name }}</p> //where name could be any field that $domain has
#foreach($domain->tags as $tag)
<p>{{ $tag->name }}</p> //where name could be any field that $tag has
#endforeach
#endforeach
Glad I was helpful :)

Resources