msOptionsPrice2 и составной товар
Возникла необходимость при помощи msOptionsPrice2 реализовать составной товар. Оказалось, что компонент не умеет прибавлять к начальной цене товара несколько цен из модификаций одной опции.
Например, есть опция Наполнители, у нее около 20 значений. На фронте, при выборе каждого из значений, цена товара должна увеличиваться на N рублей. Проблема заключается в том, что имена тегам чекбоксов мы даем в виде массива:
<input type="checkbox" name="cb[]">
Компонент, в свою очередь, с таким работать не умеет. Предлагаю решение в четыре простых шага!Решение проблемы
Предположим у нас есть опция Наполнители с ключом wok_fillers.
Шаг 1
При редактировании товара добавляем значения:Шаг 2
Создаем модификацию для каждого значения, указывая:Тип = +
Цена = Сумма, которую нужно прибавить к начальной стоимости товара
Шаг 3
На странице товара выводим список чекбоксов:{'!msOptionsPrice.modification' | snippet : [
'type' => 2,
'where' => [
'Option.key' => 'wok_fillers',
],
'sortby' => '{"msopModification.id": "ASC"}',
'limit' => 100,
'tpl' => '@INLINE
{foreach $options as $name => $value}
<div>
<label for="wok_fillers_{$idx}">
<input type="checkbox" name="options[{$name}][]" id="wok_fillers_{$idx}" value="{$value}"> {$value} <b style="color:red">(+{$modification.price} руб.)</b>
</label>
</div>
{/foreach}
',
]}
Таким образом, мы выведем список модификаций для значений опции wok_fillers в виде чекбоксов, с прибавочной ценой рядом с названием значения.Шаг 4
Создаем плагин msopModificationPrice на событие msOnGetProductPrice, указывая приоритет 999. Высокий приоритет необходим для того, чтобы наш плагин отрабатывал после плагина msOptionsPrice.switch ($modx->event->name) {
case "msOnGetProductPrice":
$returned = (array)$modx->getPlaceholder('_returned_price');
if (!isset($returned['price'])) {
return;
}
$id = $returned['id'];
$price = $returned['price'];
$options = !empty($returned['msoptionsprice_options'])
? $returned['msoptionsprice_options']
: (!empty($_REQUEST['options'])
? $_REQUEST['options']
: array());
$options = array_diff_key($options, array_flip(array('modifications', 'modification')));
foreach (array_keys($options) as $k) {
if (!empty($options[$k]) && is_array($options[$k])) {
foreach ($options[$k] as $v) {
$q = $modx->newQuery('msopModification')
->innerJoin('msopModificationOption', 'Option',
"Option.mid = msopModification.id AND Option.key = '{$k}' AND Option.value = '{$v}'")
->select(array('price'))
->where(array(
'msopModification.rid' => $id,
'msopModification.type' => 2,
'msopModification.active' => true,
))
;
if ($q->prepare() && $q->stmt->execute()) {
if ($price_add = $q->stmt->fetchColumn()) {
$price += $price_add;
}
}
}
}
}
$modx->event->returnedValues['price'] = $returned['price'] = $price;
$modx->setPlaceholder('_returned_price', $returned);
break;
}
Итого
На странице товара, при выборе чекбоксов опции Наполнители к начальной цене будет прибавляться дополнительная стоимость каждой модификации этой опции.
Поблагодарить автора
Отправить деньги
Комментарии: 42
Если я правильно понял суть, то msAddLinked это умеет делать по умолчанию. Мб, управление в некоторых случаях менее удобно. С другой стороны, изменение добавочной цены централизованно (при редактировании товара-опции), а не в каждом товаре отдельно должно сокращать время на поддержку магазина менеджерами.
Но, повторюсь, мог не уловить всей глубины задумки.
Но, повторюсь, мог не уловить всей глубины задумки.
Да, демо версия говорит, что умеет. Только значения Наполнителей будут товарами.
Однако не стал экспериментировать, т.к. клиент купил msOptionsPrice2 и msExtraFields. Второй — для решения задачи, описанной в данном посте (до сих пор не знаю, умеет ли он такое). После установки обоих, функция изменения стоимости у первого резко отвалилась. После изучения кода было ясно, что компоненты в одном проекте вместе не уживаются. Удалил второй (т.к. он не используется на проекте, в отличие от первого), еще немного поковырял первый и родилось вот такое решение.
Однако не стал экспериментировать, т.к. клиент купил msOptionsPrice2 и msExtraFields. Второй — для решения задачи, описанной в данном посте (до сих пор не знаю, умеет ли он такое). После установки обоих, функция изменения стоимости у первого резко отвалилась. После изучения кода было ясно, что компоненты в одном проекте вместе не уживаются. Удалил второй (т.к. он не используется на проекте, в отличие от первого), еще немного поковырял первый и родилось вот такое решение.
Только значения Наполнителей будут товарами.В этом свои преимущества, как я написал — изменение цены такого наполнителя в одном месте, а не в каждом товаре по отдельности.
родилось вот такое решениеКак ни крути, любые варианты решения задач полезны. Не исключаю. что в некоторых случаях такое решение будет рациональнее, чем приплетать msAddLinked.
Вот что значит свежая голова и умелые руки…
ps. И да, для таких вопросов есть ТП в которой они успешно решаются…
Оказалось, что компонент не умеет прибавлять к начальной цене товара несколько цен из модификаций одной опции.наверно неверно сформулировал. Компонент просто не работает с опциями массивами…
$modx->newQuery('msopModification')для этого можно задействовать метод getModificationByOptions
ps. И да, для таких вопросов есть ТП в которой они успешно решаются…
наверно неверно сформулировал. Компонент просто не работает с опциями массивами…Да нет, вроде верно. Если опция не массив, то он тоже не прибавит к начальной цене все выбранные значения, а только последнее выбранное.
Я бы отписал в ТП, только не я покупал компонент, а заказчик. Задачу было необходимо решить скорее, поэтому решил сам. Естественно,
Задачу было необходимо решить скорее, поэтому решил самотлично! Могу только сказать спасибо!)
Естественно, когда если ты внедришь поддержку работы с массивами опций, то данное решение утратит актуальность и я его удалю. Только просьба сообщить об этом тут. :)Не собираюсь в ближайшее время это делать. Это повлияет на логику компонента…
Да и зачем удалять. Хороший пример как можно модифицировать стандартную логику расчета.
Для этой фичи я готов купить компонент на еще один проект, может, два…
Павел, несколько вопросов:
1.
Может,
2. У меня приходит не массив опций, а только одна, последняя опция. Не проходит проверку
3. Что за плейсхолдер _returned_price?
Павел, несколько вопросов:
1.
<input type="checkbox" name="options[{$name}][]"
…Может,
<input type="checkbox" name="options[{$name}]"
..., а то не понимает скрипт, или у меня не работает…2. У меня приходит не массив опций, а только одна, последняя опция. Не проходит проверку
if (!empty($options[$k]) && is_array($options[$k])) {...
3. Что за плейсхолдер _returned_price?
При этом, если снять все чекбоксы, приходит первое значение, то есть, первая опция.
Это был очередной глюк компонента. пересоздал опции, если сняты все чекбоксы, приходит массив с пустым msoptionsprice_options.
На счет _returned_price — посмотрел его в компоненте msOptionsPrice.
Если вы сделаете все так, как описано в статье, то работать будет. Я уже на двух проектах такое повторил.
Не работает. Сижу, проверяю. Ну никак.
Приоритет плагину указали?
Все, как тут, только имя опции изменил. Если указываю name=«options[{$name}][]», то цена меняется только по первой опции, если name=«options[{$name}]», то цена меняется в зависимости от опции, но не суммируется.
К сожалению, так не смогу выявить проблему. Если готовы оплатить час работы (1.5кр) — пишите доступы на почту или в скайп.
Ок, еще поковыряюсь, коль начал, если не разберусь — отпишу.
А пример работающего сайта есть?
Есть. Не думаю, что заказчик будет рад от того, что я ссылками на его сайты разбрасываюсь…
А в личку, для личного анализа? Если нет — пойму.
Нет.
Вот тут у меня приходит только последняя опция (не массив опций из чекбоксов): $returned['msoptionsprice_options']
Все, как тут, только имя опции изменил. Если указываю name=«options[{$name}][]», то цена меняется только по первой опции, если name=«options[{$name}]», то цена меняется в зависимости от опции, но не суммируется.если вы задали опцию как
options[{$name}]
то естественно на сервер придет не массив, а только последнее значение…
Володя, еще вопрос, думаю, не только для меня актуален. На странице товара (не в списке), при штатном использовании компонента (без составного товара), если у контейнера выставлен id=«msProduct», то взаимосвязь чекбоксов и картинок теряется, а если айди убрать, то связь восстанавливается, но всегда выставляется чекнутым первый чекбокс. Пробовал и по-разному, с выставлением массивов опций, может, подскажете, как восстановить взимосвязь галереи и опций, чтобы первый чекбокс не проставлялся. Думаю, вам, как разработчику будет проще дать ответ, нежели мне снова перебирать компонент. Или, такой глюк только у меня?
При выборе картинки, к которой не привязана ни одна модификация, также выставляется первая в списке опция.
А если не указывать в форме скрытый инпут options (с любыми вариациями определения, масивом и нет), то и с id=«msProduct» все работает.
Из-за особенностей процессора, нулевая опция (из-за скрытого инпута) ломает логику. Можно легко полечить, например, этим:
unset($options['0']);
$modx->event->returnedValues['options'] = $options;
, в плагине, на событие msopOnBeforeGetModification.
MODx Revo 2.5.7-pl
pdoTools 2.9.1-pl
msOptionsPrice2 2.3.33-beta
miniShop 2.4.11-pl
pdoTools 2.9.1-pl
msOptionsPrice2 2.3.33-beta
miniShop 2.4.11-pl
А вы с галереей подружили опции?
Не пробовал даже. В задаче этого не требовалось.
Со свежей головой разобрался. Если кто-то еще будет ломать голову, то:
1. Чекбоксы указываются таки с параметром
2. У формы нужно указать (определить) скрытый
1. Чекбоксы указываются таки с параметром
name="options[{$name}][]"
, тогда в массиве опций передается массив зачений.2. У формы нужно указать (определить) скрытый
<input type="hidden" name="options[]" value="[]"> или <input type="hidden" name="options[]" value="">
, из-за этого, по сути, ничего и не выходило, проморгал.
Еще, если кому-то нужно, чтобы все это работало еще и в списке товаров и выставлялись чекбоксы с mSearch2, нужно поправить default.js у msOptionsPrice2 (/assets/components/msoptionsprice/js/web/default.js), строки 722-731:
с
с
if (rid) {
inputs = $(msOptionsPrice.Product.cost + msOptionsPrice.Product.prefix + rid)
.closest(msOptionsPrice.Product.form)
.find('[name="' + name + '"]');
}
else {
inputs = $(msOptionsPrice.Product.cost)
.closest(msOptionsPrice.Product.form)
.find('[name="' + name + '"]');
}
наif (rid) {
inputs = $(msOptionsPrice.Product.cost + msOptionsPrice.Product.prefix + rid)
.closest(msOptionsPrice.Product.form)
.find('[name*="' + name + '"]');
}
else {
inputs = $(msOptionsPrice.Product.cost)
.closest(msOptionsPrice.Product.form)
.find('[name*="' + name + '"]');
}
, то есть, добавить к селектору name звездочку — *.
Здравствуйте! Подскажите, применима ли статья к последней версии [msOptionsPrice2]?
Не проверял.
Если у товара нет зависимых опций, размер цвет, то все работает корректно, но если у товара уже есть зависимость, то есть проблема. Он все эти опции добавляет ко все вариантам + при установки чекбокса прибавляется стоимость еще раз. Кто то решал эту задачу?
Сергей, здравствуйте! Вы не нашли в чем проблема?
Возникла такая же, сразу добавляет цену к товару, а при отметке флажка добавляет еще раз…
Возникла такая же, сразу добавляет цену к товару, а при отметке флажка добавляет еще раз…
Здравствуйте! Нашли решение проблемы? такая же ситуация.
Здравствуйте! К сожалению, нет
Вопрос возможно давно не актуальный, но вот решение:
<?php
switch ($modx->event->name) {
case "msOnGetProductPrice":
$returned = (array)$modx->getPlaceholder('_returned_price');
if (!isset($returned['price'])) {
return;
}
$id = $returned['id'];
foreach (array_keys($options) as $k) {
if (!empty($options[$k]) && is_array($options[$k])) {
foreach ($options[$k] as $v) {
$q = $modx->newQuery('msopModification')
->innerJoin('msopModificationOption', 'Option',
"Option.mid = msopModification.id AND Option.key = '{$k}' AND Option.value = '{$v}'")
->select(array('price'))
->where(array(
'msopModification.rid' => $id,
'msopModification.type' => 2,
'msopModification.active' => true,
))
;
if ($q->prepare() && $q->stmt->execute()) {
$price = $price - $q->stmt->fetchColumn();
}
}
}
}
$options = !empty($returned['msoptionsprice_options'])
? $returned['msoptionsprice_options']
: (!empty($_REQUEST['options'])
? $_REQUEST['options']
: array());
$options = array_diff_key($options, array_flip(array('modifications', 'modification')));
foreach (array_keys($options) as $k) {
if (!empty($options[$k]) && is_array($options[$k])) {
foreach ($options[$k] as $v) {
$q = $modx->newQuery('msopModification')
->innerJoin('msopModificationOption', 'Option',
"Option.mid = msopModification.id AND Option.key = '{$k}' AND Option.value = '{$v}'")
->select(array('price'))
->where(array(
'msopModification.rid' => $id,
'msopModification.type' => 2,
'msopModification.active' => true,
))
;
if ($q->prepare() && $q->stmt->execute()) {
// $price = $price - $q->stmt->fetchColumn();
if ($price_add = $q->stmt->fetchColumn()) {
$price += $price_add;
}
}
}
}
}
$modx->event->returnedValues['price'] = $returned['price'] = $price;
$modx->setPlaceholder('_returned_price', $returned);
break;
}
Спасибо огромное! А как можно его допилить, чтобы и вес тоже менялся?
Я уже не имею доступа к проекту, где это делал, пиши мне в Телеграм, помогу, контакты у меня в профиле или на странице О нас.
Здравствуйте! У меня заработало только так:
В шаблоне карточки товара:
Чанк tpl.msOptionsFillers:
Но почему то не выводится добавочная цена {$modification.price}
В шаблоне карточки товара:
[[!msOptionsPrice.option?
&product=`[[*id]]`
&name=`fillers`
&type=`2`
&where=`{"Option.key": "fillers"}`
&sortby=`{"msopModification.id": "ASC"}`
&limit=`100`
&tpl=`tpl.msOptionsFillers`
]]
Чанк tpl.msOptionsFillers:
{foreach $options as $name => $values}
{foreach $values as $value} <label for="fillers_{$idx}">
<input type="checkbox" name="options[{$name}][]" id="fillers_{$idx}" value="{$value}"><input type="hidden" name="options[]" value="[]"> {$value} <b style="color:red">(+{$modification.price} руб.)</b>
</label> {/foreach}
{/foreach}
Но почему то не выводится добавочная цена {$modification.price}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.