带有本机SQL查询的Doctrine DTO

Nic*_*ick 2 dto doctrine-orm

我目前有一个相当复杂的本机SQL查询,用于报告目的.鉴于它处理的数据量,处理它的唯一有效方法是使用本机SQL.

这很好,并从标量结果返回一个数组数组.

我想做的是,为了使结果与项目中的每个其他结果集一致,使用数据传输对象(DTO).返回一组简单的DTO对象.

这些在DQL中工作得很好,但我无法看到它们与原生SQL一起使用它们.这是可能吗?

Dio*_*ter 5

Doctrine可以将原始SQL查询的结果映射到实体,如下所示:

http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/native-sql.html

除非您也愿意使用DQL,否则我看不到对DTO的支持,因此不存在直接解决方案.我尝试了一个运行良好的简单解决方法,所以这里有DQL和非DQL方法来实现您的目标.

这些示例是使用Laravel和Laravel Doctrine扩展构建的.

  1. DTO

下面的DTO支持DQL绑定和自定义映射,因此构造函数必须能够使用和不使用参数.

<?php namespace App\Dto;

/**
 * Date with corresponding statistics for the date.
 */
class DateTotal
{
    public $taskLogDate;
    public $totalHours;

    /**
     * DateTotal constructor.
     *
     * @param $taskLogDate The date for which to return totals
     * @param $totalHours The total hours worked on the given date
     */
    public function __construct($taskLogDate = null, $totalHours = null)
    {
        $this->taskLogDate = $taskLogDate;
        $this->totalHours = $totalHours;
    }

}
Run Code Online (Sandbox Code Playgroud)
  1. 使用DQL获取结果

这是使用DQL的标准版本.

public function findRecentDateTotals($taskId)
{
    $fromDate = new DateTime('6 days ago');
    $fromDate->setTime(0, 0, 0);
    $queryBuilder = $this->getQueryBuilder();

    $queryBuilder->select('NEW App\Dto\DateTotal(taskLog.taskLogDate, SUM(taskLog.taskLogHours))')
        ->from('App\Entities\TaskLog', 'taskLog')
        ->where($queryBuilder->expr()->orX(
            $queryBuilder->expr()->eq('taskLog.taskLogTask', ':taskId'),
            $queryBuilder->expr()->eq(0, ':taskId')
        ))
        ->andWhere(
            $queryBuilder->expr()->gt('taskLog.taskLogDate', ':fromDate')
        )
        ->groupBy('taskLog.taskLogDate')
        ->orderBy('taskLog.taskLogDate', 'DESC')
        ->setParameter(':fromDate', $fromDate)
        ->setParameter(':taskId', $taskId);

    $result = $queryBuilder->getQuery()->getResult();
    return $result;
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用本机SQL支持DTO

这是一个简单的帮助器,可以将原始SQL查询的数组结果编组到对象中.它可以扩展到其他东西,也许自定义更新等.

<?php namespace App\Dto;

use Doctrine\ORM\EntityManager;

/**
 * Helper class to run raw SQL.
 *
 * @package App\Dto
 */
class RawSql
{
    /**
     * Run a raw SQL query.
     *
     * @param string $sql The raw SQL
     * @param array $parameters Array of parameter names mapped to values
     * @param string $className The class to pack the results into
     * @return Object[] Array of objects mapped from the array results
     * @throws \Doctrine\DBAL\DBALException
     */
    public static function query($sql, $parameters, $className)
    {
        /** @var EntityManager $em */
        $em = app('em');
        $statement = $em->getConnection()->prepare($sql);
        $statement->execute($parameters);
        $results = $statement->fetchAll();

        $return = array();
        foreach ($results as $result) {
            $resultObject = new $className();
            foreach ($result as $key => $value) {
                $resultObject->$key = $value;
            }
            $return[] = $resultObject;
        }
        return $return;
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 运行原始SQL版本

该函数的使用和调用方式与其他存储库方法相同,只需调用上面的帮助程序即可自动将数据转换为对象.

public function findRecentDateTotals2($taskId)
{
    $fromDate = new DateTime('6 days ago');

    $sql = "
        SELECT
          task_log.task_log_date AS taskLogDate,
          SUM(task_log.task_log_hours) AS totalHours
        FROM task_log task_log
        WHERE (task_log.task_log_task = :taskId OR :taskId = 0) AND task_log.task_log_date > :fromDate
        GROUP BY task_log_date
        ORDER BY task_log_date DESC        
    ";

    $return = RawSql::query(
        $sql,
        array(
            'taskId' => $taskId,
            'fromDate' => $fromDate->format('Y-m-d')
        ),
        DateTotal::class
    );
    return $return;
}
Run Code Online (Sandbox Code Playgroud)
  1. 笔记

我不会太快解除DQL,因为它可以执行大多数SQL.然而,我最近也参与了构建管理报告,在管理信息领域,SQL查询可以和整个PHP文件一样大.在这种情况下,我会加入你并放弃Doctrine(或任何其他ORM).