Doctrine QueryBuilderで行を数える


197

私はDoctrineのQueryBuilderを使ってクエリを作成していて、クエリから結果の総数を取得したいと思っています。

$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()が機能しないのはなぜですか?
jere

doctrine2でページ分割を構築
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を使用するよりも何らかの形で優れていると感じています。4年前の回答を編集することもできました。私は彼の編集を元に戻しました。図を行きます。


彼は述語なしでカウントを要求しなかった(bar = $bar);)
Jovan Perovic

4
彼はあなたの答えを受け入れたので、私はすべてが順調だと思います。私の例が示す行を実際に取得するオーバーヘッドがない、彼はカウントのみを望んでいるという印象を受けました。もちろん、条件を追加できなかった理由はありません。
Cerad

50
getSingleScalarResult()を使用するための+1。count()on を使用する$query->getResult()と、クエリは実際に結果を返します(これは彼望んでいないことです)。これは受け入れられるべき答えだと思います
jere

18
最も移植性のある方法は、実行することです$qb->select($qb->expr()->count('account.id'))
webbiedave 2016年

1
誰かがselect('count(account.id)')代わりになぜ私を使わなければならないのか説明できselect('count(account)')ますか?
Stepan Yudin 2016年

51

クエリをフォーマットする別の方法を次に示します。

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

Fluentインターフェイスの使用は、静的クエリを作成する場合に非常に役立つ別のアプローチです。たとえば、各メソッドを独自に実行する条件がどこにあるかを切り替える必要がある場合にも、それは利点です。
barbieswimcrew 2016年

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

あなたが、より複雑なクエリをカウントする必要がある場合groupByhavingなど...あなたから借りることができますDoctrine\ORM\Tools\Pagination\Paginator

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

8
便利ですが、注意してください。このソリューションは、単一のエンティティに対するクエリで機能します。複雑なselectステートメントを使用すると、機能しなくなります。
Paolo Stefan

このソリューションSELECT COUNT(*) AS dctrn_count FROM (_ORIGINAL_SQL_) dctrn_result) dctrn_tableは、実際には特別なものではありませんが、よく知られているCOUNT(*)ソリューションのような追加のクエリを生成します
Vladyslav Kolesov

$ paginator-> getTotalItemCount()も解決策になります
cwhisperer 2017年

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
Wilt

6

グループ化、共用体などを扱う例。

問題:

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

これが機能するための可能な解決策は、カスタムハイドレーターと、「カスタム出力ウォーカーヒント」と呼ばれる奇妙なものを使用することです。

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カウントのような単純なものは、完全に複雑な自己記述式の要素で解決する必要があります...すごい、すごい!この答えをサーゲイに感謝します!
Sliq

4

Doctrine ORMではなくDoctrine DBALのみを使用している人にとっては、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

count関数を使用してデータ数を取得することもできます。

$query = $this->dm->createQueryBuilder('AppBundle:Items')
                    ->field('isDeleted')->equals(false)
                    ->getQuery()->count();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.