带有本机SQL查询的Doctrine DTO

时间:2016-01-21 17:43:26

标签: doctrine-orm dto

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

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

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

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

1 个答案:

答案 0 :(得分: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
  2. 以下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;
        }
    
    }
    
    1. 使用DQL获取结果
    2. 以下是使用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;
      }
      
      1. 支持使用本机SQL的DTO
      2. 这是一个简单的帮助程序,可以将原始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;
            }
        }
        
        1. 运行原始SQL版本
        2. 该函数的使用和调用方式与其他存储库方法相同,只需调用上面的帮助程序即可自动将数据转换为对象。

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