You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 15:33:27