web-dev-qa-db-fra.com

laravel tri éloquent par relation

J'ai 3 modèles

  • Utilisateur
  • Canal
  • Répondre

relations de modèle

  • user ont belongsToMany('App\Channel');
  • channel ont hasMany('App\Reply', 'channel_id', 'id')->oldest();

disons que j'ai 2 canaux - canal 1 - canal 2

channel-2 a des réponses plus récentes que channel-1

maintenant, je veux commander la chaîne de l'utilisateur par la réponse actuelle de sa chaîne. tout comme une application de chat. comment puis-je commander la chaîne de l'utilisateur comme ça?

  • canal-2
  • canal-1

j'ai déjà essayé quelques codes. mais rien ne se passe

// User Model
public function channels()
    {
        return $this->belongsToMany('App\Channel', 'channel_user')
                    ->withPivot('is_approved')
                    ->with(['replies'])
                    ->orderBy('replies.created_at'); // error

    }
// also
public function channels()
    {
        return $this->belongsToMany('App\Channel', 'channel_user')
                    ->withPivot('is_approved')
                    ->with(['replies' => function($qry) {
                        $qry->latest();
                    }]);
    }
// but i did not get the expected result

[~ # ~] modifier [~ # ~] aussi, j'ai essayé cela. oui j'ai obtenu le résultat attendu mais il ne chargerait pas tous les canaux s'il n'y a pas de réponse.

public function channels()
{
    return $this->belongsToMany('App\Channel')
                ->withPivot('is_approved')
                ->join('replies', 'replies.channel_id', '=', 'channels.id')
                ->groupBy('replies.channel_id')
                ->orderBy('replies.created_at', 'ASC');
}

ÉDITER:

queryresult

11
Hitori

Selon mes connaissances, charge ardente with méthode exécute la 2ème requête. C'est pourquoi vous ne pouvez pas obtenir ce que vous voulez avec la méthode de chargement rapide with.

Je pense que l'utilisation de la méthode join en combinaison avec la méthode de relation est la solution. La solution suivante est entièrement testée et fonctionne bien.

// In User Model
public function channels()
{
    return $this->belongsToMany('App\Channel', 'channel_user')
        ->withPivot('is_approved');
}

public function sortedChannels($orderBy)
{
    return $this->channels()
            ->join('replies', 'replies.channel_id', '=', 'channel.id')
            ->orderBy('replies.created_at', $orderBy)
            ->get();
}

Ensuite, vous pouvez appeler $user->sortedChannels('desc') pour obtenir la liste des canaux classés par réponses created_at Attribut.

Pour des conditions comme les canaux (qui peuvent ou non avoir des réponses), utilisez simplement la méthode leftJoin.

public function sortedChannels($orderBy)
    {
        return $this->channels()
                ->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
                ->orderBy('replies.created_at', $orderBy)
                ->get();
    }

Modifier:

Si vous souhaitez ajouter la méthode groupBy à la requête, vous devez porter une attention particulière à votre clause orderBy. Parce que dans la nature SQL , la clause Group By Est exécutée en premier avant la clause Order By. Voir le détail de ce problème sur cette question stackoverflow .

Donc, si vous ajoutez la méthode groupBy, vous devez utiliser la méthode orderByRaw et devez être implémentée comme suit.

return $this->channels()
                ->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
                ->groupBy(['channels.id'])
                ->orderByRaw('max(replies.created_at) desc')
                ->get();
11
Steve.NayLinAung

Dans votre classe de canal, vous devez créer cette relation hasOne (vous canalisez hasManyréponses, mais elle hasOnedernière réponse =):

public function latestReply()
{
    return $this->hasOne(\App\Reply)->latest();
}

Vous pouvez maintenant obtenir tous les canaux classés par dernière réponse comme ceci:

Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');

Pour obtenir toutes les chaînes de l'utilisateur commandées par dernière réponse, vous aurez besoin de cette méthode:

public function getChannelsOrderdByLatestReply()
{
    return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}

channels() est donné par:

public function channels()
{
    return $this->belongsToMany('App\Channel');
}
1
Adam

Tout d'abord, vous n'avez pas besoin de spécifier le nom du tableau croisé dynamique si vous suivez convention de dénomination de Laravel pour que votre code soit un peu plus propre:

public function channels()
{
    return $this->belongsToMany('App\Channel') ...

Deuxièmement, vous devez appeler join explicitement pour obtenir le résultat en une seule requête:

public function channels()
{
    return $this->belongsToMany(Channel::class) // a bit more clean
        ->withPivot('is_approved')
        ->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
        ->groupBy('replies.channel_id')
        ->orderBy('replies.created_at', 'desc');
}
1
Quinn Daley

Si vous avez une relation hasOne(), vous pouvez trier tous les enregistrements en faisant:

$results = Channel::with('reply')
                   ->join('replies', 'channels.replay_id', '=', 'replies.id')
                   ->orderBy('replies.created_at', 'desc')
                   ->paginate(10);

Cela trie tous les enregistrements channels par les réponses les plus récentes (en supposant que vous n'avez qu'une seule réponse par canal.) Ce n'est pas votre cas, mais quelqu'un peut chercher quelque chose comme ça (comme je l'étais.)

0
Gjaa