Số lượng hàng trong Doctrine QueryBuilder


197

Tôi đang sử dụng QueryBuilder của Doctrine để xây dựng một truy vấn và tôi muốn nhận được tổng số kết quả từ truy vấn.

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

Tôi chỉ muốn chạy một số đếm trên truy vấn này để có được tổng số hàng, nhưng không trả về kết quả thực tế. (Sau truy vấn đếm này, tôi sẽ sửa đổi thêm truy vấn với maxResults để phân trang.)


1
Bạn chỉ muốn trả về số lượng kết quả? mã của bạn không rõ ràng lắm. tại sao getQuery () không hoạt động?
Jere

Để xây dựng phân trang với doctrine2, hãy xem phần mở rộng này: github.com/beberlei/DoctrineExtensions
Stefan

3
@Stefan bây giờ là một phần của ORM. docs.doctrine-project.org/en/latest/tutorials/pagination.html
Eugene

Câu trả lời:


474

Cái gì đó như:

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

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

Một số người cảm thấy rằng các biểu thức bằng cách nào đó tốt hơn là chỉ sử dụng DQL thẳng. Một thậm chí đã đi rất xa để chỉnh sửa một câu trả lời bốn năm tuổi. Tôi quay lại chỉnh sửa của mình. Đi hình.


Anh ấy đã không yêu cầu đếm mà không có vị ngữ ( bar = $bar);)
Jovan Perovic

4
Anh ấy chấp nhận câu trả lời của bạn nên tôi đoán tất cả đều ổn. Tôi có ấn tượng rằng anh ta chỉ muốn đếm mà không có chi phí thực sự lấy ra các hàng mà ví dụ của tôi cho thấy. Tất nhiên không có lý do tại sao điều kiện không thể được thêm vào.
Cerad

50
+1 để sử dụng getSingleScalarResult (). sử dụng count()trên $query->getResult()thực sự làm cho truy vấn trả về kết quả (đó là điều anh ta không muốn). tôi nghĩ rằng điều này nên được chấp nhận câu trả lời
vào

18
Cách di động nhất là làm$qb->select($qb->expr()->count('account.id'))
webbiedave

1
bất cứ ai có thể giải thích tại sao tôi phải sử dụng select('count(account.id)')thay vì select('count(account)')?
Stepan Yudin

51

Đây là một cách khác để định dạng truy vấn:

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

Sử dụng giao diện trôi chảy là một cách tiếp cận khác rất hữu ích trong trường hợp bạn có ý định viết các truy vấn tĩnh. Nếu có nhu cầu chuyển đổi nơi các điều kiện ví dụ thực hiện từng phương thức trên chính nó cũng có lợi thế của nó.
barbieswimcrew 14/03/2016

3
Bạn có thể viết cái nàyreturn ($qb = $repository->createQueryBuilder('u'))->select($qb->expr()->count('u.id'))->getQuery()->getSingleScalarResult();
Barh

25

Tốt hơn hết là chuyển tất cả logic làm việc với cơ sở dữ liệu sang các repositor.

Vì vậy, trong bộ điều khiển bạn viết

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

Và trong Repository/FooRepository.php

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

Tốt hơn là nên chuyển $qb = ...sang hàng riêng trong trường hợp bạn muốn tạo các biểu thức phức tạp như

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

Ngoài ra, hãy suy nghĩ về việc lưu trữ kết quả truy vấn của bạn - http://symfony.com/doc/civerse/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();
}

Trong một số trường hợp đơn giản, sử dụng EXTRA_LAZYquan hệ thực thể là tốt
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html


17

Nếu bạn cần đếm một truy vấn phức tạp hơn groupBy, havingv.v ... Bạn có thể mượn từ Doctrine\ORM\Tools\Pagination\Paginator:

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

8
Hữu ích, nhưng xin lưu ý: giải pháp này sẽ hoạt động cho các truy vấn trên một thực thể duy nhất - với các câu lệnh chọn phức tạp, nó sẽ từ chối hoạt động.
Paolo Stefan

giải pháp này tạo ra truy vấn bổ sung giống như SELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tablethực sự không có gì đặc biệt nhưng giải pháp COUNT (*) nổi tiếng
Vladyslav Kolesov

$ paginator-> getTotalItemCount () cũng sẽ là một giải pháp
cwhisperer

11

Doctrine 2.6có thể sử dụng count()phương pháp trực tiếp từ EntityRepository. Để biết chi tiết xem liên kết.

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


Vâng, nó có vẻ như là một giải pháp tuyệt vời và hoạt động cho các trường hợp đơn giản hơn (bạn có thể vượt qua các tiêu chí để lọc số đếm), nhưng tôi đã không quản lý để làm cho nó hoạt động cho các tiêu chí với các hiệp hội (lọc theo các hiệp hội). Xem bài đăng liên quan tại đây: github.com/doctrine/orm/issues/6290
Héo

6

Ví dụ làm việc với nhóm, công đoàn và công cụ.

Vấn đề:

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

Để làm việc này, giải pháp khả thi là sử dụng hydrator tùy chỉnh và điều kỳ lạ này được gọi là 'TÙY CHỈNH TÙY CHỈNH TÙY CHỈNH':

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
Tôi chỉ muốn viết một truy vấn gốc hơn là xử lý mã Rube Goldberg đó.
bàn

Đó là một ví dụ điển hình cho việc Symfony tồi tệ đến mức nào: Một thứ đơn giản như số đếm SQL cơ bản hàng ngày cần phải được giải quyết bằng những thứ tự viết hoàn toàn phức tạp ... wow, ý tôi là, chỉ là wow! Vẫn cảm ơn Serge vì câu trả lời này!
Sliq

4

Đối với những người chỉ sử dụng Doctrine DBAL chứ không phải ORM của Doctrine, họ sẽ không thể truy cập getQuery()phương thức này vì nó không tồn tại. Họ cần phải làm một cái gì đó như sau.

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

4

Để đếm các mục sau một số mục (bù), $ qb-> setFirstResults () không thể được áp dụng trong trường hợp này, vì nó hoạt động không phải là điều kiện truy vấn, mà là bù cho kết quả truy vấn cho một phạm vi các mục được chọn ( tức là setFirstResult hoàn toàn không thể được sử dụng để thu thập với COUNT). Vì vậy, để đếm các mục còn lại, tôi chỉ cần làm như sau:

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

Bất cứ ai biết cách sạch hơn để làm điều đó?


0

Thêm phương thức sau vào kho lưu trữ của bạn sẽ cho phép bạn gọi $repo->getCourseCount()từ Trình điều khiển của mình.

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

Bạn cũng có thể lấy số lượng dữ liệu bằng cách sử dụng chức năng đếm.

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.