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

Acyra
quelle
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
3
@Stefan es ist jetzt Teil von ORM. docs.doctrine-project.org/en/latest/tutorials/pagination.html
Eugene

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.

Cerad
quelle
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();
HappyCoder
quelle
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

Luchaninov
quelle
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);
Nathan Kot
quelle
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

Sławomir Kania
quelle
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);
Sergey Poskachey
quelle
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);
Starx
quelle
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?

Oleksii Zymovets
quelle
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();
}
crmpicco
quelle
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();
Abhi Das
quelle