Doctrine orm 使用本机SQL查询的原则DTO

Doctrine orm 使用本机SQL查询的原则DTO,doctrine-orm,dto,Doctrine Orm,Dto,我目前有一个相当复杂的本机SQL查询,用于报告目的。考虑到它处理的数据量,这是使用本机SQL处理数据的唯一有效方法 这可以很好地工作,并从标量结果返回一个数组数组 我想做的是,使用数据传输对象(DTO),以使结果与项目中的所有其他结果集保持一致。返回简单DTO对象的数组 这些在DQL中工作得很好,但我看不出在原生SQL中使用它们。这可能吗?原则可以将原始SQL查询的结果映射到实体,如下所示: 除非您也愿意使用DQL,否则我看不到对DTO的支持,因此不存在直接解决方案。我尝试了一个简单的变通方法

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

这可以很好地工作,并从标量结果返回一个数组数组

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


这些在DQL中工作得很好,但我看不出在原生SQL中使用它们。这可能吗?

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

除非您也愿意使用DQL,否则我看不到对DTO的支持,因此不存在直接解决方案。我尝试了一个简单的变通方法,效果很好,下面是实现目标的DQL和非DQL方法

这些例子是使用拉雷维尔和拉雷维尔学说扩展构建的

  • 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;
        }
    
    }
    
  • 使用本机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;
        }
    }
    
  • 注释

  • 我不会过快地忽略DQL,因为它可以执行大多数类型的SQL。然而,我最近也参与了构建管理报告,在管理信息领域,SQL查询可以与整个PHP文件一样大。在这种情况下,我会加入你们的行列,放弃教条(或任何其他ORM)。

    伟大而详细的解决方案,我非常喜欢。我只是好奇,当有复杂的DTO或者我应该说是嵌套的DTO时,如何进行DTO转换。例如,具有标量和对象字段或对象列表的父DTO我在尝试使DTO家族化时遇到的问题是,您最终复制了许多条令本可以为您做的事情,结果是一团糟。如果涉及到非平凡的关系,我会考虑在正常的查询中运行本地查询。例如,您有一个简单的结果表,其中有一列或两列包含聚合。使用原始查询获取聚合,并通过主结果集的ID对其进行键控。然后只需查找每一行的统计数据。好处:能够遍历条令管理的关系,并一次性获得所有聚合。
    <?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;
        }
    }
    
    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;
    }