[СДЕЛАЙ САМ] AjaxFormItLogin и MiniShop2 - заказ в 1 клик - быстро, просто и бесплатно.
Приветствую, решил зафиксировать для себя и поделится с сообществом, прежде всего с новичками, актуальным способом быстро и без боли добавить в интернет-магазин на базе MiniShop2 функцию «Заказать в 1 клик.»
Функционирование модальных окон обеспечивает Bootstrap. Уточняю этот момент, потому в JS, который будет представлен далее, используются методы открытия и закрытия модального окна, и если вы используете самописные модальные окна или фреймворки отличные от Bootstrap эти методы нужно будет заменить на аналогичные из ваших библиотек.
Инструкция
1. Скачиваем и устанавливаем из репозитория modstore компоненты AjaxFormItLogin и MiniShop2, если ещё этого не сделали.
2. Везде где нужно иметь возможность быстрого заказа, добавляем кнопку
3. Создаем модальное окно с вызовом сниппета AjaxFormItLogin, в котором укажем чанк формы, хук создающий заказ, правила валидации и текст уведомления о наличие ошибок валидации.
4. Создаем чанк с формой chunks/forms/oneclickorder.tpl
5. Создаём хук, или, другими словами, сниппет, oneClickHook со следующим кодом:
6. Напишем JS, который обработает ответ сервера: закроет модалку, покажет уведомление, сделает переадресацию на оплату, если нужно. Так же этот скрипт заполнит поле с ID товара в нашей форме при открытии модального окна.
Я потратил 30 минут на реализацию этого функционала, вы же теперь успеете не только всё сделать, но и чайку попить)))
Функционирование модальных окон обеспечивает Bootstrap. Уточняю этот момент, потому в JS, который будет представлен далее, используются методы открытия и закрытия модального окна, и если вы используете самописные модальные окна или фреймворки отличные от Bootstrap эти методы нужно будет заменить на аналогичные из ваших библиотек.
Инструкция
1. Скачиваем и устанавливаем из репозитория modstore компоненты AjaxFormItLogin и MiniShop2, если ещё этого не сделали.
2. Везде где нужно иметь возможность быстрого заказа, добавляем кнопку
<button class="btn btn-warning" data-product-id="{$id}" type="button" data-bs-toggle="modal" data-bs-target="#oneClickModal"> Купить в один клик </button>
У кнопки обязательно должен быть атрибут data-product-id со значение ID добавляемого товара.3. Создаем модальное окно с вызовом сниппета AjaxFormItLogin, в котором укажем чанк формы, хук создающий заказ, правила валидации и текст уведомления о наличие ошибок валидации.
<div class="modal fade" id="oneClickModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Купить в один клик</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{'!AjaxFormItLogin' | snippet: [
'form' => '@FILE chunks/forms/oneclickorder.tpl',
'hooks' => 'oneClickHook',
'validate' => 'receiver:required,email:email:required,id:required',
'validationErrorMessage' => 'Исправьте ошибки!'
]}
</div>
</div>
</div>
</div>
Отмечу, что AjaxFormItLogin в данном случае нужен только для валидации полей и отправки формы, при желании можно написать свою реализацию этих функций или использовать другое решение.4. Создаем чанк с формой chunks/forms/oneclickorder.tpl
<form>
<input type="hidden" name="delivery" value="1">
<input type="hidden" name="payment" value="1">
<input type="hidden" name="options[]" value="">
<input type="hidden" name="id" value="">
<div class="mb-3">
<label class="form-label">Имя получателя</label>
<input type="text" class="form-control" name="receiver">
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" name="email">
</div>
<div class="mb-3">
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" value="1" min="1">
</div>
<button type="submit" class="btn btn-primary">Заказать</button>
</form>
В форме обязательно должны быть поля с именами delivery, payment и id. Поскольку мы делаем форму быстрого заказа выбор способов оплаты и доставки я не предоставляю, однако, если такая необходимость есть в вашем проекте, вы можете предоставить пользователю выбор. ПРи этом имейте в виду, если для различных способов доставки доступны различные способы оплаты, логику их взаимодействия писать придётся самостоятельно, так как скрипты MiniShop2, отвечающие за это, тут не подключаются.5. Создаём хук, или, другими словами, сниппет, oneClickHook со следующим кодом:
<?php
$values = $hook->getValues();
$ms2 = $modx->getService('minishop2');
$ms2->initialize('web');
$ms2->cart->clean();
$count = (float)$values['count'] ?: 1;
$ms2->cart->add((int)$values['id'], $count, $values['options']); // добавляем товар в корзину
$ms2->order->config['json_response'] = true; // просим вернуть нам json
// добавляем поля в заказ
$ms2->order->add('receiver', $values['receiver']);
$ms2->order->add('email', $values['email']);
$ms2->order->add('delivery', $values['delivery']);
$ms2->order->add('payment', $values['payment']);
$ms2->order->submit(); // отправляем заказ
Обратите внимание ни какой код после отправки заказа выполнен не будет, т.к. мы попросили MiniShop2 вернуть нам json, он сделает всё, что должен, и принудительно завершит работу. При этом все встроенные механизмы этого модуля отработают, а именно: будут отправлены письма клиенту и менеджеру, если вы не отключили эту функцию, будет сформирована ссылка на оплату.6. Напишем JS, который обработает ответ сервера: закроет модалку, покажет уведомление, сделает переадресацию на оплату, если нужно. Так же этот скрипт заполнит поле с ID товара в нашей форме при открытии модального окна.
document.addEventListener('DOMContentLoaded', () => {
const oneCLickModal = document.getElementById('oneClickModal')
oneCLickModal.addEventListener('show.bs.modal', function (e) {
oncLickModal.querySelector('[name="id"]').value = e.relatedTarget.dataset.productId;
});
document.addEventListener('afl_complete', (e) => {
if(e.detail.response.success){
const modal = bootstrap.Modal.getInstance(oneCLickModal);
modal.hide();
if(e.detail.response.data.redirect){
e.detail.response.message = 'Заказ успешно создан. Осталось только оплатить.';
setTimeout(() =>{
window.location.href = e.detail.response.data.redirect
}, 3000);
}else{
e.detail.response.message = 'Заказ успешно создан';
}
}
});
});
7. Укажем в системной настройке ms2_email_manager куда отправлять уведомления о новом заказе и всё готово.Я потратил 30 минут на реализацию этого функционала, вы же теперь успеете не только всё сделать, но и чайку попить)))
Поблагодарить автора
Отправить деньги
Комментарии: 44
Все сделал по инструкции, так же в pdotools_fenom_parser и установил значение ДА
Но в модальном окне выдает следующее:
Но в модальном окне выдает следующее:
Array ( [corePath] => /home/p/paldrivepm/public_html/core/components/ajaxformitlogin/ [assetsUrl] => /assets/components/ajaxformitlogin/ [actionUrl] => /assets/components/ajaxformitlogin/action.php [formSelector] => afl_form_1051631402 [json_response] => 1 [fileUplodedProgressMsg] => Загружено: [fileUplodedSuccessMsg] => Данные полностью загружены на сервер! [fileUplodedErrorMsg] => Произошла ошибка при загрузке данных на сервер! [ajaxErrorMsg] => Форма не была отправлена! Свяжитесь с администратором. [notifySettingsPath] => assets/components/ajaxformitlogin/js/message_settings.json [frontend_js] => [[+assetsUrl]]js/default.js [notifyClassPath] => ./aflizitoast.class.js [notifyClassName] => AflIziToast [form] => @FILE chunks/forms/oneclickorder.tpl [snippet] => FormIt [hooks] => oneClickHook [emailTo] => [emailFrom] => [emailSubject] => [emailTpl] => aflExampleEmail [successMessage] => Форма успешно отправлена! Менеджер свяжется с Вами в течение 5 минут. [clearFieldsOnSuccess] => 1 [transmittedParams] => ["success" => "", "error" => "aliases"] [aliases] => email==Email [validate] => receiver:required,email:email:required,id:required [showUploadProgress] => 1 [spamProtection] => 1 [validationErrorMessage] => Исправьте ошибки! [secret] => hjNKTgZS [secret.vTextContains] => Кажется Вы робот. Если это не так, обновите страницу. [pageId] => 4 [metrics] => [counterId] => )
И не совсем понял, chunks/forms/oneclickorder.tpl нужно в корне сайта создавать?
не обязательно использовать файловый чанк, можешь прописать обычный
Проверьте пути к чанкам. Ну и посмотрите в журнале ошибок.
Все заработало, только подскажите, пожалуйста (перерыл все), КАК мне выводить ID товара в атрибут кнопки — data-product-id="{$id}" (это — {$id} не выводит).
Ну для этого надо конечно документацию изучать, на предмет того, как выводить плейсхолдеры. Тут есть много вариантов, навскидку 4
{$id}
{$_modx->resource.id}
[[+id]]
[[*id]]
{$id}
{$_modx->resource.id}
[[+id]]
[[*id]]
Спасибо огромное.
Для карточки товара помогло — [[*id]]
Для списка с товарами — {$id}
Для карточки товара помогло — [[*id]]
Для списка с товарами — {$id}
Артур, если будет возможность помогите еще с одним вопросом.
Как вывести название товара в модальном окне в карточке (странице) товара — понятно, просто указать [[*pagetitle]] и все.
Подскажите, а как можно вывести название товара в модальное окно в списке (категории) товаров (pagetitle выводит название ресурса категории)?
Как вывести название товара в модальном окне в карточке (странице) товара — понятно, просто указать [[*pagetitle]] и все.
Подскажите, а как можно вывести название товара в модальное окно в списке (категории) товаров (pagetitle выводит название ресурса категории)?
Навскидку так вроде.
Кнопке добавить data-pagetitle
Кнопке добавить data-pagetitle
<button ... data-pagetitle="{$pagetitle | htmlent}" ...>
В js oncLickModal.addEventListener('show.bs.modal', function (e) {
.....
// Добавляем эту строку, ставим pagetitle в .modal-title
oncLickModal.querySelector('.modal-title').innerText = e.relatedTarget.dataset.pagetitle;
});
Добрый день. Только делал недавно.
Это ссылка
Это ссылка
<a href="#modal-order" onclick="getEmpId(this)" data-name="{$pagetitle}" class="pt uk-button uk-button-primary uk-width-1-1" uk-toggle>Запросить наличие</a>
Это jsfunction getEmpId(element) {
var empId = element.dataset.name
document.querySelector("input[name=title]").value = empId
}
Это input <input type="text" id="af_title" name="title" value="" placeholder="" class="uk-input" readonly/>
Спасибо огромное — работает!
На главной выводится список товаров, в том числе параметром optionFilters:
Если убрать optionFilters, все работает.
Можете подсказать куда копать?
[[!msProducts?
&parents=`2`
&depth=`2`
&limit=`6`
&tpl=`tpl.msProducts.row.mainpage`
&optionFilters=`{"sklad:>":0}`
&sortby=`publishedon`
&sortdir=`DESC`
]]
Если на этой странице поместить вывод формы в модальное окно{'!AjaxFormItLogin' | snippet: [
'form' => 'tpl.oneclickorder',
'hooks' => 'oneClickHook',
'validate' => 'receiver:required,email:email:required,phone:tel:required,id:required',
'validationErrorMessage' => 'Заполните обязательные поля!'
]}
то происходит ошибка[2023-02-12 15:56:37] (ERROR @ /home/p/paldrivepm/public_html/core/components/pdotools/model/pdotools/pdotools.class.php : 999) Unexpected token ':' in dab80369dc94a301e23cc1a737f03eac line 196, near '{"sklad:>":' <- there
и главная страница неотображаетсяЕсли убрать optionFilters, все работает.
Можете подсказать куда копать?
Конечно, это называется белый экран смерти сайта. Вы включили шаблонизатор Fenom на страницах и он пытает распарсить {«sklad:>»:0}, но не понимает что. Поставьте после { пробел и будет вам счастье.
const modal = bootstrap.Modal.getInstance(oncLickModal);
Что то никак не хочет работать. Здесь нет ошибки? oncLickModal это правильно?
Скорее всего есть, наверное должно быть oneClickModal
Не понятно как тогда отписывались что работает у людей если там ошибка. Или нашли но молчали.
Тем не менее что так что так не хочет работать. Бутстрап должен быть подключен или необязательно?
Обязательно должен быть подключен он отвечает за работу модалки.
И хук кстати тоже не работает.
Жаль что не работает, нужная вещь/ Хотелось вашим дополнением заменить много что было реализовано ранее. Тем самым снести разные там modalertify, ajaxform
И у меня НЕ пашет… ID в модалку не залетает. В кнопке ID подтягивается.
Кстати, модалка в примере под Bootstrap 5. Меняйте классы под себя.
Кстати, модалка в примере под Bootstrap 5. Меняйте классы под себя.
Значит так, опечатка действительно есть, но поскольку опечатка во всём скрипте одинаковая и повторяется, то скрипт работает. Более того перед тем как написать заметку, я проверил работу всего о чём тут написано на реальном сайте. Если у вас что-то не работает полагаю, что вы где-то допустили ошибку.
На работающей форме проверил не работает хук. Заявки не отправляются в заказы.
Все ок заказы отправляет. Нет закрывается окно и после отправки нет уведомления что отправлено.
Подключил 5 бутстрап.
Подключил 5 бутстрап.
Пример реализации автором написан под bootstrap 5! Если 3 или 4, то надо адаптировать кнопку, окно и скрипт под себя. У меня пока затык со скриптом…
Я и подключил 5 бутстрап
В консоли ошибка
Uncaught TypeError: bootstrap.Modal.getInstance is not a function
Uncaught TypeError: bootstrap.Modal.getInstance is not a function
Теперь выводит
Uncaught TypeError: can't access property «hide», modal is null
Uncaught TypeError: can't access property «hide», modal is null
В общем проверил хук на другой форме где все уведомления работали. Форма отправляется заказ летит в заказы, а вот окно что все отправлено не появляется. Наверное хук не дает этого сделать.
Короче говоря методом тыка выяснил что после этого хука никакие другие хуки не срабатывают. Так же и уведомления не показываются после отправки формы.
Хуки после oneClickHook выполнятся не будут потому, что минишоп завершает скрипт методом die(). Я в заметке отметил этот момент
Обратите внимание ни какой код после отправки заказа выполнен не будет, т.к. мы попросили MiniShop2 вернуть нам json, он сделает всё, что должен, и принудительно завершит работу.А вот уведомления должны показываться, если нет ошибок в JS.
Я его последним ставлю все работает только уведомлений нет.
Специально у себя протестил.
Подключил js bootstrap 5, вставил все как в примере.
По итогу заказ с модалки улетает, но модалка не закрывается и нет уведомлений. Но это баг вероятнее всего из-за уже подключенных скриптов bs4. Если Артур поможет адаптировать скрипт под bs4, то можно было бы выложить пример для bootstrap 4. Сам не силен в скриптах.
Подключил js bootstrap 5, вставил все как в примере.
По итогу заказ с модалки улетает, но модалка не закрывается и нет уведомлений. Но это баг вероятнее всего из-за уже подключенных скриптов bs4. Если Артур поможет адаптировать скрипт под bs4, то можно было бы выложить пример для bootstrap 4. Сам не силен в скриптах.
У меня есть форма. Работала без данного хука прекрасно, все окно при ошибке и при успешной отправке появлялись. Когда подключаю хук то при незаполнении формы появляется сообщение что не заполнена, а при успешной отправке она отправляет данные только не появляется уведомление что все отправлено. Так что не думаю что здесь проблема в бутстрап.
Ошибки в консоли браузера есть?
Нет.
Тогда разбираться надо, что не так.
Если не ошибаюсь, то етот код добавит в уже существующе корзину товар, после чего оформляет заказ. Но заказ в 1 клик ето покупка только поточного товара. По етомо после инициализации минишопа я бы еще добавил:
$tmp = $ms2->cart->get();
$ms2->cart->clean();
а после оформления заказа, вернуть назад прежнее содержимое корзини$ms2->cart->set($tmp);
Всё верно, корзину надо очищать перед оформлением, а вот вернуть то состояние которое было не выйдет, так как после выполнения метода submit() никакой код не выполнится.
Я использу вот такой снипет для отладки ответов после submit():
<?php
$formFields = $hook->getValues();
$count = (float) $formFields['count'];
$id = (int) $formFields['id'];
if($id <= 0){
$hook->addError( 'id', "Неправильное ID товара" );
return false;
}
if ($miniShop2 = $modx->getService('miniShop2')) {
$miniShop2->initialize($modx->context->key, array(
'json_response' => true,
'max_count' => 1000,
'allow_deleted' => false,
'allow_unpublished' => false
));
$tmp = $miniShop2->cart->get();
$miniShop2->cart->clean();
$miniShop2->cart->add( $id, $count, $formFields['options']);
$miniShop2->order->add( 'receiver', $formFields['receiver'] );
$miniShop2->order->add( 'email', $formFields['email'] );
$miniShop2->order->add( 'delivery', $formFields['delivery']); // id метода доставки
$miniShop2->order->add( 'payment', $formFields['payment']); // id метода оплаты
if($response = $miniShop2->order->submit()){
if($response['success'] != 1){
$response = json_decode($response, 1);
$hook->addError('receiver', $response['message']);
if(!empty($response['data'])){
foreach($response['data'] as $field){
$hook->addError($field, $response['message']);
}
}
return false;
}
$miniShop2->cart->set($tmp);
return true; // Успешное оформление заказа
}else{
$hook->addError('receiver', 'Ошибка при оформлении заказа');
return false;
}
}else{
$hook->addError("receiver", "Проблема инициализации магазина");
return false;
}
Ну не знаю. Факт в том, что в моём варианте с minishop2 >4.0.0 код после $miniShop2->order->submit() не выполняется.
Почему не выполняется? Должен возвращаться $response, в случае если включен json_response и существует метод оплаты. Я прекрасно реализую ecoomerce методы ПОСЛЕ submit
ХЗ, я думал из-за die(). Проверь на досуге где косяк.
Ну перед die же echo json_encode
Ответ гарантированно должен быть и проверенно работает
Ответ гарантированно должен быть и проверенно работает
Ну вот у меня всё что возвращает submit() сразу на фронт улетает, а весь код в хуке после submit() почему-то не работает. При этом если поменять echo на return, то всё норм. Может я что-то не так делал, может что-то не так в AjaxFormitLogin, я не знаю. Надо ещё раз проверить.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.