web-dev-qa-db-ja.com

説得力のある熱心な負荷

雄弁なクエリに問題があります。次のコードを使用して、「student」で「student」を取得するために、熱心な読み込み(1対1の関係)を使用しています。

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

そして、受信した行を「exam」の「result」列で並べたいと思います。使っています

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

しかし、それは機能していません。それを行う方法はありますか?

50
Andrius

これを試して:

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

結果列で生徒のコレクションを注文する必要がある場合は、テーブルを結合する必要があります。

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

この場合、列student_idがあり、examsテーブルの名前はexamであると仮定します。

42
Luis Dalmolin

常に試験結果でソートする場合は、モデルの関係関数にsortBy呼び出しを直接追加できます。

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

(この答えのクレジットはpfriendlyに送られます-彼はここで答えました: Eloquentサブクエリのソート方法

16
rosell.dk

これは私のために働いた:

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


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


    return $query->get();
13
Guru

tl; dr

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

これは、MySQL ORDER BYではなく コレクションメソッド を使用して積極的にロードした後、クエリのresultsをソートします。

説明

熱心にロードする場合、ロードされたリレーションでORDER BYを使用することはできません。リレーションは2番目のクエリの結果として要求およびアセンブルされるためです。 Laravel documentation で見ることができるように、2つのクエリで積極的な読み込みが行われます。

MySQLのORDER BYを使用する場合は、関連するテーブルを結合する必要があります。

回避策として、クエリを実行し、結果のコレクションを sortBysortByDesc 、または sort 。このソリューションには、結合ソリューションに比べて利点と欠点があります。

利点:

  • 雄弁な機能を維持します。
  • より短く、より直感的なコード。

短所:

  • ソートは、データベースエンジンの代わりにPHPによって行われます。
  • 単一の列でのみソートできます ソーター関数にカスタムクロージャーを提供しない限り
  • クエリの順序付けされた結果の一部のみが必要な場合(たとえば、LIMITを含むORDER BY)、すべてをフェッチする必要がありますeverything、それを順序付け、次に順序付けられた結果をフィルタリングします。そうしないと、フィルタリングされた部分のみが順序付けされます(順序付けは、フィルタリングされた要素を考慮しません)。したがって、このソリューションは、とにかくデータセット全体を操作する場合、またはオーバーヘッドが問題にならない場合にのみ受け入れられます。
11
totymedli

\ Illuminate\Database\Eloquent\Relations\Relationとクエリスコープを使用して、リレーションシップを通じて遠い列を追加できます。このための特性を書きました。

また、この方法は、複数の連鎖関係の深さ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;
    }
}
0
MatteoOreficeIT