2016-01-21 109 views
1

我目前有一個相當複雜的用於報告目的的原生SQL查詢。考慮到它處理的數據量,這是處理它的唯一有效方法是使用原生SQL。帶原生SQL查詢的Doctrine DTO

這工作正常,並從標量結果返回數組的數組。

我想要做的,保持結果與項目中的每個其他結果集一致的是使用數據傳輸對象(DTO)。返回一組簡單的DTO對象。

這些工作與DQL很好,但我無法看到它們與原生SQL的使用。這是可能嗎?

回答

5

學說可以將原始的SQL查詢的結果映射到實體,如下所示:

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

我不能看到DTO的支持,除非你願意用DQL爲好,所以直接解決方案不存在。我嘗試了一個簡單的解決方法,可以很好地工作,所以這裏有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; 
    } 

} 
  • 使用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; 
    } 
    
  • 爲DTO的支持與本機SQL
  • 下面是一個簡單的輔助,可以編組原始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; 
        } 
    } 
    
  • 運行
  • 該函數的使用,並且被稱爲以同樣的方式作爲其他存儲庫的方法,並且只是調用上述助手到原始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; 
    } 
    
  • 我不會解僱DQL太快,因爲它可以執行大部分種SQL的。然而,我最近也參與了構建管理報告,並且在管理信息領域中,SQL查詢可以像整個PHP文件一樣大。在這種情況下,我會加入你並放棄主義(或任何其他ORM)。

    +0

    非常詳細的解決方案我非常喜歡它。我只是好奇你在做複雜的DTO時如何做DTO轉換,或者我應該說嵌套的DTO。例如,具有標量和對象字段或對象列表的父DTO –

    +0

    在嘗試製作DTO時,遇到的問題是您最終複製了很多Doctrine會爲您做的事情,並最終變成一團糟。 如果涉及非平凡關係,我會考慮在正常的Doctrine查詢旁邊運行原生查詢。例如,您有一個簡單的結果表格,其中包含一個或兩個包含聚合的列。使用原始查詢獲取聚合,並通過主結果集的ID鍵入它。然後查看每一行的統計數據。好處:能夠遍歷Doctrine管理關係並一次性獲得所有聚合。 –