In this lesson, let's examine the hierarchy structure for eShop categories/subcategories. How can you structure that in the database with unlimited subcategories without losing too much performance? I will show you three ways, including using an external package.
The Task
I've seeded a small database of categories. The parent category, with category_id
NULL, has subcategories for toys, clothes, Lego, etc.
Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('category_id')->nullable()->constrained('categories'); $table->timestamps();});
Our task is to show the entire tree of categories with subcategories.
Without Package
So, on the Category Model, there is a hasMany
relationship to subcategories.
app/Models/Category.php:
use Illuminate\Database\Eloquent\Relations\HasMany; class Category extends Model{ protected $fillable = [ 'name', 'category_id', ]; public function subcategories(): HasMany { return $this->hasMany(Category::class); }}
In the Controller, we get categories with the whereNull('category_id')
condition, which gets the root categories and then eager loads the subcategories.
app/Http/Controllers/CategoryController.php:
use App\Models\Category;use Illuminate\Contracts\View\View; class CategoryController extends Controller{ public function __invoke(): View { $categories = Category::with('subcategories.subcategories') ->whereNull('category_id') ->get(); return view('categories.index', compact('categories')); }}
Then, in the View, we make a typical foreach loop.
resources/views/categories/index.blade.php:
<ul> @foreach($categories as $category) <li>{{ $category->name }}</li> @if($category->subcategories->count()) <ul> @foreach($category->subcategories as $subcategory) <li>{{ $subcategory->name }}</li> @if($subcategory->subcategories->count()) <ul> @foreach($subcategory->subcategories as $subcategory) <li>{{ $subcategory->name }}</li> @endforeach </ul> @endif @endforeach </ul> @endif @endforeach</ul>
This gives us two-level deep categories.
Unlimited Levels?
How do we transform that into an unlimited level if you want to show level four, which exists in the database?
Do we add another level when eager loading and another level in the View? What if...