Filament: 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 50 Premium Examples for $99 Only Table Examples for $39