Courses

Structuring Databases in Laravel 11

Unlimited Levels of Parent-Children

Summary of this lesson:
- Managing unlimited subcategories
- Implementing recursive queries
- Using package solutions
- Optimizing hierarchy structures

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...

The full lesson is only for Premium Members.
Want to access all 18 lessons of this course? (81 min read)

You also get:

  • 69 courses (majority in latest Laravel 11)
  • Premium tutorials
  • Access to repositories
  • Private Discord