Всем привет. Есть такая ситуация: нужно вывести страницу со списком статей, причем статей на этой странице довольно много, в пределах 20. Считываю их все из базы данных. Допустим, так:
Post::orderBy('id', 'desc')->take(20)->get();
Далее, в этом же списке, надо вывести количество комментариев для каждой статьи. Получается, в foreach по каждой статье еще выполнять запрос на получение количества комментариев для нее. На 20 статей - это еще 20 запросов. Как-то многовато. Есть ли варианты минимизировать число запросов?
Читал про "жадную загрузку", но это, вроде, не тот случай...
Silm: Потому что, насколько я понимаю, там считываются связанные данные. Например, комментарии вместе со статьями. Нигде информацию о том, как считать не комментарии, а просто их количество, я не нашел.
photosho: вы получите все комментарии, но за 1 запрос, вместо 20, сможете выполнить count(). Другой вариант, это строить в ручную запрос с джойнами и агрегацией.
При попытке получить значение "commentsCount" выдает "0". Если писать без "with", то в дальнейшем обращение к "$post->commentsCount" выдает правильное значение. Свойство "productsCount" выдает правильное значение только, если предварительно было сделано обращение непосредственно к "commentsCount" этой статьи.
То есть, получается так, что с "with()" работать не хочет, а работает только тогда, когда для статьи "commentsCount" вызывается индивидуально. Почему может такое происходить? Вот запрос, который генерируется сервером:
select count(*) as count from `comments` where `comments`.`material_id` in ('10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21') and `comments`.`material_type` = 'App\Models\Post'
Вот функция "commentsCount" в модели "Post":
public function commentsCount() {
return $this->morphMany('App\Models\Comment', 'material')->selectRaw('count(*) as count');
}
Andrzej Wielski: Полностью скопировал ваш вариант, причем, что странно, вариант с "groupBy" всегда выдает "1", а без него - правильное количество комментариев. Но, опять же, если выполнять запрос отдельно для каждой статьи. Если выполнить через "with", то всегда выдает "0".
И еще странность. В первом варианте вашего ответа была функция "getProductsCountAttribute" - вот если так, название этой функции отличается от "commentsCount" (даже если просто написать "getCommentCountAttribute", в единственном числе), то удается получить хоть что-то, хотя бы и выполняя запрос для каждой отдельной статьи. Если же название включает в себя имя функции "commentsCount", то везде - одни сплошные нули.
photosho: копировал из своей модели, не углядел. Думаю проблема в morphMany.
Этот код нормально работает для hasMany, с некоторыми изменениями работает с belongsToMany.
Можете вывести всю коллекцию материалов с помощью dd, пришлите пожалуйста скриншот данных в relations и attributes.
Без доступа к дебагу и коду сразу сказать в чем может быть проблема не смогу
Andrzej Wielski: Кстати, переключил на "hasMany" в статьях и "belongsTo" в комментариях - работает точно так же, включая "groupBy('id')", после которой возвращается только 1, несмотря на то, сколько там комментариев.
Andrzej Wielski: Спасибо, кажется, разобрался. Для того, чтобы правильно работало, - необходимо указать в "selectRaw" помимо "count(*)" еще и значение внешнего ключа:
return $this->morphMany('App\Models\Comment', 'material')->selectRaw('material_id, count(*) as count')->groupBy('material_id');
photosho: Это лучше вариант, чем есть, но можно сделать кастомную коллекцию для статей и сдлать в ней метод ,который будет одним запросом выбирать количество комментариев для каждого поста из коллекции. А потом средствами php в этом же методе сделать каждому элементу переменную count_comments, например, и вложить туда результаты через foreach
Станислав Почепко: Что-то не понял, это же то же самое, что уже есть (ваш второй вариант) - обращаться с отдельным запросом для каждой статьи из коллекции для считывания количества ее комментариев...