Laravel Eloquent - building 'where not' query with relationship - php

I have 5 database rows with the same client_id, 3 labelled completed, Yes.
This code pulls through 3 results as expected:
$indGoal = $client->indGoal()->where('completed','=','Yes')->get();
This code pulls through no results: I would expect 2.
$indGoal = $client->indGoal()->where('completed','!=','Yes')->get();
This question suggests adding ->orWhereNull('completed') - which works, but ignores the client_id relationship. The request brings through all non-Yes results, regardless of $client
My Client model for reference:
public function indGoal()
{
return $this->hasMany('App\Models\IndGoal');
}

You should group orWhere filters in a callback so they don't interfere with existing filters.
$indGoal = $client->indGoal()
->where(function ($query) {
$query->orWhere('completed', '!=', 'yes')
->orWhereNull('completed');
})
->get();
This way, the query builder knows any of the grouped conditions should be true and all other conditions are independent.

Related

Dynamic Property call giving error (Property [game] does not exist on the Eloquent builder instance.)

I have two models: Game and Game_Assignment. Game_Assignment tells whose job it is to play a game.
I am trying to count the number of Game_Assignment's that a user has their id on that also have a specific value on the Game model that it relates to. I'll just get into the Models/the code
Game Model Relationship:
public function assignments() {
return $this->hasMany('App\Models\Game_Assignment', 'game_id');
}
Game_Assignment Relationship:
public function game() {
return $this->belongsTo('App\Models\Game', 'game_id');
}
Where things are going wrong (in a queue job, if that makes a difference)
$gamesDue = Game_Assignment::where('statistician_id', $statistician->id)->game->where('stats_done', '!=', 'yes')->count();
I have also tried the following two things, neither worked:
$gamesDue = Game_Assignment::where('statistician_id', $statistician->id)->game()->where('stats_done', '!=', 'yes')->count();
and...
$gamesDue = Game_Assignment::where('statistician_id', $defaultStatistician->id)->with(['games' => function($query) {
$query->where('stats_done', '!=', 'yes');
}])->count();
None of these work, and the first one I showed threw an error:
Property [game] does not exist on the Eloquent builder instance.
Anyone have an idea of where I am going wrong? I am using this link as my reference https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
When using the query builder of your Game_Assignment model, you cannot simply switch context to the query builder of Game. You can only call ->game() or ->game after you retrieved one or many model instances of Game_Assignment with first() or get().
So, in your particular case, you were looking for whereHas('game', $callback) (where $callback is a function that applies constraints on the foreign table) in order to add a constraint on the foreign table:
use Illuminate\Database\Eloquent\Builder;
$gamesDue = Game_Assignment::query()
->where('statistician_id', $statistician->id)
->whereHas('game', function (Builder $query) {
$query->where('stats_done', '!=', 'yes');
})
->count();
Side note: a column (stats_done) that seems to hold a boolean value (yes/no) should be of boolean type and not string/varchar.

Apply where condition only to one occurrence

I'm building search functionality for my app. To simplify things:
there are two tables: shops and subscriptions
Each shop can have multiple subscription records, subscription has field expires_at. Now, I assume that shop has active subscription if subscription exsists and at least one of shop's subscripion expires_at date is bigger than now().
It is one of the conditions to the whole query. Here is code:
$shops = Shop::when($subscription, function($query, $subscription) {
$query->doesntHave('subscriptions')->orWhereHas('subscriptions', function($q) use ($subscription, $query) {
$query->where('expires_at', '<', now());
});
});
It doesn't work as expected because if i.e. shop has three related subscriptions and at least one of them is expired – it assumes that shop has no active subscription (even though it has).
I would need to implement some nested function inside or whereHas, I guess, to sort by expires_at desc and then limit to one and only then pass where expires_at clause, however I've no idea how.
And I rather need to stick with Eloquent Query Builder rather than DB facade or raw sql.
Basically, it is the same problem what wasn't answered here:
https://laracasts.com/discuss/channels/eloquent/latest-record-from-relationship-in-wherehas?page=1
Try this:
$shops = Shop::doesntHave('subscriptions')->orWhereHas('subscriptions', function ($query) {
$query->whereDate('expires_at', '<', now());
})->get();
try this :
$shops = Shop::WhereHas('subscriptions')->withCount('subscriptions as
active_subscriptions_count' => function ($query) {
$query->where('expires_at', '<', now());
}])->having('active_subscriptions_count', '>=', 3)->get();
Ok, so after some tries in raw sql I figured it out:
$query->select('shops.*')
->leftJoin('subscriptions', 'shops.id', 'subscriptions.shop_id')
->whereNull('subscriptions.id')
->orWhere('subscriptions.expires_at', '<', now())
->where('subscriptions.id', function($q) {
$q->select('id')
->from('subscriptions')
->whereColumn('shop_id', 'shops.id')
->latest('expires_at')
->limit(1);
});
It is not only faster than where exists clause but also gives me what I needed – only the "highest" subscription for given shop is under consideration.

Eloquent queries work separately but not together in a subquery

I'm trying to fetch some data with a subquery using Eloquent but dding returns nothing. Separately, this
$discountArticles = $discountTableItemIdIn
->where('recipient_type', '=', 'article')
->toArray();
or this
$discountArticles = $discountTableItemIdIn
->where('recipient_id', '=', $articleId)
->toArray();
work fine.
However when I try something like this, it fails (or rather, returns nothing):
$discountArticles = $discountTableItemIdIn->where(function ($subQuery) {
$subQuery
->where('recipient_type', '=', 'article')
->where('recipient_id', '=', $articleId);
})->toArray();
I know I can do separate queries on the same collection and do an array_merge but I'd like to get this way working instead. Not sure what's happening.
So $discountTableItemIdIn is a collection of the entire table? That means you're gonna need a different function, as the ->where() logic on a collection is different from how it functions on a builder (eloquent) instance.
Try using filter():
$discountArticles = $discountTableItemIdIn->filter(function ($item) use($articleId) {
return $item->recipient_type == "article" && $item->recipient_id == $articleId;
})->toArray();
What this will do is filter your $discountTableItemIdIn collection for records that have a type of article and a recipient_id of whatever $articleId contains, return a new collection and convert that to an array.
Just a note, this is quite inefficient; you should try to avoid loading the whole table into a collection and just query the table directly using the subquery logic in your question.

Search On Custom Query Result-Collection With Algolia - Laravel

I have collection that created with complicated laravel query and this query's result is too big. So i think i must use algolia. As i know, algolia gets the model table data to itself as json and serve from there.
$result = User::search("UserName")->get();
It needs to some model configurations like searchAs etc.. all are related with existing model and you can make search from model with search method (above example). What i want to ask is, i have complicated query and result has too many attributes that come from another tables (joined). I want to make search on my custom query result. Is it possible ?
My example query :
$friendShips = Friend::
join("vp_users as users","users.id","=","friendships.friendID")
->leftJoin("vp_friendships as friendshipsForFriend",function($join) use ($request)
{
$join->on("friendships.friendID","=","friendshipsForFriend.userID");
$join->on("friendshipsForFriend.friendID","=",DB::raw($request->userID));
})
->leftJoin("vp_videos_friends as videosFromFriendMedias",function($join)
{
$join->on("videosFromFriendMedias.userID","=","friendships.friendID");
$join->on("videosFromFriendMedias.friendID", "=" ,"friendships.userID");
$join->on("videosFromFriendMedias.isCalled", "=" , DB::raw(self::CALLED));
})
->leftJoin("vp_videos_friends as videosToFriendMedias",function($join)
{
$join->on("videosToFriendMedias.userID", '=', "friendships.userID");
$join->on("videosToFriendMedias.friendID", '=', "friendships.friendID");
$join->on(function($join){
$join->on("videosToFriendMedias.isCalled", '=', DB::raw(self::CALLED));
$join->orOn("videosToFriendMedias.isActive", '=', DB::raw(self::ACTIVE));
});
})
->leftJoin("vp_videos_friends as
//some join rules too
})...
I believe the best way would be to use this request and chain the searchable() method. It will index the collection returned by the query to Algolia.
$friendShips = Friend::
join("vp_users as users","users.id","=","friendships.friendID")
->leftJoin("vp_friendships as friendshipsForFriend",function($join) use ($request) {
$join->on("friendships.friendID","=","friendshipsForFriend.userID");
$join->on("friendshipsForFriend.friendID","=",DB::raw($request->userID));
})
->searchable();

Select all records where a relationship count is zero using Eloquent?

In plain English: I have three tables. subscription_type which has many email_subscriptions which has many emails.
I'm trying to select all email_subscription records that have a particular subscription_type, that also don't have any associated email records that have a status of Held.
The particular bit I am stuck on is only returning email_subscriptions which have zero emails (with an additional where clause stacked in there described above).
Using Eloquent, I've been able to get a bit of the way, but I don't have any idea how to select all the records that have a relationship count of zero:
$subscriptionsWithNoCorrespondingHeldEmail = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})-; // What do I chain here to complete my query?
Additionally, is this even possible with Eloquent or will I need to use Fluent syntax instead?
You can use the has() method to query the relationship existence:
has('emails', '=', 0)
Eg:
$tooLong = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})->has('emails', '=', 0)->get();
Edit
You can do more advanced queries with the whereHas() and whereDoesntHave() methods:
$tooLong = EmailSubscriptions::whereHas('subscriptionType', function($q) {
$q->where('name', 'New Mission');
})
->whereDoesntHave('emails', function ($query) {
$query->where('status', '=', 'whatever');
})->get();
OK what I have under stand from your Question is you would like to have a All Emails which have
specific subscription_type, Zero(0) association and status = status
If yes so you canuse array in where statement.
Like:
$q->->where(array('status' => 'status','subscription_type'=>'what ever you want));

Categories