С mysql он может делать с этим запросом
SELECT
gift.*, tag.*
FROM
gift
LEFT JOIN tag ON gift.id = tag.gift_id
WHERE gift.id NOT IN (
SELECT g.id FROM gift AS g LEFT JOIN tag AS t ON g.id = t.gift_id WHERE t.name IN ('test', 'test2')
)
Так что с doctrine QB это может быть что-то вроде этого
$subQb = $this->_em->createQueryBuilder()
->select('gift.id')
->leftJoin('gift.tags', 'tags')
->where($qb->expr()->orX(
$qb->expr()->in("tags.name", ":avoidTags")),
))
->getQuery();
$response = $qb
->select('g, tags')
->leftJoin('g.tags', 't')
->where($qb->expr()->notin("g.id", $subQb->getDql())))
->setParameters(new ArrayCollection([
new Parameter('avoidTags', $avoidTags),
]))
->getQuery()
->getResult();