web-dev-qa-db-fra.com

Eloquent impatient charger Trier par

J'ai un problème avec la requête éloquente. J'utilise un chargement rapide (une à une relation) pour obtenir ' student ' With the ' exam ', en utilisant le code ci-dessous.

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

Et je veux commander les lignes reçues par la colonne ' result ' dans ' exam '. j'utilise

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

Mais ça ne fonctionne pas. Des idées comment le faire?

46
Andrius

Essaye ça:

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

Si vous devez commander votre collection d'étudiants par la colonne de résultats, vous devrez rejoindre les tables.

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

Dans ce cas, supposons que vous ayez une colonne student_id et que votre table d'examens s'appelle exam.

42
Luis Dalmolin

Si vous voulez TOUJOURS le trier par résultat d'examen, vous pouvez ajouter l'appel sortBy directement dans la fonction de relation du modèle.

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

(le crédit pour cette réponse va à pfriendly - il y a répondu ici: Comment trier une sous-requête Eloquent )

10
rosell.dk

Cela a fonctionné pour moi: 

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


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


    return $query->get();
10
Guru

tl; dr

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

Cela va trier les résultats de la requête après un chargement rapide à l’aide de méthodes de collecte et non d’un MySQL ORDER BY.

Explication

Lorsque vous chargez avec impatience, vous ne pouvez pas utiliser un ORDER BY sur les relations chargées car celles-ci seront demandées et assemblées à la suite d'une seconde requête. Comme vous pouvez le voir dans la documentation Laravel le chargement rapide se fait en 2 requêtes.

Si vous voulez utiliser le ORDER BY de MySQL, vous devez rejoindre les tables associées.

Pour résoudre ce problème, vous pouvez exécuter votre requête et trier la collection résultante avec sortBy , sortByDesc ou même sort . Cette solution présente des avantages et des inconvénients par rapport à la solution de jointure:

Avantages:

  • Vous conservez la fonctionnalité Eloquent.
  • Code plus court et plus intuitif.

Désavantages:

  • Le tri sera effectué par PHP au lieu du moteur de base de données.
  • Vous ne pouvez trier que sur une seule colonne, sauf si vous fournissez une fermeture personnalisée pour les fonctions de tri .
  • Si vous n'avez besoin que d'une partie des résultats ordonnés d'une requête (par exemple, ORDER BY avec LIMIT), vous devez extraire tout, le commander, puis filtrer le résultat commandé, sinon vous n'obtiendrez que la partie filtrée ordonné (l'ordre ne tiendra pas compte des éléments filtrés). Cette solution n’est donc acceptable que si vous travaillez de toute façon sur l’ensemble du jeu de données ou que la surcharge ne pose pas de problème.
9
totymedli

Vous pouvez utiliser\Illuminate\Database\Eloquent\Relations\Relation et champs de requête pour ajouter une colonne à l'autre, j'ai écrit un trait pour cela, il manque HasOne o HasMany mais avoir BelongsTo et BelongsToMany pourrait facilement s'adapter

De plus, la méthode pourrait être améliorée pour prendre en charge plus que la profondeur 1 pour une relation à chaînes multiples, j’ai fait de la place pour cela.

<?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