นับแถวใน Doctrine QueryBuilder


197

ฉันใช้ QueryBuilder ของ Doctrine เพื่อสร้างแบบสอบถามและฉันต้องการรับจำนวนผลลัพธ์ทั้งหมดจากแบบสอบถาม

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

ฉันต้องการเรียกใช้การนับในแบบสอบถามนี้เพื่อรับแถวทั้งหมด แต่ไม่ส่งคืนผลลัพธ์ที่แท้จริง (หลังจากคิวรี่การนับนี้ฉันจะแก้ไขเคียวรีเพิ่มเติมด้วย maxResults สำหรับการแบ่งหน้า)


1
คุณแค่ต้องการส่งคืนจำนวนผลลัพธ์หรือไม่ รหัสของคุณไม่ชัดเจน ทำไม getQuery () ไม่ทำงาน
เพียง

สำหรับการสร้างเลขหน้าด้วยหลักคำสอน 2 ดูที่ส่วนขยายนี้: github.com/beberlei/DoctrineExtensions
Stefan

3
@Stefan ตอนนี้เป็นส่วนหนึ่งของ ORM docs.doctrine-project.org/en/latest/tutorials/pagination.html
Eugene

คำตอบ:


474

สิ่งที่ต้องการ:

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

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

บางคนรู้สึกว่าการแสดงออกนั้นดีกว่าการใช้ DQL แบบตรง แม้แต่คนเดียวก็ยังแก้ไขคำตอบอายุสี่ขวบ ฉันย้อนกลับแก้ไขของเขา ไปคิด


เขาไม่ได้ขอนับโดยไม่มีภาคแสดง ( bar = $bar);)
Jovan Perovic

4
เขายอมรับคำตอบของคุณดังนั้นฉันเดาว่าทุกอย่างดี ฉันอยู่ภายใต้ความประทับใจว่าเขาต้องการเพียงการนับโดยไม่มีค่าใช้จ่ายในการดึงแถวที่ตัวอย่างของฉันแสดง แน่นอนไม่มีเหตุผลว่าทำไมไม่สามารถเพิ่มเงื่อนไขได้
Cerad

50
+1 สำหรับการใช้ getSingleScalarResult () การใช้งานcount()บน$query->getResult()ทำให้การสืบค้นส่งคืนผลลัพธ์ (ซึ่งเป็นสิ่งที่เขาไม่ต้องการ) ฉันคิดว่าคำตอบที่ควรจะได้รับการยอมรับ
jere

18
วิธีพกพาที่สุดคือทำ$qb->select($qb->expr()->count('account.id'))
webbiedave

1
ใครสามารถอธิบายได้ว่าทำไมฉันต้องใช้select('count(account.id)')แทนselect('count(account)')?
Stepan Yudin

51

นี่เป็นอีกวิธีในการจัดรูปแบบแบบสอบถาม:

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

การใช้ส่วนต่อประสานที่คล่องแคล่วเป็นวิธีการที่แตกต่างกันซึ่งมีประโยชน์มากในกรณีที่คุณต้องการเขียนแบบสอบถามแบบคงที่ หากมีความจำเป็นต้องสลับที่เงื่อนไขเช่นดำเนินการแต่ละวิธีด้วยตัวเองก็มีข้อดีเช่นกัน
barbieswimcrew

3
คุณสามารถเขียนสิ่งนี้ได้return ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
Barh

25

มันเป็นการดีกว่าที่จะย้ายตรรกะทั้งหมดของการทำงานกับฐานข้อมูลไปยังตัวแทน

ดังนั้นในตัวควบคุมที่คุณเขียน

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

และใน Repository/FooRepository.php

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

จะดีกว่าถ้าคุณย้าย$qb = ...ไปที่แถวที่แยกกันในกรณีที่คุณต้องการสร้างนิพจน์ที่ซับซ้อน

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

คิดเกี่ยวกับการแคชผลการสืบค้นด้วย - 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();
}

ในบางกรณีใช้ง่าย EXTRA_LAZYความสัมพันธ์เอนทิตีเป็นสิ่งที่ดีhttp://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.htmlดี


17

หากคุณต้องการที่จะนับแบบสอบถามที่ซับซ้อนมากขึ้นด้วยgroupBy, havingฯลฯ ... คุณสามารถกู้ยืมเงินจากDoctrine\ORM\Tools\Pagination\Paginator:

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

8
มีประโยชน์ แต่โปรดทราบว่า: วิธีนี้จะใช้งานได้กับการสืบค้นในองค์กรเดียวที่มีคำสั่งเลือกอย่างซับซ้อนมันจะปฏิเสธการทำงาน
เปาโลสเตฟาน

วิธีแก้ปัญหานี้สร้างข้อความค้นหาเพิ่มเติมSELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableซึ่งจริงๆแล้วไม่มีอะไรพิเศษ แต่เป็นที่รู้จักกันดี COUNT (*) วิธีแก้ปัญหา
Vladyslav Kolesov

$ paginator-> getTotalItemCount () จะแก้ปัญหาด้วย
cwhisperer

11

เนื่องจากDoctrine 2.6เป็นไปได้ที่จะใช้count()วิธีการโดยตรงจากEntityRepositoryวิธีการโดยตรงจากสำหรับรายละเอียดดูลิงค์

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


ใช่ดูเหมือนเป็นวิธีแก้ปัญหาที่ยอดเยี่ยมและใช้งานได้ง่ายขึ้น (คุณสามารถส่งผ่านเกณฑ์สำหรับการกรองการนับ) แต่ฉันไม่ได้จัดการเพื่อให้ได้เกณฑ์สำหรับการเชื่อมโยง (การกรองตามการเชื่อมโยง) ดูโพสต์ที่เกี่ยวข้องที่นี่: github.com/doctrine/orm/issues/6290
ร่วงโรย

6

ตัวอย่างการทำงานกับการจัดกลุ่มสหภาพและสิ่งต่าง ๆ

ปัญหา:

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

สำหรับวิธีการแก้ปัญหาที่เป็นไปได้ในการใช้งานคือการใช้ hydrator ที่กำหนดเองและสิ่งแปลก ๆ ที่เรียกว่า '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);

7
ฉันควรเขียนแบบสอบถามดั้งเดิมมากกว่าที่จะจัดการกับรหัส Rube Goldberg
keyboardSmasher

นี่เป็นตัวอย่างที่ดีของ Symfony ที่น่าอับอายคืออะไรบางอย่างที่เรียบง่ายเหมือนกับการนับ SQL พื้นฐานในชีวิตประจำวันต้องได้รับการแก้ไขด้วยสิ่งที่เขียนด้วยตนเองที่ซับซ้อนโดยสิ้นเชิง ... ว้าวฉันหมายถึงว้าว! ยังต้องขอบคุณ Sergey สำหรับคำตอบนี้!
Sliq

4

สำหรับผู้ที่ใช้เพียงหลักคำสอน DBAL และไม่ใช่หลักคำสอน ORM พวกเขาจะไม่สามารถเข้าถึงgetQuery()วิธีการได้เนื่องจากไม่มีอยู่ พวกเขาต้องทำสิ่งต่อไปนี้

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

4

หากต้องการนับรายการหลังจากบางรายการ (หักล้าง) ไม่สามารถใช้ $ qb-> setFirstResults () ในกรณีนี้เนื่องจากไม่ได้ทำงานตามเงื่อนไขการสืบค้น แต่เป็นการชดเชยผลลัพธ์การค้นหาสำหรับช่วงของรายการที่เลือก ( เช่น setFirstResult ไม่สามารถใช้ร่วมกับ COUNT ได้เลย) ดังนั้นในการนับรายการที่เหลือฉันทำต่อไปนี้:

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

ใครรู้วิธีทำความสะอาดมากกว่านี้


0

การเพิ่มวิธีการต่อไปนี้ไปยังที่เก็บของคุณควรอนุญาตให้คุณโทร$repo->getCourseCount()จากคอนโทรลเลอร์ของคุณ

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

นอกจากนี้คุณยังสามารถรับจำนวนข้อมูลโดยใช้ฟังก์ชั่นการนับ

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.