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;
}
 

Итого

На странице товара, при выборе чекбоксов опции Наполнители к начальной цене будет прибавляться дополнительная стоимость каждой модификации этой опции.
12 августа 2017, 09:56    Павел Гвоздь   
16    218 +14

Комментарии (6)

  1. Воеводский Михаил 12 августа 2017, 10:02 # +2
    Если я правильно понял суть, то msAddLinked это умеет делать по умолчанию. Мб, управление в некоторых случаях менее удобно. С другой стороны, изменение добавочной цены централизованно (при редактировании товара-опции), а не в каждом товаре отдельно должно сокращать время на поддержку магазина менеджерами.

    Но, повторюсь, мог не уловить всей глубины задумки.
    1. Павел Гвоздь 12 августа 2017, 10:09 # +1
      Да, демо версия говорит, что умеет. Только значения Наполнителей будут товарами.
      Однако не стал экспериментировать, т.к. клиент купил msOptionsPrice2 и msExtraFields. Второй — для решения задачи, описанной в данном посте (до сих пор не знаю, умеет ли он такое). После установки обоих, функция изменения стоимости у первого резко отвалилась. После изучения кода было ясно, что компоненты в одном проекте вместе не уживаются. Удалил второй (т.к. он не используется на проекте, в отличие от первого), еще немного поковырял первый и родилось вот такое решение.
      1. Воеводский Михаил 12 августа 2017, 10:44 # +1
        Только значения Наполнителей будут товарами.
        В этом свои преимущества, как я написал — изменение цены такого наполнителя в одном месте, а не в каждом товаре по отдельности.

        родилось вот такое решение
        Как ни крути, любые варианты решения задач полезны. Не исключаю. что в некоторых случаях такое решение будет рациональнее, чем приплетать msAddLinked.
    2. Володя 12 августа 2017, 11:04 # +1
      Вот что значит свежая голова и умелые руки…
      Оказалось, что компонент не умеет прибавлять к начальной цене товара несколько цен из модификаций одной опции.
      наверно неверно сформулировал. Компонент просто не работает с опциями массивами…
      $modx->newQuery('msopModification')
      для этого можно задействовать метод getModificationByOptions
      ps. И да, для таких вопросов есть ТП в которой они успешно решаются…
      1. Павел Гвоздь 12 августа 2017, 11:09 # 0
        наверно неверно сформулировал. Компонент просто не работает с опциями массивами…
        Да нет, вроде верно. Если опция не массив, то он тоже не прибавит к начальной цене все выбранные значения, а только последнее выбранное.

        Я бы отписал в ТП, только не я покупал компонент, а заказчик. Задачу было необходимо решить скорее, поэтому решил сам. Естественно, когда если ты внедришь поддержку работы с массивами опций, то данное решение утратит актуальность и я его удалю. Только просьба сообщить об этом тут. :)
        1. Володя 12 августа 2017, 11:13 # +3
          Задачу было необходимо решить скорее, поэтому решил сам
          отлично! Могу только сказать спасибо!)
          Естественно, когда если ты внедришь поддержку работы с массивами опций, то данное решение утратит актуальность и я его удалю. Только просьба сообщить об этом тут. :)
          Не собираюсь в ближайшее время это делать. Это повлияет на логику компонента…
          Да и зачем удалять. Хороший пример как можно модифицировать стандартную логику расчета.
      Вы должны авторизоваться, чтобы оставлять комментарии.