Aşağıdaki örnekte doctrine QueryBuilder ile HAVING cümleciği içinde ikinci bir query kullanmayı göreceğiz.


QueryBuilder


public function findMessagesOlderThanOneDayWithNoReplies()
{
$qb0 = $this->createQueryBuilder('m0');
$qb1 = $this->createQueryBuilder('m1');

$qb1->select('IDENTITY(m1.inReplyTo)')
->where('m1.inReplyTo IS NOT NULL')
->groupBy('m1.inReplyTo');

$qb0->select('m0.id, m0.senderId')
->where('m0.inReplyTo IS NULL')
->andWhere('m0.senderId IS NOT NULL')
->andWhere('DATE_DIFF(CURRENT_DATE(), m0.createdAt) > 1')
->groupBy('m0.senderId')
->having($qb0->expr()->notIn('m0.id', $qb1->getDQL()));

echo $qb0->getDQL();

return $qb0->getQuery()->getResult();
}

Fonksiyon CURRENT_DATE() sadece yyyy-mm-dd bölümünü kontrol eder ama eğer yyyy-mm-dd h:i:s şeklinde bir kontrol isterseniz, aşağıdaki gibi bir değişiklik yapmanız gerekir.


->andWhere('DATE_DIFF(:now, m0.createdAt) > 1')
->setParameter('now', new DateTime('now'))

Eğer IDENTITY kelimesi kullanılmaz ise aşağıdaki hata oluşur.


[Semantical Error] line 0, col 261 near 'inReplyTo FROM': Error: Invalid PathExpression. Must be a StateFieldPathExpression.

Sonuç


SELECT m0.id,
m0.senderId
FROM Application\WebsiteBundle\Entity\Message m0
WHERE m0.inReplyTo IS NULL
AND m0.senderId IS NOT NULL
AND DATE_DIFF(CURRENT_DATE(), m0.createdAt) > 1
GROUP BY m0.senderId
HAVING m0.id NOT IN
(SELECT IDENTITY(m1.inReplyTo)
FROM Application\WebsiteBundle\Entity\Message m1
WHERE m1.inReplyTo IS NOT NULL
GROUP BY m1.inReplyTo)

Asıl amaçlanan


Yukarıdaki DQL'de gördüğümüz gibi, extradan bir GROUP BY m0.senderId kontrolümüz var. Bunun nedeni, programcıların çoğu test ortamında Sqlite kullanırlar ve de doctrine bizi bunu kullanmaya mecbur eder.


SELECT id, sender_id
FROM message
WHERE in_reply_to IS NULL
AND created_at <= NOW() + INTERVAL 1 DAY
HAVING id NOT IN (
SELECT in_reply_to
FROM message
WHERE in_reply_to IS NOT NULL
GROUP BY in_reply_to
)