我目前有一个相当复杂的本机SQL查询,用于报告目的。鉴于它处理的数据量,处理它的唯一有效方法是使用本机SQL。
这很好,并从标量结果中返回一个数组数组。
我想做的是,为了使结果与项目中的其他结果集保持一致,请使用数据传输对象(DTO)。返回一组简单的DTO对象。
这些在DQL中工作得很好,但我无法看到将它们与原生SQL一起使用。这有可能吗?
答案 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扩展程序构建的。
以下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;
}
}
该函数的使用和调用方式与其他存储库方法相同,只需调用上面的帮助程序即可自动将数据转换为对象。
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文件一样大。在这种情况下,我会加入你并放弃Doctrine(或任何其他ORM)。