Изменяем форму заказа minishop2
За год работы с минишопом я понял одну вещь — никто не знает как просто и быстро кастомизировать стандартную форму заказов. Для меня это довольно частая задача, по этому соберу тут несколько приемов которыми сам пользуюсь:
Это очень частая просьба и даже есть метод который гуглится, но этот метод завязан на изменении исходников minishop2, я же покажу как сделать без изменения исходников, да еще и с кнопкой которая пересохранит заказ
Это самый примитивный способ сохранения, если кому не лень поковыряться в исходниках и сказать как запустить updateOrder — скажите, мне — лень
Теперь нам нужен плагин, который будет реагировать на изменение заказа и пересчитывать общую цену, создаем его:
По пути assets/components/customField/ создаем файл action.php, в него помещаем вот этот код:
В наш default.js помщаем следующий код:
Это самый объемный пункт будет и его я уже описывал в статье про «ГдеПосылка» но там все же было в другом контексте, по этому повторюсь и тут. Поля будут полностью нативными и редактируемыми
Таким образом (по последнему примеру) мы видим что можно очень гибко управлять полями (к примеру если покупатель не юр лицо, то блоки с юр данными не отобразятся). Я надеюсь что эта статья приоткроет тайну extjs и ms2 для многих новичков и мы навсегда закроем вопросы о том как изменять/добавлять форму заказов minishop2. А я тем временем готовлю дополнение, которое позволит управлять полями заказа minishop2 с помощью графического интерфейса :)
Подготовка
- Создаем плагин с любым названием и помещаем в него код:
Выставляем ему событие msOnManagerCustomCssJsswitch ($modx->event->name) { case 'msOnManagerCustomCssJs': if ($page != 'orders') return; $modx->controller->addLastJavascript(MODX_ASSETS_URL.'components/customField/default.js'); break; }
- По пути /assets/components/customField/ создаем файл default.js в нем то мы и будем работать
1. Редактируемая цена доставки
Это очень частая просьба и даже есть метод который гуглится, но этот метод завязан на изменении исходников minishop2, я же покажу как сделать без изменения исходников, да еще и с кнопкой которая пересохранит заказ
Ext.ComponentMgr.onAvailable('minishop2-window-order-update', function(){
this.fields.items[0].items[2].items[1].items[1].xtype = 'textfield'; //меняем тип поля с diaplayfield на textfield
//добавляем кнопку "сохранить"
this.fields.items[0].items[2].items[1].items.push({
xtype: 'button',
name: 'no_rec',
fieldLabel: '',
anchor: '100%',
text: '<i class="icon icon-refresh"></i> Сохранить',
handler: function() {
var url = window.location.href; //сохраняем текущий урл
Ext.getCmp('minishop2-window-order-update').submit(); //сохраняем текущий заказ
setTimeout(function() {
window.location.href = url; //перезагружаем страницу по предыдущему урлу что откроет перед нами это же окно заказа
}, 1000)
}
});
});
Также если мы просто нажмем на кнопку сохранить в самом низу — все сохранится, кнопка под доставкой нужна для удобства и вы можете ее не создавать.Это самый примитивный способ сохранения, если кому не лень поковыряться в исходниках и сказать как запустить updateOrder — скажите, мне — лень
Теперь нам нужен плагин, который будет реагировать на изменение заказа и пересчитывать общую цену, создаем его:
switch ($modx->event->name) {
case 'msOnBeforeUpdateOrder': //событие
$old = $modx->getObject('msOrder', $id); //старый заказ
$oldDeliveryCost = $old->get('delivery_cost'); //старая цена доставки
$newDeliveryCost = $object->get('delivery_cost'); //новая цена доставки
if ($oldDeliveryCost != $newDeliveryCost) { //если были изменения - меняем cost
$tmp1 = $newDeliveryCost;
$tmp2 = $object->get('cart_cost');
$object->set('cost', $tmp1+$tmp2);
}
break;
}
Вот собственно и все, едем дальше.2. Добавляем email заказчика в таблицу адреса
Принцип:
Т.к. в форму заказа не приходит email пользователя, а только его id — мы будем слать ajax запрос на наш контроллер и получать email посредством api modx.По пути assets/components/customField/ создаем файл action.php, в него помещаем вот этот код:
<?php
define('MODX_API_MODE', true);
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/index.php';
$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_ERROR);
$modx->setLogTarget('FILE');
if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {
$modx->sendRedirect($modx->makeUrl($modx->getOption('site_start'),'','','full')); //отфильтровываем все не ajax запросы
}
if (!$modx->hasPermission('msorder_view')) { //проверяем имеет ли текущий пользователь права на просмотр заказа
die('Доступ запрещен!');
}
$id = (int) $_POST['id'];
$user = $modx->getObject('modUser', $id);
if (!$user) {
die('ошибка');
}
die($user->Profile->get('email'));
В наш default.js помщаем следующий код:
Ext.ComponentMgr.onAvailable('minishop2-window-order-update', function(){
var user_id = this.record.user_id; //получаем пользователя текущего заказа
changeFields(this, user_id);
function changeFields (self) {
self.fields.items[2].items[1].items[1].columnWidth = 0.3; //меняем размеры колонок
self.fields.items[2].items[1].items[0].columnWidth = 0.3;
self.fields.items[2].items[1].items.push({ //добавляем новый input diplayfield
columnWidth: 0.3,
border: false,
layout: 'form',
items: [
{
anchor: '100%',
fieldLabel: 'Email',
name: 'non_rec',
xtype: 'displayfield',
html: 'Еще не загружено',
id: 'emailFieldAjax' //создаем alias для того, чтобы в дальнейшем получить объект через getCmp
}
]
});
Ext.Ajax.request({ //делаем ajax запрос на наш контроллер
url: '/assets/components/customField/action.php',
success: function(resp) {
Ext.getCmp('emailFieldAjax').setValue(resp.responseText); //получаем то, что вернул наш контроллер
},
failure: function(resp) {
Ext.Msg.alert('Внимание', 'Ошибка ajax запроса');
},
params: { id: user_id }
});
}
});
3. Кастомизируем вкладку адреса по полной
Это самый объемный пункт будет и его я уже описывал в статье про «ГдеПосылка» но там все же было в другом контексте, по этому повторюсь и тут. Поля будут полностью нативными и редактируемыми
База данных
Создаем нужные нам поля в базе данных, для этого открываем дополнение Console и вставляем туда этот код:$fields = array('inn', 'fio_two', 'tel_two', 'ogrn'); //массив с именами нужных нам полей
foreach ($fields as $item) {
$table = $modx->getTableName('msOrderAddress'); //название класса в таблицу которого будем записывать
$sql = 'ALTER TABLE ' . $table . ' ADD `'.$item.'` VARCHAR(255) NULL;';
$modx->exec($sql);
}
Теперь нам надо сделать так, чтобы miniShop2 подхватил наши поля, для этого нам надо расширить его карту, для расширения карт у minishop2 есть собственный функционал плагинов, по пути core/components/orderCustomField/ создаем index.php со следующим содержимым:<?php
return array(
'map' => array(
'msOrderAddress' => require_once 'msOrderAddress.inc.php',
),
);
В этой же папке создаем msOrderAddress.inc.php который описывает сущности наших полей. Внимательно посмотрите на него, если вы видите массивы не в первый раз, то думаю как переделать его под себя для вас будет очевидно :)<?php
return array(
'fields' => array (
'inn' => NULL,
'fio_two' => NULL,
'tel_two' => NULL,
'ogrn' => NULL,
),
'fieldMeta' => array(
'inn' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
),
'fio_two' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
),
'tel_two' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
),
'ogrn' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
),
),
);
Далее открываем опять консоль и подключаем наш index.php в плагины минишопа:if ($miniShop2 = $modx->getService('miniShop2')) {
$miniShop2->addPlugin('customField', '{core_path}components/orderCustomField/index.php');
}
По технической части все, на данном этапе ваши поля полностью функционируют и вы можете получить/установить/обновить/сохранить их через MODX API. Для того, чтобы на странице оформления заказа у вас они заработали просто создайте инпут с name того поля, который добавляли в бд :<inpu type="text" name="fieldName" placeholder="fieldName" />
Добавляем на вкладку адреса:
На самом деле абсолютно не важно на какую вкладку вы добавляете поля, добавленные в таблицу msOrderAddress, самое важное чтобы их name имел префикс addr_ (в противном случае сохраняться они не будут) процедура добавления ничем не отличается от пункта 2, единственное что если вы хотите чтобы поле было изменяемым — используйте xtype: 'textfield', если информативным xtype: 'displayfield', я приведу пример разметки из самого первого скриншота, а вы там думаю уже сами разберетесь что к чему:Ext.ComponentMgr.onAvailable('minishop2-window-order-update', function(){
var face = { //разметка типа покупателя (юр/физ/ип)
border: false,
layout: 'column',
items: [
{
border: false,
columnWidth: 0.5,
autoHeight: true,
layout: 'form',
items: {
xtype: 'displayfield',
name: 'types',
fieldLabel: 'Лицо',
anchor: '100%'
}
},
{
border: false,
columnWidth: 0.5,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_gift',
fieldLabel: 'Подарок',
anchor: '100%'
}
}
],
autoHeight: true,
}
var last_adress = { //разметка второго адреса
border: false,
layout: 'column',
autoHeight: true,
items: [
{
border: true,
columnWidth: 1,
autoHeight: true,
layout: 'form',
items: {
xtype: 'displayfield',
name: '',
fieldLabel: 'Другой получатель',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.4,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_receiver_last',
fieldLabel: 'Получатель',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_phone_last',
fieldLabel: 'Телефон',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_email_last',
fieldLabel: 'Email',
anchor: '100%'
}
}
],
}
var ur_data = { //разметка юр. данных
border: false,
layout: 'column',
autoHeight: true,
items: [
{
border: true,
columnWidth: 1,
autoHeight: true,
layout: 'form',
items: {
xtype: 'displayfield',
name: '',
fieldLabel: 'Юридические данные',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.5,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_name',
fieldLabel: 'Название организации',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.5,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_adress',
fieldLabel: 'Адрес организации',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.4,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_inn',
fieldLabel: 'ИНН',
anchor: '100%'
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_kpp',
fieldLabel: 'КПП',
anchor: '100%'
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_ogrnip',
fieldLabel: 'ОГРНИП',
anchor: '100%'
}
}
],
}
var rs_data = { //разметка расчетного счета
border: false,
layout: 'column',
autoHeight: true,
items: [
{
border: true,
columnWidth: 1,
autoHeight: true,
layout: 'form',
items: {
xtype: 'displayfield',
name: '',
fieldLabel: 'Расчетный счет',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.4,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_bank',
fieldLabel: 'Банк',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_bik',
fieldLabel: 'БИК',
anchor: '100%',
}
}, {
border: false,
columnWidth: 0.3,
autoHeight: true,
layout: 'form',
items: {
xtype: 'textfield',
name: 'addr_ur_rs',
fieldLabel: 'Расчетный счет',
anchor: '100%'
}
}
],
}
//если заказчик не физ лицо, добавляем разметку юр данных в начало списка
if (this.record.types != 'Физ.лицо') {
this.fields.items[2].items.unshift(rs_data);
this.fields.items[2].items.unshift(ur_data);
}
//если присутствует хоть одно заполненное поле второго адреса - добавляем его в разметку
if (!this.record.addr_email_last && !this.record.addr_phone_last && !this.record.addr_receiver_last) {
} else {
this.fields.items[2].items.unshift(last_adress);
}
this.fields.items[2].items.unshift(face);
});
Заключение
Таким образом (по последнему примеру) мы видим что можно очень гибко управлять полями (к примеру если покупатель не юр лицо, то блоки с юр данными не отобразятся). Я надеюсь что эта статья приоткроет тайну extjs и ms2 для многих новичков и мы навсегда закроем вопросы о том как изменять/добавлять форму заказов minishop2. А я тем временем готовлю дополнение, которое позволит управлять полями заказа minishop2 с помощью графического интерфейса :)
Поблагодарить автора
Отправить деньги
Комментарии: 18
P.s. всегда когда подходите к расширеню разметки extjs но не знаете куда добавить те или иные поля в какую — то вкладку, просто распечатайте объект field и уже в консоле разбирайтесь что куда
console.log(this.fields);
Там достаточно все очевидно, для того, чтобы понять человеку который только-только начал постигать js
Отличная заметка!
Было бы неплохо отформатировать её в markdown и прислать в docs.modx.pro
Было бы неплохо отформатировать её в markdown и прислать в docs.modx.pro
Отличная заметка! Спасибо!
Сам как то мучился с extJs, а тут все отлично описано
Сам как то мучился с extJs, а тут все отлично описано
Добавлял поля по инструкции, наткнулся на ошибку, сохранялось только последнее поле, оказалось что приведенная структура массива для файла msOrderAddress.inc.php не совсем верна:
Вот так все работает:
Вот так все работает:
return array(
'fields' => array(
'field_name_1' => NULL,
'field_name_2' => NULL,
),
'fieldMeta' => array(
'field_name_1' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
),
'field_name_2' => array (
'dbtype' => 'varchar',
'precision' => '255',
'phptype' => 'string',
'null' => true,
)
)
);
Если вас не затруднит, напишите пожалуйста в чем отличие вашей структуры от моей:)
А, точно, увидел ошибку, извиняюсь, сейчас поправлю
Павел, спасибо!
Всегда пожалуйста! :)
Павел, Вы писали, что создадите компонент для управления полями заказа с графическим интерфейсом. Когда приблизительно ожидать его выход?
К сожалению сейчас нет не временных возможностей на создание компонентов, если вы хотите создать подобный компонент — создавайте, я не против) Если же вы как покупатель будущий интересуетесь, увы — не скоро
Спасибо за ответ! Я, к сожалению, как разработчик компонентов, специалист никакой. Мне еще учиться годами. А так мне просто понадобилось добавить несколько дополнительных полей в форму заказа, а внизу инструкции я увидел, что у Вас в планах сделать компонент, поэтому я и интересуюсь. Он бы сейчас значительно ускорил и упростил работу. Но для самообразования лучше, конечно, по Вашей инструкции делать.
Добрый день,@Pavel Zarubin
Ps
Сорри, разобрался, пропустил этап подготовки в заметке
В наш default.js помщаем следующий код:какой именно файл имеется ввиду, не подскажете?
Ps
Сорри, разобрался, пропустил этап подготовки в заметке
Отличная заметка, спасибо что делитесь опытом.
Огромное спасибо за статью. Очень помогли.
От себя добавлю, что во избежание некрасивых отступов слева у полей, ширина которых равна половине или трети строки, нужно каждую строку с инпутами добавлять отдельным лэйаутом. Возможно есть более изящное решение.
От себя добавлю, что во избежание некрасивых отступов слева у полей, ширина которых равна половине или трети строки, нужно каждую строку с инпутами добавлять отдельным лэйаутом. Возможно есть более изящное решение.
Супер спасибо. Классная статья.
Павел, по доставке бьется ошибка по этой строке is.gd/D9303C
ms2 2.5.0
ms2 2.5.0
За 2 пункт больше спасибо!
Быстро, просто и удобно
Быстро, просто и удобно
Вопросы по п.3:
1. Как сделать дополнительные поля из п.3 обязательными для заполнения на стадии оформления заказа?
2. При добавления данного плагина в MiniShop2 в консоль массово сыпятся ошибки ((ERROR @ /***/core/model/modx/modx.class.php: ****) [OnMODXInit]), но доп. поля работают и в заказе и админ. панели.
1. Как сделать дополнительные поля из п.3 обязательными для заполнения на стадии оформления заказа?
2. При добавления данного плагина в MiniShop2 в консоль массово сыпятся ошибки ((ERROR @ /***/core/model/modx/modx.class.php: ****) [OnMODXInit]), но доп. поля работают и в заказе и админ. панели.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.