如何在Laravel Filament中实现Material Selection-Quotation-Addendums嵌套资源?
三级嵌套Filament资源实现方案
针对Material Selection > Quotation > Addendums的三级关联需求,完全基于Filament原生资源系统实现层级跳转和关联过滤,无需自定义Livewire页面,步骤如下:
1. 确保模型关联正确
先确认三个模型的关联关系定义:
// MaterialSelection.php public function quotations() { return $this->hasMany(Quotation::class); } // Quotation.php public function materialSelection() { return $this->belongsTo(MaterialSelection::class); } public function addendums() { return $this->hasMany(Addendum::class); } // Addendum.php public function quotation() { return $this->belongsTo(Quotation::class); }
2. 第一层:Material Selection详情页添加跳转按钮
在MaterialSelectionResource的详情页(View/Edit页面)添加跳转至对应Quotation列表的按钮,携带关联ID参数:
// 比如在ViewMaterialSelection.php的headerActions方法中 use Filament\Pages\Actions\Action; public function headerActions(): array { return [ // 其他原有按钮 Action::make('viewQuotations') ->label('查看关联报价单') ->url(fn ($record) => QuotationResource::getUrl('index', ['material_selection_id' => $record->id])) ->icon('heroicon-o-document-text'), ]; }
3. 第二层:Quotation资源处理关联过滤与跳转
3.1 列表页过滤关联数据
修改QuotationResource的List页面,自动过滤当前Material Selection下的Quotation,同时添加返回上级的按钮:
// ListQuotations.php use Filament\Resources\Pages\ListRecords; use Illuminate\Database\Eloquent\Builder; class ListQuotations extends ListRecords { protected static string $resource = QuotationResource::class; // 过滤仅显示当前物料选择下的报价单 protected function getTableQuery(): Builder { $query = parent::getTableQuery(); if ($materialId = request()->query('material_selection_id')) { $query->where('material_selection_id', $materialId); } return $query; } // 添加返回上级的按钮 protected function getHeaderActions(): array { $actions = parent::getHeaderActions(); if ($materialId = request()->query('material_selection_id')) { $actions[] = Action::make('back') ->label('返回物料选择') ->url(MaterialSelectionResource::getUrl('view', ['record' => $materialId])) ->icon('heroicon-o-arrow-left'); } return $actions; } }
3.2 创建页自动填充关联ID
修改QuotationResource的Create页面,自动填充material_selection_id参数,创建后返回关联列表页:
// CreateQuotation.php use Filament\Resources\Pages\CreateRecord; class CreateQuotation extends CreateRecord { protected static string $resource = QuotationResource::class; protected function mutateFormDataBeforeCreate(array $data): array { if ($materialId = request()->query('material_selection_id')) { $data['material_selection_id'] = $materialId; } return $data; } protected function getRedirectUrl(): string { if ($materialId = request()->query('material_selection_id')) { return QuotationResource::getUrl('index', ['material_selection_id' => $materialId]); } return parent::getRedirectUrl(); } }
3.3 详情页添加跳转至Addendums的按钮
在QuotationResource的详情页添加跳转按钮:
// ViewQuotation.php的headerActions方法中 Action::make('viewAddendums') ->label('查看关联附录') ->url(fn ($record) => AddendumResource::getUrl('index', ['quotation_id' => $record->id])) ->icon('heroicon-o-document-duplicate'),
4. 第三层:Addendum资源复用Quotation的逻辑
完全参照Quotation资源的处理方式,实现:
- 列表页过滤当前Quotation下的Addendums
- 创建页自动填充
quotation_id - 列表页添加返回Quotation详情的按钮
示例代码(ListAddendums.php):
use Filament\Resources\Pages\ListRecords; use Illuminate\Database\Eloquent\Builder; class ListAddendums extends ListRecords { protected static string $resource = AddendumResource::class; protected function getTableQuery(): Builder { $query = parent::getTableQuery(); if ($quotationId = request()->query('quotation_id')) { $query->where('quotation_id', $quotationId); } return $query; } protected function getHeaderActions(): array { $actions = parent::getHeaderActions(); if ($quotationId = request()->query('quotation_id')) { $actions[] = Action::make('back') ->label('返回报价单') ->url(QuotationResource::getUrl('view', ['record' => $quotationId])) ->icon('heroicon-o-arrow-left'); } return $actions; } }
5. 优化:隐藏嵌套资源的主导航入口
如果不需要在Filament主导航直接显示Quotation和Addendum,在对应资源类中添加:
// QuotationResource.php/AddendumResource.php public static function navigation(): ?NavigationItem { return null; }
可选:自定义面包屑显示完整层级
在各层级列表页重写getBreadcrumbs方法,比如Quotation列表页:
protected function getBreadcrumbs(): array { $breadcrumbs = [ '/' => '仪表盘', MaterialSelectionResource::getUrl('index') => '物料选择', ]; if ($materialId = request()->query('material_selection_id')) { $material = MaterialSelection::findOrFail($materialId); $breadcrumbs[MaterialSelectionResource::getUrl('view', ['record' => $materialId])] = $material->name; } $breadcrumbs[] = '报价单'; return $breadcrumbs; }
内容的提问来源于stack exchange,提问作者Ali Safi
相关产品推荐
相关产品推荐

