雄辩的急切的加载顺序
我有雄辩的查询问题。 我正在使用急切的加载(一对一的关系)来获得“ 学生 ”与“ 考试 ”,使用下面的代码。
Student::with('exam')->orderBy('exam.result', 'DESC')->get()
而且我想通过“ 考试 ”中的“ 结果 ”列来订购收到的行。 我在用
->orderBy('exam.result', 'DESC')
但它不工作。 任何想法如何做到这一点?
尝试这个:
Student::with(array('exam' => function($query) { $query->orderBy('result', 'DESC'); })) ->get();
如果您需要通过结果列来订购学生馆藏,则需要join表格。
Student::with('exam') ->join('exam', 'students.id', '=', 'exam.student_id') ->orderBy('exam.result', 'DESC') ->get()
在这种情况下,假设你有一个专栏student_id
,你的考试表被命名为exam
。
TL;博士
Student::with('exam')->get()->sortByDesc('exam.result');
这将使用集合方法而不是MySQL ORDER BY
预先加载后的查询结果进行sorting。
说明
当你急于加载时,你不能在加载的关系上使用ORDER BY
,因为第二个查询的结果会被请求和汇编。 正如你可以在Laravel文档中看到的那样,在2个查询中发生急切的加载。
如果你想使用MySQL的ORDER BY
你必须join相关的表。
作为一种解决方法,您可以运行您的查询并使用sortBy
, sortByDesc
或者sort
。 与解决scheme相比,此解决scheme具有优点和缺点:
优点:
- 你保持雄辩的function。
- 更短,更直观的代码。
缺点:
- sorting将由PHP而不是数据库引擎完成。
- 除非您为分拣机function提供自定义closures , 否则只能按单列进行sorting 。
- 如果你只需要一部分查询的有序结果(例如
ORDER BY
LIMIT
),那么你必须获取所有东西 ,sorting,然后过滤sorting的结果,否则最终只会筛选被过滤的部分(sorting将不会考虑过滤掉的元素)。 所以这个解决scheme只有在你处理整个数据集或者开销不是问题时才可以接受。
这对我工作:
$query = Student::select(['id','name']); $query->has('exam')->with(['exam' => function ($query) { return $query->orderBy('result','ASC'); }]); return $query->get();
你可以使用\ Illuminate \ Database \ Eloquent \ Relations \ Relation和查询范围来添加远列的关系,我为此写了一个特性,它错过了HasOne或HasMany,但是可以很容易地使用BelongsTo和BelongsToMany
此外,该方法可以增强支持多个链接关系的深度1以上,我为此提供了空间
<?php /** * User: matteo.orefice * Date: 16/05/2017 * Time: 10:54 */ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; use Illuminate\Database\Eloquent\Builder; trait WithFarColumnsTrait { public function scopeWithFarColumns(Builder $query , $relationPath , $columns , $tableAliasPrefix = null) { $relationPath = array_wrap($relationPath); $tableAliasPrefix = $tableAliasPrefix ?: WithFarColumnsTrait::randomStringAlpha(3); $currentModel = $this; $subQueries = []; $relationIndex = 0; foreach ($relationPath as $relationName) { if (method_exists($currentModel , $relationName)) { $relation = $currentModel->$relationName(); } else { throw new BadMethodCallException("Relationship $relationName does not exist, cannot join."); } $currentTable = $currentModel->getTable(); if ($relationIndex == 0) { $query->addSelect($currentTable . '.*'); } $relatedModel = $relation->getRelated(); /** * @var string */ $relatedTable = $relatedModel->getTable(); if ($relation instanceof BelongsTo) { foreach ($columns as $alias => $column) { $tableAlias = $tableAliasPrefix . $relationIndex; $tableAndAlias = $relatedTable . ' AS ' . $tableAlias; /** * Al momento gestisce soltanto la prima relazione * todo: navigare le far relationships e creare delle join composte */ if (!isset($subQueries[$alias])) { $subQueries[$alias] = $currentQuery = DB::query() ->from($tableAndAlias) ->whereColumn( $relation->getQualifiedForeignKey() , // 'child-table.fk-column' '=' , $tableAlias . '.' . $relation->getOwnerKey() // 'parent-table.id-column' ) ->select($tableAlias . '.' . $column); // se la colonna ha una chiave stringa e' un alias /** * todo: in caso di relazioni multiple aggiungere solo per la piu lontana */ if (is_string($alias)) { $query->selectSub($currentQuery , $alias); } else { throw new \InvalidArgumentException('Columns must be an associative array'); } } else { throw new \Exception('Multiple relation chain not implemented yet'); } } // end foreach <COLUMNs> } // endif else if ($relation instanceof BelongsToMany) { foreach ($columns as $alias => $column) { $tableAlias = $tableAliasPrefix . $relationIndex; $tableAndAlias = $relatedTable . ' AS ' . $tableAlias; if (!isset($subQueries[$alias])) { $pivotTable = $relation->getTable(); $subQueries[$alias] = $currentQuery = DB::query() ->from($tableAndAlias) ->select($tableAlias . '.' . $column) // final table vs pivot table ->join( $pivotTable , // tabelle pivot $relation->getQualifiedRelatedKeyName() , // pivot.fk_related_id '=' , $tableAlias . '.' . $relatedModel->getKeyName() // related_with_alias.id ) ->whereColumn( $relation->getQualifiedForeignKeyName() , '=' , $relation->getParent()->getQualifiedKeyName() ); if (is_string($alias)) { $query->selectSub($currentQuery , $alias); } else { throw new \InvalidArgumentException('Columns must be an associative array'); } } else { throw new \Exception('Multiple relation chain not implemented yet'); } } // end foreach <COLUMNs> } else { throw new \InvalidArgumentException( sprintf("Relation $relationName of type %s is not supported" , get_class($relation)) ); } $currentModel = $relatedModel; $relationIndex++; } // end foreach <RELATIONs> } /** * @param $length * @return string */ public static function randomStringAlpha($length) { $pool = array_merge(range('a', 'z'),range('A', 'Z')); $key = ''; for($i=0; $i < $length; $i++) { $key .= $pool[mt_rand(0, count($pool) - 1)]; } return $key; } }
有一种替代的方式来实现你想要的结果而不使用连接。 您可以根据考试成绩对学生进行sorting。 (Laravel 5.1):
$students = Student::with('exam')->get(); $students = $students->sortByDesc(function ($student, $key) { return $student->exam->result; });