Выведение опций товара по категориям
Сниппет msProductOptions выводит товары общим массивом. В принципе, никто не запрещает в чанке вывода делать с этим массивом всё что угодно, в том числе и делить по категориям. Однако всё чаще поступает запрос от клиента не только вывести категории, но и соблюсти их сортировку.
Для этих целей мне пришлось msProductOptions переписать. Мой вариант сниппета воспроизводит запрос msProductData::loadOptions с сортировкой по полю rank категории, затем группирует все опции по категориям с соблюдением сортировки самих опций.
На фронт получаем массив вида:
В чанке вывода можно сделать например так:
Выкладываю сюда код сниппета, вдруг кому-то пригодится. Воть:
UPD. 30.11.2019 Исправлен код сниппета, который некорректно обрабатывал опции с множественным значением
Для этих целей мне пришлось msProductOptions переписать. Мой вариант сниппета воспроизводит запрос msProductData::loadOptions с сортировкой по полю rank категории, затем группирует все опции по категориям с соблюдением сортировки самих опций.
На фронт получаем массив вида:
[
'категория 1' => [
'option1' => [
(все поля опции и массив value)
],
'option2' => [
(все поля опции и массив value)
]
],
'категория 2' => [
'option3' => [
(все поля опции и массив value)
],
'option4' => [
(все поля опции и массив value)
]
],
]
В чанке вывода можно сделать например так:
{foreach $data as $category => $options}
<h4>{$category}</h4>
<dl>
{foreach $options as $option}
<dt>{$option.caption}</dt>
<dd>{$option.value|join:', '}{$option.measure_unit}</dd>
{/foreach}
</dl>
{/foreach}
Выкладываю сюда код сниппета, вдруг кому-то пригодится. Воть:
<?php
/** @var modX $modx */
/** @var array $scriptProperties */
$tpl = $modx->getOption('tpl', $scriptProperties, 'tpl.msOptions');
if (!empty($input) && empty($product)) {
$product = $input;
}
$product = !empty($product) && $product != $modx->resource->id
? $modx->getObject('msProduct', ['id' => $product])
: $modx->resource;
if (!($product instanceof msProduct)) {
return "[msProductOptions] The resource with id = {$product->id} is not instance of msProduct.";
}
$ignoreOptions = array_filter(array_map('trim', explode(',', $modx->getOption('ignoreOptions', $scriptProperties, ''))));
$onlyOptions = array_filter(array_map('trim', explode(',', $modx->getOption('onlyOptions', $scriptProperties, ''))));
$groups = array_filter(array_map('trim', explode(',', $modx->getOption('groups', $scriptProperties, ''))));
/** @var msProductData $data */
if ($data = $product->getOne('Data')) {
$optionKeys = $data->getOptionKeys();
}
if (empty($optionKeys)) {
return '';
}
$q = $modx->newQuery('msProductOption');
$q->rightJoin('msOption', 'msOption', 'msProductOption.key = msOption.key');
$q->leftJoin('modCategory', 'Category', 'Category.id = msOption.category');
$q->where(['msProductOption.product_id' => $product->get('id')]);
if (!empty($groups)) {
$q->where([
['Category.id:IN' => $groups, 'OR:Category.category:IN' => $groups]
]);
}
if (!empty($onlyOptions)) {
$q->where(['msProductOption.key:IN' => $onlyOptions]);
} elseif (!empty($ignoreOptions)) {
$q->where(['msProductOption.key:NOT IN' => $ignoreOptions]);
}
$q->select($modx->getSelectColumns('msOption', 'msOption'));
$q->select($modx->getSelectColumns('msProductOption', 'msProductOption', '', ['key'], true));
$q->select('Category.category AS category_name');
$q->sortby('Category.rank');
$tmp = [];
if ($q->prepare() && $q->stmt->execute()) {
while ($option = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
if (empty($option['value'])) continue;
$option['value'] = [$option['value']];
if (empty($option['category_name'])) {
$option['category_name'] = 'без категории';
}
$key = $option['key'];
$category = $option['category_name'];
if (!isset($tmp[$category])) {
$tmp[$category] = [];
}
if (!isset($tmp[$category][$key])) {
$tmp[$category][$key] = $option;
} elseif (!empty($tmp[$category][$key]['value']) and is_array($tmp[$category][$key]['value'])) {
$tmp[$category][$key]['value'] = array_merge($tmp[$category][$key]['value'], $option['value']);
} else {
$tmp[$category][$key]['value'] = $option['value'];
}
}
}
$output = [];
foreach ($tmp as $category => $options) {
if (empty($output[$category])) {
$output[$category] = [];
}
foreach ($optionKeys as $optionKey) {
if (isset($options[$optionKey])) {
$output[$category][$optionKey] = $options[$optionKey];
}
}
}
/** @var pdoTools $pdoTools */
$pdoTools = $modx->getService('pdoTools');
return $pdoTools->getChunk($tpl, ['data' => $output]);
UPD. 30.11.2019 Исправлен код сниппета, который некорректно обрабатывал опции с множественным значением
Комментарии: 2
@mngatoff Отличная тема для обсуждения. И хорошее решение. Спасибо что поделились решением. Я как раз хотел писать о том что можно сделать с опциями. Я думаю актуальный вопрос про групировку, перелинковку и возможно подумать еще на тему подсказок и других ништяков. Вариантов решений много как и возможностей. Обязательно изучу на тестовом стенде и ваш вариант.
А как задать сортировку опций внутри категорий? Для тех кто не знает и чтобы сэкономить себе время и нервы подсказываю (сам долго тупил и не знал как настроить):
Чтобы опции внутри категории сортировались в нужном порядке, заходим в редактирование Категории товаров в Minishop и находим вкладку Настройки. Там должны быть опции категории. Перетаскиваем строки вверх вниз формируя нужную сортировку.
Поле rank у опций категории (таблица modx_ms2_category_options) по умолчанию стоит видимо 0, но после перетаскивания строк формируется последовательность сортировки.
Чтобы опции внутри категории сортировались в нужном порядке, заходим в редактирование Категории товаров в Minishop и находим вкладку Настройки. Там должны быть опции категории. Перетаскиваем строки вверх вниз формируя нужную сортировку.
Поле rank у опций категории (таблица modx_ms2_category_options) по умолчанию стоит видимо 0, но после перетаскивания строк формируется последовательность сортировки.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.