lev89's avatar
Level 1

Laravel display product by category

Model

class Category extends Model
{
    use Sluggable;
    public function sluggable(): array
    {
        return [
            'slug' => [
                'source' => 'name'
            ]
        ];
    }
    public function parent()
    {
        return $this->belongsTo(Category::class, 'parent_id');
    }
    public function categories()
    {
        return $this->hasMany(Category::class, 'parent_id');
    }

    public function children()
    {
        return $this->hasMany(Category::class, 'parent_id')->with('categories');
    }


    public function products()
    {
        return $this->hasMany(Product::class);
    }
}

Controller

class CategoryController extends Controller
{
    public function show($slug)
    {
        $category = Category::where('slug', $slug)->first();
        $products = $category->products()->orderBy('created_at')->paginate(12);
        return view('category.product', [
            'products' => $products,
            'category' => $category
        ]);
    }
}

Route

Route::get('/category/{slug}', [\App\Http\Controllers\CategoryController::class, 'show'])->name('category.product');

This code displays all products of one category, by the slug key. But the problem is that categories can be nested and I need all products to be displayed, including child (if any) categories. For example, if you select a parent category, then show a list of all products (including products of child categories), and if you select only a child category, then show only those products that are in this child category. How to implement such a method?

0 likes
3 replies
Sourdough's avatar

First I would define your database categories and subcategories table structures:

// A common rule is to not have more than one main category and one subcategory, everything else should go into a filter where you can define rest sub-subcategories (If you need to)

categories table
			id - unique integer
			name - unique string
			slug - unique slug
			// ... Everything else
subcategories table
			id - unique integer
			name - unique string
			slug - unique slug
			category_id - integer
			// ... Everything else

And then I would bind categories with One To Many relationship:

// Category Model

public function subcategories() 
{
	return $this->hasMany(Subcategory::class);
}

That way you'll get all categories and subcategories you need, just bind them to what categories are for. For example you can bind items to your subcategory_id with One To Many relationship, and then just display your items using subcategory->products on your main category page:

// Your view
	@foreach($category->subcategories as $subcategory)
			@foreach($subcategory->products as $product)
				<x-product-template>
					{{ $product->name }} // ...
				</x-product-template>
			@endforeach
	 @endforeach

In your routes web.php file:

use App\Http\Controllers\CategoryController;

Route::get('categories/{category:slug}', [CategoryController::class, 'show'])->name('category.product'); // You don't need to define full path to your controller
lev89's avatar
Level 1

@Sourdough but I have a single table for the categories. the database structure is like this:

categories(id, parent_id, name, slug, ...)
products(id, category_id, name, slug, price, ...)

Please or to participate in this conversation.