好的,这就是问题所在。
我有一个名为 HelpDocuments 的实体和一个名为 LogEntry 的实体。
用户可以关闭帮助文档。发生这种情况时,我创建一个具有以下属性的 LogEntry:
当我实现自己的鉴别器逻辑时,HelpDocument 和 LogEntry 之间没有创建任何关系。
所以我想要实现的是查询所有尚未被驳回的帮助文档。我可以使用 sql 来做到这一点,左外部子查询连接如下所示:
SELECT HelpDocument.*, temp.*
FROM HelpDocument
LEFT OUTER JOIN(
SELECT LogEntry.entity_id
FROM LogEntry
WHERE LogEntry.entityDiscriminator = 'HelpDocument'
AND LogEntry.event = 'helpDocument.dismiss'
AND LogEntry.entity_id = 11
) as temp ON HelpDocument.id = temp.entity_id
WHERE temp.entity_id IS NULL;
我的问题是,如果没有定义关系,如何将其转换为 DQL?
更新的解决方案:
所以解决方案是不使用 LEFT OUTER JOIN,因为它们在 Doctrine2 中不存在/没有意义。最后我不得不做一个子查询连接:
/**
* Filter by User Dismissed
*
* @param $qb
* @param $route
* @return mixed
*/
public function filterQueryByUserDismissed(QueryBuilder $qb, $args)
{
$args = array_merge(array(
"user" => null,
"dismissed" => false
), $args);
/** @var $dismissedQB QueryBuilder */
$dismissedQB = $this->_em->createQueryBuilder();
/*
This line is important. We select an alternative attribute rather than
letting Doctrine select le.id
*/
$dismissedQB->select('le.entityId')
->from('\Mlf\AppBundle\Entity\UserEntityEventLog', 'le')
->where('le.entityDiscriminator = :entityDiscriminator')
->andWhere('le.event = :event')
->andWhere('le.user = :userId');
$function = (true === $args['dismissed']) ? "in" : "notIn";
$expr = $qb->expr()->$function($this->classAlias.'.id', $dismissedQB->getDQL());
/** @var $qb QueryBuilder */
$qb->andWhere($expr)
->setParameter("entityDiscriminator", HelpDocument::getDiscriminator())
->setParameter("event", HelpDocumentEvents::HELPDOCUMENT_DISMISS)
->setParameter("userId", $args["user"]);
// exit($result = $qb->getQuery()->getSQL());
return $qb;
}
此 DQL 查询会生成以下 SQL:
SELECT h0_.id AS id0
FROM HelpDocument h0_
WHERE (
h0_.id NOT IN (
SELECT l1_.entity_id
FROM LogEntry l1_
WHERE l1_.entityDiscriminator = 'helpDocument'
AND l1_.event = 'helpDocument.dismiss'
AND l1_.user_id = 1
)
)
耶!
我看到了你的解决方案,我做了一个小小的改变,这将是一个巨大的性能改进。特别是如果您有超过几千行。
public function filterQueryByUserDismissed(QueryBuilder $qb, $args)
{
$args = array_merge(array(
"user" => null,
"dismissed" => false
), $args);
/** @var $dismissedQB QueryBuilder */
$dismissedQB = $this->_em->createQueryBuilder();
/*
This line is important. We select an alternative attribute rather than
letting Doctrine select le.id
*/
$dismissedQB->select('le.entityId')
->from('\Mlf\AppBundle\Entity\UserEntityEventLog', 'le')
->where('le.entityDiscriminator = :entityDiscriminator')
->andWhere('le.event = :event')
->andWhere('le.user = :userId');
// ---- My changes below
// Get an array with the ids
$dismissedIdsMap = $dismissedQB->getQuery()->getResults();
$dismissedIds = array_map(
function($a){
return $a['entityId'];
},
$dismissedIdsMap);
$function = (true === $args['dismissed']) ? "in" : "notIn";
$expr = $qb->expr()->$function($this->classAlias.'.id', $dismissedIds);
// ---- My changes above
/** @var $qb QueryBuilder */
$qb->andWhere($expr)
->setParameter("entityDiscriminator", HelpDocument::getDiscriminator())
->setParameter("event", HelpDocumentEvents::HELPDOCUMENT_DISMISS)
->setParameter("userId", $args["user"]);
// exit($result = $qb->getQuery()->getSQL());
return $qb;
}
上面的代码使用了两个查询。如果您使用一个查询,MySQL 将从您的子查询创建一个临时视图,然后使用主查询查询该视图。创建此视图的开销很大。通过两个查询,您将在PHP内存中保留“视图”,这将大大减少开销。