web-dev-qa-db-fra.com

Comment trier par un champ du tableau croisé dynamique une relation plusieurs à plusieurs dans Eloquent ORM

J'utilise Eloquent ORM depuis quelque temps déjà et je le connais très bien, mais je ne peux pas faire ce qui suit, alors que c'est très facile à faire avec Fluent.

J'ai des utilisateurs avec plusieurs chansons, la table intermédiaire étant song_user (comme il se doit). J'aimerais avoir les meilleures chansons d'un utilisateur, à en juger par le nombre de lectures. Bien entendu, le nombre de lectures est stocké dans la table intermédiaire.

Je peux le faire en courant:

$songs = DB::table('songs')
    ->join('song_user', 'songs.id', '=', 'song_user.song_id')
    ->where('song_user.user_id', '=', $user->id)
    ->orderBy("song_user.play_count", "desc")
    ->get();

Facile. Mais je veux le faire dans Eloquent, qui bien sûr ne fonctionne pas:

$songs = Song::
    with(array("song_user" => function($query) use ($user) {
        $query->where("user_id", "=", $user->id)->orderBy("play_count", "desc");
    }))
29
duality_

Vous n'êtes pas sûr de vouloir passer à Laravel 4, mais voici un exemple éloquent pour trier les champs des tableaux croisés croisés:

public function songs() {
  return $this
    ->belongsToMany('Song')
    ->withPivot('play_count')
    ->orderBy('pivot_play_count', 'desc');
}

withPivot est comme l'éloquent with et ajoutera le champ play_count du tableau croisé dynamique aux autres clés déjà incluses. pivot est préfixé dans les résultats pour tous les champs du tableau croisé dynamique; vous pouvez donc les référencer directement dans la orderBy.

Je n'ai aucune idée de ce à quoi cela pourrait ressembler dans Laravel 3, mais cela vous aidera peut-être à vous orienter dans la bonne direction.

À votre santé!

48
BigBlueHat

Je viens de trouver quelque chose dans le mode d'emploi, vous avez apparemment besoin de la méthode with() .

Depuis le Guide de l'utilisateur :

Par défaut, seuls certains champs de la table pivot sont retournés (les deux champs id et les horodatages). Si votre tableau croisé dynamique contient colonnes supplémentaires, vous pouvez aussi les récupérer en utilisant la méthode with () :

class User extends Eloquent {
  public function roles()
  {
    return $this->has_many_and_belongs_to('Role', 'user_roles')->with('column');
  }
}

Vous pouvez donc utiliser quelque chose de similaire à ceci pour définir votre relation:

$this->has_many_and_belongs_to('User')->with('playcount');

Exemple

Je viens de l'utiliser pour m'assurer que cela fonctionne ...

class Song extends Eloquent {
    function users()
    {
        return $this->has_many_and_belongs_to('User')->with('playcount');
    }
}

class User extends Eloquent {
    function songs()
    {
        return $this->has_many_and_belongs_to('Song')->with('playcount');
    }
}

// My test method
class TestOrm extends PHPUnit_Framework_TestCase {
    public function testSomethingIsTrue()
    {
        foreach(User::find(3)->songs()->order_by('playcount')->get() as $song)
            echo $song->name, ': ', $song->pivot->playcount, "\n";
        echo "\n";
        foreach(User::find(3)->songs()->order_by('playcount','desc')->get() as $song)
            echo $song->name, ': ', $song->pivot->playcount, "\n";
    }
}

Sortie

Jingle Bells: 5
Mary had a little lamb: 10
Soft Kitty: 20
The Catalyst: 100

The Catalyst: 100
Soft Kitty: 20
Mary had a little lamb: 10
Jingle Bells: 5

Note: Ce n'est pas un hasard si, sans utiliser order_by(), le résultat apparaît trié par ascending par ordre playcount. Je l'ai confirmé par des tests (car je ne sais pas encore comment afficher les requêtes dans les tests unitaires), mais vous ne devriez probablement pas compter sur ce comportement.

5
dualed

Toute méthode disponible dans Fluent devrait également l'être avec Eloquent. C'est peut-être ce que vous cherchez?

$songs = Song->join('song_user', 'songs.id', '=', 'song_user.song_id')
->where('song_user.user_id', '=', $user->id)
->orderBy("song_user.play_count", "desc")
->get();
1
Travis Bennett

Je l'ai fait (sur plusieurs versions) simplement en utilisant la méthode de relation que vous avez. J'utilise souvent une colonne 'ordre' dans le tableau croisé dynamique, puis je fais quelque chose comme ça.

$article->tags()->order_by( 'order')->get();

Cela peut être ambigu si vous avez des colonnes nommées 'order' dans la table de jointure. Si tel est le cas, vous devez spécifier -> order_by ('article_tag.order'). Et oui, vous devez utiliser -> avec () pour obtenir cette colonne dans le jeu de résultats. En tant que question de style, je laisserais le paramètre with () en dehors de la méthode de relation et renverrai simplement l'objet de relation Vanilla.

0
Collin James

Dans votre modèle Eloquent, vous pouvez chaîner la colonne orderBy si vous incluez le nom de la table:

return $this->belongsToMany('App\Post')->withTimestamps()->orderByDesc('posts.created_at');
0
Josh LaMar