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


198

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


1
Вы просто хотите вернуть количество результатов? Ваш код не очень понятен. почему не работает getQuery ()?
— Джер

Для построения нумерации страниц с помощью doctrine2 взгляните на это расширение: github.com/beberlei/DoctrineExtensions
— Stefan

Ответы:


475

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

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

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

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


Он не просил счет без предикатов ( bar = $bar);)
— Йован Перович

4
Он принял ваш ответ, так что, думаю, все хорошо. У меня сложилось впечатление, что ему нужен только счет без дополнительных затрат на фактическое извлечение строк, которые показывает мой пример. Конечно, нет причин, по которым условия не могут быть добавлены.
— Cerad

50
+1 за использование getSingleScalarResult (). использование count()on $query->getResult()фактически заставляет запрос возвращать результаты (чего он не хотел). Я думаю, что это должен быть принят ответ
— Джер

18
Самый переносимый способ сделать это$qb->select($qb->expr()->count('account.id'))
— webbiedave

1
Кто-нибудь может объяснить, почему я должен использовать select('count(account.id)')вместо select('count(account)')?
— Степан Юдин

51

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

return $repository->createQueryBuilder('u')
            ->select('count(u.id)')
            ->getQuery()
            ->getSingleScalarResult();

Использование свободного интерфейса - это другой подход, который очень полезен, если вы собираетесь писать статические запросы. Если есть необходимость переключения, где условия, например, выполнение каждого метода самостоятельно, также имеют свои преимущества.
— barbieswimcrew

3
Вы можете написать этоreturn ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
— Барх

25

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

Так в контроллере пишешь

/* you can also inject "FooRepository $repository" using autowire */
$repository = $this->getDoctrine()->getRepository(Foo::class);
$count = $repository->count();

И в 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


17

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

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);

8
Полезно, но, пожалуйста, обратите внимание: это решение будет работать для запросов к одному объекту - со сложными операторами выбора, оно просто откажется работать.
— Паоло Стефан

это решение создает дополнительный запрос, подобный SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableкоторому на самом деле ничего особенного, но хорошо известное решение COUNT (*)
— Владислав Колесов

$ paginator-> getTotalItemCount () тоже будет решением
— cwhisperer

11

Так Doctrine 2.6как можно использовать count()метод непосредственно из EntityRepository. Подробности смотрите по ссылке.

https://github.com/doctrine/doctrine2/blob/77e3e5c96c1beec7b28443c5b59145eeadbc0baf/lib/Doctrine/ORM/EntityRepository.php#L161


Да, это выглядит как отличное решение и работает для более простых случаев (вы можете пройти критерии фильтрации отсчетов), но мне не удалось заставить его работать для критериев с ассоциациями (фильтрация по ассоциациям). Смотрите похожие сообщения здесь: github.com/doctrine/orm/issues/6290
— Wilt

6

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

Проблема:

 $qb = $em->createQueryBuilder()
     ->select('m.id', 'rm.id')
     ->from('Model', 'm')
     ->join('m.relatedModels', 'rm')
     ->groupBy('m.id');

Для того, чтобы это работало, возможное решение состоит в том, чтобы использовать собственный гидратор и эту странную вещь, называемую «ПОЛЬЗОВАТЕЛЬСКИЙ ВЫХОДНОЙ СОВЕТ»:

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);

7
Я предпочел бы просто написать собственный запрос, чем иметь дело с этим кодом Rube Goldberg.
— клавиатурыSmasher

Это хороший пример того, как дерьмовый Symfony: что-то простое, например, ежедневный подсчет SQL, нужно решить с помощью совершенно сложных самописных вещей ... вау, я имею в виду, просто вау! Еще спасибо Сергею за этот ответ!
— Sliq

4

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

$qb = new QueryBuilder($conn);
$count = $qb->select("count(id)")->from($tableName)->execute()->fetchColumn(0);

4

Для подсчета элементов после некоторого количества элементов (смещение) в этом случае нельзя применить $ 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;

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


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();
}

0

Вы также можете получить количество данных с помощью функции подсчета.

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.