Count Строки в Doctrine QueryBuilder
Я использую QueryBuilder Doctrine для построения запроса, и я хочу получить общее количество результатов запроса.
$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 для разбивки на страницы).
- Первичный ключ владельца в качестве столбца объединения
- Использование отношений с несколькими менеджерами сущностей
- symfony2: как использовать group_concat в QueryBuilder
Что-то вроде:
$qb = $entityManager->createQueryBuilder(); $qb->select('count(account.id)'); $qb->from('ZaysoCoreBundle:Account','account'); $count = $qb->getQuery()->getSingleScalarResult();
РЕДАКТИРОВАТЬ
Некоторые люди считают, что выражения как-то лучше, чем просто использование прямого DQL. Один даже дошел до того, чтобы отредактировать четырехлетний ответ. Я перевернул его обратно. Идите фигуру.
Вот еще один способ отформатировать запрос:
return $repository->createQueryBuilder('u') ->select('count(u.id)') ->getQuery() ->getSingleScalarResult();
Лучше всего переместить всю логику работы с базой данных в репозитории.
Поэтому в controllerе вы пишете
$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);
Пример работы с группировкой, объединением и т. Д.
Проблема:
$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);
Начиная с Doctrine 2.6
можно использовать метод count()
непосредственно из EntityRepository
. Подробнее см. Ссылку.
Добавление следующего метода в ваш repository позволит вам вызвать $repo->getCourseCount()
из вашего controllerа.
/** * @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(); }