Laravel 9 多态关联排序:按example_data的external键值排序Example模型
问题描述
现有如下表结构:
-- table: examples + -- + ---- + | id | name | + -- + ---- + | 1 | Test | | 2 | Test | + -- + ---- + -- table: example_data + -- + --------------- + ------------------ + ---------- + --------- + | id | example_data_id | exmaple_data_type | key | value | + -- + --------------- + ------------------ + ---------- + --------- + | 1 | 1 | App\Models\Example | external | 1 | | 2 | 1 | App\Models\Example | otherKey | string | | 3 | 2 | App\Models\Example | external | 0 | + -- + --------------- + ------------------ + ---------- + --------- +
模型代码:
// Example Model: public function daten() { return $this->morphMany(ExampleData::class, 'example_data'); } // ExampleData Model: public function example_data() { return $this->morphTo(); }
需求:按example_data表中key为"external"对应的value字段对Example模型数据进行排序,现有查询代码如下:
$examples = Example::with('daten') ->orderBy(***value from external in daten***) // 期望实现:Example->daten中key="external"时,按对应的value排序 ->paginate($request->total);
请问是否可以使用orderBy结合回调实现该需求?如果可以,对应的回调写法是怎样的?
解决方案
直接通过orderBy结合回调无法实现该需求,因为orderBy是数据库层面的排序逻辑,回调无法直接关联到关联模型的特定字段。可以通过以下两种方式实现:
方法一:数据库关联排序(推荐,支持原生分页)
通过join关联example_data表,筛选出key='external'的记录后按value排序:
$examples = Example::with('daten') ->join('example_data', function ($join) { $join->on('examples.id', '=', 'example_data.example_data_id') ->where('example_data.exmaple_data_type', '=', 'App\Models\Example') ->where('example_data.key', '=', 'external'); }) ->orderBy('example_data.value') ->select('examples.*') // 避免关联表字段冲突 ->paginate($request->total);
若单个
Example对应多条key='external'的example_data记录,需追加->groupBy('examples.id')避免数据重复。
方法二:集合层面排序(适合小数据量,分页需手动处理)
先查询所有数据,再在Eloquent集合中通过回调排序:
// 先获取全部数据并预加载关联 $examples = Example::with('daten')->get(); // 按external的value排序 $sortedExamples = $examples->sortBy(function ($example) { $externalItem = $example->daten->where('key', 'external')->first(); return $externalItem ? $externalItem->value : 0; // 无数据时用默认值0排序 }); // 手动实现分页,需引入LengthAwarePaginator use Illuminate\Pagination\LengthAwarePaginator; $currentPage = LengthAwarePaginator::resolveCurrentPage(); $perPage = $request->total; $paginatedExamples = new LengthAwarePaginator( $sortedExamples->forPage($currentPage, $perPage), $sortedExamples->count(), $perPage, $currentPage, ['path' => LengthAwarePaginator::resolveCurrentPath()] );
内容的提问来源于stack exchange,提问作者Patric Kersten
相关产品推荐
相关产品推荐

