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.)
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.
Voici un autre moyen de formater la requête:
return $repository->createQueryBuilder('u')
->select('count(u.id)')
->getQuery()
->getSingleScalarResult();
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
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);
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);
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.
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);
$ 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?
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();
}