如何在Filament中实现无限层级子分类表?
在Filament中实现无限层级子分类
1. 修正数据库与模型关联
首先你的分类表parent_id需要允许为空(顶级分类无父类),并且类型要和id匹配(推荐用unsignedBigInteger):
// 迁移文件 Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->unsignedBigInteger('parent_id')->nullable(); $table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade'); $table->timestamps(); });
在Category模型中定义父子关联,支持递归查询:
// app/Models/Category.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; class Category extends Model { protected $fillable = ['name', 'parent_id']; // 关联父分类 public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent_id'); } // 关联子分类 public function children(): HasMany { return $this->hasMany(Category::class, 'parent_id'); } // 递归生成树形结构(用于层级选择器) public static function tree(): array { return static::with('children')->whereNull('parent_id')->get()->map(function ($category) { return $this->formatTreeItem($category); })->toArray(); } private static function formatTreeItem($category): array { return [ 'id' => $category->id, 'name' => $category->name, 'children' => $category->children->isNotEmpty() ? $category->children->map(fn($child) => static::formatTreeItem($child)) : [] ]; } }
2. 更新分类Resource表单
添加层级选择器,支持从URL参数自动填充父分类ID:
// app/Filament/Resources/CategoryResource.php use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TreeSelect; use Filament\Forms\Form; public static function form(Form $form): Form { $parentId = request()->query('parent_id'); return $form ->schema([ TextInput::make('name') ->label('分类名称') ->required(), TreeSelect::make('parent_id') ->label('父分类') ->options(Category::tree()) ->searchable() ->default($parentId) // 从子分类页面进入时,自动锁定父分类 ->disabled(fn() => $parentId !== null) ->hidden(fn() => $parentId !== null), ]); }
3. 调整表格逻辑:过滤子分类+层级显示
修改表格查询规则,根据URL参数过滤子分类,同时给分类名称添加缩进区分层级:
use Filament\Tables\Actions\Action; use Filament\Tables\Actions\EditAction; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('name') ->label('分类名称') // 根据层级添加缩进 ->formatStateUsing(function ($state, $record) { $depth = $record->parent ? static::getCategoryDepth($record) : 0; return str_repeat(' ', $depth) . $state; }) ->html(), ]) ->actions([ EditAction::make(), Action::make('subCategory') ->label('查看子分类') ->url(fn($record) => static::getUrl('index', ['parent_id' => $record->id])), ]) ->query(function (Builder $query, Request $request) { // 根据URL参数过滤子分类 if ($parentId = $request->query('parent_id')) { return $query->where('parent_id', $parentId); } // 默认显示顶级分类 return $query->whereNull('parent_id'); }); } // 计算分类层级的辅助方法 private static function getCategoryDepth($record): int { $depth = 0; while ($record->parent) { $depth++; $record = $record->parent; } return $depth; }
4. 优化页面体验:动态标题与面包屑
生成自定义列表页(执行命令:php artisan make:filament-page ListCategories --resource=CategoryResource),添加动态标题和面包屑导航:
// app/Filament/Resources/CategoryResource/Pages/ListCategories.php namespace App\Filament\Resources\CategoryResource\Pages; use App\Filament\Resources\CategoryResource; use Filament\Resources\Pages\ListRecords; class ListCategories extends ListRecords { protected static string $resource = CategoryResource::class; public function getTitle(): string { $parentId = request()->query('parent_id'); if ($parentId) { $parentCategory = \App\Models\Category::find($parentId); return "子分类:{$parentCategory->name}"; } return '分类管理'; } protected function getBreadcrumbs(): array { $breadcrumbs = parent::getBreadcrumbs(); $parentId = request()->query('parent_id'); if ($parentId) { $parentCategory = \App\Models\Category::find($parentId); // 插入父分类面包屑 array_splice($breadcrumbs, -1, 0, [ CategoryResource::getUrl('index') => '分类管理', $parentCategory->name => '#', ]); } return $breadcrumbs; } }
最终效果
- 默认列表显示所有顶级分类,点击「查看子分类」会跳转到该分类的子列表页面
- 在子分类页面创建新分类时,会自动绑定当前父分类ID
- 分类名称按层级缩进显示,面包屑支持返回上级分类,页面标题动态显示当前层级
内容的提问来源于stack exchange,提问作者user1995781
相关产品推荐
相关产品推荐

