Изменение количества товара в корзине после добавления с помощью ZoomX и Alpine.js
На многих интернет-магазинах при добавлении товара в корзину появляются кнопки +−, меняющие количество товара. При попытке сделать подобное, используя api miniShop2, пришлось городить костыли, плюс работало это с багами. Поэтому для этой цели пришлось запилить собственное api.
На бэкенде мы создадим метод, который добавляет товар, если его нет в корзине, изменяет его количество, удаляет, если передано количество 0. На фронтенде создадим метод для его вызова.
В статье используются ZoomX и Alpine.js, но можно обойтись и без них. Вместо контроллера можно создать php файл, вместо Smarty-модификатора — обычный сниппет, вместо Alpine.js можно использовать ванильный js или jQuery. Для оповещений я использовал Toastify.
Теперь код.
Для начала добавим роут и создадим контроллер.
Про роуты и контроллеры на сайте уже была статья modx.pro/howto/22727, повторяться, думаю, не стоит.
В core/config/routes.php добавляем:
Контроллеры у меня расположены в core/elements/controllers. Чтобы контроллеры «подцепились» из этой папки, в core/config/elements.php добавляем:
Создаём контроллер. Файл core/elements/controllers/Ms.php:
Создаём Smarty-модификатор ms_in_cart, который будет получать кол-во товара в корзине. Модификатор — это разновидность плагина Smarty. Плагины Smarty у меня расположены в core/elements/smarty, путь можно прописать в системной настройке zoomx_smarty_custom_plugin_dir. Подробнее про плагины Smarty: modzone.ru/blog/2020/12/19/zoomx-creating-smarty-plugins/.
Файл core/elements/smarty/modifier.ms_in_cart.php:
В карточке товара поменяем стандартную форму добавления, добавим класс js-ms-item и is-added, если товар добавлен в корзину. Пример карточки:
Здесь используется Alpine.js. Подробнее про этот фреймворк можно почитать тут: habr.com/ru/post/501312/. С его помощью мы меняем qty (количество) при нажатии на кнопки, а при изменении qty вызываем метод Ms.cart.set(), который добавляет товар в корзину, меняет его количество или удаляет. Также добавляем класс is-loading, его можно использовать для отображения состояния загрузки.
Через css скроем кнопку «в корзину» или кнопки +− в зависимости от того, добавлен товар в корзину или нет:
Создаём js-файл, в котором добавляем наше api, а также добавляем или удаляем класс is-added у карточки товара.
Подключим нужные библиотеки и всё, готово.
Советую скачать библиотеки и подключить локально, так как cdn вполне могут забанить.
На бэкенде мы создадим метод, который добавляет товар, если его нет в корзине, изменяет его количество, удаляет, если передано количество 0. На фронтенде создадим метод для его вызова.
В статье используются ZoomX и Alpine.js, но можно обойтись и без них. Вместо контроллера можно создать php файл, вместо Smarty-модификатора — обычный сниппет, вместо Alpine.js можно использовать ванильный js или jQuery. Для оповещений я использовал Toastify.
Теперь код.
Для начала добавим роут и создадим контроллер.
Про роуты и контроллеры на сайте уже была статья modx.pro/howto/22727, повторяться, думаю, не стоит.
В core/config/routes.php добавляем:
$router->get('api/ms/cart/set', ['\Site\Controllers\Ms', 'cart_set']);
Контроллеры у меня расположены в core/elements/controllers. Чтобы контроллеры «подцепились» из этой папки, в core/config/elements.php добавляем:
zoomx()->getLoader()->addPsr4('Site\\Controllers\\', MODX_CORE_PATH . 'elements/controllers/');
Создаём контроллер. Файл core/elements/controllers/Ms.php:
<?php
namespace Site\Controllers;
use Zoomx\Controllers;
class Ms extends \Zoomx\Controllers\Controller
{
public function cart_set()
{
zoomx()->autoloadResource(false);
$id = $_GET['id'] ?? 0;
$count = $_GET['count'] ?? 1;
$ms2 = $this->modx->getService('minishop2');
$ms2->initialize($this->modx->context->key ?? 'web');
$cart = $ms2->cart->get();
// получаем ключ товара
$key = false;
foreach ($cart as $item) {
if ($item['id'] == $id) {
$key = $item['key'];
break;
}
}
// определяем действие
$action = 'add';
if ($key && $count) {
$action = 'change';
}
if ($key && $count == 0) {
$action = 'remove';
}
// добавляем или меняем кол-во
if ($action == 'add') {
$response = $ms2->cart->add($id, $count);
} else {
$response = $ms2->cart->change($key, $count);
}
$response['action'] = $action;
$response['id'] = $id;
return jsonx($response);
}
}
Создаём Smarty-модификатор ms_in_cart, который будет получать кол-во товара в корзине. Модификатор — это разновидность плагина Smarty. Плагины Smarty у меня расположены в core/elements/smarty, путь можно прописать в системной настройке zoomx_smarty_custom_plugin_dir. Подробнее про плагины Smarty: modzone.ru/blog/2020/12/19/zoomx-creating-smarty-plugins/.
Файл core/elements/smarty/modifier.ms_in_cart.php:
<?php
/*
* Smarty plugin
* -------------------------------------------------------------
* Файл: modifier.ms_in_cart.php
* Тип: modifier
* Имя: ms_in_cart
* Назначение: получение кол-ва товара в корзине по id
* -------------------------------------------------------------
*/
function smarty_modifier_ms_in_cart($id)
{
global $modx;
$ms2 = $modx->getService('minishop2');
$ms2->initialize('web');
$cart = $ms2->cart->get();
$count = 0;
foreach ($cart as $item) {
if ($item['id'] == $id) {
$count = $item['count'];
break;
}
}
return $count;
}
В карточке товара поменяем стандартную форму добавления, добавим класс js-ms-item и is-added, если товар добавлен в корзину. Пример карточки:
{$in_cart = $item.id|ms_in_cart}
<div class="prod-card {($in_cart) ? 'is-added' : ''} js-ms-item" data-id="{$item.id}">
<div class="prod-card-footer"
x-data="{ qty: {$in_cart} }"
x-init="$watch('qty', () => { $el.classList.add('is-loading'); Ms.cart.set({$item.id}, qty); })">
<button class="prod-card-add button" @click="qty++"><span>В корзину</span></button>
<div class="prod-card-qty">
<button @click="qty && qty--">−</button>
<span x-text="qty"></span>
<button @click="qty++">+</button>
</div>
</div>
</div>
Здесь используется Alpine.js. Подробнее про этот фреймворк можно почитать тут: habr.com/ru/post/501312/. С его помощью мы меняем qty (количество) при нажатии на кнопки, а при изменении qty вызываем метод Ms.cart.set(), который добавляет товар в корзину, меняет его количество или удаляет. Также добавляем класс is-loading, его можно использовать для отображения состояния загрузки.
Через css скроем кнопку «в корзину» или кнопки +− в зависимости от того, добавлен товар в корзину или нет:
.prod-card.is-added .prod-card-add,
.prod-card:not(.is-added) .prod-card-qty {
display: none;
}
Создаём js-файл, в котором добавляем наше api, а также добавляем или удаляем класс is-added у карточки товара.
const Ms = {
cart: {
set(id, count = 1) {
fetch(`/api/ms/cart/set?id=${id}&count=${count}`)
.then(response => response.json())
.then(response => {
if (response.data.message) {
Ms.message(response.data.message, response.data.success);
}
for (let i of Ms.cart.classes) {
for (let el of document.querySelectorAll(i.class)) {
el.innerHTML = response.data.data[i.key];
}
}
document.dispatchEvent(new CustomEvent('ms:cart-set', { detail: response }));
});
},
classes: [
{ class: '.ms2_total_weight', key: 'total_weight' },
{ class: '.ms2_total_count', key: 'total_count' },
{ class: '.ms2_total_cost', key: 'total_cost' },
{ class: '.ms2_total_discount', key: 'total_discount' },
]
},
message(message, success) {
const theme = success ? 'is-success' : 'is-danger';
Toastify({
text: message,
gravity: 'bottom',
position: 'center',
className: `message ${theme}`,
duration: 3000,
}).showToast();
}
}
/** добавляем или убираем .is-added у карточки товара, убираем .is-loading */
document.addEventListener('ms:cart-set', (e) => {
const item = document.querySelector(`.js-ms-item[data-id="${e.detail.data.id}"]`);
if (item) {
if (e.detail.data.action == 'remove') {
item.classList.remove('is-added');
} else {
item.classList.add('is-added');
}
item.querySelector('.is-loading').classList.remove('is-loading');
}
});
Подключим нужные библиотеки и всё, готово.
Советую скачать библиотеки и подключить локально, так как cdn вполне могут забанить.
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
<script src="//cdn.jsdelivr.net/npm/toastify-js"></script>
<script src="//unpkg.com/alpinejs" defer></script>
Комментарии: 3
Это будет работать только если в корзине товары без опций.
Это да. С опциями не представляю вообще такой функционал.
Подскажите, в minishop 4 не планируете подобное реализовать?
Подскажите, в minishop 4 не планируете подобное реализовать?
Будет, в этом году уже будет. Щас я с коммерческими проектами разберусь и сделаю. Ещё и изменение опций в корзине будет.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.