Как запросить ассоциацию HasMany на основе одного значения в CakePHP 3.x

У меня есть таблица статей и таблица комментариев. Статьи имеют много комментариев. Каждый комментарий имеет статус комментария (comment_status_id): 1. Хороший, 2. Плохой или 3. Уродливый.

Я хочу сделать запрос, чтобы найти все статьи, которые имеют ТОЛЬКО комментарии со статусом 3 (уродливые). То есть исключить Статьи, у которых есть какие-либо Комментарии со статусом 1 или 2.

Я могу написать подзапрос и запрос, чтобы получить все статьи с комментариями со статусом «Уродливый»:

$matchingComments = $this->Articles->getAssociation('Comments')->find()
    ->select(['article_id'])
    ->distinct()
    ->where(['comment_status_id' => 3]);

$query = $this->Articles->find()
    ->where(['Articles.id IN' => $matchingComments]);

Это дает мне все статьи, у которых есть любой комментарий со статусом 3. Но он также включает статьи, у которых есть комментарии со статусом 2 или 1, а также по крайней мере один комментарий со статусом 3.

Итак, мой вопрос: существует ли эффективный/элегантный способ заставить этот запрос работать с Query Builder, чтобы результатом были только статьи, содержащие комментарии, которые имеют ВСЕ comment_status 3 (Ugly)?

Я уверен, что смогу проанализировать результаты $query с помощью цикла for и построить новый массив результатов, но я хотел бы посмотреть, есть ли лучший способ сделать это в исходном запросе и/или/с подзапросом. Спасибо заранее за любые советы!

D.


person dividedbyzero    schedule 09.04.2019    source источник
comment
Вы знаете, как это сделать в чистом SQL? Всегда лучше сначала выяснить это.   -  person ndm    schedule 09.04.2019
comment
Спасибо, ndm.... да, это правильный подход к решению вопроса. Я добавлю свое решение ниже.   -  person dividedbyzero    schedule 10.04.2019


Ответы (2)


Следуя совету ndm сначала заставить работать необработанный sql, этот запрос сработал для моего запроса matchComments.

SELECT `article_id`
FROM `comments`
GROUP BY `article_id`
HAVING MIN(`comment_status_id`) = 3
AND MAX(`comment_status_id`) = 3;

Итак, в моем контроллере Cake это работает:

$matchingComments = $this->Articles->getAssociation('Comments')->find()
    ->select(['article_id'])
    ->group('article_id')
    ->having(['MIN(comment_status_id)' => 3])
    ->having(['MAX(comment_status_id)' => 3])

$query = $this->Articles->find()
    ->where(['Articles.id IN' => $matchingComments]);

Не уверен, что есть лучший способ, но это работает хорошо. Еще раз спасибо ндм. Д.

person dividedbyzero    schedule 10.04.2019

From Cake Book — получение данных и наборов результатов

$articles = $this->Articles
    ->find()
    ->contain(['Comments'])
    ->notMatching('Comments.CommentStatus', function($q) {
        return $q->where(['CommentStatus.comment_status_id' => '3'];
    });
person Dennis Gabriel Kruk    schedule 10.04.2019
comment
Спасибо Денис. Этот подход создает список всех статей с комментариями, которые не имеют статус 3, и перечисляет их по комментариям, поэтому несколько повторений одного и того же article_id в списке не являются целью. В любом случае, я поиграю с комбинациями соответствия и несоответствия и посмотрю, что работает. Спасибо. - person dividedbyzero; 10.04.2019
comment
Просто попробуйте поместить notMatching в статическую функцию внутри, например 'Comments' =› static function ($q) = {return $q-›notMatching.....}; - person Dennis Gabriel Kruk; 11.05.2020