Ordre de charge éloquent par
j'ai un problème avec la requête éloquente. J'utilise eager loading (one to one Relationship) pour obtenir ' étudiant 'avec le' examen ', en utilisant le code ci-dessous.
Student::with('exam')->orderBy('exam.result', 'DESC')->get()
Et je veux commander reçus lignes de l ' résultat 'colonne' examen '. J'utilise
->orderBy('exam.result', 'DESC')
mais ça ne marche pas. Toutes les idées de comment le faire ?
7 réponses
essayez ceci:
Student::with(array('exam' => function($query) {
$query->orderBy('result', 'DESC');
}))
->get();
si vous devez commander votre collection d'étudiants par la colonne Résultat, vous devrez rejoindre les tables.
Student::with('exam')
->join('exam', 'students.id', '=', 'exam.student_id')
->orderBy('exam.result', 'DESC')
->get()
dans ce cas, en supposant que vous ayez une colonne student_id
et que votre table d'examens s'appelle exam
.
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();
tl; dr
Student::with('exam')->get()->sortByDesc('exam.result');
cela va trier les résultats de la requête après chargement pressé en utilisant méthodes de collecte et non par un MySQL ORDER BY
.
explication
lorsque vous chargez avec empressement vous ne pouvez pas utiliser un ORDER BY
sur les relations chargées parce que celles-ci seront demandées et assemblées à la suite d'une seconde requête. Comme vous pouvez le voir dans le Laravel documentation chargement eager se produit dans 2 requête.
si vous voulez utiliser le ORDER BY
de MySQL, vous devez rejoindre les tables correspondantes.
comme solution de contournement, 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:
- gardez-Vous des Éloquent de la fonctionnalité.
- code plus court et plus intuitif.
inconvénients:
- le tri sera effectué par PHP au lieu du moteur de base de données.
- vous ne pouvez trier que par une seule colonne, à moins que vous ne fournissiez une fermeture personnalisée pour les fonctions de tri .
- si vous avez besoin seulement d'une partie des résultats commandés d'une requête (par exemple
ORDER BY
avecLIMIT
), vous devez aller chercher tout , le commander, puis filtrer le résultat commandé, sinon vous finirez avec seulement la partie filtrée étant commandé (la commande ne tiendra pas compte des éléments filtrés). Donc cette solution n'est acceptable que lorsque vous travaillez sur l'ensemble de données complet ou la surcharge n'est pas un problème.
si vous voulez toujours qu'il trie par résultat d'examen, vous pouvez ajouter l'appel de sortBy directement dans la fonction de relation sur le modèle.
public function exam() {
return this->hasMany(Exam::class)->orderBy('result');
}
(le crédit de cette réponse va à pfriendly - il a répondu ici: comment trier un subquery éloquent )
, Vous pouvez utiliser \Éclairer\Database\Éloquent\Relations\Rapport et de requêtes étendues d'ajouter la mesure de la colonne, la relation d'aide, j'ai écrit un des traits pour cela, il manque HasOne o HasMany, mais ayant BelongsTo et BelongsToMany pourrait facilement adapté
aussi la méthode pourrait être améliorée pour soutenir plus que la profondeur 1 pour les relations à chaînes multiples, j'ai fait de la place pour que
<?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;
}
}
il y a une autre façon d'obtenir le résultat que vous voulez sans utiliser les jointures. Vous pouvez faire ce qui suit pour trier les élèves en fonction du résultat de leur examen. (Laravel 5.1):
$students = Student::with('exam')->get();
$students = $students->sortByDesc(function ($student, $key)
{
return $student->exam->result;
});