ChatGPT解决这个技术问题 Extra ChatGPT

雄辩的急切加载顺序

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

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

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

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

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

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

G
Glad To Help

尝试这个:

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

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

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

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])
r
rosell.dk

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

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

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


这永远适用。当您不想按结果排序时?
C
Community

tl;博士

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

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

解释

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

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

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

优点:

你保留了 Eloquent 的功能。

更短更直观的代码。

缺点:

排序将由 PHP 而不是数据库引擎完成。

您只能按单列排序,除非您为排序器函数提供自定义闭包。

如果您只需要查询的排序结果的一部分(例如 ORDER BY with LIMIT),则必须获取所有内容,对其进行排序,然后过滤排序结果,否则您最终将仅对过滤后的部分进行排序(排序不会考虑过滤掉的元素)。因此,仅当您无论如何都要处理整个数据集或开销不是问题时,此解决方案才可接受。


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

这对我有用:

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


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


    return $query->get();

M
MatteoOreficeIT

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

J
Joschi Vzfam

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

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

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

这只会对返回的那些进行排序,所以如果你有 100 个并且只得到第 10 个,你不会/可能不会得到想要的