雄辩的渴望加载顺序

时间:2013-09-17 22:52:59

标签: laravel laravel-4 eloquent eager-loading

我有滔滔不绝的查询问题。我正在使用急切加载(一对一的关系)来获得“学生”使用“考试”,使用以下代码。

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

我想通过“考试”中的“结果”列来订购收到的行。我正在使用

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

但它不起作用。任何想法怎么做?

7 个答案:

答案 0 :(得分:62)

试试这个:

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

答案 1 :(得分:42)

如果您需要通过结果列订购学生集合,则需要加入表格。

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

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

答案 2 :(得分:12)

这对我有用:

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


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


    return $query->get();

答案 3 :(得分:12)

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

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

(这个答案归功于友好 - 他在这里回答:How to sort an Eloquent subquery

答案 4 :(得分:10)

TL;博士

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

这将在使用collection methods而不是MySQL ORDER BY进行预先加载后对查询的结果进行排序。

说明

当您急切加载时,您无法在加载的关系上使用ORDER BY,因为这些将在第二次查询时被请求和汇编。正如你在Laravel documentation中看到的那样,急切加载发生在2个查询中。

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

作为一种变通方法,您可以运行查询并使用sortBysortByDesc甚至sort对生成的集合进行排序。该解决方案优于连接解决方​​案的优点和缺点:

优点:

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

缺点:

  • 排序将由PHP而不是数据库引擎完成。
  • 您只能按一列unless you provide a custom closure for the sorter functions排序。
  • 如果您只需要查询的部分有序结果(例如ORDER BYLIMIT),则必须提取所有内容,对其进行排序,然后过滤有序的结果,否则你将只得到被排序的过滤部分(排序将不考虑过滤掉的元素)。所以这个解决方案只有在您处理整个数据集或者开销不是问题时才可以接受。

答案 5 :(得分: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;
    }
}

答案 6 :(得分:-1)

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

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

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