Laravel:如何实现关联模型排序及分页与多条件筛选?
解决关联模型排序问题
我来帮你搞定这个卡了三天的关联模型排序问题!你的代码已经实现了主模型排序、多条件搜索和分页,就差关联模型的排序逻辑。我们只需要在else分支里添加关联表Join和排序的逻辑,就能实现sort=group__name这类场景的排序。
修改后的完整代码
use Illuminate\Database\Eloquent\Builder; public static function pagination(Request $request, $model, $relation, $modelStringify) { $sortParam = $request->query('sort'); // 处理sort参数为空的情况,给个默认值 if (empty($sortParam)) { $modelSortBy = $modelStringify; $tableSortBy = 'created_at'; } else { [$modelSortBy, $tableSortBy] = explode('__', $sortParam); } $query = $request->query(); $direction = $request->query('direction', 'asc'); // 默认升序 $direction = in_array(strtolower($direction), ['asc', 'desc']) ? strtolower($direction) : 'asc'; // 校验排序方向 try { $model = $model->with($relation); // 处理排序逻辑 if ($modelSortBy === $modelStringify) { // 主模型排序:添加表名前缀,避免join后字段冲突 $model->orderBy("$modelStringify.$tableSortBy", $direction); } else { // 关联模型排序:动态获取关联信息并Join表 if (!method_exists($model, $modelSortBy)) { throw new \Exception("关联关系 {$modelSortBy} 不存在"); } $relationInstance = $model->$modelSortBy(); $relationModel = $relationInstance->getRelated(); $relationTable = $relationModel->getTable(); $foreignKey = $relationInstance->getForeignKeyName(); $ownerKey = $relationInstance->getOwnerKeyName(); // 避免重复Join同一张表 if (!str_contains($model->getQuery()->toSql(), $relationTable)) { $model->join( $relationTable, "$modelStringify.$foreignKey", '=', "$relationTable.$ownerKey" ); } // 按关联表字段排序 $model->orderBy("$relationTable.$tableSortBy", $direction); } // 处理多条件搜索逻辑 foreach (array_keys($query) as $q) { if (strpos($q, 'search__') === 0) { [$prefix, $modelSearchBy, $tableSearchBy] = explode('__', $q); $valueSearchBy = $query[$q]; if ($modelSearchBy === $modelStringify) { $model->where("$modelStringify.$tableSearchBy", 'like', "%{$valueSearchBy}%"); } else { $model->whereHas($modelSearchBy, function (Builder $q) use ($valueSearchBy, $tableSearchBy) { $q->where($tableSearchBy, 'like', "%{$valueSearchBy}%"); }); } } } return $model; } catch (Exception $e) { return response()->json(['message' => $e->getMessage()], 400); } }
关键修改说明
参数校验与默认值:
- 增加了
sort参数为空的默认处理,避免空指针报错 - 校验
direction参数,确保只有asc/desc两种合法值,防止非法参数导致SQL错误
- 增加了
主模型排序优化:
- 给主模型字段添加表名前缀(
$modelStringify.$tableSortBy),避免Join关联表后出现字段名冲突的问题(比如主表和关联表都有name字段时)
- 给主模型字段添加表名前缀(
关联模型排序核心逻辑:
- 通过
method_exists检查关联关系是否存在,提前拦截非法关联参数 - 动态获取关联模型的表名、外键和主键,不用硬编码表结构信息,适配
belongsTo/hasOne等多种关联场景 - 加入重复Join检查,防止多次Join同一张表导致的SQL语法错误
- 使用Join后的关联表字段进行排序,确保排序逻辑精准指向关联模型的字段
- 通过
搜索逻辑优化:
- 主模型搜索也添加了表名前缀,和排序逻辑保持一致,避免字段歧义
- 模糊搜索的通配符调整为前后都加(
%{$valueSearchBy}%),匹配更灵活,你可以根据需求改回前缀匹配($valueSearchBy . '%')
测试场景验证
当你请求http://127.0.0.1:8000/api/user/list?page=1&limit=30&sort=group__name&direction=desc&search__group__name=Client时:
- 代码会自动Join
groups表 - 按
groups.name降序排序 - 同时筛选出所属分组名称包含
Client的用户
这样就能完美实现你需要的关联模型排序功能了!
内容的提问来源于stack exchange,提问作者Alexis Gatuingt
相关产品推荐
相关产品推荐

