Как переписать сложный запрос в Doctrine DBAL в TYPO3?
Я изо всех сил пытаюсь перенести сложный SQL-запрос в Doctrine DBAL в TYPO3. Мой старый запрос к репозиторию выглядит так:
$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 для объединенных таблиц?
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' */);
Я не часто использую свободный интерфейс при выполнении подобных вычислений (хотя, конечно, это можно сделать, используя любую функцию 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:paginate
ViewHelper, который не должен создавать экземпляры всех объектов, а только тех, которые показаны на текущей странице. Такой возможности нет (пока?) При использовании Doctrine QueryBuilder. Таким образом, использование моделей Extbase сейчас должно быть последним средством, а не по умолчанию. Казалось, что это "лучшая практика" - это уже не так, ИМХО.