如何在Laravel Filament表格中按HasOne关联列排序
我有三个模型:Lead、LeadStatus和LeadUpdates。Lead模型代表某一产品生成的销售线索;LeadStatus表存储Lead可拥有的所有状态(如'Created'、'Followed up'或'Sale');LeadUpdates表是跟踪所有线索状态历史的中间表。
在Lead模型中,我定义了获取最新状态更新的方法:
public function latestStatusUpdate(): HasOne { return $this->hasOne(LeadUpdate::class)->latestOfMany(); }
在Filament面板的LeadResource中,我用表格展示所有Lead,其中一列是最新状态更新的时间:
Tables\Columns\TextColumn::make('latestStatusUpdate.created_at') ->label('Geüpdate op') ->dateTime('d-m-Y H:i') ->sortable(),
调用sortable()方法后,期望表格能按最新状态更新的created_at列排序,但点击表头时,表格却是按Lead的id升序或降序排序。请问如何实现按最新状态更新时间排序?
问题核心是Filament无法直接基于latestOfMany()这种动态关联的结果自动生成排序逻辑,需要手动在查询层面处理排序字段。以下是两种可行方案:
方法一:通过子查询添加计算字段
在LeadResource的表格查询中,预先通过子查询获取每个Lead的最新更新时间,将其作为计算字段添加到主查询,再基于该字段排序:
use Illuminate\Database\Eloquent\Builder; public static function table(Table $table): Table { return $table ->query(Lead::query() ->addSelect([ 'latest_update_at' => LeadUpdate::query() ->select('created_at') ->whereColumn('lead_id', 'leads.id') ->latest() ->limit(1), ]) ) ->columns([ // 其他列配置... Tables\Columns\TextColumn::make('latest_update_at') ->label('Geüpdate op') ->dateTime('d-m-Y H:i') ->sortable(), ]); }
这里通过addSelect()新增latest_update_at字段,直接从LeadUpdates表中拉取对应线索的最新更新时间,Filament可以直接基于这个明确的字段完成排序。
方法二:自定义sortable()的排序逻辑
如果不想修改主查询结构,可以给sortable()传入回调函数,手动构建关联排序的查询逻辑:
use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; // 在LeadResource的columns配置中 Tables\Columns\TextColumn::make('latestStatusUpdate.created_at') ->label('Geüpdate op') ->dateTime('d-m-Y H:i') ->sortable(function (Builder $query, string $direction) { return $query ->leftJoinSub( LeadUpdate::query() ->select('lead_id', DB::raw('MAX(created_at) as latest_created_at')) ->groupBy('lead_id'), 'latest_updates', 'latest_updates.lead_id', 'leads.id' ) ->orderBy('latest_updates.latest_created_at', $direction); }),
该方案通过leftJoinSub关联一个子查询,子查询按lead_id分组并提取每组的最新更新时间,最终基于这个关联字段完成排序。
内容的提问来源于stack exchange,提问作者The Stompiest

