我想用至少2个左连接对复杂请求进行分页,但我正在使用的分页包(KnpPaginationBundle)无法告诉Doctrine如何计算结果(which is needed for the pagination process),并继续使用这个例外。
Cannot count query which selects two FROM components, cannot make distinction
以下是使用Doctrine QueryBuilder构建的示例请求。
public function findGroupsByUser(User $user, $listFilter, $getQuery = false, $order = 'ASC')
{
$query = $this->createQueryBuilder('r')
->select('r as main,g')
->select('r as main,g, count(gg) as members')
->leftjoin('r.group', 'g')
->innerjoin('MyBundle:GroupMemberRel', 'gg', 'WITH', 'r.group = gg.group')
->addGroupBy('g.groupId')
->add('orderBy', 'g.name ' . $order);
if ($getQuery == true) {
return $query;
}
return $query->getQuery()->getResult();
}
然后我将此请求提供给knp_paginator服务,然后我得到了异常
$groupQuery = $this->em->getRepository('MyBundle:GroupMemberRel')->findGroupsByUser($user, $listFilter, true);
$paginator = $this->container->get('knp_paginator');
/* @var $groups Knp\Component\Pager\Pagination\PaginationInterface */
$groups = $paginator->paginate(
$groupQuery, $this->container->get('request')->query->get('page', 1), 10 /* limit per page */
);
关于如何对复杂请求进行分页的任何想法,我很确定这个用例很常见,不希望在分页后保留我的结果。
答案 0 :(得分:22)
对于那些正在寻找答案的人来说,有一个很好的解决方案: https://github.com/KnpLabs/KnpPaginatorBundle/blob/master/Resources/doc/manual_counting.md
$paginator = new Paginator;
// determine the count of the entire result set
$count = $entityManager
->createQuery('SELECT COUNT(c) FROM Entity\CompositeKey c')
->getSingleScalarResult();
// create a query to be used with the paginator, and specify a hint
$query = $entityManager
->createQuery('SELECT c FROM Entity\CompositeKey c')
->setHint(
'knp_paginator.count',
$count
);
// paginate, and set "disctinct" option to false
$pagination = $paginator->paginate(
$query,
1,
10,
array(
'distinct' => false,
)
);
基本上,你在做什么,是你在创造自己的'计数'查询并指示knp paginator使用它。
答案 1 :(得分:14)
原始问题中的实体很难理解,但我遇到了同样的问题而且它确实是可以解决的。
假设您有这样的实体:
class User
{
// ...
/**
* @ORM\OneToMany(targetEntity="Registration", mappedBy="user")
*/
private $registrations;
// ...
}
重要的是它与另一个实体有一对多的关系,并且你希望能够通过QueryBuilder以任何理由加入它(例如,你可能想要添加一个{{1您的查询的子句只选择那些具有一个或多个其他实体的实体。)
您的原始代码可能如下所示:
HAVING
这将抛出异常: $qb = $this->createQueryBuilder();
$query = $qb
->select('u')
->add('from', '\YourBundle\ORM\Model\User u')
->leftJoin('\YourBundle\ORM\Model\Registration', 'r', 'WITH', 'u.id = r.user')
->groupBy('u.id')
->having($qb->expr()->gte($qb->expr()->count('u.registrations'), '1')
->getQuery();
要解决此问题,请重写查询,以便QueryBuilder只有一个“from”组件 - 正如异常所示 - 通过内联移动连接。像这样:
Cannot count query which selects two FROM components, cannot make distinction
使用Doctrine的Paginator类可以正常工作,不需要其他捆绑包。还要注意连接的简写语法;使用Doctrine你没有看到明确指定连接实体(尽管你可以),因为映射已经知道它是什么。
希望这可以帮助别人,在这个问题上没有太多。您可以通过查看@halfer's comment here了解有关内部处理方式的更多信息。
答案 2 :(得分:3)
将doctrine/dbal
更新为版本2.5
为我修复此问题。
答案 3 :(得分:0)
如果您不按照上述答案中提供的方式使用常规映射实体,则连接将在结果集中创建其他组件。这意味着该集合无法计算,因为它不是标量结果。
所以关键是将结果集保存到一个组件,这样就可以生成和计算标量结果。
您可以在运行时将常规字段转换为实体映射。这允许您编写只返回一个组件的查询,即使您随机加入其他实体也是如此。 “其他实体”成为主要实体的子项,而不是其他根实体或结果的额外组成部分。
下面的示例显示了一对一的映射。我没有尝试过更复杂的映射,但是我的成功表明它应该是可能的。 Registration实体有一个名为user的字段,其中包含一个用户Id,但它不是一个映射实体,只是一个普通字段。
(这已从一个工作示例进行了修改,但未按原样进行测试 - 因此视为伪代码)
$queryBuilder // The query builder already has the other entities added.
->innerJoin('r.user', 'user')
;
$mapping['fieldName'] = 'user';
$mapping['targetEntity'] = '\YourBundle\ORM\Model\User';
$mapping['sourceEntity'] = '\YourBundle\ORM\Model\Registration';
$mapping['sourceToTargetKeyColumns'] = array('user' => 'id');
$mapping['targetToSourceKeyColumns'] = array('id' => 'user');
$mapping['fetch'] = 2;
$mapping['joinColumns'] = array(
array(
'name' => 'user',
'unique' => false,
'nullable' => false,
'onDelete' => null,
'columnDefinition' => null,
'referencedColumnName' => 'id',
)
);
$mapping['mappedBy'] = 'user';
$mapping['inversedBy'] = null; // User doesn't have to link to registrations
$mapping['orphanRemoval'] = false;
$mapping['isOwningSide'] = true;
$mapping['type'] = ClassMetadataInfo::MANY_TO_ONE;
$vm = $this->em->getClassMetadata('YourBundle:Registration');
$vm->associationMappings["user"] = $mapping;
注意:我使用的是PagerFanta而不是KNP - 但问题在于Doctrine所以我希望这可以在任何地方使用。