Eloquent - Get Model with relationship as boolean - laravel

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
]);

Related

Laravel - Filter collection with closure on relationship

Wondering if there is a way to use closure on a relationship in order to filter the whole line if there isn't a value which corresponds?
Example code:
foreach ($paiementMethods as $value) {
$giftCards = GiftCard::with(['userEntered', 'userEmployee', 'client', 'payement', 'payement.payementMethods' => function($query) use ($value)
{
$query->where('id', $value);
}])
->where('date','>=', $dateStart)
->where('date','<=', $dateEnd)
->orderBy('updated_at', 'desc')->get();
$giftCardsCollection = $giftCardsCollection->merge($giftCards);
}
}
In this example, if $query->where doesn't find a row for the payement.payementMethods which matches the $value, I want the giftCard to return null. The result I get is the giftCard with only the relation on payement.payementMethods being empty.
My other solution would be to query the giftCard and after that checking the $value to see if I have to include the giftCard in the collection or not.
Best regards
I think using whereHas, it can be solved
$giftCards = GiftCard::with(['userEntered', 'userEmployee', 'client', 'payement', 'payement.payementMethods'])
->whereHas( 'payement.payementMethods', function($query) use ($paiementMethods) {
$query->whereIn('id', $paiementMethods);
})
->where('date','>=', $dateStart)
->where('date','<=', $dateEnd)
->orderBy('updated_at', 'desc')
->get();

Query relationship inside the relationship in Laravel Eloquent

I am trying to get the news of a writer according to date passed. I have written function so far:
public static function getNewsByAuthorByDate($id, $year, $limit = 1){
return User::where('id', $id)->has('news')
->with(['news' => function($q) use(&$limit, &$year) {
$q->where('created_at', 'like', '%' . $year . '%')->latest()->limit($limit);
}])
->first();
}
But the news[] is null. Is it possible to get writer news according to date or do I have to try another way?
you can use whereYear
public static function getNewsByAuthorByDate($id, $year, $limit = 1){
return User::where('id', $id)->has('news')
->with(['news' => function($q) use(&$limit, &$year) {
$q->whereYear('created_at',$year)->latest()->limit($limit);
}])
->first();
}
and to use limit with eager loading you should use the package eloquent-eager-limit
According to the documentation:
The limit and take query builder methods may not be used when constraining eager loads.
Also, You don't need to use has method, the news would be empty if the user has no news:
return User::with(['news' => function($q) use($year) {
$q->whereYear('created_at', $year)->latest();
}])
->find($id);

Laravel Eloquent - Having trouble querying relationship with ownership

I want to get all categories and within those categories, only items that belong to the logged in user. If there are none, the category should still be there.
This is what I tried. Still getting items from other users.
$categories = Category::parents()
->with(['lineItems' => function ($query) use($id) {
$query->where('user_id', $id);
}])->get();
Haven't been able to find anything that works. (using laravel 5.7)
Relationships
Category
public function lineItems()
{
return $this->hasMany(LineItem::class);
}
LineItems
public function category()
{
return $this->belongsTo(Category::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
I'm not sure where the ::parents() method is coming from (I can't find documentation for it anywhere; is it something you wrote?), but it seems like
$categories = Category::all()
->with(['lineItems' => function ($query) use ($id) {
$query->where('user_id', $id);
}])->get();
might work?
So, Travis's question made me think about my loop. My with was affecting my top level categories which left its children's line items without the where user. There's probably a more elegant method of doing this but this is what ended up working.
This queries the child's line items and the grandchild's line items.
$categories = Category::parents()->ordered()
->with(['children.lineItems' => function($q) use($id) {
$q->where('user_id', '=', $id);
}, 'children.children.lineItems' => function($q) use($id) {
$q->where('user_id', '=', $id);
}])->get();
Try this:
$categories = Category::parents()
->whereHas('lineItems', function ($query) use($id) {
$query->where('user_id', $id);
})->get();

Laravel, where, orwhere in main table an pivot table

I have two tables with belongsToMany relation: message_topics and users
The pivot table is message_topics_users and contains 2 columns: message_id and user_id.
In table message_topics, I have a field called sender_id
I'm trying to write the correct eloquent syntax to get all the records:
where message_topics.sender_id = $user_id
OR Message_topics_users.receiver_id = $user_id
I tried many things, like for instance:
$topics = MessageTopic::where('sender_id', $user_id)
->wherePivot('receiver_id', $user_id)->orderBy('sent_at','desc')->get();
Any idea?
You can use the whereHas method (or in this case the orWhereHas method):
$topics = MessageTopic::where('sender_id', $user_id)
->orWhereHas('users', function ($query) use ($user_id) {
$query->where('id', $user_id);
})
->orderBy('sent_at', 'desc')
->get();
I'm assuming you have two relationships from the topics? Since it's too arbitrary to use both columns and the same relationship... Like this
//On your MessageTopic model
public function sender(){
return $this->belongsToMany('App\User', 'message_topics_users', 'message_id', 'sender_id');
}
public function receiver(){
return $this->belongsToMany('App\User', 'message_topics_users', 'message_id', 'receiver_id'));
}
Then you can use whereHas and orWhereHas like this:
//Again assuming you have your User model loaded as $user
$topics = App\Topic::whereHas('sender', function($q) use($user){
$q->where('sender_id', '=', $user->id);
})
->orWhereHas('receiver', function($q) use($user){
$q->where('receiver_id', '=', $user->id
})
->orderByDesc('sent_at')
->get();
whereHas and orWhereHas both query the model (MessageTopic in this case) checking for the existence of the specified relationship (App\Topic::whereHas('sender')...). They also allow you to pass the constraint that you're looking for (function($q) use($user){ $q->... })
So it is basically saying "Give me ONLY the MessageTopics that have a Sender or Receiver with the id $user->id"

Laravel 4 Eager Loading constraints

I want to get all Items (topics) WITH their comments, if comments user_id = $id. I try something like this, but it isn't working. So if Item hasn't got any comment with user_id = $id, then I don't need this Item.
In DiscussionsItem model I have methode:
public function discussionsComments() {
return $this->hasMany('DiscussionsComment', 'discussionsitem_id');
}
My query in controller is like this:
$items = DiscussionsItem::whereBrandId($brand_id)
->whereBrandCountryId($country_id)
->with(['discussionsComments' => function($query) use ($id) {
$query->where('user_id', '=', $id);
}])
->whereHas('discussionsComments', function($query) use ($id) {
$query->where('user_id', '=', $id);
})
->with(['views' => function($query) use ($id) {
$query->where('user_id', $id)->count();
}])
->orderBy('created_at', 'DESC')->get();
My problem is that I get items with comments, where comments user_id != $id.
P.S. I need to take 5 comments, but I cant imagine how to do that, because ->take(5) in my eager load is not working.
You can do a custom scope, or just limit the amount returned by your relation:
public function discussionsComments() {
return $this->hasMany('DiscussionsComment', 'discussionsitem_id')
->latest()
->take(5);
}

Resources