Conta righe in Doctrine QueryBuilder


197

Sto usando QueryBuilder di Doctrine per creare una query e desidero ottenere il conteggio totale dei risultati dalla query.

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

Voglio solo eseguire un conteggio su questa query per ottenere le righe totali, ma non restituire i risultati effettivi. (Dopo questa query di conteggio, ho intenzione di modificare ulteriormente la query con maxResults per l'impaginazione.)


1
vuoi solo restituire il numero di risultati? il tuo codice non è molto chiaro. perché getQuery () non funziona?
jere,

Per creare l'impaginazione con doctrine2 dai un'occhiata a questa estensione: github.com/beberlei/DoctrineExtensions
Stefan

Risposte:


474

Qualcosa di simile a:

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

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

Alcune persone ritengono che le espressioni siano in qualche modo migliori rispetto all'uso del DQL diretto. Uno addirittura è arrivato al punto di modificare una risposta di quattro anni. Ho ripristinato la sua modifica. Vai a capire.


Non ha chiesto un conteggio senza predicati ( bar = $bar);)
Jovan Perovic,

4
Ha accettato la tua risposta, quindi credo che tutto vada bene. Avevo l'impressione che volesse solo un conteggio senza il sovraccarico di recuperare effettivamente le righe mostrate dal mio esempio. Naturalmente non vi è alcun motivo per cui non è possibile aggiungere condizioni.
Cerad,

50
+1 per l'utilizzo di getSingleScalarResult (). usare count()on $query->getResult()sta effettivamente facendo sì che la query restituisca i risultati (che è ciò che non voleva). penso che questa dovrebbe essere una risposta accettata
jere

18
Il modo più portatile è farlo$qb->select($qb->expr()->count('account.id'))
webbiedave

1
qualcuno può spiegare perché devo usare select('count(account.id)')invece di select('count(account)')?
Stepan Yudin,

51

Ecco un altro modo per formattare la query:

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

L'uso dell'interfaccia fluida è un approccio diverso che è molto utile nel caso in cui si intenda scrivere query statiche. Se è necessario attivare o disattivare le condizioni in cui, ad esempio, l'esecuzione di ciascun metodo da solo presenta vantaggi.
barbieswimcrew

3
Puoi scrivere questoreturn ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
Barh il

25

È meglio spostare tutta la logica di lavorare con il database nei repository.

Quindi nel controller scrivi

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

E dentro Repository/FooRepository.php

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

È meglio passare $qb = ...a una riga separata nel caso in cui si desideri creare espressioni complesse come

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

Pensa anche alla memorizzazione nella cache del risultato della query: 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();
}

In alcuni casi semplici l'uso delle EXTRA_LAZYrelazioni tra entità è buono
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html


17

Se devi contare una query più complessa, con groupBy, havingecc ... Puoi prendere in prestito da Doctrine\ORM\Tools\Pagination\Paginator:

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

8
Utile, ma tieni presente che questa soluzione funzionerà per le query su una singola entità, con istruzioni di selezione complesse, si rifiuterà semplicemente di funzionare.
Paolo Stefan,

questa soluzione produce query aggiuntive come quelle SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableche in realtà non sono niente di speciale ma ben nota COUNT (*) soluzione
Vladyslav Kolesov

$ paginator-> getTotalItemCount () sarebbe anche una soluzione
cwhisperer

11

Da Doctrine 2.6 è possibile utilizzare il count()metodo direttamente da EntityRepository. Per i dettagli consultare il link.

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


Sì, sembra un'ottima soluzione e funziona per casi più semplici (puoi passare criteri per filtrare il conteggio), ma non sono riuscito a farlo funzionare per criteri con le associazioni (filtro per associazioni). Vedi post correlato qui: github.com/doctrine/orm/issues/6290
Wilt

6

Esempio di lavoro con raggruppamento, unione e cose.

Problema:

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

Affinché ciò funzioni, la soluzione possibile è utilizzare un idratatore personalizzato e questa strana cosa chiamata "SUGGERIMENTO WALKER PER USCITE PERSONALIZZATE":

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
Preferirei solo scrivere una query nativa piuttosto che occuparmi di quel codice Rube Goldberg.
KeyboardSmasher

Questo è un buon esempio di quanto merda Symfony sia: Qualcosa di semplice come un conteggio SQL di base di tutti i giorni deve essere risolto con cose autodidattiche totalmente complicate ... wow, voglio dire, wow! Ancora grazie a Sergey per questa risposta!
Sliq,

4

Per le persone che utilizzano solo Doctrine DBAL e non Doctrine ORM, non saranno in grado di accedere al getQuery()metodo perché non esiste. Devono fare qualcosa di simile al seguente.

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

4

Per contare gli elementi dopo un certo numero di elementi (offset), in questo caso $ qb-> setFirstResults () non può essere applicato in quanto non funziona come condizione della query, ma come offset del risultato della query per un intervallo di elementi selezionati ( cioè setFirstResult non può essere usato insieme con COUNT). Quindi, per contare gli oggetti rimasti, ho semplicemente fatto quanto segue:

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

Qualcuno sa un modo più pulito per farlo?


0

L'aggiunta del seguente metodo al repository dovrebbe consentire di chiamare $repo->getCourseCount() dal proprio 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();
}

0

È inoltre possibile ottenere il numero di dati utilizzando la funzione di conteggio.

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.