3つのモデルがあります
モデル関係
user
have belongsToMany('App\Channel');
channel
have hasMany('App\Reply', 'channel_id', 'id')->oldest();
私は2つのチャンネルを持っているとしましょう-チャンネル1-チャンネル2
channel-2
の返信はchannel-1
今、私はそのチャンネルの現在の返信でユーザーのチャンネルを注文したい。チャットアプリケーションのように。このようにユーザーのチャンネルを注文するにはどうすればよいですか?
私はすでにいくつかのコードを試しました。しかし、何も起こりません
// 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
[〜#〜] edit [〜#〜]また、これを試しました。はい、期待どおりの結果が得られましたが、応答がない場合、すべてのチャネルがロードされません。
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');
}
編集:
私の知識によると、eager loadwith
メソッドは2番目のクエリを実行します。そのため、積極的な読み込みwith
メソッドでは目的を達成できません。
join
メソッドをrelationship methodと組み合わせて使用することが解決策だと思います。次のソリューションは完全にテストされており、正常に機能します。
_// 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();
}
_
次に、$user->sortedChannels('desc')
を呼び出して、応答_created_at
_属性によるchannels順序のリストを取得できます。
channels(返信がある場合とない場合がある)のような条件の場合は、leftJoin
メソッドを使用します。
_public function sortedChannels($orderBy)
{
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
}
_
編集:
groupBy
メソッドをクエリに追加する場合は、orderBy
句に特別な注意を払う必要があります。 Sqlの性質上、_Group By
_句の前に_Order By
_句が最初に実行されるためです。 this stackoverflow question でこの問題の詳細を参照してください。
したがって、groupBy
メソッドを追加する場合は、orderByRaw
メソッドを使用する必要があり、次のように実装する必要があります。
_return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
_
チャンネルクラス内で、このhasOne
リレーションを作成する必要があります(チャンネルhasMany
repliesですが、hasOne
latest reply =):
_public function latestReply()
{
return $this->hasOne(\App\Reply)->latest();
}
_
次のように、最新の返信ですべてのチャンネルを注文できます。
_Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
_
最新の返信で注文されたユーザーからすべてのチャネルを取得するには、そのメソッドが必要です。
_public function getChannelsOrderdByLatestReply()
{
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}
_
ここで、channels()
は次によって与えられます。
_public function channels()
{
return $this->belongsToMany('App\Channel');
}
_
まず、 Laravelの命名規則 に従う場合、ピボットテーブルの名前を指定する必要はありません。そのため、コードは少し簡潔になります。
public function channels()
{
return $this->belongsToMany('App\Channel') ...
次に、join
を明示的に呼び出して、1つのクエリで結果を取得する必要があります。
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');
}
hasOne()
関係がある場合、以下を実行することですべてのレコードをソートできます。
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
これにより、すべてのchannels
レコードが最新の返信でソートされます(チャンネルごとに返信が1つしかない場合)。これはあなたの場合ではありませんが、誰かが(このように)探しているかもしれません。