Doctrine 如何在TYPO3中将复杂查询重写为原则DBAL?
我正在努力将复杂的SQL查询迁移到TYPO3中的原则DBAL。我的旧存储库查询如下所示:Doctrine 如何在TYPO3中将复杂查询重写为原则DBAL?,doctrine,typo3,extbase,typo3-10.x,Doctrine,Typo3,Extbase,Typo3 10.x,我正在努力将复杂的SQL查询迁移到TYPO3中的原则DBAL。我的旧存储库查询如下所示: $enableFields = $GLOBALS['TSFE']->sys_page->enableFields('tx_test'); // calculate distance between geo coordinates $distance = ''; if ($geoData) { $distance = ', 3956 * 2 * ASIN( SQRT (POWER ( S
$enableFields = $GLOBALS['TSFE']->sys_page->enableFields('tx_test');
// calculate distance between geo coordinates
$distance = '';
if ($geoData) {
$distance = ', 3956 * 2 * ASIN( SQRT (POWER ( SIN((' . $geoData['latitude'] . ' - abs(tx_test.latitude)) * pi() / 180 / 2), 2) + COS(' . $geoData['latitude'] . ' * pi() / 180) * ' . 'COS(abs(tx_test.latitude)' .
' * pi() / 180) * POWER (SIN((' . $geoData['longitude'] .
' - tx_test.longitude)' .
' * pi() / 180 / 2), 2))) AS distance';
}
// General query
$sql = 'SELECT * ' . $distance .
' FROM tx_test ' .
' WHERE DATE_FORMAT(FROM_UNIXTIME(start), "%Y") = '.(int)$settings['flexformYear'].' AND published = 1 ' . $enableFields;
if($headline) {
$sql .= ' AND headline like '.$GLOBALS['TYPO3_DB']->quoteStr($headline).'%';
}
// ... and some more ...
$query = $this->createQuery();
$results = $query->statement($sql)->execute();
转为教条
现在,我可以轻松地删除$GLOBALS['TYPO3'u DB']->quoteStr()
,并用以下代码替换上面的最后两行代码:
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_test');
$results = $connection->executeQuery($sql)->fetchAll();
但这将返回一个结果数组,而不是带有附加子对象的对象,例如FileReference
对象或另一个附加模型
有没有其他方法可以达到预期的效果?如果没有,我如何保护/清理
$headline
的用户输入?我需要自己为联接表编写SQL吗?这里是一些距离计算的一部分,应该适合您关于使用原则和引用的用例
使用Doctrine,您也可以将记录作为数组。如果需要Extbase对象,请参见下文
获得DBAL QueryBuilder的TYPO3风格:
$q = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table = 'tx_....');
$q->select(
...
'a.lat',
'a.lng'
)
->from($table /*, 'alias if you want' */);
在进行类似的计算时,我很少使用fluent接口(当然,通过使用任何带有$q->selectLiteral()
的MySQL函数都可以做到这一点)
参数
$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
$objects = $dataMapper->map(YourExtbaseModel::class, $aRes);
为了防止SQL注入,您应该使用$q->quote()
/$q->quoteIdentifier()
引用所有可能的用户输入,或者使用参数$q->createNamedParameter()
约束示例
这只是约束的一部分。它包含一个示例,说明如何根据搜索函数中常见的条件将它们组合起来
if ((float)$searchObject->radiusKm > .5) {
$_radiusOrs = [
'IF (
' . $q->quoteIdentifier('lat') . ' = 0,
100000,
12742 * ASIN(
SQRT(
POWER(
SIN(
( ' . $q->quote((float)$searchObject->lat) . '
- ABS( ' . $q->quoteIdentifier('lat') . ' )
) * 0.0087266
),
2
)
+
COS( ' . $q->quote((float)$searchObject->lng) . ' * 0.01745329 ) * COS(
ABS( ' . $q->quoteIdentifier('lat') . ' ) * 0.01745329
) * POWER(
SIN(
( ' . $q->quote((float)$searchObject->lng) . '
- ' . $q->quoteIdentifier('lng') . '
) * 0.0087266
),
2
)
)
)
) < ' . $q->quote((float)$searchObject->radiusKm),
];
$q->andWhere(
$q->expr()->orX(...$_radiusOrs)
);
}
...
$aRes = $q->execute()->fetchAll();
我建议:只有在对象很少或内存足够且不关心性能的情况下才使用Extbase对象。通常情况下,您应该使用同样好的普通数组
Extbase查询生成器允许在输出到流体模板时进行一些优化:传递
\TYPO3\CMS\Extbase\Persistence\Generic\QueryResult
允许在其上使用f:paginate
ViewHelper,它不必实例化所有对象,只实例化当前页面上显示的对象。在使用条令QueryBuilder时,这种可能性还不存在。因此,现在使用Extbase模型应该是最后的选择,而不是默认的选择。这似乎是“最佳实践”-这已不再是事实 非常感谢你的详细回答,它把我推向了正确的方向。但我并没有考虑这个问题,即如何按距离排序,因为这是我想要实现的目标。如果用户提供地理坐标,我的查询将选择所有内容并按距离排序结果。。。。以及如何使用where子句中的DATE\u格式(FROM\u UNIXTIME(start),“%Y”)
。将$\u radiusOrs=…
行替换为$q->addSelectLiteral(
..作为dist
并按其排序:$q->orderBy('dist','ASC');
对于另一个问题,您可以像编写一样编写它,或者使用$q->quoteIdentifier()
如果你想说得非常正确……你真的应该现在就开始编码。如果答案对你有帮助,请竖起大拇指。