I have two models:
User
Post
Comment
User has many posts. Post has many comments and Comment belongs to a Post. That is simple.
Now I want to load all the post of authenticated user with its comments if the post has comment created today. If the post doesn't have comment today, it will not be loaded. The posts will also be paginated. How to build the query in laravel eloquent?
I tried something like this:
Auth::user()->posts->load(['comments' => function($query) {
$query->where('created_at',Carbon::today());
}]);
The issue in your query is that you are already executing it. Check this part:
Auth::user()->posts->...
^^^^^^^^
You are accessing the relationship as a property. When you do that the query is already being executed. What you need to do is to limit the results before executing the query. To do that, access the relationship but as a function, this way you receive an instance of the Query Builder to apply all your constrains before executing the query (with ->get() in this example). To limit a relationship with conditions, you can make use of the whereHas():
$posts = Auth::user()->posts()->whereHas(['comments' => function($query) {
$query->where('created_at', '>=', today());
}])
->get();
In the above code, you'll receive all the posts that has at least one comment today. But you are yet to receive the comments attached to them. To do so, you need to eager load the relationship with the with() method.
$posts = Auth::user()->posts()->whereHas('comments', function($query) {
$query->where('created_at', '>=', today());
})
->with('comments') // <--
->get();
That should do it. But given that you need the elements paginated, exchange the get() with paginate() method:
$posts = Auth::user()->posts()->whereHas('comments', function($query) {
$query->where('created_at', '>=', today());
})
->with('comments')
->paginate(15); // <---
Related
I'm creating a SNS app, in which User and Post are in many-to-many relationship, with a pivot table Favorite. Is there way to fetch all posts you didn't faved?
$posts = Post::wherehas("users",function($q)use($your_id){
$q->where('user_id', '!=', $your_id);
})->orDoesntHave('users')->get();
return $posts;
The above method was what I came up with, but it seemed not to work when more than 2 users faved the same post.
you need to fetch all posts which not related with users favorites thats mean dont have relationship with specific user , so you can use whereDoesntHave like this :-
$posts = Post::whereDoesntHave("users",function($q)use($your_id){
$q->where('user_id', '!=', $your_id);
})->get();
return $posts;
I want to retrieve a collection of data, which is ordered by the start_date of the relation
Basically I want to achieve this, with Laravel Models (the code below works perfectly)
$posts = DB::table('posts')
->leftJoin(
'threads',
'posts.id',
'=',
'threads.postable_id'
)
->where('threads.postable_type', '=', 'App\Post')
->orderBy('threads.start_date')
->paginate($request->input('limit', 2));
So in this case, I'm fetching ALL Posts and those are ordered by the start_date of the thread relation.
Those are not my actual tables but this works perfectly!
Because I'm using https://laravel.com/docs/8.x/eloquent-resources this is not the ideal solution to retrieve sorted data.
So instead I want to use the orderBy clause somewhere here
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now());
});
But I just cannot make this work. I've tried this
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
and I also appended this to the actual relation:
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
If you look at your code:
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
the whereHas will only return Post associate with a thread which the function return true.
Try this:
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
This will fetch all Post with Thread only if they have at least one Thread and then orderBy the start_date of the Thread.
You don't have to do the whereHas function because when you call ->with('thread') it'll use you this :
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
whereHas doesnt retrieve the relationship.
If you need even more power, you may use the whereHas and orWhereHas methods to define additional query constraints on your has queries, such as inspecting the content of a comment: Laravel whereHas
Don't do :
$posts = Post::with('thread')->orderBy('thread.start_date');
If there is no thread on some post, post without thread will be fetch with value null for their key thread and you will have an unexpected result when you try to orderBy.
First of all I want to thank Elie Morin for his help but I found out that I definitely need to use joins for that task.
In my example, I wanted to order the main query (posts) by the relation's start_date
Doing what you suggested
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
Would only order the thread by start_date and not the ENTIRE query.
Which is why I came up with something like this:
$posts = Post::has('thread')
->select('posts.id')
->leftJoin(
'thread',
'posts.id',
'=',
'thread.postable_id'
)
->where('thread.postable_type', '=', 'App\Post')
->where('thread.end_date', '>=', Carbon::now())
->orderBy('thread.start_date')
->with('thread');
return PostResource::collection($posts->paginate(2));
I'm trying to understand some advanced eloquent commands and on the Laravel official documentation, there is not so much about the Eloquent orWhereHas method and there isn't also an example about how it works.
https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Can somebody help me to understand it with also a simple example?
How to use it: just chain as any other Eloquent method
User::whereHas(...)->orWhereHas(...)->get();
When to use it: imagine you have Users, Posts and Comments, and each user can write posts and comments. Then you need to get active users. For example, you assume active as user, who has made posts OR comments last 7 days. So, you can get it this way:
$users = App\Models\User::query()
->whereHas('posts', function (Builder $query) {
$query->where('created_at', '>=', Carbon::now()->subDays(7));
})
->orWhereHas('comments', function (Builder $query) {
$query->where('created_at', '>=', Carbon::now()->subDays(7));
})
->get();
Say there's a blog kind of app. The main entity/model of the app would be Post (Blog Post).
When any author writes and publishes a Post,
visitors to the blog site can leave Comment(s) for the Post
visitors can Like a Post
So we have 3 models here
Post - which can have many Comment(s)
Post - can have many Like(s)
Now let's say for some reason we want to get all Post records from the database which either have 10 or more comments in the current month or 3 or more likes in the current month
We can write a query like
$posts = Post::whereHas('comments', function($query) {
$query->where('created_at', '>', now()->startOfMonth();
}, '>=', 10)
->orWhereHas('likes', function($query){
$query->where('created_at', '> ', now()->startOfMonth();
}, '>=', 3)
->get();
Laravel docs: https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Just like where both whereHas and orWhereHas accepts closure as 2nd argument for more fine grained query control.
Actually whereHas is supposed to be used when you want to have more power on constraints.
If you just want to check the existence of relation records you can use has for eg:
Get all post records which either have comment or like and paginate 20 per page
$postsWithCommentsOrLikes = Post::has('comments')
->orHas('likes')
->paginate(20);
Im trying to make a query using whereHas with eloquent. The query is like this:
$projects = Project::whereHas('investments', function($q) {
$q->where('status','=','paid');
})
->with('investments')
->get();
Im using Laravel 5.2 using a Postgres driver.
The Project model is:
public function investments()
{
return $this->hasMany('App\Investment');
}
The investments model has:
public function project() {
return $this->belongsTo('App\Project');
}
The projects table has fields id,fields...
The investments table has the fields id,project_id,status,created_at
My issue is that the query runs and returns a collection of the projects which have at least one investment, however the where clause inside the whereHas is ignored, because the resulting collection includes investments with status values different than paid.
Does anyone has any idea of what is going on?
I believe this is what you need
$projects = Project::whereHas('investments', function($q) {
$q->where('status','=','paid');
})->with(['investments' => function($q) {
$q->where('status','=','paid');
}])->get();
whereHas wil check all projects that have paid investments, with will eagerload all those investments.
You're confusing whereHas and with.
The with method will let you load the relationship only if the query returns true.
The whereHas method will let you get only the models which have the relationship which returns true to the query.
So you need to only use with and not mix with with whereHas:
$projects = Project::with(['investments' =>
function($query){ $query->where('status','=','paid'); }])
->get();
Try like this:
$projects = Project::with('investments')->whereHas('investments', function($q) {
$q->where('status','like','paid'); //strings are compared with wildcards.
})
->get();
Change the order. Use with() before the whereHas(). I had a similar problem few weeks ago. Btw, is the only real difference between the problem and the functional example that you made.
I know, we can do this in the controller:
User::with('post')->get();
It will get every user's post from the database, based on users.id.
But the problem is, I want to do this:
User::with(['post' => function($query) {
# Throw users.id here...
}])->get();
How to do that?
You should get the users first, and then load related posts with a separate query and merge them manually.
$users = User::get();
$posts = Post::whereIn('user_id', $users->pluck('id'))->get(); // Get your additional data in this query
$users->each(function ($user) use ($posts)
{
$user->posts = $posts->where('user_id', $user->id);
});
Note: I did not test the code above. It's just an example to show you how to accomplish what you are trying to do.