Автовывод заполненных модификаций и опций msOptionPrice2
Доброго дня!
Нужна помощь. Кто готов сделать полностью на платной основе — пожалуйста https://modx.pro/work/16473
На фронтенде выводятся модификации товара и опции автоматически — без указания в чанке, какую опцию выводить, а какую нет.
На фронтенде это выглядит так:
Модификации (для понимания) такие:
Цвет (Белый) — Ширина умывальника (80) — +1000р
Цвет (Белый) — Ширина умывальника (100) — +2000р
Цвет (Черный) — Ширина умывальника (80) — +3000р
Цвет (Черный) — Ширина умывальника (100) — +4000р
Нужно сделать некоторые доработки:
1. В модификациях вывести автоматически не только опции товара, но и свойства товара. Компонент позволяет в модификациях использовать не только опции товара, но и свойства. Сейчас настроен автовывод только опций — нужно и модификаций.
2. Сделать вывод названия опции и свойства из caption (так же, как называется в админке, а не из лексикона) — на изображении ms2_product_color2 не задавать через словарь.
3. Из списка опций, которые под модификациями, исключить автоматически те, которые использованы в модификациях. На изображении под модификациями автоматически выводятся опции. Стоит, видимо, объединить вывод модификаций и вывод списком опций и в процессе перебора опций и свойств разделить на те, которые использованы в модификациях и на остальные. Модификации вывести с возможностью выбора значений, а остальные — просто в виде таблицы.
Логику действий понимаю, но знаний реализовать не хватает. Буду рад любым промежуточным вариантам или готовому решению.
Общий вывод такой:
Нужна помощь. Кто готов сделать полностью на платной основе — пожалуйста https://modx.pro/work/16473
На фронтенде выводятся модификации товара и опции автоматически — без указания в чанке, какую опцию выводить, а какую нет.
На фронтенде это выглядит так:
Модификации (для понимания) такие:
Цвет (Белый) — Ширина умывальника (80) — +1000р
Цвет (Белый) — Ширина умывальника (100) — +2000р
Цвет (Черный) — Ширина умывальника (80) — +3000р
Цвет (Черный) — Ширина умывальника (100) — +4000р
Нужно сделать некоторые доработки:
1. В модификациях вывести автоматически не только опции товара, но и свойства товара. Компонент позволяет в модификациях использовать не только опции товара, но и свойства. Сейчас настроен автовывод только опций — нужно и модификаций.
2. Сделать вывод названия опции и свойства из caption (так же, как называется в админке, а не из лексикона) — на изображении ms2_product_color2 не задавать через словарь.
3. Из списка опций, которые под модификациями, исключить автоматически те, которые использованы в модификациях. На изображении под модификациями автоматически выводятся опции. Стоит, видимо, объединить вывод модификаций и вывод списком опций и в процессе перебора опций и свойств разделить на те, которые использованы в модификациях и на остальные. Модификации вывести с возможностью выбора значений, а остальные — просто в виде таблицы.
Логику действий понимаю, но знаний реализовать не хватает. Буду рад любым промежуточным вариантам или готовому решению.
{*модификации*}
{set $properties = 'msOptionsPrice.properties'|snippet:[
'tpl' => 'tpl.modifications',
]}
{'msOptionsPrice.option'|snippet:$properties}
{*/модификации*}
{*опции*}
{'msProductOptions' | snippet : [
'tpl' => 'productOptions',
'ignoreOptions' => 'add,select'
]}
{*/опции*}
msOptionsPrice.properties:<?php
// msOptionsPrice.properties
/** @var modX $modx */
/** @var array $scriptProperties */
$product = !empty($product) && $product != $modx->resource->id ? $modx->getObject('msProduct', $product) : $modx->resource;
if (!($product instanceof msProduct)) {
$modx->log(1, print_r("[msOptions] The resource with id = {$product->id} is not instance of msProduct.", 1));
return $scriptProperties;
}
$options = array_map('trim', explode(',', $options));
$options = @array_diff($options, ['']);
$c = $modx->newQuery('msCategoryOption');
$c->innerJoin('msOption', 'Option');
$c->innerJoin('msProductOption', 'ProductOption', 'ProductOption.key = Option.key AND ProductOption.value != "" AND ProductOption.product_id = ' . $product->id);
$c->select('Option.key');
if (empty($options)) {
$c->where([
'category_id' => $product->parent,
]);
} else {
$c->where([
'category_id' => $product->parent,
'ProductOption.key:IN' => $options,
]);
}
$c->sortby('rank', 'asc');
$c->groupby('Option.key');
if ($c->prepare() AND $c->stmt->execute()) {
$options = $c->stmt->fetchAll(PDO::FETCH_COLUMN);
}
$options = $options ? $options : [];
$constraintOptions = [];
if (!empty($options)) {
foreach ($options as $idx => $key) {
if ($idx) {
$constraintOptions[$key] = [$options[$idx - 1]];
}
}
}
return array_merge($scriptProperties, ['options' => implode(',', $options), 'constraintOptions' => $constraintOptions]);
tpl.modifications:{foreach $options as $name => $values}
<div class="sm-text"><b>{('ms2_product_' ~ $name) | lexicon}</b></div>
<div
{if $constraints[$name]}
data-constraints="{$constraints[$name]| json_encode: 256 | htmlentities}"
{/if}
>
{foreach $values as $value index=$index}
<label class="input-parent">
<input type="radio" value="{$values[$index]}" name="options[{$name}]"
{if $index == 0}checked="checked"{/if}
{if $constraints[$name]}
data-relations="{$relations[$name][$value]| json_encode: 256 | htmlentities}"
{/if}
/>
{$values[$index]}
</label>
{/foreach}
</div>
{/foreach}
msOptionsPrice.option:<?php
$classModification = 'msopModification';
$classOption = 'msopModificationOption';
if (!function_exists('getModificationOptions')) {
function getModificationOptions(modX & $modx, $rid = null, $showZeroCount = true)
{
$options = array();
$classModification = 'msopModification';
$classOption = 'msopModificationOption';
$classMsOption = 'msOption';
$q = $modx->newQuery($classOption);
$q->innerJoin($classModification, $classModification, "{$classModification}.id = {$classOption}.mid");
$q->leftJoin($classMsOption, $classMsOption, "{$classOption}.key = {$classMsOption}.key");
$q->select($modx->getSelectColumns($classOption, $classOption));
$q->select($modx->getSelectColumns($classMsOption, $classMsOption, '', array('caption'), false));
$q->where(array(
"{$classOption}.rid" => "{$rid}",
"{$classModification}.active" => true,
));
if (!$showZeroCount) {
$q->andCondition(array(
"{$classModification}.count:>" => 0,
));
}
if ($q->prepare() AND $q->stmt->execute()) {
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
$k = $row['key'];
if (!isset($options[$k])) {
$options[$k] = array($row['value']);
} else {
$options[$k][] = $row['value'];
}
foreach ($row as $x => $value) {
$options[$k . '.' . $x] = $value;
}
}
}
return $options;
}
}
if (!function_exists('getOptionColors')) {
function getOptionColors(modX & $modx, $rid = null, $key = null)
{
$colors = array();
$classColor = 'msocColor';
$q = $modx->newQuery($classColor);
$q->where(array(
"{$classColor}.rid" => "{$rid}",
"{$classColor}.key" => "{$key}",
));
$q->andCondition(array(
"{$classColor}.color:!=" => "",
"OR:{$classColor}.color2:!=" => "",
"OR:{$classColor}.pattern:!=" => "",
"OR:{$classColor}.pattern2:!=" => "",
));
$q->select($modx->getSelectColumns($classColor, $classColor, '', array('rid', 'key'), true));
if ($q->prepare() AND $q->stmt->execute()) {
while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
$k = $row['value'];
$colors[$k] = $row;
}
}
return $colors;
}
}
/** @var modX $modx */
/** @var array $scriptProperties */
$tpl = $modx->getOption('tpl', $scriptProperties, 'tpl.msOptions');
$showZeroCount = (bool)$modx->getOption('showZeroCount', $scriptProperties, true);
$showProductOptions = (bool)$modx->getOption('showProductOptions', $scriptProperties, false);
$processColors = (bool)$modx->getOption('processColors', $scriptProperties, false);
if (!empty($input) && empty($product)) {
$product = $input;
}
if (!empty($name) && empty($options)) {
$options = $name;
}
$names = array_map('trim', explode(',', $options));
$names = array_diff($names, array(''));
$product = !empty($product) && $product != $modx->resource->id
? $modx->getObject('msProduct', $product)
: $modx->resource;
if (!($product instanceof msProduct)) {
return "[msOptions] The resource with id = {$product->id} is not instance of msProduct.";
}
$modx->lexicon->load('minishop2:product');
$constraints = $modx->getOption('constraintOptions', $scriptProperties);
if ($constraints AND !is_array($constraints)) {
$constraints = json_decode($constraints, true);
}
$data = $productData = getModificationOptions($modx, $product->id, $showZeroCount);
if ($showProductOptions) {
$productData = $product->loadOptions();
}
$options = $captions = $relations = $colors = array();
foreach ($names as $name) {
$option = $modx->getOption($name, $data);
if (!$option AND $showProductOptions) {
$option = $modx->getOption($name, $productData);
}
if ($option) {
$option = array_unique($option);
sort($option);
$options[$name] = $option;
// process captions
if (isset($data[$name . '.caption'])) {
$captions[$name] = $data[$name . '.caption'];
} else {
$captions[$name] = $modx->lexicon('ms2_product_' . $name);
}
// process relations
if (!empty($constraints) AND array_key_exists($name, $constraints)) {
$relations[$name] = array();
$q = $modx->newQuery($classOption);
$q->innerJoin($classModification, $classModification, "{$classModification}.id = {$classOption}.mid");
$q->leftJoin($classOption, 'Values', "{$classOption}.mid = Values.mid");
$q->where(array(
"{$classOption}.rid" => "{$product->id}",
"{$classOption}.key" => "{$name}",
"{$classModification}.active" => true,
));
if (!$showZeroCount) {
$q->andCondition(array(
"{$classModification}.count:>" => 0,
));
}
$q->limit(0);
$q->sortby("{$classOption}.key", "ASC");
$q->groupby("{$classOption}.mid");
$q->select("{$classOption}.value, GROUP_CONCAT(CONCAT_WS('=',`Values`.`key`,`Values`.`value`) SEPARATOR '&') as value");
$rows = array();
if ($q->prepare() && $q->stmt->execute()) {
if (!$rows = $q->stmt->fetchAll(PDO::FETCH_COLUMN | PDO::FETCH_GROUP)) {
$rows = array();
}
foreach ($rows as $key => &$row) {
foreach ($row as $k => $v) {
parse_str($v, $row[$k]);
ksort($row[$k]);
unset($row[$k][$name]);
$v = array();
foreach ($row[$k] as $param => $value) {
$v[] = "{$param}={$value}";
}
$row[$k] = implode('&', $v);
}
}
$relations[$name] = $rows;
}
}
// process colors
if ($processColors) {
$colors[$name] = getOptionColors($modx, $product->id, $name);
}
}
}
if (!empty($scriptProperties['sortOptions'])) {
$sorts = array_map('trim', explode(',', $scriptProperties['sortOptions']));
foreach ($sorts as $sort) {
$sort = explode(':', $sort);
$key = $sort[0];
$order = SORT_ASC;
if (!empty($sort[1])) {
$order = constant($sort[1]);
}
$type = SORT_STRING;
if (!empty($sort[2])) {
$type = constant($sort[2]);
}
$first = null;
if (!empty($sort[3])) {
$first = $sort[3];
}
if (array_key_exists($key, $options) AND is_array($options[$key]) AND !empty($options[$key])) {
array_multisort($options[$key], $order, $type);
if ($first && ($index = array_search($first, $options[$key])) !== false) {
unset($options[$key][$index]);
array_unshift($options[$key], $first);
}
}
}
}
/** @var pdoTools $pdoTools */
$pdoTools = $modx->getService('pdoTools');
return $pdoTools->getChunk($tpl, array(
'product' => isset($product) ? $product->toArray() : array(),
'options' => $options,
'captions' => $captions,
'relations' => $relations,
'constraints' => $constraints,
'colors' => $colors,
));
msProductOptions:<?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', $product)
: $modx->resource;
if (!($product instanceof msProduct)) {
return "[msProductOptions] The resource with id = {$product->id} is not instance of msProduct.";
}
$ignoreOptions = array_map('trim', explode(',', $modx->getOption('ignoreOptions', $scriptProperties, '')));
$onlyOptions = array_map('trim', explode(',', $modx->getOption('onlyOptions', $scriptProperties, '')));
$groups = !empty($groups)
? array_map('trim', explode(',', $groups))
: array();
/** @var msProductData $data */
if ($data = $product->getOne('Data')) {
$optionKeys = $data->getOptionKeys();
}
if (empty($optionKeys)) {
return '';
}
$productData = $product->loadOptions();
$options = array();
foreach ($optionKeys as $key) {
// Filter by key
if (!empty($onlyOptions) && $onlyOptions[0] != '' && !in_array($key, $onlyOptions)) {
continue;
} elseif (in_array($key, $ignoreOptions)) {
continue;
}
$option = array();
foreach ($productData as $dataKey => $dataValue) {
$dataKey = explode('.', $dataKey);
if ($dataKey[0] == $key && (count($dataKey) > 1)) {
$option[$dataKey[1]] = $dataValue;
}
}
$option['value'] = $product->get($key);
// Filter by groups
$skip = !empty($groups) && !in_array($option['category'], $groups) && !in_array($option['category_name'], $groups);
if ($skip || empty($option['value'])) {
continue;
}
$options[$key] = $option;
}
/** @var pdoTools $pdoTools */
$pdoTools = $modx->getService('pdoTools');
return $pdoTools->getChunk($tpl, array(
'options' => $options,
));
productOptions:{if $options} {* Проверяем на пустоту *}
<h4 class="m-b-20">Характеристики товара</h4>
<div class="chartable">
{foreach $options as $option} {* Перебираем *}
<div class="tr">
<div class="td">{$option.caption}</div> {* Выводим название опции *}
<div class="td">
{if $option.value is array} {* Проверям, если значение опции является массивом *}
<b>{$option.value | join : ', '} {$option.measure_unit}</b> {* то выводим с помощью разделителя *}
{else} {* иначе, т.е. строка, число и другое *}
</b>{$option.value} {$option.measure_unit}</b> {* просто выводим *}
{/if}
</div>
</div>
{/foreach}
</div>
{/if}