0
votes

I am working on a project and got stuck in a situation. Let's say I have 4 tables; vendors, products, category_products (pivot), category. Let's assume each table has this column below:

  • vendors id name etc.

  • products id vendor_id name stock etc.

  • category_products id product_id category_id timestamps

  • category id name

Models: Vendors

    public function products(){
        return $this->hasMany('App\Models\Sewa\Products', 'vendor_id')->with('product_images');
    }

Products

    public function product_category(){
        return $this->hasOne('App\Models\Sewa\ProductCategories', 'product_id');
    }

ProductCategories

    public function category(){
        return $this->belongsTo('App\Models\Sewa\Categories', 'category_id');
    }

    public function product(){
        return $this->belongsTo('App\Models\Sewa\Products', 'product_id');
    }

Category

    public function product_category(){
        return $this->hasMany('App\Models\Sewa\ProductCategories', 'category_id');
    }

I was trying to filter products from vendors by their category and price. But each product only has one category, but a category can be used in many products. What I did is:

$vendors = Vendors::with('products.product_category.category')->whereHas('products', function($query) use($request){
    if($request->category !== 'all'){
        $query->whereHas('product_category', function($query) use($request){
            $query->where('category_id', $request->category);
        });
    }

    if($request->price === 'low'){
        $query->whereBetween('price', [0, 10000]);
    }

    if($request->price === 'middle'){
        $query->whereBetween('price', [10000, 250000]);
    }

    if($request->price === 'high'){
        $query->where('price', '>', 250000);
    }
});

I tried to get category id = 1, but I got whole records. I really don't know why it's ignored the where clause conditions. What did I do wrong here?

2

2 Answers

1
votes

Docs: https://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads

use your constraints of the relations like this:

$vendors = Vendors::with(['products.product_category.category' => function($request){
    // your condition
}])->whereHas('products', function($query) use($request){
    if($request->category !== 'all'){
        $query->whereHas('product_category', function($query) 
use($request){
             $query->where('category_id', $request->category);
        });
    }

    if($request->price === 'low'){
        $query->whereBetween('price', [0, 10000]);
    }

    if($request->price === 'middle'){
        $query->whereBetween('price', [10000, 250000]);
    }

    if($request->price === 'high'){
        $query->where('price', '>', 250000);
    }
});
0
votes

I got my answer thanks to my friend. So basically, it's still a many-to-many relationship. Correct me if I'm wrong. I changed several things;

Model

Products

public function product_category(){
    return $this->hasMany('App\Models\Sewa\ProductCategories', 'product_id');
}

I changed hasOne() to hasMany(), but both works perfectly. But in this case, my friend told me to use many-to-many relationships even though each product only has one category.

Controller

$vendors = Vendors::with(['products' => function($query) use($request){
    if($request->category !== 'all'){
        $query->whereHas('product_category', function($query) use($request){
            $query->where('category_id', $request->category);
        });
    }

    if($request->price === 'low'){
        $query->whereBetween('price', [0, 10000]);
    }

    if($request->price === 'middle'){
        $query->whereBetween('price', [10000, 250000]);
    }

    if($request->price === 'high'){
        $query->where('price', '>', 250000);
    }
}, 'products.product_category.category']);

This logic solved my problem. Instead of using whereHas() in the first place, I use eager loading with conditions in it like @Psycho mentioned. Thanks for my friend and @Psycho.

Note: If this code looks 'messy' please let me know so I and everyone who reads this can improve to be a better programmer.