Количество строк в Doctrine QueryBuilder

153

Я использую 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 для разбивки на страницы.)

  • 1
    Вы просто хотите вернуть количество результатов? Ваш код не очень понятен. почему не работает getQuery ()?
  • 0
    Для построения нумерации страниц с помощью doctrine2 посмотрите на это расширение: github.com/beberlei/DoctrineExtensions
Показать ещё 1 комментарий
Теги:
doctrine2

8 ответов

360
Лучший ответ

Что-то вроде:

$qb = $entityManager->createQueryBuilder();
$qb->select('count(account.id)');
$qb->from('ZaysoCoreBundle:Account','account');

$count = $qb->getQuery()->getSingleScalarResult();

ИЗМЕНИТЬ

Некоторые люди считают, что выражения как-то лучше, чем просто использование прямого DQL. Один даже дошел до того, чтобы отредактировать четырехлетний ответ. Я перевернул его обратно. Наведите указатель мыши.

  • 0
    Он не просил счет без предикатов ( bar = $bar );)
  • 4
    Он принял твой ответ, так что, думаю, все хорошо. У меня сложилось впечатление, что ему нужен только счет без дополнительных затрат на фактическое извлечение строк, которые показывает мой пример. Конечно, нет причин, по которым условия не могут быть добавлены.
Показать ещё 12 комментариев
33

Вот еще один способ отформатировать запрос:

return $repository->createQueryBuilder('u')
            ->select('count(u.id)')
            ->getQuery()
            ->getSingleScalarResult();
  • 0
    Использование свободного интерфейса - это другой подход, который очень полезен, если вы собираетесь писать статические запросы. Если есть необходимость переключения, где условия, например, выполнение каждого метода самостоятельно, также имеют свои преимущества.
  • 2
    Вы можете написать это return ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
12

Лучше всего переместить всю логику работы с базой данных в репозитории.

Итак, в контроллере вы пишете

$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

11

Если вам нужно посчитать более сложный запрос, groupBy, having и т.д. Вы можете взять у Doctrine\ORM\Tools\Pagination\Paginator:

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);
  • 8
    Полезно, но, пожалуйста, обратите внимание: это решение будет работать для запросов к одному объекту - со сложными операторами выбора, оно просто откажется работать.
  • 0
    это решение создает дополнительный запрос, такой как SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_table который на самом деле не является чем-то особенным, но хорошо известным решением COUNT (*)
Показать ещё 1 комментарий
5

Пример работы с группировкой, объединением и т.д.

Проблема:

 $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);
  • 5
    Я предпочел бы просто написать собственный запрос, чем иметь дело с этим кодом Rube Goldberg.
4

Чтобы подсчитать элементы после некоторого количества элементов (смещение), $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;

Кто-нибудь знает более чистый способ сделать это?

2

Для людей, которые используют только Doctrine DBAL, а не ORM Doctrine, они не смогут получить доступ к методу getQuery(), потому что он не существует. Им нужно сделать что-то вроде следующего.

$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);
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();
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню