Я использую Doctrine QueryBuilder для построения запроса, и хочу получить общее количество результатов запроса.
$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();
ИЗМЕНИТЬ
Некоторые люди считают, что выражения как-то лучше, чем просто использование прямого DQL. Один даже дошел до того, чтобы отредактировать четырехлетний ответ. Я перевернул его обратно. Наведите указатель мыши.
bar = $bar
);)
Вот еще один способ отформатировать запрос:
return $repository->createQueryBuilder('u')
->select('count(u.id)')
->getQuery()
->getSingleScalarResult();
return ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->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();
}
Лучше переместить $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();
}
Также подумайте о кешировании результата запроса - 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
, having
и т.д. Вы можете взять у Doctrine\ORM\Tools\Pagination\Paginator
:
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);
SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_table
который на самом деле не является чем-то особенным, но хорошо известным решением COUNT (*)
Пример работы с группировкой, объединением и т.д.
Проблема:
$qb = $em->createQueryBuilder()
->select('m.id', 'rm.id')
->from('Model', 'm')
->join('m.relatedModels', 'rm')
->groupBy('m.id');
Для этого возможным решением является использование специального гидратора и этой странной вещи называемый "CUSTOM OUTPUT WALKER HINT":
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 нельзя использовать togather с 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, а не ORM Doctrine, они не смогут получить доступ к методу getQuery()
, потому что он не существует. Им нужно сделать что-то вроде следующего.
$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);
Добавление следующего метода в ваш репозиторий позволит вам вызвать $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();
}