[msAltCart] Динамическая корзина для MiniShop2

Приветствую! Предлагаю вашему вниманию современный вариант полюбившегося многим компонента msMCD. Как водится постарался учесть большинство популярных кейсов использования корзины: миникорзина в шапке, динамическая корзина сбоку, всплывающее окно с последним добавленным товаров, основная корзина. Подробности под катом.

Особенности
Использовать данный компонент как основную корзину чревато тем, что с некоторыми компонентами она может работать некорректно. Например msPromoCode2 ломает логику работы, так как его недрах генерируется некорректный ключ товара.
Так же при вызове на одной странице стандартной корзины и корзины компонента между ними не будет синхронизации.

Требования
PHP 7.4 и Modx 2.6-2.8

Зависимости
  • SendIt v1.1.0 и выше (деинсталировать предыдущие версии и только потом обновлять)
  • MiniShop2 4.0.0 и выше
  • pdoTools 2.13.2 и выше
Возможности
  • Вывод любого количества корзин на одной страницы
  • Динамическое обновление всех корзин
  • Каждая корзина может иметь собственный шаблон
  • Изменение опций в корзине
  • JS API для программного управления корзиной
  • Получение статуса и состава корзины на фронте
  • Поддержка компонентов msOptionsPrice2 и msProductDiscounts
  • Изменение логики работы посредством JS событий

Быстрый старт
  1. Установить компонент
  2. Подготовить чанки
  3. Вызвать некешированным сниппет getCarts

Подготовка чанков

Форме добавления товара в корзину нужно добавить атрибуты data-si-preset=«cart_add» data-si-form=«product-{$id}» data-si-nosave.
Кнопке в этой форме нужно установить type в значение button и добавить атрибут data-si-event=«click».
Родную разметку miniShop2 для формы и кнопки при этом следует удалить.

В отличие от оригинального чанка корзины, тут чанк разбит на два: обёртку и товар. Корневой элемент чанка товара должен содержать атрибут data-msac-product="{$key}".
Внутри коневого элемента можно разместить форму или несколько форм с изменяемыми полями, например количество и опции. Каждая такая форма должна иметь атрибуты
data-si-form=«change-options-{$key}» data-si-preset=«cart_change» data-si-event=«change» data-si-nosave. Атрибут data-si-form иметь произвольное,
но уникальное в рамках одной страницы значение.
Также внутри корневого элемента следует разместить форму удаления товара из корзины со следующими атрибутами data-si-form=«remove-{$key}» data-si-preset=«cart_remove».
Внутри этой формы должна быть кнопка с атрибутами type=«button» data-si-event=«click».
Для вставки изменяемых значений, например, стоимости товара, количества товара, картинки товара, общей стоимости корзины, общего веса, общего количества,
нужно чтобы каждый такой элементы был отдельным html элементом с атрибутом data-msac-prop. В качестве значения этого атрибута следует указывать ключ поля
cost, total_cost и т.д.
Внутри чанка-обёртки доступны следующие плейсхолдеры:
  • $rows — список товаров, должен быть расположен внутри блока с атрибутом data-msac-rows
  • $total — массив общих значений, должен быть расположен внутри блока с атрибутом data-msac-totals
Разместите в чанке-обёртке блок с атрибутом data-msac-empty для отображения пустой корзины.
В стилях должен быть реализован класс d-none для скрытия элементов на страницы. Каждый из трёх блоков (data-msac-rows, data-msac-totals, data-msac-empty)
должен отображаться в зависимости от пустоты плейсхолдера $rows. Логика понятна: если товары есть ($rows непустой) показываем data-msac-rows, data-msac-totals
и скрываем data-msac-empty. И наоборот, когда нет товаров в корзине ($rows пустой) скрываем data-msac-rows, data-msac-totals и показываем data-msac-empty.
В комплекте есть примеры всех чанков, кому непонятно словесное описание — смотрите пример.

Вызов сниппета

В шаблонах, где необходимо выводить корзины нужно вызвать сниппет getCarts.
{'!getCarts' | snippet: [
'tpls' => '{
 "maincart": { "wrapper":"@FILE msac/cart.tpl","row":"@FILE msac/cartrow.tpl" },
  "modal": { "wrapper":"@FILE msac/cartmodal.tpl","row":"@FILE msac/cartmodalrow.tpl" }
   }'
]}
Он принимает один параметр tpls — список корзин, которые расположены в данном шаблоне с чанками для каждой.
Данные можно передавать в формате JSON или в иде массива.
В этом же шаблоне ПОСЛЕ вызова необходимо добавить блоки для вывода корзин. Для вызова выше они будут такими
<div class="offcanvas-body" data-msac-cart="maincart" data-mspd-cart-wrap>
   {'maincart' | placeholder}
</div>

<div class="modal-body" data-msac-cart="modal" data-mspd-cart-wrap>
   {'modal' | placeholder}
</div>
Базовая настройка на этом закончена.

Быстрый переход к оформлению заказа

Если вы хотите, чтобы при добавлении в корзину всплывало окно с предложением оформить заказ, и при этом сайт использует Bootstrap можно использовать такой код
document.addEventListener('DOMContentLoaded', e => {
        const modalCartEl = document.getElementById('modalCart')
        if (modalCartEl) {
            const modalCart = new bootstrap.Modal(modalCartEl, {})
            modalCartEl.addEventListener('hide.bs.modal', () => {
                const rows = modalCartEl.querySelector(MsAltCart.config.rowsSelector);
                rows && (rows.innerHTML = '');
            })
            document.addEventListener('si:send:after', (e) => {
                    if(['add', 'change'].includes(e.detail.result.data.action)){
                        const rows = modalCartEl.querySelector(MsAltCart.config.rowsSelector);
                        rows && (rows.innerHTML = '');
                    }                   
            })
            document.addEventListener('msac:row:add:after', e => {
                if (e.detail.cartName === 'modal') {
                    modalCart.show();
                }
            })
            document.addEventListener('msac:row:remove:before', e => {
                if (e.detail.cartName === 'modal') {
                    modalCart.hide();
                }
            })
        }
    })

JS API для управления корзиной

Добавить товар
MsAltCart.add(30, 2, {
  color: 'Оранжевый',
})
Удалить товар
MsAltCart.remove('msb1906def283e69f84e5700559f51582c')
Изменить товар
MsAltCart.change('msa6943f19b712772e2103beb283d615b4', 4, {
  color: 'Оранжевый',
})
Очистить корзину
MsAltCart.clean()
Получить статус и состав корзины
const promise = MsAltCart.status();
promise.then((result) => console.log(result));
Посмотреть как работает можно тут

Компонент доступен на modstore

Спасибо за внимание!
Артур Шевченко
25 декабря 2023, 20:16
modx.pro
3
932
+8
Поблагодарить автора Отправить деньги

Комментарии: 10

Евгений Webinmd
26 декабря 2023, 12:14
0
1) Количество товара в корзине не обновляется без перезагрузки
2) если сменить в сайдбаре опцию, то количество уже изменить нельзя
    Артур Шевченко
    26 декабря 2023, 14:49
    0
    Похоже ты там был, когда я как раз кое-что дорабатывал. Посмотри теперь.
    Александр
    28 декабря 2023, 11:54
    0
    Быстрая поддержка и отличный плагин!
      Ivan K.
      25 июня 2024, 01:53
      0
      Странный компонент, завести не смог)) купил установил и удалил))
        Артур Шевченко
        25 июня 2024, 10:08
        0
        Согласен, мои компоненты не для всех, они для тех, кто понимает, что делает.
          Ivan K.
          25 июня 2024, 10:19
          0
          С утра на свежею голову, вроде завел )). Все таки есть в документации не точности.
          Ivan K.
          27 июня 2024, 01:48
          0
          Видимо не дорос я до ваших компонентов))

          Не работает у меня мой самописный плагин, который реагирует на событие msOnAddToCart и берет цену из другого поля, не price, в зависимости от контекста. С обычной корзиной, работает, кстати.
            Артур Шевченко
            27 июня 2024, 09:10
            0
            Код плагина хотя бы показал.
              Ivan K.
              27 июня 2024, 09:13
              0
              <?php
              
              switch ($modx->event->name) {
                  case 'msOnAddToCart':
                      // Проверка на контекст
                      if ($modx->context->key !== 'web') {
                          // Получаем текущие товары в корзине
                          $tmp = $cart->get();
              
                          // Получаем id добавленного продукта
                          foreach ($tmp as $key => $item) {
                              $productId = $item['id'];
                              $product = $modx->getObject('msProduct', $productId);
                              if ($product) {
                                  // Получаем цену для монобренда "my_price_mono"
                                  $newPrice = $product->get('my_price_mono');
              
                                  // Проверяем, что цена задана и больше нуля
                                  if (!empty($newPrice) && $newPrice > 0) {
                                  // Устанавливаем новую цену
                                      $tmp[$key]['price'] = $newPrice;
                                  } else {
                                      // Логгирование ошибки для пустой или некорректной цены
                                      $modx->log(modX::LOG_LEVEL_ERROR, 'Invalid or empty my_price_mono for product ID ' . $product->get('id'));
                                  }
                              }
                          }
              
                          // Сохраняем измененную корзину
                          $cart->set($tmp);
                      }
                      break;
              }
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        10