雄辩的急切加载订单

我对雄辩的问题有疑问。我使用急切加载(一对一的关系) ,以获得’学生’与’考试’,使用下面的代码。

Student::with('exam')->orderBy('exam.result', 'DESC')->get()

我想通过‘ 考试’中的‘ 结果’列对接收到的行进行排序

->orderBy('exam.result', 'DESC')

但是没有用,有什么办法吗?

74651 次浏览

试试这个:

Student::with(array('exam' => function($query) {
$query->orderBy('result', 'DESC');
}))
->get();

如果需要按照结果列对学生集合进行排序,则需要联接这些表。

Student::with('exam')
->join('exam', 'students.id', '=', 'exam.student_id')
->orderBy('exam.result', 'DESC')
->get()

在这种情况下,假设您有一列 student_id,并且您的考试表名为 exam

还有一种不使用联接实现您想要的结果的替代方法。您可以执行以下操作,根据学生的考试成绩对他们进行排序。(幼虫5.1) :

$students = Student::with('exam')->get();


$students = $students->sortByDesc(function ($student, $key)
{
return $student->exam->result;
});

博士

Student::with('exam')->get()->sortByDesc('exam.result');

这将使用 收集方法对查询的 结果进行排序,而不是使用 MySQLORDER BY

解释

当您渴望加载时,您不能在加载的关系上使用 ORDER BY,因为这些关系将被请求并组装为第二个查询的结果。正如您在 Laravel 文档热切加载中看到的,它发生在2个查询中。

如果你想使用 MySQL 的 ORDER BY,你必须连接相关的表。

作为解决方案,您可以运行查询并使用 sortBysortByDesc甚至 sort对结果集合进行排序。与联接解决方案相比,此解决方案有优点和缺点:

优点:

  • 你保持雄辩的功能。
  • 更短更直观的代码。

缺点:

  • 排序将由 PHP 而不是数据库引擎完成。
  • 只能按单列 除非为排序函数提供自定义闭包进行排序。
  • 如果您只需要一个查询的有序结果的一部分(例如 ORDER BYLIMIT) ,您必须获取 一切,排序它,然后过滤排序的结果,否则您将最终只有被排序的被过滤的部分(排序不会考虑被过滤掉的元素)。因此,只有当您无论如何都要处理整个数据集或开销不成问题时,这种解决方案才是可以接受的。

你可以使用照明数据库雄辩关系关系和查询范围添加远列通过关系,我为此写了一个特点,它错过了 HasOne o 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;
}
}

这对我很有效:

$query = Student::select(['id','name']);




$query->has('exam')->with(['exam' => function ($query) {
return $query->orderBy('result','ASC');
}]);




return $query->get();

如果您总是希望它按照考试结果排序,那么可以直接在模型的关系函数中添加 sortBy 调用。

public function exam() {
return this->hasMany(Exam::class)->orderBy('result');
}

(这个问题的答案归功于 pfriends-他在这里回答了: 如何对 Eloquent 子查询排序)