如何在TYPO3中重写复杂查询到Doctrine DBAL?

问题描述 投票:0回答:1

我正在努力将一个复杂的SQL查询迁移到TYPO3中的Doctrine DBAL中。我的旧仓库查询是这样的。

$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();

要迁移到Doctrine

现在,我可以很容易地去除 $GLOBALS['TYPO3_DB']->quoteStr() 并将上面代码的最后两行替换成这样。

$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_test');
$results = $connection->executeQuery($sql)->fetchAll();

但是这将返回一个结果数组 而不是返回附加子对象的对象,例如: FileReference 对象或其他附加的模型。

是否有其他方法来实现所需的结果?如果没有,我怎样才能使用户输入的数据安全化,以实现 $headline? 我需要自己写加入表的SQL吗?

doctrine typo3 extbase typo3-10.x
1个回答
1
投票

这里是一些距离计算的一部分,它应该适合你关于使用Doctrine和引用的用例。

使用Doctrine你也会得到数组的记录。如果你想要Extbase对象,请看下文。

获得TYPO3风味的Doctrine DBAL QueryBuilder。

$q = GeneralUtility::makeInstance(ConnectionPool::class)
  ->getQueryBuilderForTable($table = 'tx_....');
$q->select(
  ...
  'a.lat',
  'a.lng'
)
  ->from($table /*, 'alias if you want' */);

在做这样的计算时,我是不怎么使用fluent接口的(当然也可以通过使用任何MySQL函数,以 $q->selectLiteral()).

参数

为了防止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();

(如果你想调试。你得到的SQL是 $q->getSQL(), $q->getParameters())

映射到Extbase对象

$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
$objects = $dataMapper->map(YourExtbaseModel::class, $aRes);            

我建议。只有当你的对象不多 或者你有足够的内存而你又不关心性能的时候 才使用Extbase对象。大部分情况下,你应该用普通数组就可以了。

在输出到Fluid模板中时,Extbase查询构建器允许进行一点优化:通过将 \TYPO3\CMS\Extbase\Persistence\Generic\QueryResult 可使用 f:paginate ViewHelper,它不需要实例化所有对象,而只需要实例化当前页面上显示的对象。当使用Doctrine QueryBuilder时,这种可能性是不存在的(还没有?)。所以使用Extbase模型应该是最后的手段,而不是默认的。这似乎是 "最佳实践"--但现在已经不是了,我认为。

© www.soinside.com 2019 - 2024. All rights reserved.