如何使用子查询创建正确的查询构建器

How to make right query builder with subqueries

我有一个 table 付款,我需要获取所有 table 列和四个自定义列。我当前的工作查询:

$this->createQueryBuilder('payments')
            ->select('DATE_FORMAT(payments.createdAt, \'%Y-%m\') as date_ym, payments')
            ->addSelect('(SELECT SUM(ps1.summ) FROM App\Entity\Payments ps1 WHERE ps1.isPayed = 1 AND ps1.type = 1 AND ps1.user = :user AND DATE_FORMAT(ps1.createdAt, \'%Y-%m\') = date_ym) as pay_in')
            ->addSelect('(SELECT COUNT(ps3.id) FROM App\Entity\Payments ps3 WHERE ps3.isPayed = 1 AND ps3.type = 1 AND ps3.user = :user AND DATE_FORMAT(ps3.createdAt, \'%Y-%m\') = date_ym) as pay_in_count')
            ->addSelect('(SELECT SUM(ps2.summ) FROM App\Entity\Payments ps2 WHERE ps2.isPayed = 1 AND ps2.type != 1 AND ps2.user = :user AND DATE_FORMAT(ps2.createdAt, \'%Y-%m\') = date_ym) as pay_out')
            ->addSelect('(SELECT COUNT(ps4.id) FROM App\Entity\Payments ps4 WHERE ps4.isPayed = 1 AND ps4.type != 1 AND ps4.user = :user AND DATE_FORMAT(ps4.createdAt, \'%Y-%m\') = date_ym) as pay_out_count')
            ->where('payments.user = :user')
            ->setParameter('user', $user)
            ->andWhere('payments.isPayed = 1')
            ->andWhere('payments.createdAt >= :days')
            ->setParameter('days', $days->format('Y-m-d'))
            ->orderBy('date_ym', 'DESC')
            ->getQuery()->getResult();

在控制器中我这样做:

$payments = $em->getRepository(Payments::class)->myFunc($user, 180);
$paymentsTable = [];
foreach ($payments as $payment) {
    $date = $payment['date_ym'];
    /** @var PaySchet $pay */
    $pay = $payment[0];
    $paymentsTable[$date]['pay_in'] = $payment['pay_in'];
    $paymentsTable[$date]['pay_in_count'] = $payment['pay_in_count'];
    $paymentsTable[$date]['pay_out'] = $payment['pay_out'];
    $paymentsTable[$date]['pay_out_count'] = $payment['pay_out_count'];
    $paymentsTable[$date]['data'][$pay->getType()][] = $pay;
}

我得到以下结果:

array:3 [▼
  "2020-03" => array:5 [▼
    "pay_in" => "15"
    "pay_in_count" => "1"
    "pay_out" => "230"
    "pay_out_count" => "1"
    "data" => array:1 [▼
      0 => App\Entity\Payments {#2288 ▶}
    ]
  ]
  "2020-01" => array:5 [▼
    "pay_in" => "2620"
    "pay_in_count" => "5"
    "pay_out" => "13732"
    "pay_out_count" => "3"
    "data" => array:3 [▼
      0 => App\Entity\Payments {#2267 ▶}
      1 => App\Entity\Payments {#2257 ▶}
      2 => App\Entity\Payments {#2251 ▶}
    ]
  ]
  "2019-12" => array:5 [▶]
]

这是正确的输出,但我该如何改进我的 DQL 查询?我不喜欢现在的样子。

您可以创建多个查询生成器并在主要查询生成器中使用它们。请注意,您必须在所有这些中使用唯一的实体别名。您可以使用这些别名来引用来自其他查询构建器的实体。

/** @val $em EntityManager */

$query1 = $em->createQueryBuilder()->from(Payments::class, 'ps1')->select('')->getDql();
$query2 = $em->createQueryBuilder()->from(Payments::class, 'ps2')->select('')->getDql();
$query3 = $em->createQueryBuilder()->from(Payments::class, 'ps3')->select('')->getDql();
$query4 = $em->createQueryBuilder()->from(Payments::class, 'ps4')->select('')->getDql();

// All value binds need to be on the main query builder
$query = $qb->select('DATE_FORMAT(payments.createdAt, \'%Y-%m\') as date_ym, payments')
    ->addSelect($query1)
    ->addSelect($query2)
    ->addSelect($query3)
    ->addSelect($query4)
    ->from(Payments::class, 'payments')
    ->where('payments.user = :user')
    ->setParameter('user', $user)
    ->andWhere('payments.isPayed = 1')
    ->andWhere('payments.createdAt >= :days')
    ->setParameter('days', $days->format('Y-m-d'))
    ->orderBy('date_ym', 'DESC')
    ->getQuery()
    ->getResult();