Laravel - how to get records with exact match of categories using belongsToMany relation - laravel-5.6

Products
-id
-title
Categories
-id
-name
ProductCategories
-Products_id
-Categories_id
what i would like to achieve is get get all the products with at least matching all selected categories (eg: [1,4,6,9] )
and skip products that has only one or more of matching categories (eg: [6,9])
my code so far ... which displays the products that are needed to skip
relationship
public function productcats()
{
return $this->belongsToMany('App\Categories', 'ProductCategories', 'Products_id ', 'Categories_id');
}
$cats = [1,4,6,9];
Products ::whereHas('productcats', function($query) use ($cats){
$query->whereIn('Categories_id', $cats );
})
->paginate(10);

$products = Products ::whereHas('productcats', function($query) use ($cats){
$query->whereIn('Categories_id', $cats )
->groupBy('Products_id')
->havingRaw( 'COUNT(Products_id) = ?', [count($cats)] );
})
->paginate(10);
for any one who may need it, i got the answer with guidance from here: http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/
3. Find all articles that are tagged with at least tag1, tag2, ..., tagn

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

products with categories using category_products

I have a Relation of products with categories using category_products as a pivot table. I want to get/take 5 products from each category but it not working correctly.
I have tried all the methods known to me I could find the solution
$query = $this->getQuery();
$query->where('parent_id','0')->with('categoryProducts',function($q1){
$q1->with('product',function ($q){
$q->orderBy('sold', 'desc');
})->take(4);
})->whereHas('categoryProducts');
return $query->get();

Counting data before sending to view in Laravel

I have two tables, products and product_images, now I want to show product which has at least one image.
In controller I have a simple function in ProductController to fetch all the products:
public function products(){
$allProducts = $this->product->paginate(15);
return view('frontend.pages.products',compact('allProducts'));
}
But, I want to send the products which has at least one image of each product.
What should I do to achieve that?
Edit:
I have created relationship between tables, now how can I get my desired answer?
I have written this in the Controller:
$allProducts = $this->product->whereHas('product_images', function ($query){
$query->where();
})->get();
Assuming table schema
product
-id
-name
product_image
-id
-product_id
-url //any columns you needed
$product_ids = DB::table('product')
->join('product_image','product_image.product_id','=','product.id')
->select('product.id','roduct.name')
->groupBy('product.id')
->get();
$product_count = count($product_ids);
Eloquent has this built in.
Example
// Retrieve all posts that have three or more comments...
$posts = App\Post::has('comments', '>=', 3)->get();
In your case
In your case you could change the $allProducts line to be
$allProducts = $this->product()->has('product_image', '>=', 1)->paginate(15);
I didn't test the code above.
See documentation for more information on this topic.
https://laravel.com/docs/6.x/eloquent-relationships#querying-relationship-existence
You can use selectRaw:
$products = Product::leftJoin('product_images',function ($join){
$join->on('products.product_id','=','product_images.product_id');
})->selectRaw("products.product_id i, count(product_images.id) c")
->groupBy('products.product_id')
->where('c','>=',3)
->get();
Getting answer querying realtionship:
The query will look like following:
$allProducts = $this->product->has('Images')->paginate(15);
But, make you sure you have created relation in Model like the following:
public function Images(){
return $this->hasMany('\App\Models\ProductImages');
}
I have used hasMany relationship because one product can have multiple images.

check count in one to many relation in laravel

I have products table that each of the product has many orders,product.php
public function orders()
{
return $this->hasMany(Order::class,'product_id');
}
I can get products that order by order_count with this code:
$products = Product::withCount('orders')->orderBy('orders_count', 'desc')->get();
Now I want to get products in controller that their orders count is bigger than 5,
How I can do it?
One way to do this would to use whereHas():
$products = Product::withCount('orders')
->whereHas('orders', null, '>', 5)
->orderBy('orders_count', 'desc')
->get();
I don't think it's mentioned in the docs but you don't have to pass a closure as the 2nd param, and the 3rd and 4th can be used to pass the operator and the count.
Alternatively, you could use having() instead:
$products = Product::withCount('orders')
->having('orders_count', '>', 5)
->orderBy('orders_count', 'desc')
->get();
You can use mapping on your collection to filter out records e.g. :`
$collection = collect($products)->map(function ($product) {
return $product->orders->count() > 5;
})
->reject(function ($name) {
return $product->orders->count() < 5;
});`
This is just an example, you can put more conditions if required.
Hope this helps.
The below code should give you just the orders with a count greater than 5
$products = Product::withCount('orders')->where('orders_count', '>', 5)->orderBy('orders_count', 'desc')->get();
Adding ->where() should do it.
Hope that helps.
Update
Try
->whereRaw('count("orders.id") > 5')
This should work to get products with more than 5 orders.

Multiple Category Nested Query Laravel 5

I've followed this code structure for a multi-level category system on Laravel 5:
This works fine. But I'm trying to put together a query to return posts in a category (by slug) but also the children categories underneath.
So heres what I've got at the moment:
$posts = Post::with('images')->whereHas('categories', function($q) use ($slug)
{
$q->where('slug', '=', $slug);
})->orderBy('created_at', 'DESC')->paginate(10);
This returns everything under the category that matches the slug passed through, but not the children. Any suggestions?
Thanks.
If you have only one depth of subcategories, the following solution is what I would use.
<?php
$category = Category::where('slug',$slug)->first();
$posts = Post::with('images')->whereHas('categories', function($q) use ($category)
{
$q->where(function($q) use ($category) {
$q->where('id', $category->id)->orWhere('parent_id',$category->id);
});
})->orderBy('created_at', 'DESC')->paginate(10);
In your code, you only fetched the Posts and their images. If I understood correctly, what you want to fetch are the Posts, their images, the category their in and childCategories (this is a made up name) of that category. You just need to add category.childCategories in your with method like so:
$posts = Post::with('images', 'categories.childCategories')->whereHas('categories', function($q) use ($slug)
{
$q->where('slug', '=', $slug);
})->orderBy('created_at', 'DESC')->paginate(10);
Remember to change the childCategories name to whatever the child category relationship name in your Category model.

Resources