75

我对雄辩的查询有疑问。我正在使用急切加载(一对一关系)来获得“学生”和“考试”,使用下面的代码。

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

我想按“考试”中的“结果”列对接收到的行进行排序。我在用

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

但它不起作用。任何想法如何做到这一点?

4

7 回答 7

79

尝试这个:

Student::with(array('exam' => function($query) {
        $query->orderBy('result', 'DESC');
    }))
    ->get();
于 2013-09-18T01:49:50.943 回答
51

如果您需要按结果列排序学生集合,则需要加入表格。

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

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

于 2013-09-18T20:57:28.633 回答
37

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

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

(这个答案的功劳归于 pfriendly - 他在这里回答了这个问题:How to sort an Eloquent subquery

于 2018-02-25T12:26:30.717 回答
27

tl;博士

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

这将使用集合方法而不是 MySQL对预加载后的查询结果进行排序。ORDER BY

解释

当您急切加载时,您不能ORDER BY在加载的关系上使用 an,因为第二次查询将请求和组装这些关系。正如您在Laravel 文档中看到的那样,急切加载发生在 2 个查询中。

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

作为一种解决方法,您可以运行查询并使用或什至对结果集合sortBy进行排序。与join解决方案相比,此解决方案具有优点和缺点:sortByDescsort

优点:

  • 你保留了 Eloquent 的功能。
  • 更短更直观的代码。

缺点:

  • 排序将由 PHP 而不是数据库引擎完成。
  • 您只能按单列排序,除非您为排序函数提供自定义闭包
  • 如果您只需要查询的排序结果的一部分(例如ORDER BYwith LIMIT),则必须获取所有内容,对其进行排序,然后过滤排序结果,否则您最终将仅对过滤后的部分进行排序(排序将不考虑过滤掉的元素)。因此,仅当您无论如何都要处理整个数据集或开销不是问题时,此解决方案才可接受。
于 2017-05-04T12:58:51.277 回答
20

这对我有用:

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


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


    return $query->get();
于 2017-06-04T14:47:25.807 回答
0

您可以使用 \Illuminate\Database\Eloquent\Relations\Relation 和查询范围通过关系添加远列,我为此编写了一个特征,它错过了 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;
    }
}
于 2017-05-16T10:02:58.283 回答
-2

有一种替代方法可以在不使用连接的情况下实现您想要的结果。您可以执行以下操作以根据学生的考试结果对学生进行排序。(Laravel 5.1):

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

$students = $students->sortByDesc(function ($student, $key)
{
    return $student->exam->result;
});
于 2016-01-20T22:24:58.557 回答