web-dev-qa-db-fra.com

Compter les lignes dans Doctrine QueryBuilder

J'utilise QueryBuilder de Doctrine pour créer une requête et je souhaite obtenir le nombre total de résultats de la requête.

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

Je veux juste exécuter un compte sur cette requête pour obtenir le nombre total de lignes, mais ne pas renvoyer les résultats réels. (Après cette requête de comptage, je vais modifier davantage la requête avec maxResults pour la pagination.)

184
Acyra

Quelque chose comme:

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

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

Certaines personnes pensent que les expressions sont mieux que de simplement utiliser du DQL pur. On est même allé jusqu'à modifier une réponse vieille de quatre ans. J'ai annulé son montage. Allez comprendre.

446
Cerad

Voici un autre moyen de formater la requête:

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

Il est préférable de déplacer toute logique de travail avec base de données vers des dépôts.

Donc, dans le contrôleur, vous écrivez

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

Et dans Repository/FooRepository.php

public function count()
{
    $qb = $repository->createQueryBuilder('t');
    return $qb
        ->select('count(t.id)')
        ->getQuery()
        ->getSingleScalarResult();
}

Il est préférable de déplacer $qb = ... vers une ligne distincte au cas où vous souhaiteriez créer des expressions complexes telles que

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

Pensez également à la mise en cache du résultat de votre requête - 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();
}

Dans quelques cas simples, utiliser EXTRA_LAZY relations d'entité est bon
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

20
luchaninov

Si vous avez besoin de compter une requête plus complexe, avec groupBy, having etc ... Vous pouvez emprunter de Doctrine\ORM\Tools\Pagination\Paginator:

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

Exemple de travail avec le groupement, l'union et d'autres choses.

Problème:

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

Pour que cela fonctionne, une solution possible consiste à utiliser un hydrateur personnalisé et cette chose étrange appelée '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);
6
Sergey Poskachey

Depuis Doctrine 2.6, il est possible d'utiliser la méthode count() directement à partir de EntityRepository. Pour plus de détails, voir le lien.

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

5
Sławomir Kania

Pour les personnes qui utilisent uniquement Doctrine DBAL et non le Doctrine ORM, elles ne pourront pas accéder à la méthode getQuery() car elle n'existe pas. Ils doivent faire quelque chose comme ce qui suit.

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

$ Qb-> setFirstResults () ne peut pas être appliqué dans ce cas car il fonctionne non pas comme une condition de requête, mais comme un décalage du résultat de la requête pour une plage d'éléments sélectionnés ( ie setFirstResult ne peut pas être utilisé avec COUNT). Donc, pour compter les articles qui restent, j’ai simplement fait ce qui suit:

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

Quelqu'un sait-il une façon plus propre de le faire?

4
Oleksii Zymovets

L'ajout de la méthode suivante à votre référentiel devrait vous permettre d'appeler $repo->getCourseCount() à partir de votre contrôleur.

/**
 * @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
crmpicco