Skip to content

fix(manager): опции — дерево категорий при вложенном каталоге#239

Open
Ibochkarev wants to merge 4 commits into
betafrom
fix/237-options-category-tree-root
Open

fix(manager): опции — дерево категорий при вложенном каталоге#239
Ibochkarev wants to merge 4 commits into
betafrom
fix/237-options-category-tree-root

Conversation

@Ibochkarev
Copy link
Copy Markdown
Member

@Ibochkarev Ibochkarev commented May 8, 2026

Описание

Исправлено пустое дерево категорий в UI привязки опций при GET /api/mgr/options/tree?parent=0, когда категории магазина (msCategory) не являются прямыми потомками корня сайта.

Дерево теперь строится по семантике ресурсов без дополнительных системных настроек:

  • msCategory отображаются как выбираемые узлы с чекбоксом.
  • Контейнеры MODX (modResource / modDocument / modWebLink с isfolder = 1) отображаются только для навигации, без чекбокса.
  • msProduct и обычные страницы без isfolder исключаются из дерева.

Backend (OptionsController::getTree) фильтрует узлы по class_key и isfolder, добавляет флаг selectable для каждого узла. Vue-компонент OptionCategoryTree рендерит чекбокс только для узлов с selectable = true, навигационные контейнеры визуально отличаются (opacity, italic).

Такой подход поддерживает вложенные каталоги, несколько витрин/магазинов под разными контейнерами и смешанные структуры без дополнительной настройки. OptionCategoryTree всегда начинает загрузку с parent = 0, а backend сам отдаёт только релевантные узлы дерева.

Тип изменений

  • Исправление бага (non-breaking change)
  • Рефакторинг (без изменения функциональности)

Связанные Issues

Closes #237

Как это было протестировано?

  • Ручное тестирование / сценарии для проверки: msCategory под контейнером, несколько контейнеров-магазинов под parent = 0, регрессия с msCategory под корнем.
  • Автоматические проверки: php -l по изменённым PHP-файлам, targeted ESLint по изменённым Vue-компонентам, npm run build, git diff --check.

Конфигурация тестирования:

  • MiniShop3: текущая ветка
  • MODX: 3.x (менеджер API)
  • PHP: 8.x

Команды:

  • php -l core/components/minishop3/src/Controllers/Api/Manager/OptionsController.php
  • npx eslint src/components/OptionCategoryTree.vue src/components/OptionsGrid.vue src/components/CategoryOptionsTab.vue --fix в vueManager
  • npm run build в vueManager
  • git diff --check

Чеклист

  • Код соответствует стилю проекта
  • Добавлены/обновлены комментарии в сложных местах
  • Изменения не ломают существующую функциональность
  • Обновлён CHANGELOG.md (для значимых изменений)

Дополнительные заметки

Системная настройка ms3_option_category_tree_parent не используется и не требуется: дерево категорий работает для простой структуры, вложенного каталога и мульти-магазинных конфигураций из коробки.

Навигационные контейнеры нужны только для прохода к вложенным msCategory; выбрать их как категорию для опций нельзя.

@Ibochkarev Ibochkarev requested a review from biz87 May 8, 2026 04:02
@Ibochkarev Ibochkarev changed the title fix(manager): опции — дерево категорий при вложенном каталоге (#237) fix(manager): опции — дерево категорий при вложенном каталоге May 8, 2026
@Ibochkarev Ibochkarev force-pushed the fix/237-options-category-tree-root branch from cb0af6c to b8b4a8e Compare May 17, 2026 05:37
@biz87 biz87 force-pushed the fix/237-options-category-tree-root branch from b8b4a8e to 31a4c7c Compare May 17, 2026 11:59
@biz87
Copy link
Copy Markdown
Member

biz87 commented May 17, 2026

Сделал ребейз на актуальный beta для разрешения конфликта в CHANGELOG.md (после мержа #245 / #253 / #254 / #256 раздел «Май 2026 → Разработка → Исправлено» уже содержал свои пункты).

Содержание фикса не менялось — авторство в коммите сохранено.

При разрешении CHANGELOG: подпункт «Дерево категорий в UI привязки опций при отсутствии msCategory под корнем сайта (#237)» добавлен в существующий блок ## Май 2026 → ### Разработка → #### 🐛 Исправлено рядом с уже мерженными пунктами. Исходная заголовочная структура (### [2026-05-08] / #### Исправлено) переформатирована под единый стиль раздела.

Проверки:

  • PHPStan на OptionsController.php — 0 ошибок.
  • ESLint на трёх Vue-компонентах — 0 ошибок.
  • Vue-бандл пересобран, файлы синхронизированы на dev-сайт.

Готов к мержу.

@biz87
Copy link
Copy Markdown
Member

biz87 commented May 17, 2026

Ревью PR #239

Спасибо, баг #237 описан точно — на сайтах, где msCategory лежит под промежуточным контейнером, дерево опций действительно пустое. Сам код фикса аккуратный: PHPStan и ESLint чистые, лексиконы добавлены ru/en, проброс optionCategoryTreeParent через ms3.config в Vue сделан согласованно с серверной подстановкой — старые кешированные бандлы не сломаются. Хорошая инженерия.

ℹ️ Технический момент: ветку пришлось ребейзить на актуальный beta из-за конфликта CHANGELOG.md после мержа #245 / #253 / #254 / #256. Содержание фикса не менялось, авторство сохранено. Подробности — в комментарии выше.

Но при разборе подхода всплыла проблема, которую хочется решить до мержа.

Что не покрывается

Системная настройка ms3_option_category_tree_parent принимает один ID-родителя. Это закрывает кейс «один промежуточный контейнер с одним каталогом», но в реальных проектах встречаются:

```
Сайт (parent=0)
├── ИМ Москва (modResource, isfolder=1)
│ └── Каталог (msCategory)
│ └── ...
├── ИМ Астана (modResource, isfolder=1)
│ └── Каталог (msCategory)
│ └── ...
└── ИМ Стамбул (modResource, isfolder=1)
└── Каталог (msCategory)
└── ...
```

Все три магазина — обычные контейнеры под `parent=0`, и `msCategory` лежат под каждым из них. Опции в MS3 при этом глобальные (одна `Цвет` может быть привязана к категориям из всех трёх магазинов), но настройка позволит выбрать только один корень. В двух магазинах из трёх UI привязки опций останется неработоспособным.

Похожие конфигурации: маркетплейс с несколькими витринами, мультибренд с раздельными каталогами, мультирегиональный сайт без `context_key`-разделения.

Что предлагаю взамен

Убрать настройку и фильтровать дерево по семантике, без зависимости от структуры сайта. На каждом уровне отдавать:

  • `msCategory` — это категории. Рисуются с чекбоксом, выбираются.
  • Контейнеры MODX (`modResource` / `modWebLink` с `isfolder=1`) — только для навигации, без чекбокса.

Отсекаются:

  • `msProduct` (товары) — десятки тысяч записей, в дереве категорий им не место.
  • `modResource` с `isfolder=0` (страницы блога, статьи, новости, контактные) — не контейнеры.

На фронте в `OptionCategoryTree.vue` достаточно одной проверки на класс узла, чтобы решать — рисовать чекбокс или нет.

Преимущества подхода

  • Любая структура сайта работает «из коробки»: простой каталог под parent=0, один магазин под контейнером, мульти-магазин, смешанные конфигурации.
  • Никаких системных настроек — пользователю ничего не нужно настраивать после установки.
  • Семантика чёткая: «опции глобальные → дерево показывает всю структуру магазина, выбираешь любые msCategory».
  • UX совпадает со стандартным деревом ресурсов MODX (знакомо администраторам).
  • Не пропустит ни одной `msCategory`, где бы она ни лежала.

Точки переработки

  1. `_build/elements/settings.php` — удалить `ms3_option_category_tree_parent`.
  2. `lexicon/{en,ru}/setting.inc.php` — удалить ключи и `area_ms3_options` (область не нужна, если в ней нет других настроек).
  3. `controllers/category/update.class.php` и `controllers/mgr/settings.class.php` — удалить проброс `optionCategoryTreeParent` в `ms3.config`.
  4. `OptionsController::getTree` — убрать `resolveOptionsTreeParentId`. На каждом уровне (включая `parent=0`) — фильтр по `class_key IN ('MiniShop3\Model\msCategory', 'modResource', 'modWebLink')` AND (`class_key = 'msCategory' OR isfolder = 1`). Точную форму подскажет xPDO API.
  5. `OptionCategoryTree.vue` — добавить условный рендер чекбокса по `class_key` узла. Узлы без чекбокса — только для навигации (можно слегка приглушить иконку, чтобы было визуально понятно).
  6. `OptionsGrid.vue` / `CategoryOptionsTab.vue` — убрать `optionCategoryTreeRootParent` computed и проброс `rootParent` props. Вернуть `parent: 0` в API-запросе.
  7. CHANGELOG — переписать пункт под новую формулировку: «Дерево категорий в UI привязки опций показывает структуру магазина целиком — `msCategory` (выбираемые) + контейнеры MODX (навигация). Поддерживает любую глубину и мульти-магазинные конфигурации без настройки. Closes [Bug] Пустое дерево категорий в UI привязки опций при parent=0 (getTree) #237

Альтернатива (более точная, но дороже)

Если простой фильтр по `isfolder=1` смущает тем, что покажет «лишние» контейнеры типа «Блог» или «Документация», можно отдавать только те контейнеры, которые ведут к `msCategory` где-то в потомках. Это один дополнительный запрос при первой загрузке (собрать ID-предков всех msCategory). Для типовых проектов на 3–5 контейнеров под корнем — overkill, простой фильтр работает не хуже. Но если хочется идеальной чистоты дерева — рассмотреть.


Технически фикс правильный для одного частного сценария. Архитектурно — лучше сделать универсальное решение один раз, чем потом добавлять второй параметр под мульти-магазин. Поэтому прошу переработать и отправить v2.

Если по подходу остались сомнения или хочется обсудить детали — давайте в этом треде.

@Ibochkarev Ibochkarev force-pushed the fix/237-options-category-tree-root branch 2 times, most recently from e39e052 to 89ab64c Compare May 17, 2026 15:34
Copy link
Copy Markdown
Member

@biz87 biz87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Спасибо, что переработали. К сожалению, после анализа кода — описание PR расходится с реализацией, и проблема issue #237 в полной мере не решена.

Что обещано в описании

  • msCategory отображаются как выбираемые узлы с чекбоксом.
  • Контейнеры MODX (modResource / modDocument / modWebLink с isfolder = 1) отображаются только для навигации, без чекбокса.
  • msProduct и обычные страницы без isfolder исключаются из дерева.

Такой подход поддерживает вложенные каталоги, несколько витрин/магазинов под разными контейнерами и смешанные структуры без дополнительных системных настроек.

Что есть в коде

  1. OptionsController::getTree() жёстко фильтрует только msCategory:
$q->where([
    'modResource.parent' => $parent,
    'modResource.deleted' => 0,
    'modResource.class_key' => msCategory::class,
]);

Никакой логики isfolder = 1 для контейнеров нет. Контейнеры из выдачи просто выпадают, до вложенных msCategory пройти невозможно.

  1. Системная настройка ms3_option_category_tree_parent не удалена:
    • _build/elements/settings.php — настройка по-прежнему регистрируется;
    • лексиконы setting_ms3_option_category_tree_parent оставлены на ru/en;
    • OptionsController::resolveOptionsTreeParentId() использует её как fallback при parent = 0:
$configured = (int)$this->modx->getOption('ms3_option_category_tree_parent', null, 0);
return $configured > 0 ? $configured : 0;
  1. Vue-компонент OptionCategoryTree.vue поменял только PHPDoc-комментарий к prop rootParent. Никакой клиентской логики для различения «навигационный контейнер vs выбираемая категория» нет.

Что это значит на практике

Issue #237 (несколько витрин / вложенные каталоги под контейнерами) по-прежнему не закрывается:

  • Если msCategory лежат не под parent = 0, а под /shop/catalog (isfolder = 1) — без явного указания ms3_option_category_tree_parent = <id /shop/catalog> дерево останется пустым. То же поведение, что в v1.
  • Если несколько магазинов в разных контейнерах (/shop1/catalog, /shop2/catalog) — настройка одна, поддержать оба сценария нельзя.

Это та же проблема, что v1, только переименована.

Что ожидаем в v3

  1. Удалить ms3_option_category_tree_parent целиком: _build/elements/settings.php, лексиконы (setting_ms3_option_category_tree_parent + area_ms3_options если не используется в других настройках), OptionsController::resolveOptionsTreeParentId() и его вызовы, controllers/category/update.class.php, controllers/mgr/settings.class.php, OptionCategoryTree.vue prop rootParent (если он больше не используется).

  2. OptionsController::getTree должен на уровне parent отдавать:

    • все msCategory — выбираемые узлы, с чекбоксом (как сейчас);
    • все modResource isfolder = 1 (modResource / modDocument / modWebLink), которые содержат msCategory в поддереве — как навигационные, без чекбокса. Узлы без msCategory в поддереве в дерево не попадают (фильтруем мусор).

    Один из способов реализации проверки «есть ли msCategory в поддереве» — рекурсивный CTE, либо два запроса (сначала собрать кандидатов-контейнеров, потом для каждого проверить наличие потомков). Конкретный механизм на ваше усмотрение.

  3. В ответе getTree для каждого узла добавить флаг selectable (true для msCategory, false для контейнеров) или просто отдавать class_key — фронт уже решит.

  4. Vue-компонент OptionCategoryTree.vue: рендерить чекбокс только для узлов с selectable = true / class_key = msCategory; для остальных оставить только стрелку раскрытия. Клик по навигационному узлу не должен добавлять его в modelValue.

CHANGELOG

Запись в текущем PR описывает архитектуру через системную настройку — нужно переписать под реальную семантику v3 (без настройки) и убрать из 🐛 Исправлено неверные пункты.


Понимаю, что задача нетривиальная — особенно «isfolder с проверкой msCategory в поддереве». Если хочется упростить — можно отдавать все isfolder = 1 контейнеры без проверки поддерева; тогда в дереве будет шум (пустые навигационные ветки), но менеджер сможет пройти к категориям. Это запасной вариант, обсудим если упрётесь в производительность.

Ждём v3.

@Ibochkarev Ibochkarev force-pushed the fix/237-options-category-tree-root branch 2 times, most recently from 1ad67f7 to 0ec6863 Compare May 18, 2026 01:57
@Ibochkarev Ibochkarev requested a review from biz87 May 18, 2026 01:57
Add ms3_option_category_tree_parent setting and resolve parent=0 in OptionsController::getTree. Expose optionCategoryTreeParent in ms3.config; Vue passes rootParent. CHANGELOG + ru/en lexicons.
…237)

Treat missing `selectable` as true (old tree payload). Document category class_key variants in OptionsController.
@Ibochkarev Ibochkarev force-pushed the fix/237-options-category-tree-root branch from a7b593f to 77ac826 Compare May 18, 2026 17:07
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.

[Bug] Пустое дерево категорий в UI привязки опций при parent=0 (getTree)

2 participants