Skip to content

msProducts: сортировка по menuindex с parents даёт ошибку SQL — пустая выдача #736

Closed

Description

@biz87

Кратко

С #627 (c3c84ddf, «Индивидуальный menuindex для товаров в дополнительных категориях») сниппет msProducts меняет sortby при двух условиях: sortby содержит menuindex и заданы parents. Туда подставляется SQL-выражение CASE … COALESCE(…, …) END.

pdoTools режет строку sortby по запятым и экранирует куски по отдельности. Выражение ломается, MariaDB возвращает синтаксическую ошибку, сниппет не выводит ни одного товара.

Любой каталог, где msProducts вызван с parents и сортировкой по menuindex, остаётся пустым. Через mFilter пустеет вся выдача каталога: mFilter получает базовые id товаров вызовом msProducts с returnIds и sortby из вызова сниппета.

Окружение

  • MiniShop3 — ветка beta, f0b1c9c3; пакет на тестовом сайте minishop3-1.13.0-beta1
  • pdoTools 3.0.2-pl
  • MariaDB 10.6.23

Воспроизведение

{'!msProducts' | snippet : ['parents' => 5, 'sortby' => 'menuindex', 'sortdir' => 'ASC', 'limit' => 3]}
{'!msProducts' | snippet : ['parents' => 5, 'sortby' => 'msProduct.menuindex', 'sortdir' => 'ASC', 'limit' => 3]}
{'!msProducts' | snippet : ['parents' => 5, 'sortby' => 'pagetitle', 'sortdir' => 'ASC', 'limit' => 3]}

Проверено на тестовом сайте, на разделах с 2 023 и 30 232 товарами:

sortby Результат
menuindex Пусто, ошибка в журнале
pagetitle 3 товара

Запрос целиком выводит тот же вызов с 'return' => 'sql'.

Журнал ошибок MODX на каждый вызов:

(ERROR @ core/components/pdotools/src/Fetch.php : 185) [pdoTools] Error 42000: You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for the right syntax to use near
'ELSE COALESCE(MIN(CategoryMember.menuindex),` msProduct`.`menuindex)`END ASC ...' at line 20

Причина

1. Сниппет подставляет выражение в sortby — ms3_products.php:163-186. CategoryProductMenuindexService::effectiveMenuindexCase() возвращает для нескольких разделов:

CASE WHEN msProduct.parent IN (5,6,7) THEN msProduct.menuindex
ELSE COALESCE(MIN(CategoryMember.menuindex), msProduct.menuindex) END

Для одного раздела выражение то же, но с COALESCE(CategoryMember.menuindex, msProduct.menuindex).

2. pdoTools режет sortby по запятым — Fetch.php, addSort():

$tmp = explode(',', $sortby);
array_walk($tmp, function (&$value) {
    if (strpos($value, '`') === false) {
        $value = preg_replace('#(.*?)\.(.*?)\s#', '`$1`.`$2`', $value);
    }
});
$sortby = implode(',', $tmp);
Кусок после explode После замены
CASE WHEN msProduct.parent IN (5 `CASE WHEN msProduct`.`parent`IN (5
6, … без изменений
7) THEN msProduct.menuindex ELSE COALESCE(MIN(CategoryMember.menuindex) `7) THEN msProduct`.`menuindex`ELSE COALESCE(MIN(CategoryMember.menuindex)
msProduct.menuindex) END ` msProduct`.`menuindex)`END

Последние два куска после implode дают в точности фрагмент из текста ошибки. Для одного раздела выражение ломается тем же preg_replace, даже без запятой: замена срабатывает на любом куске с точкой и пробелом.

3. Почему тест не поймал. tests/CategoryProductMenuindexServiceTest.php:39-45 проверяет только, что строка выражения содержит нужные фрагменты. Через pdoTools выражение не прогоняется.

Другие места с тем же выражением работают:

  • грид категории (CategoryProductsListService) сортирует по псевдониму из SELECT;
  • Web API (ProductCatalogService) сортирует через xPDO без pdoTools.

Как исправить

Экранирование пропускает куски с обратной кавычкой. Если все идентификаторы выражения записать в обратных кавычках, каждый кусок после explode будет либо с кавычкой, либо из одних цифр, и implode вернёт выражение целым:

CASE WHEN `msProduct`.`parent` IN (5,6,7) THEN `msProduct`.`menuindex`
ELSE COALESCE(MIN(`CategoryMember`.`menuindex`), `msProduct`.`menuindex`) END

xPDOQuery::isValidClause() такое выражение пропускает, GROUP BY msProduct.id для MIN() уже есть.

Вариант «выражение в select с псевдонимом», как в гриде, для сниппета не подходит:

  • режим returnIds: pdoTools выбирает только первичный ключ, и псевдонима в ORDER BY не будет;
  • вложенные скобки: addSelects() защищает запятые только внутри одной пары скобок.

Нужен тест, который прогоняет выражение через экранирование sortby pdoTools, в обычном режиме и с returnIds.

Что не установлено

Код попал на тестовый сайт 7–8 сентября, а выдача каталога через mFilter ещё днём работала. Гипотеза: базовые id брались из кэша mFilter, и msProducts не вызывался, пока кэш не сбросился. Не проверено.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority: highВажно исправить в ближайшее время

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions