web-dev-qa-db-ja.com

laravel関係による雄弁なソート

3つのモデルがあります

  • ユーザー
  • チャネル
  • 応答

モデル関係

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

私は2つのチャンネルを持っているとしましょう-チャンネル1-チャンネル2

channel-2の返信はchannel-1

今、私はそのチャンネルの現在の返信でユーザーのチャンネルを注文したい。チャットアプリケーションのように。このようにユーザーのチャンネルを注文するにはどうすればよいですか?

  • チャンネル2
  • チャンネル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');
}

編集:

queryresult

11
Hitori

私の知識によると、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();
_
11

チャンネルクラス内で、このhasOneリレーションを作成する必要があります(チャンネルhasManyrepliesですが、hasOnelatest 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');
}
_
1
Adam

まず、 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');
}
1
Quinn Daley

hasOne()関係がある場合、以下を実行することですべてのレコードをソートできます。

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

これにより、すべてのchannelsレコードが最新の返信でソートされます(チャンネルごとに返信が1つしかない場合)。これはあなたの場合ではありませんが、誰かが(このように)探しているかもしれません。

0
Gjaa