在Doctrine QueryBuilder中计算行数
我正在使用Doctrine的QueryBuilder来build立一个查询,我想从查询中得到结果的总数。
$repository = $em->getRepository('FooBundle:Foo'); $qb = $repository->createQueryBuilder('n') ->where('n.bar = :bar') ->setParameter('bar', $bar); $query = $qb->getQuery(); //this doesn't work $totalrows = $query->getResult()->count();
我只是想运行一个计数在这个查询来获得总行,但不返回实际的结果。 (在这个计数查询之后,我将使用maxResults进一步修改查询分页。)
就像是:
$qb = $entityManager->createQueryBuilder(); $qb->select('count(account.id)'); $qb->from('ZaysoCoreBundle:Account','account'); $count = $qb->getQuery()->getSingleScalarResult();
编辑
有些人认为expression式比直接使用DQL更好。 甚至还编辑了一个四岁的答案。 我把他的编辑回来了。 去搞清楚。
这是格式化查询的另一种方法:
return $repository->createQueryBuilder('u') ->select('count(u.id)') ->getQuery() ->getSingleScalarResult();
将所有与数据库一起工作的逻辑移动到存储库更好。
所以在控制器你写
$repository = $this->getDoctrine()->getRepository('FooBundle:Foo'); $count = $repository->count();
在FooBundle/Repository/FooRepository.php
public function count() { $qb = $repository->createQueryBuilder('t'); return $qb ->select('count(t.id)') ->getQuery() ->getSingleScalarResult(); }
如果你想制作复杂的expression式,最好移动$qb = ...
来分隔行
public function count() { $qb = $repository->createQueryBuilder('t'); return $qb ->select('count(t.id)') ->where($qb->expr()->isNotNull('t.fieldName')) ->andWhere($qb->expr()->orX( $qb->expr()->in('t.fieldName2', 0), $qb->expr()->isNull('t.fieldName2') )) ->getQuery() ->getSingleScalarResult(); }
也考虑caching您的查询结果 – http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
public function count() { $qb = $repository->createQueryBuilder('t'); return $qb ->select('count(t.id)') ->getQuery() ->useQueryCache(true) ->useResultCache(true, 3600) ->getSingleScalarResult(); }
在一些简单的情况下使用EXTRA_LAZY
实体关系是很好的
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
如果你需要计算一个更复杂的查询,用groupBy
,等等…你可以借用Doctrine\ORM\Tools\Pagination\Paginator
:
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query); $totalRows = count($paginator);
使用分组,联合和东西的例子。
问题:
$qb = $em->createQueryBuilder() ->select('m.id', 'rm.id') ->from('Model', 'm') ->join('m.relatedModels', 'rm') ->groupBy('m.id');
为了这个工作可能的解决scheme是使用自定义hydrator和这个奇怪的事情称为“自定义输出步行者提示”:
class CountHydrator extends AbstractHydrator { const NAME = 'count_hydrator'; const FIELD = 'count'; /** * {@inheritDoc} */ protected function hydrateAllData() { return (int)$this->_stmt->fetchColumn(0); } } class CountSqlWalker extends SqlWalker { /** * {@inheritDoc} */ public function walkSelectStatement(AST\SelectStatement $AST) { return sprintf("SELECT COUNT(*) AS %s FROM (%s) AS t", CountHydrator::FIELD, parent::walkSelectStatement($AST)); } } $doctrineConfig->addCustomHydrationMode(CountHydrator::NAME, CountHydrator::class); // $qb from example above $countQuery = clone $qb->getQuery(); // Doctrine bug ? Doesn't make a deep copy... (as of "doctrine/orm": "2.4.6") $countQuery->setParameters($this->getQuery()->getParameters()); // set custom 'hint' stuff $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountSqlWalker::class); $count = $countQuery->getResult(CountHydrator::NAME);
为了对一些项目(偏移量)之后的项目进行计数,在这种情况下$ qb-> setFirstResults()不能被应用,因为它不是作为查询的条件,而是作为所选项目的范围的查询结果的偏移即setFirstResult根本不能用于COUNT)。 所以计算项目,剩下的只是做了以下几点:
//in repository class: $count = $qb->select('count(p.id)') ->from('Products', 'p') ->getQuery() ->getSingleScalarResult(); return $count; //in controller class: $count = $this->em->getRepository('RepositoryBundle')->... return $count-$offset;
有人知道更干净的方法来做到这一点?
对于只使用Doctrine DBAL而不使用Doctrine ORM的人,他们将不能访问getQuery()
方法,因为它不存在。 他们需要做如下的事情。
$qb = new QueryBuilder($conn); $count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);
将以下方法添加到您的存储库应允许您从Controller中调用$repo->getCourseCount()
。
/** * @return array */ public function getCourseCount() { $qb = $this->getEntityManager()->createQueryBuilder(); $qb ->select('count(course.id)') ->from('CRMPicco\Component\Course\Model\Course', 'course') ; $query = $qb->getQuery(); return $query->getSingleScalarResult(); }