我目前有一個相當複雜的用於報告目的的原生SQL查詢。考慮到它處理的數據量,這是處理它的唯一有效方法是使用原生SQL。帶原生SQL查詢的Doctrine DTO
這工作正常,並從標量結果返回數組的數組。
我想要做的,保持結果與項目中的每個其他結果集一致的是使用數據傳輸對象(DTO)。返回一組簡單的DTO對象。
這些工作與DQL很好,但我無法看到它們與原生SQL的使用。這是可能嗎?
我目前有一個相當複雜的用於報告目的的原生SQL查詢。考慮到它處理的數據量,這是處理它的唯一有效方法是使用原生SQL。帶原生SQL查詢的Doctrine DTO
這工作正常,並從標量結果返回數組的數組。
我想要做的,保持結果與項目中的每個其他結果集一致的是使用數據傳輸對象(DTO)。返回一組簡單的DTO對象。
這些工作與DQL很好,但我無法看到它們與原生SQL的使用。這是可能嗎?
學說可以將原始的SQL查詢的結果映射到實體,如下所示:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/native-sql.html
我不能看到DTO的支持,除非你願意用DQL爲好,所以直接解決方案不存在。我嘗試了一個簡單的解決方法,可以很好地工作,所以這裏有DQL和非DQL方法來實現您的目標。
這些例子是使用Laravel和Laravel Doctrine擴展構建的。
下面的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。
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;
}
下面是一個簡單的輔助,可以編組原始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)。
非常詳細的解決方案我非常喜歡它。我只是好奇你在做複雜的DTO時如何做DTO轉換,或者我應該說嵌套的DTO。例如,具有標量和對象字段或對象列表的父DTO –
在嘗試製作DTO時,遇到的問題是您最終複製了很多Doctrine會爲您做的事情,並最終變成一團糟。 如果涉及非平凡關係,我會考慮在正常的Doctrine查詢旁邊運行原生查詢。例如,您有一個簡單的結果表格,其中包含一個或兩個包含聚合的列。使用原始查詢獲取聚合,並通過主結果集的ID鍵入它。然後查看每一行的統計數據。好處:能夠遍歷Doctrine管理關係並一次性獲得所有聚合。 –