【问题标题】:Eloquent eager load Order by雄辩的急切加载顺序
【发布时间】:2013-09-22 13:27:36
【问题描述】:

我对 eloquent 查询有疑问。我正在使用急切加载(一对一关系)来获得“student”和“exam”,使用下面的代码。

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

我想按“exam”中的“result”列对收到的行进行排序。我正在使用

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

但它不起作用。有什么想法吗?

【问题讨论】:

  • 我相信对于什么构成一对一关系可能会有一些混淆,因为一对一不应该需要急切加载。如果一个学生有很多考试(我假设是这种情况,因为你想订购它们),你需要将你的相关函数从 hasOne() 更新为 hasMany()。考虑到这一点,Glad To Help 的回答应该是正确的。

标签: laravel laravel-4 eloquent eager-loading


【解决方案1】:

试试这个:

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

【讨论】:

  • 对于那些仍在为 laravel 3 苦苦挣扎的人 - 通过急切加载的关系订购是行不通的。在我的情况下,我可以使用静态排序并在模型中使用 order_by。
  • 小心以这种方式进行排序,因为它只会对预先加载的模型进行排序,而不是对所有结果进行排序。
  • 那么您将如何对所有结果进行整体排序?
  • @GaryGreen 你将如何实现对结果查询的排序
  • @Notflip 和 riliwanrabo 以及 get() 方法调用之前的 orderBy。
【解决方案2】:

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

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

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

【讨论】:

  • 为什么我会同时进行加入和急切加载?必须是另一种从急切加载的查询中订购的方式。我被困住了!
  • 您为订购结果而进行的联接。急切加载是出于性能目的。如果您可以在从数据库中获取结果后对结果进行排序,则无法进行连接然后对集合进行排序 (laravel.com/docs/5.2/collections#method-sortby)。
  • 这对我有用,直到我将 ->paginate() 附加到查询中
  • @riliwanrabo 尝试在查询开头添加 ->select('students.*')。
  • @LuisDalmolin 你能检查我的问题吗,它很相似,但我尝试了你的方法stackoverflow.com/questions/63348113/… 不幸的是我仍然遇到错误SQLSTATE[42S02]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid object name 'backorders'. (SQL: select count(*) as aggregate from [IV00102] inner join [backorders] on [items].[ITEMNMBR] = [backorders].[ITEMNMBR])
【解决方案3】:

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

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

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

【讨论】:

  • 这永远适用。当您不想按结果排序时?
【解决方案4】:

tl;博士

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

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

说明

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

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

作为一种解决方法,您可以运行查询并使用sortBy、sortByDesc 甚至sort 对结果集合进行排序。该方案相对于join方案有优缺点:

优点:

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

缺点:

  • 排序将由 PHP 而不是数据库引擎完成。
  • 您只能按单列排序,unless you provide a custom closure for the sorter functions。
  • 如果您只需要查询的排序结果的一部分(例如ORDER BY 和LIMIT),则必须获取所有内容,对其进行排序,然后过滤排序结果,否则您最终将只订购过滤后的部分(订购不会考虑过滤掉的元素)。因此,仅当您无论如何都要处理整个数据集或开销不是问题时,此解决方案才可接受。

【讨论】:

  • 我所需要的很好的答案。谢谢
  • 这是一个很好的答案,但如果您只使用 get() 函数,它就可以工作。如果您使用 paginate() 排序仅在当前页面上完成,而不是总结果。
【解决方案5】:

这对我有用:

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


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


    return $query->get();

【讨论】:

    【解决方案6】:

    您可以使用 \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;
        }
    }
    

    【讨论】:

      【解决方案7】:

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

      $students = Student::with('exam')->get();
      
      $students = $students->sortByDesc(function ($student, $key)
      {
          return $student->exam->result;
      });
      

      【讨论】:

      • 这只会对返回的那些进行排序,所以如果你有 100 个并且只获得第 10 个,那么你不会/可能不会得到想要的那些
      猜你喜欢
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多