feat(extra-fields): repeater field type (ms3-repeater)#301
Conversation
|
Спасибо за PR — фича архитектурно правильная и закрывает большой класс кейсов (характеристики товара, варианты в карточке, наборы файлов, бонусные уровни и т.п.). Прошёлся по backend, прогнал PHPStan. Есть один блокер и пара замечаний. 🔴 Блокер — регрессия PR #310Ветка отрезалась от
Текущий beta: if (array_key_exists($field, $params)) { ... }В PR #301: if (isset($params[$field])) { ... } // откат
В текущем beta — В PR #301 — старый
В текущем beta — В PR #301 — старый Что нужно: Без rebase мерж нельзя — это публичный регресс контракта API через сутки после фикса. Что хорошо (после исправления регрессии)
🟡 Минорные замечания1. Hardcoded class в $this->productRepeaterFields = $this->getRepeaterFieldService()->getRepeaterFieldsForClass(
'MiniShop3\Model\msProductData'
);Если repeater задумывался расширяемым на другие модели — стоит вынести в константу/метод. Не блокер, но запах. 2. Лог не пишется при невалидном
3. Dead check в
4. Кеш Если в течение одного запроса админ удалил/изменил repeater extra field и сразу сохранил товар — увидит старую конфигурацию. На практике редко (как правило, разные сессии), но стоит документировать ограничение или предусмотреть Vue / Frontend — нужна ручная проверкаНе разбирал детально 8 Vue-файлов (+600 строк), но в чеклисте PR не отмечен «ручное тестирование» — а это критично для UI-фичи такого объёма. До мержа стоит прокликать и отчитаться:
РезюмеОбязательно до мержа:
Желательно:
После rebase и UI-теста — мержим. Фича сильная, в CHANGELOG пойдёт топовым пунктом релиза. |
b3e2980 to
a66b180
Compare
- Warn in error log when repeater_config JSON is malformed (with field key) - Use msProductData::class instead of hardcoded string in ProductDataService - Drop redundant maxRows check in validateRows after normalizeRows enforces limit
|
@biz87 можно смотреть повторно |
|
Прокликал фичу на dev-стенде. Дополняю первый ревью результатами ручного теста — нашёл два блокера в самом RepeaterField, не связанных с rebase #310. 🔴 Блокер 1 — фокус сбрасывается на каждый keystroke
normalized._ms3RowId = row?._ms3RowId ?? `row-${index}-${Date.now()}`И
Фикс — guard в watch от собственного emit'а: let isInternalEmit = false
function emitRows() {
const normalized = normalizeRepeaterRows(internalRows.value, schema.value)
isInternalEmit = true
emit('update:modelValue', stripRepeaterRowMeta(normalized, schema.value))
}
watch(
[() => props.modelValue, () => props.config],
() => {
if (isInternalEmit) {
isInternalEmit = false
return
}
internalRows.value = normalizeRepeaterRows(
parseRepeaterModelValue(props.modelValue),
schema.value
)
},
{ immediate: true, deep: true }
)Затрагивает оба столбца, но если один из них — 🔴 Блокер 2 — сохранение не работаетЗаполнил поле в карточке товара ID 152351, нажал MODX Save в шапке, перезагрузил — поле пустое. В БД проверил: То есть колонка получила пустой массив, а не введённые строки. Причина — отсутствие bridge между Vue-стейтом и legacy формой. В DynamicField.vue:185-191: <RepeaterField
v-else-if="fieldConfig.xtype === 'ms3-repeater'"
v-model="localValue"
:config="repeaterConfig"
:disabled="disabled"
/>Нет Что происходит при Save:
То есть Фикс — добавить hidden input в DynamicField: <RepeaterField ... />
<input
v-if="fieldConfig.xtype === 'ms3-repeater'"
type="hidden"
:name="fieldConfig.name"
:value="JSON.stringify(localValue || [])"
/>xPDO с 🟡 UX-замечания (не блокеры)1. Конфигуратор не помещается в модалку. В RepeaterSchemaEditor.vue:185-190 сетка 2. «Поле rank» вынесено первым в форме. Это служебная настройка имени ключа для авто-сортировки (по аналогии с MIGX). 99% пользователей не должны её видеть. Спрятать в свёрнутый блок «Расширенные настройки» или вообще убрать из UI, оставить хардкод 3. Frontend (public output)Поле в БД лежит как JSON-строка с Это MIGX-паттерн, который сообщество знает и принимает. Не блокер, но стоит явно зафиксировать в документации к фиче, чтобы не было сюрприза «почему не работает {$product.specs}». Итоговый список блокеров для PR #301
UX-полировка и документирование public output — желательно, но мерж не блокирует. После фикса этих трёх — пересмотрю PR, прогоню повторно. Готов помочь с реализацией, если нужно. |
…d address Introduces repeater extra fields with schema-driven rows, DnD sorting, rank normalization, and manager UI across product data and order screens. Closes #299
… saves Validate repeater rows after normalization, return 422 from product-data API on invalid payload, and strip client-only row metadata before submit.
- Warn in error log when repeater_config JSON is malformed (with field key) - Use msProductData::class instead of hardcoded string in ProductDataService - Drop redundant maxRows check in validateRows after normalizeRows enforces limit
f52e4d9 to
025ccb1
Compare
|
Прогресс заметный — закрыто два из трёх блокеров. Но главный остался. ✅ Закрыто1. Rebase #310 — в 2. Focus loss — починен через 🔴 Не закрыто — save всё ещё не персиститЭто критический блокер. У него два корня, ни один не закрыт. Без починки фичу нельзя использовать, что бы внешне ни выглядело корректно. Корень 1 —
|
Описание
Добавлен тип extra field «повторитель» (
ms3-repeater): JSON-массив объектов со схемой колонок, drag-and-drop сортировкой строк и автоматическим проставлениемrankпри сохранении (MIGX-паттерн).Backend: миграция
repeater_config,RepeaterFieldService, интеграция вExtraFieldsService,ProductDataService(исключение из option-sync) иOrdersController(422 при invalid payload).Vue Manager:
RepeaterField, редактор схемы в ExtraFieldsManager,OrderExtraFieldsSectionдля msOrder/msOrderAddress.Тип изменений
Связанные Issues
Closes #299
Refs #298
Как это было протестировано?
Конфигурация тестирования:
Чеклист
Дополнительные заметки
Repeater исключён из
prepareOptionValuesиsaveOptionsдля msProductData. Order/address extra fields получили UI черезOrderExtraFieldsSection. Grid sort/filter по JSON-path — follow-up из issue.Для ревьюера: перед merge нужна миграция Phinx и
npm run buildв vueManager на стенде.