web-dev-qa-db-ja.com

Doctrine QueryBuilderの行数をカウントする

私は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を使用してクエリをさらに変更します。)

179
Acyra

何かのようなもの:

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

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

編集

何人かの人々は、式は単純なDQLを使うよりもどうにか良いと感じています。 4歳の答えを編集することさえした。私は彼の編集を元に戻しました。図に行きます。

440
Cerad

これは、クエリをフォーマットするための別の方法です。

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

データベースを扱うすべてのロジックをリポジトリに移すことをお勧めします。

だからコントローラであなたが書く

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

19
luchaninov

groupByhavingなどでもっと複雑なクエリを数える必要があるなら、Doctrine\ORM\Tools\Pagination\Paginatorから借りることができます。

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

グループ化、共用体、その他のものを使った作業の例。

問題:

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

これを機能させるには、カスタムハイドレータと「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);
5

Doctrine 2.6以降、EntityRepositoryから直接count()メソッドを使用することが可能です。詳細についてはリンクを参照してください。

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

5
Sławomir Kania

Doctrine DBALのみを使用し、Doctrine ORMを使用していない人にとっては、getQuery()メソッドが存在しないためアクセスできません。彼らは以下のようなことをする必要があります。

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

ある数のアイテム(offset)の後のアイテムを数えるために、$ 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;

誰かがそれを行うためのよりクリーンな方法を知っていますか?

4

以下のメソッドをあなたのリポジトリに追加すると、あなたのコントローラから$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
crmpicco