Hitung Baris dalam QueryBuilder Ajaran

197

Saya menggunakan QueryBuilder Doctrine untuk membangun kueri, dan saya ingin mendapatkan jumlah total hasil dari kueri.

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

Saya hanya ingin menjalankan hitungan pada permintaan ini untuk mendapatkan baris total, tetapi tidak mengembalikan hasil yang sebenarnya. (Setelah kueri hitung ini, saya akan lebih jauh memodifikasi kueri dengan maxResults untuk pagination.)

Acyra
sumber
1
Anda hanya ingin mengembalikan jumlah hasil? kode Anda tidak begitu jelas. mengapa getQuery () tidak berfungsi?
jere
Untuk membangun pagination dengan doctrine2 lihat ekstensi ini: github.com/beberlei/DoctrineExtensions
Stefan
3
@Stefan sekarang bagian dari ORM. docs.doctrine-project.org/en/latest/tutorials/pagination.html
Eugene

Jawaban:

474

Sesuatu seperti:

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

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

Beberapa orang merasa bahwa ekspresi entah bagaimana lebih baik daripada hanya menggunakan DQL lurus. Bahkan ada yang mengedit jawaban empat tahun. Saya memutar kembali editnya. Sosok pergi.

Cerad
sumber
Dia tidak meminta hitungan tanpa predikat ( bar = $bar);)
Jovan Perovic
4
Dia menerima jawaban Anda, jadi saya kira semuanya baik-baik saja. Saya mendapat kesan bahwa dia hanya menginginkan hitungan tanpa overhead untuk benar-benar mengambil baris yang ditunjukkan oleh contoh saya. Tentu saja tidak ada alasan mengapa kondisi tidak dapat ditambahkan.
Cerad
50
+1 untuk menggunakan getSingleScalarResult (). menggunakan count()pada $query->getResult()sebenarnya membuat permintaan mengembalikan hasil (yang tidak dia inginkan). saya pikir ini harus diterima jawaban
jere
18
Cara yang paling portabel adalah dengan melakukan$qb->select($qb->expr()->count('account.id'))
webbiedave
1
siapa pun dapat menjelaskan mengapa saya harus menggunakan select('count(account.id)')bukan select('count(account)')?
Stepan Yudin
51

Berikut ini cara lain untuk memformat kueri:

return $repository->createQueryBuilder('u')
            ->select('count(u.id)')
            ->getQuery()
            ->getSingleScalarResult();
HappyCoder
sumber
Menggunakan antarmuka yang lancar adalah pendekatan yang berbeda yang sangat membantu jika Anda bermaksud untuk menulis pertanyaan statis. Jika ada kebutuhan untuk beralih di mana kondisi misalnya mengeksekusi setiap metode itu sendiri memiliki kelebihan juga.
barbieswimcrew
3
Anda dapat menulis inireturn ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
Barh
25

Lebih baik untuk memindahkan semua logika bekerja dengan database ke repositori.

Jadi di controller Anda menulis

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

Dan masuk Repository/FooRepository.php

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

Lebih baik pindah $qb = ...ke baris terpisah jika Anda ingin membuat ekspresi yang kompleks seperti

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

Juga pikirkan tentang caching hasil kueri Anda - 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();
}

Dalam beberapa kasus sederhana menggunakan EXTRA_LAZYhubungan entitas baik
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

luchaninov
sumber
17

Jika Anda perlu menghitung kueri yang lebih kompleks, dengan groupBy, havingdll ... Anda dapat meminjam dari Doctrine\ORM\Tools\Pagination\Paginator:

$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalRows = count($paginator);
Nathan Kot
sumber
8
Berguna, tetapi harap dicatat: solusi ini akan bekerja untuk permintaan pada entitas tunggal - dengan pernyataan pilih yang kompleks, itu hanya akan menolak untuk bekerja.
Paolo Stefan
solusi ini menghasilkan permintaan tambahan seperti SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableyang sebenarnya tidak ada yang istimewa tetapi solusi COUNT (*) yang terkenal
Vladyslav Kolesov
$ paginator-> getTotalItemCount () akan menjadi solusi juga
cwhisperer
11

Karena Doctrine 2.6dimungkinkan untuk menggunakan count()metode langsung dari EntityRepository. Untuk detailnya lihat tautan.

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

Sławomir Kania
sumber
Ya, sepertinya ini adalah solusi hebat dan berfungsi untuk kasus yang lebih sederhana (Anda dapat melewati kriteria untuk memfilter penghitungan), tetapi saya tidak berhasil membuatnya berfungsi untuk kriteria dengan asosiasi (memfilter menurut asosiasi). Lihat posting terkait di sini: github.com/doctrine/orm/issues/6290
Wilt
6

Contoh bekerja dengan pengelompokan, penyatuan dan hal-hal lainnya.

Masalah:

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

Agar ini berfungsi, solusi yang mungkin adalah dengan menggunakan custom hydrator dan hal aneh ini disebut '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);
Sergey Poskachey
sumber
7
Saya lebih suka menulis kueri asli daripada berurusan dengan kode Rube Goldberg itu.
keyboardSmasher
Itu contoh yang bagus tentang seberapa buruknya Symfony: Sesuatu yang sederhana seperti hitungan-SQL dasar sehari-hari perlu dipecahkan dengan hal-hal yang ditulis sendiri dengan sangat rumit ... wow, maksud saya, wow! Masih terima kasih kepada Sergey untuk jawaban ini!
Sliq
4

Untuk orang-orang yang hanya menggunakan DBAL Doktrin dan bukan ORM Doktrin, mereka tidak akan dapat mengakses getQuery()metode karena itu tidak ada. Mereka perlu melakukan sesuatu seperti berikut ini.

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

Untuk menghitung item setelah beberapa item (offset), $ qb-> setFirstResults () tidak dapat diterapkan dalam kasus ini, karena berfungsi bukan sebagai kondisi permintaan, tetapi sebagai offset hasil permintaan untuk berbagai item yang dipilih ( yaitu setFirstResult tidak dapat digunakan untuk mengumpulkan COUNT sama sekali). Jadi untuk menghitung item, yang tersisa saya hanya melakukan hal berikut:

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

Adakah yang tahu cara yang lebih bersih untuk melakukannya?

Oleksii Zymovets
sumber
0

Menambahkan metode berikut ke repositori Anda akan memungkinkan Anda menelepon $repo->getCourseCount()dari Kontroler Anda.

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

Anda juga bisa mendapatkan jumlah data dengan menggunakan fungsi hitung.

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
Abhi Das
sumber