Skip to content

Обход проблемы getChildIds - #955

Open
biz87 wants to merge 1 commit into
masterfrom
dev/remove-getchildids
Open

biz87 wants to merge 1 commit into
masterfrom
dev/remove-getchildids

Conversation

@biz87

@biz87 biz87 commented Jan 29, 2026

Copy link
Copy Markdown
Member

В сниппете msProducts при построении запроса pdoTools запихивает в parent IN все что найдет в дереве ресурсов через $modx->getChildIds() - таким образом конечный SQL запрос для получения товаров может содержать десятки а то и сотни тысяч родителей, тогда как реальных категорий обычно не более 10 штук.

Решение предлагает не пробрасывать в pdoTools логику построения parents, а строит условие для parents самостоятельно.

@biz87
biz87 requested a review from webinmd January 29, 2026 18:39
@AgelxNash

Copy link
Copy Markdown
Contributor

Основная проблема понятна: getChildIds действительно тяжелый для больших деревьев, где товары лежат вперемешку с категориями. Но перенос этой логики прямо в тело сниппета ms_products создает больше проблем, чем решает.

В перспективе архитектуры: выборка дерева и резолвинг parents — это задача слоя выборки данных (pdoFetch / ядро), а не отдельного сниппета дополнения. Если решать это локальным обходом внутри ms_products, ломается и стабильность, и обратная совместимость (BC):

  1. Производительность на реальных базах:

    • Поуровневый цикл по глубине делает до 20 синхронных запросов в базу на каждый запуск сниппета без кэширования.
    • Сборка product_id из msCategoryMember через fetchAll в память PHP и последующая склейка в сырой IN ({}) на магазинах от 20–50k товаров гарантированно упрется либо в memory_limit, либо в переполнение пакета MySQL (max_allowed_packet).
  2. Обратная совместимость (BC):

    • Захардкоженные published = 1, deleted = 0 ломают штатные параметры &showUnpublished и &showDeleted.
    • Запрос категорий не учитывает context_key, что приведет к подтягиванию чужих категорий в мультиконтекстных сайтах.
    • Мутация $scriptProperties['parents'] = 0 затирает исходные свойства в чанке-обертке (tplWrapper) и может ломать логику пагинации в pdoPage.

Если закрывать эту проблему на уровне miniShop2 до системных правок в pdoTools/ядре, делать это нужно без выгрузки списков ID в память PHP и с полным сохранением контракта параметров сниппета — через подзапрос/JOIN к msCategoryMember и с учетом контекста и флагов публикации.

@AgelxNash

AgelxNash commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

В продолжение ревью — подготовил и проверил решение, которое полностью устраняет проблему тысяч ID в parents без регрессий и без N+1.

Оформил готовыми взаимосвязанными Pull Requests:

1. Безопасный слой в msProducts (miniShop2): #956

👉 #956

  • Дерево категорий обходится послойно (1 быстрый запрос на уровень глубины, всего 2–3 индексных запроса по таблице категорий на весь каталог).
  • Учитываются флаги published = 1 (только если не задан &showUnpublished=1) и deleted = 0.
  • Связи с дополнительными категориями проверяются через ANSI SQL предикат EXISTS, а исключение категорий — через NOT EXISTS.
  • Наличие msProduct.parent в $where сообщает pdoFetch автоматически пропустить свой устаревший блок parents (pdofetch.class.php:644-668). При этом $scriptProperties['parents'] не затирается и сохраняет своё значение для плейсхолдеров tplWrapper.
  • Расход памяти в PHP: 0 байт под ID товаров (вместо выгрузки массивов). В MySQL подзапрос EXISTS мгновенно попадает в составной первичный ключ (product_id, category_id) в msCategoryMember.

2. Целевое решение в ядре (pdoTools и pdoTools3)

В ядре поддержка категорий msCategoryMember переведена с fetchAll(PDO::FETCH_COLUMN) на EXISTS / NOT EXISTS со строгой изоляцией по is_a($class, 'msProduct', true) (чтобы не задевать Tickets и другие ресурсы):

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants