У меня 3 модели
модельные отношения
user
есть belongsToMany('App\Channel');
channel
имеет 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'); }
РЕДАКТИРОВАТЬ:
По моим сведениям, нетерпеливая загрузка методом запускает второй запрос. Вот почему вы не можете достичь того, чего хотите, с энергичной загрузкой методом.
Я думаю, что метод join
в сочетании с методом отношений является решением. Следующее решение полностью протестировано и работает хорошо.
// 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
атрибут created_at
.
Для таких условий, как каналы (которые могут иметь или не иметь ответов), просто используйте метод 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
. См. Подробно эту проблему в этом вопросе stackoverflow .
Поэтому, если вы добавляете метод groupBy
, вы должны использовать метод orderByRaw
и должны быть реализованы следующим образом.
return $this->channels() ->leftJoin('replies', 'channels.id', '=', 'replies.channel_id') ->groupBy(['channels.id']) ->orderByRaw('max(replies.created_at) desc') ->get();
Во-первых, вам не нужно указывать имя сводной таблицы, если вы следуете соглашению об именах Laravel, чтобы ваш код выглядел немного чище:
public function channels() { return $this->belongsToMany('App\Channel') ...
Во-вторых, вам нужно будет вызвать join
явно для достижения результата в одном запросе:
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'); }