OneToMany + ManyToMany - laravel

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.

Related

Laravel eloquent many to many relation deep conditions

I have two Model named Product and Category. Admin can change the status of Product and Category.
Here Product and Category has many to many relations. So that, all Product can belongs to multiple Category
Now at my user panel,
I want to show that all Product whose all Category's status is active.
you can do this in your product model
public function categories(){
return $this->belongsToMany(Category::class, CategoryProduct::class, 'product_id', 'category_id');
//->withPivot([]); if you need anything from the pivot table for any reason you can add the column names in the array
}
this is assuming that the pivot table have the columns product_id and category_id
$products = Product::with('categories')
->whereHas('categories', function ($query) {
$query->where('categories.status', 'active');
})
->get();
inside the where the word categories is the table name
The solution is:
$products = Product::with('category')
->whereHas('category', function ($query) {
$query->where('status', 'active'); // use your column & condition
})
->get();
It works for me, got all the products that have all the categories active. First time I thought complicated so I didn't solve.
$products = Product::whereDoesntHave('catagories',function($category){
$category->where('status','!=',1);
});

Join tables in Laravel Eloquent method

How to write this code in eloquent method ?
$product = DB::table('products')
->join('purchase', 'products.id', '=', 'purchase.id')
->join('sales', 'purchase.id', '=', 'sales.id')
->select('sales.*', 'purchase.*','products.*')
->get();
Create model Product and add one to many relationship with Purchase in Product model.
public function purchases()
{
return $this->hasMany('App\Models\Purchase');
}
Create model Purchase and add one to many relationship with Sale in Purchase model.
public function sales()
{
return $this->hasMany('App\Models\Sale');
}
Create model Sale.
You can retrieve data using following statement.
$products = Product::with('purchases.sales')->get();
Note: I am assuming the relationship as one to many you can also declare as per your data, also you can define one to many inverse relationship, please refer to laravel docs https://laravel.com/docs/8.x/eloquent-relationships#one-to-many.
You will get purchases and sales data in different key so you can use below syntax to loop over it.
foreach ($products as $product) {
foreach ($product->purchases as $purchase) {
//Purchase data for current product
foreach($purchase->sales as $sale){
//Sale data for current purchase
}
}
}

Eloquent select with() based on foreign key

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

Constraining eager loaded relationship

I found a very bizarre behavior of with function for overloading relationships. I have Product and Deal relationships, such that Product belongsTo() Deal (through product_id in deals table). Now, when I try to get all products on sale:
Product::with(['deal' => function($query) {
$query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
}])->get()
this returns a collection of all products, even though there are no records in deals table and all products have deal_id set to NULL. At the same time Product::has('deal')->get() returns an empty collection, as you would expect.
I initially discovered this problem while trying to fetch five random products on sale together with Deal and Image relationships:
Product::with(['deal' => function ($query) {
$query->whereDate('ends_at', '>', // promo still active
Carbon::now()->toDateTimeString());
},
'images' => function ($query) {
$query->where('featured', true); // image featured on homepage
}])
->where('status', 'IN_STOCK') // 'In Stock'
->whereNull('deleted_at') // wasn't soft-deleted
->orderByRaw('RAND()')
->take(5)->get())
This yields a collection with 5 random Products out of all Products. I tried with query->whereNotNull('ends_at')->whereDate('ends_at' ..... ); but got same results.
What am I doing wrong here?
Your understanding of the concept is completely wrong here.
If you are saying that a Product belongsTo() Deal, then lets assume that a Deal hasMany() Products.
This is the deals table
deals
id | name | ends_at | blah | blah
products
id | deal_id | name | blah | blah
So basically, the Product::with('deal') should return you all products with their Deals being Eager loaded. But Deal::with('products') will return you an empty collection, since no products have a valid deal_id in it.
It is important to note that, since Product can only belongTo a single Deal, you will always get the Deal Model rather than a collection when you perform Product::with('deal') query. But when you perform Deal::with('products') you are bound to get a collection.
So basically, when you say
This returns a collection of all products, even though there are no records in deals table and all products have deal_id set to NULL.
It is pretty obvious.... because the query here is being done on Products and not Deal. If you are trying to find the Deal where ends_at > Carbon::now(), you'll have to do this.
Deal::with('product')->where('ends_at', '>', Carbon::now()->toDateTimeString())
When you use with then it only eager loads the relations on the constraints provided but if you want to filter the parent model by their relations then whereHas is your friend. So your query should be as:
Product::whereHas('deal' => function($query) {
$query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
})->get();
Now it will fetch only those Product which satisfy the given constraint.
You can also use the combination of with and whereHas as:
Product::whereHas('deal' => function($query) {
$query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
})
->with(['deal' => function($query) {
$query->whereDate('ends_at', '>', Carbon::now()->toDateTimeString());
}])
->get();

Laravel Get products from multiple taxonomies

I have 3 tables, products, taxonomies and product_taxonomy. The 3rd table is a pivot table that contains product_id and taxonomy_id, meaning that product and taxonomy are many to many relationship. Given a list of taxonomy ids, how can I get all the products that belong to these taxonomies? NOTE: I want to have the products result set being able to paginated or order by maybe it's price or something.
You need to create many-to-many relationship and that requires relationship methods like:
// Product Model
public function taxonomies()
{
return $this->belongsToMany('Taxonomy');
}
// Taxonomy Model
public function products()
{
return $this->belongsToMany('Product');
}
The query:
$listOfTaxonomyIds = [1,2,3];
$products = Product::whereHas('taxonomies', function($query) use ($listOfTaxonomyIds){
$query->whereIn('taxonomy_id', $listOfTaxonomyIds);
})->get();

Resources