0

我正在努力将复杂的 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();

迁移到教义

现在我可以轻松地删除$GLOBALS['TYPO3_DB']->quoteStr()并替换上面代码的最后两行:

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

但这会返回一个结果数组,而不是带有附加子对象的对象,例如。一个FileReference对象或另一个附加模型。

有没有其他方法可以达到预期的效果?如果没有,我该如何保护/清理用户输入$headline?我需要自己为连接的表编写 SQL 吗?

4

1 回答 1

1

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

使用 Doctrine,您也可以将记录作为数组获取。如果您想要 Extbase 对象,请参见下文。

获取 Doctrine DBAL QueryBuilder 的 TYPO3 风格:

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

在进行这样的计算时,我并没有过多地使用流利的界面(尽管当然可以通过使用任何 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 对象。你应该尽量避免使用普通数组。

Extbase 查询构建器在输出到 Fluid 模板时允许进行一些优化:传递\TYPO3\CMS\Extbase\Persistence\Generic\QueryResult允许在其上使用f:paginateViewHelper,它不必实例化所有对象,而仅实例化当前页面上显示的对象。使用 Doctrine QueryBuilder 时,这种可能性不存在(还没有?)。所以现在使用 Extbase 模型应该是最后的手段,而不是默认。这似乎是“最佳实践”——恕我直言,这不再是真的。

于 2020-05-02T19:43:03.753 回答