Laravel - condition on nested models - laravel

I'm writing a code to get nested objects in Laravel. I was wondering if it is possible to write conditions in hasMany or belongsTo.
Here is what I'm doing, that makes the question clear:
$posts = Post::where(
array(
'status' => 'active'
)
)
->orderBy('id', 'asc')
->with(['postResponsibilities' => function($query){
$query->where('status', 'active');
}])
->with(['postRequirements' => function($query){
$query->where('status', 'active');
}])
->with(['postSalaries' => function($query){
$query->where('status', 'active');
}])
->skip($limit * ($page - 1))->take($limit)->get();
So, I have to put nested queries to get only those records whose status is active.
In the Post model, I've written:
public function postRequirements(){
return $this->hasMany('App\Models\PostRequirement', 'post_id');
}
public function postResponsibilities(){
return $this->hasMany('App\Models\PostResponsibility', 'post_id');
}
public function postSalaries(){
return $this->hasMany('App\Models\PostSalary', 'post_id');
}
Is there a way such that I can define status condition inside the nested models?
So that I can write:
$posts = Post::where(
array(
'status' => 'active'
)
)
->orderBy('id', 'asc')
->with('postResponsibilities')
->with('postRequirements')
->with('postSalaries')
->skip($limit * ($page - 1))->take($limit)->get();
I hope the question is clear, thanks

What you can do is apply those conditions inside the relationship methods you put on the Post model, for example:
class Post
{
public function postRequirements() {
return $this->hasMany('App\Models\PostRequirement', 'post_id')
->where('status', 'active');
}
}

Yes it is possible to eager load multiple relationships.
See: https://laravel.com/docs/master/eloquent-relationships#eager-loading
In your case it would be something like:
Post::with('postResponsibilities', 'postRequirements', 'postSalaries')->where()....

Related

Laravel one of many relationship with argument

coming from this relationship from the docs:
https://laravel.com/docs/9.x/eloquent-relationships#advanced-has-one-of-many-relationships
/**
* Get the current pricing for the product.
*/
public function currentPricing()
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}
how can I make such an relation with a specific date?
The relation down below will work
/**
* Get pricing for the product of one specific date.
*/
public function priceOfDay(Carbon $date)
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', $date());
});
}
but how can I use it with Eloquent? How can I pass the date to this:
Product::with('priceOfDay')->get();
update
I now use the one to many relation with a closure
->with(['prices' => function ($query) use ($month) {
$query->where('published_at', '<', $month)
->orderByDesc('published_at')
->orderByDesc('id')
->first();
}])
it works with the little drawback of having a collection instead of an object as relation, but it fills my needs for the moment.
It would be nice if there was something like
->with(['relation', $param])
update 2
since there seems to bo no direct solution here the workarround i came up with:
->first() does not work in the query, you will end up getting all prices, so I finished with an each()
->with(['prices' => function ($query) use ($month) {
$query->where('published_at', '<', $month)
->orderByDesc('published_at')
->orderByDesc('id');
}])
->get()
->each(function ($product) {
$product->price = $product->prices->first()->price;
})

Eloquent - Get Model with relationship as boolean

My Model has the following one-to-many relationship
class Comment extends Model
{
public function likes(): HasMany
{
return $this->hasMany(CommentLike::class);
}
}
In my Controller I want to get all Comments alongside with a boolean value which indicates if the current user exists in the likes table
$result = Comment::with(['user', 'likes' => function($q) use($user){
$q->where('user_id', $user->id);
}])
->withCount('likes')
->where('post_id', $postId)
->get();
Currently the above query will return the correct result alongside with a row from the likes table if the user is found.
I'm looking for a proper way to return a boolean value to indicate if the user has liked the comment instead.
First, you can find how many likes does user make to the comment, in this way.
$likes = Comment::whereHas(['likes' => function($q) use ($user){
$q->where('user_id', $user->id);
})->where('post_id', $postId)->count();
Then using $likes variable value, you can conditionally create a boolean value and assign to new variable or same $likes variable.
$hasLike = $likes ? true : false;
You can make use of withExists() method. It's not one of the best documented ones, but I think it's quite simple.
In your example:
$result = Comment::with(['user', 'likes' => function($q) use($user){
$q->where('user_id', $user->id);
}])
->withCount('likes')
->where('post_id', $postId)
->get();
Consider changing it to:
$result = Comment::with(['user', 'likes'])
->withCount('likes')
->withExists(['likes' => function ($query) use ($user) {
$query->where('user_id', $user->id);
}])
->where('post_id', $postId)
->get();
Result model will contain additional bool property likes_exists where true means that $user liked the comment and false means that they didn't do it.
Here is how I achieved this
in my model.php file
public function likedByCurrentUser(){
return $this->hasOne(Like::class, "tweet_id", "id")
->where("user_id", auth()->user()->id);
}
likedByCurrentUser method will either return null if no rows match else it will return an eloquent object. So in the controller method we can do something like below to get a boolean.
In the controller method by using withExists
$tweets = TweetModel::with(["user", "likes", "comments.user"])
->withExists([
"likedByCurrentUser as liked_by_current_user" => function($result){
return $result === null;
}
])
->orderBy("updated_at", "desc")
->get();
return response()->json([
"tweets" => $tweets
]);

Laravel - Get array with relationship

I have an ajax call that returns an array:
$reports = Report::where('submission_id', $submissionID)
->where('status', 'pending')
->get(['description','rule']);
return [
'message' => 'Success.',
'reports' => $reports,
];
From this array, I only want to return the fields 'description' and 'rule'. However I also want to return the owner() relationship from the Report model. How could I do this? Do I have to load the relationship and do some kind of array push, or is there a more elegant solution?
You can use with() to eager load related model
$reports = Report::with('owner')
->where('submission_id', $submissionID)
->where('status', 'pending')
->get(['id','description','rule']);
Note you need to include id in get() from report model to map (owner) related model
you will have probably one to many relationship with Reports and owners table like below
Report Model
public function owner() {
return $this->belongsTo('App\Owner');
}
Owner Model
public function reports() {
return $this->hasMany('App\Report');
}
your controller code
$reports = Report::with('owner')->
where('submission_id', $submissionID)->where('status', 'pending')->get()
return [
'message' => 'Success.',
'reports' => $reports,
];
This is what I ended up going with:
$reports = Report::
with(['owner' => function($q)
{
$q->select('username', 'id');
}])
->where('submission_id', $submissionID)
->where('status', 'pending')
->select('description', 'rule','created_by')
->get();
The other answers were right, I needed to load in the ID of the user. But I had to use a function for it to work.

Callback function of with() returns empty collection

I have three tables: realties, room_types and realty_room_type:
realty_room_type
----------------
id
realty_id
room_type_id
room_type
----------------
id
code
In my Realty model I set a rooms() relationship:
public function rooms()
{
return $this->hasMany(Room::class);
}
I am trying to eager load the rooms() relationship using the with() method. I want to custom what is returned from the relationship, so I am passing a callback function like this:
$realty = Realty::
where('id', $realtyId)
->with([
'rooms' => function ($query) use ($realtyId) {
$query
->leftJoin('room_types', 'room_types.id', '=', 'realty_room_type.room_type_id')
->selectRaw('code, COUNT(*)')
->groupBy('code');
}
])
->get()
);
The problem is I get an empty collection when accessing the relationship using $realty->rooms. Any idea why?
However if I dump and die the statements of the callback function like this:
Realty::
where('id', $realtyId)
->with([
'rooms' => function ($query) use ($realtyId) {
dd($query
->leftJoin('room_types', 'room_types.id', '=', 'realty_room_type.room_type_id')
->selectRaw('code, COUNT(*)')
->groupBy('code'));
}
])
->get()
);
I get what I'd like to be in the rooms() relationship.
Thank you in advance.
You don't need to return inside callback function and call get(). Here you can find the details.
$realty = Realty::
where('id', $realtyId)
->with([
'rooms' => function ($query) use ($realtyId) {
$query
->leftJoin('room_types', 'room_types.id', '=', 'realty_room_type.room_type_id')
->selectRaw('code, COUNT(*)')
->groupBy('code');
}
])
->get();

Eloquent - How should I make a condition in a join table

I have a tournament table.
Each tournament hasMany Championships.
I want to get the tournament that match the championshipID = 333.
So, I do it :
$tournament = Tournament::with([
'championships' => function ($query) use ($request) {
$query->where('id', '=', 333);
},
'championships.settings',
'championships.category',
'championships.tree.user1',
'championships.tree.user2',
'championships.tree.user3',
'championships.tree.user4',
'championships.tree.user5'
])->first();
Example of 1 of my relations:
public function settings()
{
return $this->hasOne(ChampionshipSettings::class);
}
Tell me if you need all, to post it.
But as I put 1 eager loading relationship, I get all my tournaments instead of getting just one.
What Am I missing???
I think you're looking for whereHas() which will allow you to filter a model based on a related model's constraints. You should also use a subquery for the nested constraints if you're getting the related model more than once to avoid query duplication like:
$tournament = Tournament::whereHas('championships', function($query) use ($championshipId) {
return $query->where('id', $championshipId);
})
->with(['championships' => function ($query) use ($request) {
$query->where('id', '=', 333)
->with([
'settings',
'category',
'tree' => function($query) {
return $query->with('user1', 'user2', 'user3', 'user4', 'user5');
}]);
}])
->first();

Resources