Eloquent select with() based on foreign key - laravel

I have a table with user data (users) and a table with prices (prices).
My prices table can contain multiple prices pr. user since I want to keep historical data.
I've defined my relation as a one-to-one
$this->hasOne("App\Model\Price","userid","id")->orderBy("id","desc")->take(1);
to allow me to see the users current price.
What I want to do now, is to select every user that has a current price of 100, but how do I do this? I know I could go for a left join, but as I read the documentation, it should be possible without a left join.
I've built a pseudo-query to explain what I'm after;
User::with("price")->where("prices.price","100")->get();
I've read through the documentation (Eloquent: Querying relationships), but that doesn't seem to be useful to my question.
I've also read several questions here on SO but unfortunately to no avail.

You may try this:
$currentPrice = 100;
$users = User::whereHas('price', function($query) use ($currentPrice) {
$query->where('price', $currentPrice); // price is the field name
})
->with("price")->get();
Since you have more than a single price for per user then you may also declare another relationship method to get all the price models instead of one and you may do it using something like this:
// In User model
public function prices()
{
return $this->hasMany("App\Model\Price", "userid", "id");
}
In this case, with::price will give you the last single record and with::prices will give you all the related prices. So, if you want then you may write something like the following to get all users with their all related prices who has the (latest/current) price of 100:
$currentPrice = 100;
$users = User::whereHas('price', function($query) use($currentPrice) {
$query->where('price', $currentPrice); // price is the field name
})
->with("prices") // with all prices
->get();

You can use the combination of whereHas() and with() as:
$users = User::whereHas("price", function($q) use ($currentPrice) {
$q->where("price", $currentPrice);
})
->with(["price" => function ($q) {
$query->where("price", $currentPrice);
})
->get();

Related

Many to many Eloquent

Ok so I tried whith your answers, I can return Invoices associated to a user but i cant get the products inside this invoice, I want to get the Invoice associated to the user with his products only.
If i mention the id of the user who creates a product and the id of the invoice, i want the query to return me the invoice with the products associated to the id of the user mentionned.
when Iam using this :
$Invoices = Commande::find(1)->whereHas('Product', function($query){
return $query->where('products.user_id', 3);
})->with('Product')->get();
I get only the Invoices.
But i want the Invoice and the products in this invoice that are associated to a certain user.
That's where the whereHas() method comes into place. You should be able to do something like this:
$user = User::find($userId); // Let's assume `$userId` is `1`
$invoices = Invoice::whereHas('products', function ($query) use ($user) {
return $query->where('products.user_id', $user->id);
})->get();
That will return all Invoice records that are associated with the $user record returned by User::find($userId) (again, in this case, user_id: 1)
You can see the full documentation here:
https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence
Note: You might need to adjust the model/relationship names I used in the code above to fit your needs, but the basic idea remains the same.
You can use this query to get invoices and their products belonging to a specific user
// my user id is currently authenticated, user
Invoice::query()->where('user_id', auth()->user()->id)->with('products')->paginate();

how to get list of users who are not under belongsToMany relation under a table in laravel?

Consider the following case.
we have the Users table and Tasks table. they are in relation with belongsToMany with table task_user.
How to get the list of all users who are not under any task? i.e. their user_id is not at all under that given task or even in the task_user table.
why I need this is because like this we can only provide a list of users who are yet to be assigned a task. the task will be assigned to users and not a single user at a time.
Editing_____________
also how to filter with users based on group table? below is not working
$users = Group::with(['subscribers' => function ($q){
$q->doesntHave("tasks");
}])->whereId($gid)->latest()->get();
Assuming you've named your relationships properly, you should be able to use doesntHave("tasks"):
$tasklessUsers = User::doesntHave("tasks")->get();
doesntHave() checks for the non-existence of the supplied relationship ("tasks", in this case) and returns all objects that pass this check.
If your function name is different, use that, but the relationship should be:
User.php:
public function tasks(){
return $this->belongsToMany(Task::class, "task_user");
}
Edit: doesntHave() is the simple version, whereDoesntHave() allows a custom query. See https://laravel.com/docs/5.8/eloquent-relationships#querying-relationship-absence for full details.
Second Edit:
As stated in the comments below, with() will not filter the Model it is being called on, so this query won't work as you'd expect:
$users = Group::with(['subscribers' => function ($q){
$q->doesntHave("tasks");
}])->whereId($gid)->latest()->get();
To fix this, use a chained doesntHave() query:
$query = Group::doesntHave('subscribers.tasks')
->where('id', '=', $gid)
->latest()
->first();
// OR
$query = Group::whereHas('subscribers', function($subQuery){
$subQuery->doesntHave('tasks');
})->where('id', '=', $gid)
->latest()
->first();
$users = $query->subscribers; // Return `users` (aliased to `subscribers`)
Either approach will check the existence of subscribers that don't have any associated tasks relationship, and also only return where id is $gid.
Note: Used first() for the queries, as using id in a query should only ever return a single Group record, and get() is for returning multiple records in a Collection

OneToMany + ManyToMany

I have 3 main tables (sellers, stores, products), and there is another table for relation between stores and products (store_product)
A seller has many stores (One to Many relationship)
A store has many products, but any of those products can be assigned to multiple stores, maybe in another seller's stores (Many To Many relationship)
Now, I have a confusion, I want to get all products for a specific seller.
If you defined the reserve of the relationships, you can do:
// your seller's id
$seller_id = 1;
// get your products
$products = Product::whereHas('stores.seller', function ($query) use ($seller_id) {
$query->where('id', $seller_id);
})->get();
Update
To get the count of products under every seller, you could use the withCount() method, just like this:
$sellers = Seller::withCount(['stores' => function ($query){
$query->withCount('products');
})->get();
which will place a {relation}_count column inside the stores relationship of your resulting models. In this case, products_count:
foreach ($sellers as $seller) {
echo $seller->stores->first()->products_count;
}
What you need is the builder function whereHas('relation', $callback). With it your query is very straight forward:
$products = Product::query()
->whereHas('stores.seller', function ($query) use ($sellerId) {
$query->where('sellers.id', $sellerId);
})
->get();
Apparently using sellers.id (where sellers is the table name) is important because you most likely have a column called id on all three tables. If you omit the table name, the query will fail.

How to do scope with subquery

I have a model Partner_deal which has lots of fields but the only one you really need to know about is called quantity which is an integer that specifies how many times the deal can be redeemed.
I then have another model Partner_deal_redemption to keep track of the redemptions. This has a partner_deal_id column and a user_id column to record which users have redeemed which deal.
I want to create a scope in my Partner_deal model so that it will only return deals where the number of redemptions is less than the quantity field.
I know how to do this in MySql by doing a subquery that counts how many redemptions each deal has had and uses a HAVING clause to filter out the ones where the number of redemptions = quantity.
I have no idea where to begin doing this in eloquent, this is my best attempt:
function scopeNotRunOut($query)
{
return $query->having('quantity', '>', function($q)
{
$q->from('partner_deal_redemptions')
->selectRaw('count(*)')
->where('partner_deal_id', '=', 'id');
});
}
You can probably use the has() function of Eloquent:
http://laravel.com/docs/5.0/eloquent#querying-relations
function scopeNotRunOut($query)
{
return $query->has('redemptions', '<', DB::raw('quantity'));
}
To use this function you need to define the redemptions function in your Partner_deal model, which will represent the relation between the Partner_deal and the Partner_deal_redemption models.
MrShibby was correct but then I realised I needed to check if quantity was null as well so I modified his solution slightly to the following:
function scopeNotRunOut($query)
{
return $query->where(function($q) {
$q->orHas('redemptions', '<', DB::raw('quantity'))
->orWhere('quantity', '=', null);
});
}

Laravel 4.2 - Use where clause on related tables

I am working with two models, UserType and User - UserType hasMany User.
I am trying to retrieve a list of Users associated with a UserType that has the property receive_email set to 1 (true).
I have tried:
$userGroups = UserType::with(['Users' => function($query) {
$query->whereReceiveEmail(1)->whereNotNull('email')->whereNull('status');
}])->whereIn('id', [10, 1])->get();
and the Where clause seems to be totally ignored. From the Laravel 4.2 docs -
$users = User::with(array('posts' => function($query)
{
$query->where('title', 'like', '%first%');
}))->get();
I have seen many people say that this is not the correct way to use eager loading constraints but I really do not know what that would be, they do not seem to do anything. So, the short question, how can I retrieve a listing of Users with receive_email set to 1 through the UserType relation?
UPDATE
Can someone explain to me what the example code from the docs above is supposed to do? I'm assuming that it is supposed to return Posts associated with Users that match the constraint of having a title LIKE "first." In my case, I'm trying to find Users associated with UserTypes where each User has receive_email set to 1. The only significant differences between my code and the example code is that I am applying whereIn() and the model names are different.
So, with the results from the example, would the following be true?
foreach ($users as $user) {
foreach ($user->posts as $post) {
// matching posts with titles LIKE "first"
}
}
If you're after a list of users, then I suggest you actually start with that model and make use of whereHas to filter by user type:
$users = User::where('receive_email', 1)
->whereNotNull('email')
->whereNull('status')
->whereHas('UserType', function($q){
$q->whereIn('id', [1, 10]);
})
->get();
And actually, since the user type id should exist as foreign key in the users table, you don't even need whereHas:
$users = User::where('receive_email', 1)
->whereNotNull('email')
->whereNull('status')
->whereIn('user_type_id', [1, 10]);
->get();
For RosterMember it's basically the same. Although now you have to use whereHas since it's a many-to-many relation:
$rosterMembers = RosterMember::where('receive_email', 1)
->whereNotNull('email')
->whereHas('UserType', function($q){
$q->whereIn('user_type_id', [1, 10]);
})
->get();
I'm not 100% familiar with relationships, they've always been a bit tricky. But from what I understand, you want all the Users in UserType 1 and 10 that have receive_emails set to 1. So, this should work:
$result =
UserType::whereIn("id", array(1, 10))
->first()
->users()
->where("receive_email", "=", 1)
->whereNotNull("emails")
->whereNull("status")
->get()
;
What this should do is return all the accessible fields from both id 1 and 10 of UserType as well as all fields from the User table. If you run a dd($result) on this query, you should see an entry for UserType id 1 connected to all the Users that have receive_email set to 1, and another set for UserType id 10.
I can't guarantee that this will work without seeing your UserType.php and User.php classes, as the relationships might not be set, but if you followed Laravel convention:
public function users(){
return $this->hasMany("User");
}
and the inverse
public function userType(){
return $this->belongsTo("UserType");
}
then it should work. Hope this helps! Also, I'm sure there are better ways to accomplish this, but this is what I came up with, and it seems to work on some of my existing projects with relationships.
Cheers!

Resources