Zeilen in Doctrine QueryBuilder zählen


197

Ich verwende den QueryBuilder von Doctrine, um eine Abfrage zu erstellen, und möchte die Gesamtzahl der Ergebnisse aus der Abfrage abrufen.

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

Ich möchte nur eine Zählung für diese Abfrage ausführen, um die Gesamtzahl der Zeilen zu erhalten, aber nicht die tatsächlichen Ergebnisse zurückgeben. (Nach dieser Zählabfrage werde ich die Abfrage mit maxResults für die Paginierung weiter ändern.)


1
Sie möchten nur die Anzahl der Ergebnisse zurückgeben? Ihr Code ist nicht sehr klar. Warum funktioniert getQuery () nicht?
Nur

Um eine Paginierung mit Doctrine2 aufzubauen,
Stefan

Antworten:


474

Etwas wie:

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

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

Einige Leute glauben, dass Ausdrücke irgendwie besser sind als nur DQL zu verwenden. Man ging sogar so weit, eine vier Jahre alte Antwort zu bearbeiten. Ich rollte seine Bearbeitung zurück. Stelle dir das vor.


Er hat nicht um eine Zählung ohne Prädikate gebeten ( bar = $bar);)
Jovan Perovic

4
Er hat Ihre Antwort akzeptiert, also denke ich, dass alles in Ordnung ist. Ich hatte den Eindruck, dass er nur eine Zählung wollte, ohne den Aufwand, die in meinem Beispiel gezeigten Zeilen tatsächlich abzurufen. Es gibt natürlich keinen Grund, warum Bedingungen nicht hinzugefügt werden konnten.
Cerad

50
+1 für die Verwendung von getSingleScalarResult (). Mit count()on $query->getResult()wird die Abfrage tatsächlich die Ergebnisse zurückgeben (was er nicht wollte). Ich denke, dies sollte akzeptiert werden Antwort
nur

18
Der $qb->select($qb->expr()->count('account.id'))
tragbarste

1
kann jemand erklären , warum ich verwenden müssen , select('count(account.id)')statt select('count(account)')?
Stepan Yudin

51

Hier ist eine andere Möglichkeit, die Abfrage zu formatieren:

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

Die Verwendung der fließenden Benutzeroberfläche ist ein anderer Ansatz, der sehr hilfreich ist, wenn Sie statische Abfragen schreiben möchten. Wenn umgeschaltet werden muss, wo Bedingungen, z. B. das Ausführen jeder Methode für sich, ebenfalls Vorteile hat.
Barbieswimcrew

3
Sie können dies schreibenreturn ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
Barh

25

Es ist besser, die gesamte Logik der Arbeit mit Datenbanken auf Repositores zu verschieben.

Also in Controller schreiben Sie

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

Und in Repository/FooRepository.php

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

Es ist besser, in $qb = ...eine separate Zeile zu wechseln , wenn Sie komplexe Ausdrücke wie erstellen möchten

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

Denken Sie auch daran, Ihr Abfrageergebnis zwischenzuspeichern - 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 einigen einfachen Fällen ist die Verwendung von EXTRA_LAZYEntitätsbeziehungen gut.
Http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html


17

Wenn Sie eine komplexere Abfrage zählen müssen, mit groupBy, havingetc ... Sie können ausleihen von Doctrine\ORM\Tools\Pagination\Paginator:

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

8
Nützlich, aber bitte beachten Sie: Diese Lösung funktioniert für Abfragen in einer einzelnen Entität - mit komplexen select-Anweisungen wird sie nur nicht funktionieren.
Paolo Stefan

Diese Lösung erzeugt eine zusätzliche Abfrage, wie sie SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableeigentlich nichts Besonderes ist, aber eine bekannte COUNT (*)
Vladyslav Kolesov

$ paginator-> getTotalItemCount () wäre auch eine Lösung
cwhisperer

11

Da ist Doctrine 2.6es möglich, count()Methode direkt von zu verwenden EntityRepository. Details finden Sie unter dem Link.

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


Ja, es sieht nach einer großartigen Lösung aus und funktioniert für einfachere Fälle (Sie können Kriterien zum Filtern der Zählung übergeben), aber ich habe es nicht geschafft, es für Kriterien mit Assoziationen (Filtern nach Assoziationen) zum Laufen zu bringen. Siehe verwandten Beitrag hier: github.com/doctrine/orm/issues/6290
Wilt

6

Beispiel für die Arbeit mit Gruppierung, Vereinigung und anderen Dingen.

Problem:

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

Damit dies funktioniert, besteht die mögliche Lösung darin, einen benutzerdefinierten Hydrator und dieses seltsame Ding namens "CUSTOM OUTPUT WALKER TIPP" zu verwenden:

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
Ich würde lieber nur eine native Abfrage schreiben, als mich mit diesem Rube Goldberg-Code zu befassen.
KeyboardSmasher

Das ist ein gutes Beispiel dafür, wie beschissen Symfony ist: Etwas Einfaches wie eine grundlegende alltägliche SQL-Zählung muss mit völlig komplizierten selbstgeschriebenen Dingen gelöst werden ... wow, ich meine, nur wow! Trotzdem danke an Sergey für diese Antwort!
Sliq

4

Personen, die nur Doctrine DBAL und nicht Doctrine ORM verwenden, können nicht auf die getQuery()Methode zugreifen , da sie nicht vorhanden ist. Sie müssen so etwas wie das Folgende tun.

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

4

$ Qb-> setFirstResults () kann in diesem Fall nicht angewendet werden, um Elemente nach einer bestimmten Anzahl von Elementen (Versatz) zu zählen, da dies nicht als Abfragebedingung, sondern als Versatz des Abfrageergebnisses für einen ausgewählten Elementbereich ( dh setFirstResult kann überhaupt nicht zum Sammeln mit COUNT verwendet werden. Um die verbleibenden Gegenstände zu zählen, habe ich einfach Folgendes getan:

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

Weiß jemand einen saubereren Weg, es zu tun?


0

Durch Hinzufügen der folgenden Methode zu Ihrem Repository sollten Sie $repo->getCourseCount()von Ihrem Controller aus aufrufen können.

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

Sie können die Anzahl der Daten auch mithilfe der Zählfunktion abrufen.

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.