如何在其他查询中将格式化的Doctrine 2与QueryBuilder格式化的查询一起重用

时间:2019-04-17 07:30:41

标签: doctrine-orm doctrine symfony4 query-builder doctrine-query

我已使用学问查询构建器格式化了查询。这有点复杂,因为在该查询中有一些我自己定义的教义函数和教义扩展。同样非常重要的是要提到使用过滤器的查询需要一些预选的值。问题是当我想从该查询中计数记录时。我当然可以写计数(结果),但这对性能非常不利。

我尝试使用自己的预定义查询作为源来创建新的sql查询,但是没有注入参数的问题。

查询哪种格式以及我要计算的结果:

private function getCategoriesQuery(Filter $filter = null, Sort $sort = null, Pagination $pagination = null)
{
        $qb = $this->createQueryBuilder('c')
            ->select('c.id as id')
            ->addSelect('c.title as title')
            ->addSelect('DATE_FORMAT(c.updationDate, \'%Y-%m-%d %H:%i:%S\') as updationDate')
            ->addSelect('DATE_FORMAT(c.creationDate, \'%Y-%m-%d %H:%i:%S\') as creationDate')
            ->addSelect('coalesce(sum(r.amount), 0.00) as total')
            ->addSelect('round(AveragePerMonth(coalesce(sum(r.amount), 0.00), c.creationDate, CURRENT_DATE()), 2) as averagePerMonth')
            ->leftJoin('c.records', 'r')
            ->groupBy('c.id')
            ->andWhere('c.user = :user')->setParameter('user', $this->user);

        if ($filter) $this->applyFilters($filter, $qb);
        if ($sort) $this->applySort($sort, $qb);
        if ($pagination) $this->applyPagination($pagination, $qb);

        return $qb->getQuery();
}

我尝试过:

public function countUserCategories(Filter $filter = null)
{
        $rsm = new ResultSetMapping();
        $sql = $this->_em->createNativeQuery('select count(*) from ('.$this->getCategoriesQuery($filter)->getSQL().') as src', $rsm);
        return $sql->getSingleScalarResult();
}

如果有可能合并这些查询,以及如果可以的话,我希望得到答案。谢谢

ApplyFilter方法:

private function applyFilters(Filter $filter, QueryBuilder $qb)
{
        if ($filter->getCategories()) {
            $qb->andWhere('c.id IN(:categories)')->setParameter('categories', $filter->getCategories());
        }

        if ($filter->getEndDate()) {
            $qb->andWhere('r.date <= :endDate')->setParameter('endDate', $filter->getEndDate());
        }

        if ($filter->getStartDate()) {
            $qb->andWhere('r.date >= :startDate')->setParameter('startDate', $filter->getStartDate());
        }

        if ($filter->getStartTotal() !== null) {
            $qb->andHaving('total >= :startTotal')->setParameter('startTotal', $filter->getStartTotal());
        }

        if ($filter->getEndTotal() !== null) {
            $qb->andHaving('total <= :endTotal')->setParameter('endTotal', $filter->getEndTotal());
        }

        if ($filter->getStartAveragePerMonth() !== null) {
            $qb->andHaving('averagePerMonth >= :startAveragePerMonth')->setParameter('startAveragePerMonth', $filter->getStartAveragePerMonth());
        }

        if ($filter->getEndAveragePerMonth() !== null) {
            $qb->andHaving('averagePerMonth <= :endAveragePerMonth')->setParameter('endAveragePerMonth', $filter->getEndAveragePerMonth());
        }
}

解决此问题的建议方法,但返回响应:The query returned multiple rows. Change the query or use a different result function like getScalarResult().

public function countUserCategories(Filter $filter = null)
{
        return (clone $this->getCategoriesQueryBuilder($filter))
            ->resetDQLPart('select')
            ->select('COUNT(c)')
            ->getQuery()
            ->getSingleScalarResult();
}

更新

我通过使用SQL查询生成器来完成这项工作

查询构建器的创建

private function getCategoriesQueryBuilder(Filter $filter = null, Sort $sort = null, Pagination $pagination = null)
{
        $qb = $this->_em->getConnection()->createQueryBuilder()
            ->select('c.id AS id')
            ->addSelect('c.title AS title')
            ->addSelect('DATE_FORMAT(c.updation_date, \'%Y-%m-%d %H:%i:%S\') AS updationDate')
            ->addSelect('DATE_FORMAT(c.creation_date, \'%Y-%m-%d %H:%i:%S\') AS creationDate')
            ->addSelect('COALESCE(SUM(r.amount), 0.00) AS total')
            ->addSelect('(SELECT ROUND(COALESCE(SUM(r.amount), 0.00) / COUNT(DISTINCT(DATE_FORMAT(calendar.date, \'%Y-%m\'))), 2)
                                FROM calendar as calendar
                                WHERE calendar.date BETWEEN DATE_FORMAT(c.creation_date, \'%Y-%m-%d\') AND DATE_FORMAT(CURRENT_DATE(), \'%Y-%m-%d\')) 
                                AS averagePerMonth')
            ->from('category', 'c')
            ->leftJoin('c', 'record', 'r', 'r.category_id = c.id')
            ->join('c', 'users', 'u', 'u.id = c.user_id')
            ->where('u.id = :userId')->setParameter('userId', $this->user->getId())
            ->groupBy('c.id');

        if ($sort) $this->applySort($sort, $qb);
        if ($pagination) $this->applyPagination($pagination, $qb);
        if ($filter) $this->applyFilter($filter, $qb);

        return $qb;
}

过滤器

private function applyFilter(Filter $filter, QueryBuilder $qb)
{
        if ($filter->getCategories()) {
            $qb->andWhere('c.id IN(:categories)')->setParameter('categories', $filter->getCategories());
        }

        if ($filter->getEndDate()) {
            $qb->andWhere('r.date <= :endDate')->setParameter('endDate', $filter->getEndDate());
        }

        if ($filter->getStartDate()) {
            $qb->andWhere('r.date >= :startDate')->setParameter('startDate', $filter->getStartDate());
        }

        if ($filter->getStartTotal() !== null) {
            $qb->andHaving('total >= :startTotal')->setParameter('startTotal', $filter->getStartTotal());
        }

        if ($filter->getEndTotal() !== null) {
            $qb->andHaving('total <= :endTotal')->setParameter('endTotal', $filter->getEndTotal());
        }

        if ($filter->getStartAveragePerMonth() !== null) {
            $qb->andHaving('averagePerMonth >= :startAveragePerMonth')->setParameter('startAveragePerMonth', $filter->getStartAveragePerMonth());
        }

        if ($filter->getEndAveragePerMonth() !== null) {
            $qb->andHaving('averagePerMonth <= :endAveragePerMonth')->setParameter('endAveragePerMonth', $filter->getEndAveragePerMonth());
        }
}

计数

public function countUserCategories(Filter $filter = null)
{
        $subQuery = $this->getCategoriesQueryBuilder($filter);
        $params = $subQuery->getParameters();

        return $this->_em->getConnection()->createQueryBuilder()
            ->select('COUNT(src.id)')
            ->from('(' . $subQuery . ')', 'src')
            ->setParameters($params)
            ->execute()
            ->fetchColumn(0);
}

获取

public function findUserCategories(Filter $filter = null, Sort $sort = null, Pagination $pagination = null)
{
        return $this->getCategoriesQueryBuilder($filter, $sort, $pagination)->execute()->fetchAll();
}

1 个答案:

答案 0 :(得分:0)

您可以将原始查询用作子查询来获取计数。

public function countUserCategories(Filter $filter = null)
{
    $querysub = $this->getCategoriesQuery($filter);

    $query = $this->getDoctrine()->getConnection()->createQueryBuilder();

    $query
        ->select('COUNT(*)')
        ->from('(' . $querysub->getSQL() . ')', 'ttt');

    $i = 0;
    foreach ($querysub->getParameters() as $parameter) {
        $query->setParameter($i++, $parameter->getValue(), $parameter->getType());
    }

    $count = $query->execute()->fetchColumn(0);

    return $count;
}

如果参数按其在查询中的出现顺序设置,这将起作用。