Black Friday: coupon FRIDAY24 for 40% off Yearly/Lifetime membership! Read more here

Restaurant Menu Management

Demonstrates how to create nested resources, with restaurant menu categories and dishes. You can reorder Categories and Dishes in the Filament table.

Screenshot 2023-10-24 at 16.03.20

Get the Source Code:

How it works

In this project, we have two Models:

  • Category
    • name
    • position (for list ordering)
    • dishes (HasMany relationship)
  • Dish
    • name
    • price (stored as cents)
    • position (for list ordering)
    • category (BelongsTo relationship)

Models

Here are the contents of both models with fields and relationships defined.

app/Models/Category.php

namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class Category extends Model
{
use HasFactory;
 
protected $fillable = ['name', 'position'];
 
public function dishes(): HasMany
{
return $this->hasMany(Dish::class);
}
}

app/Models/Dish.php

namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Dish extends Model
{
use HasFactory;
 
protected $fillable = [
'category_id',
'name',
'price',
'position',
];
 
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}

Category Resource And Nested Pages

First, we need a way to abstract this functionality to have nested resources. The main idea is for every child page to have a parent resource. For that reason, we implement this trait.

app/Filament/Traits/HasParentResource.php

namespace App\Filament\Traits;
 
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
 
trait HasParentResource
{
public Model|int|string|null $parent = null;
 
public function bootHasParentResource(): void
{
if ($parent = (request()->route('parent') ?? request()->input('parent'))) {
$parentResource = $this->getParentResource();
 
$this->parent = $parentResource::resolveRecordRouteBinding($parent);
 
if (! $this->parent) {
throw new ModelNotFoundException();
}
}
}
 
public function getParentResource(): string
{
if (! isset(static::$parentResource)) {
throw new Exception('Parent resource is not set for ' . static::class);
}
 
return static::$parentResource;
}
 
protected function applyFiltersToTableQuery(Builder $query): Builder
{
return $query->where(str($this->parent?->getTable())->singular()->append('_id'), $this->parent->getKey());
}
 
public function ...
The FULL tutorial is available after the purchase: in the Readme file of the official repository you would get invited to.
Get the Source Code: All 64 Premium Examples for $99