Ajaxify Node Basket 7.2: релиз модуля легкого и простого магазина на AJAX для Drupal 7

Таки свершилось. Конечная версия модуля под "семерку", с новым функционалом и возможностями, успешно допилена.

Краткий экскурс в ключевые обновленные возможности

  • Добавление нод в корзину происходит так же, как и в ранних версиях. По крайней мере внешне. Внутренняя же жизнь сего процесса отличается от былого чрезмерно. Вся JavaScript`овая динамика ныне исполняется исключительно через Drupal Ajax API. В свете чего отпала надобность использования модулем своих собственных js-файлов.
  • У блока корзины появился свой темплейт-файл. Дефолтно, "нарисованное" в нем отображает свернутый список отложенного в корзину, где ссылка на "развернуть" информирует так же и о количестве находящихся в корзине товарных позиций. Если у товаров есть цена, то под списком появляется строка с общей суммой заказа. Ну и в самом низу - ссылка на страницу корзины.
  • Заказы. Они теперь сохраняются, как ноды соответствующего типа. Со всеми вытекающими из этого радостями.
  • Уведомления о создании новых заказов ныне умеренно настраиваемы. Помимо администрации сайта письмо о создании заказа отправляется и пользователю, автору заказа. Если данная опция включена в настройках. Отдельный шаблон письма место быть имеет, но как theme-функция, в отдельный файл выводить его посчитал лишним.

Подробное описание настроек и принципов работы модуля

Первоначальные настройки

На странице модулей найти Node Basket можно будет в группе "Коммерция"(или "Node Commerce" в иноземном варианте).

После установки модуля в меню конфигурации сайта появится новый пункт - "Node Basket" (/admin/config/nodebasket).

Ссылка на страницу настроек модул Node Basket

На вкладке основных настроек отмечаем типы нод из всех созданных на сайте. За исключением типа "заказ", естественно.

Далее, включаем/отключаем опции отображения в корзине полей "количество" и "стоимость". При включении отображения стоимости, и наличия в выбранных типах подходящих полей, будет предложено выбрать поле цены и суффикс, добавляемый к значению этого поля.

Модуль Node Basket: настройка полей для типа товар.

После сохранения основных настроек, вас автоматом перекинет на страницу настройки почтовых уведомлений. Где можно выбрать поля заказа, данные которых будут отправляться на адрес электронной почты сайта и пользователя(если включена соответствующая опция). Подробнее о полях заказа будет написано ниже.

Модуль Node Basket: настройка почтовых уведомлений.

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

Модуль Node Basket: настройки доступа для ролей пользователей.

Ну и наконец финальная часть. Включаем отображение блока корзины в нужном регионе текущей темы. На станице настройки блоков сайта, если кто не догадался.

Модуль Node Basket: включение отображения блока с содержимым корзины.

Настройка заказа

Как уже было сказано ранее, заказ у нас теперь есть ни что иное, как рядовая нодо-единица.

Тип материала "Заказ" будет автоматически создан при установке модуля. С двумя, лишь, "вшитыми" экстра-полями с настраиваемым отображением в теле заказа:

  • "E-mail пользователя" - почтовый адрес организма, сотворившего заказ;
  • "Содержимое заказа" - собственно, означает то, что означает;

Страница настроек отображения полей типа материала 'Заказ' модуля Node Basket.

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

Работа модуля. Добавление товаров/нод в корзину

Модуль настроен, права на его использование обозначены, и блок корзины находиться в нужном месте. Что видит пользователь(которому видеть разрешено)?

У типов нод, что нами выбраны, как товары, теперь есть кнопка "Отложить в корзину". По нажатии на оную в блоке с содержимым корзины происходят соответствующие метаморфозы, а текст на кнопке меняется на "В корзине". Все изменения при манипуляции с данными корзины, и при ее редактировании в том числе, происходят без перезагрузки страниц, путем ajax-запросов.

Ajax-корзина с обновленными данными после добавление в нее нового товара.

Редактирование содержимого корзины и оформление заказа

После того, как пользователем набрано в корзину все ему необходимое, он по ссылке в блоке переходит на страницу управления своей корзиной. Тут ему предоставляется возможность подправить ее содержимое(удалить, изменить количество). А если все устраивает, то приступить к завершающей стадии - оформлению заказа. Путем нажатия на кнопку с неожиданной надписью "Оформление заказа".

Страница редактирования содержимого корзины.

На странице оформления пользователь заполняет обязательное "вшитое" поле контактного e-mail`а, и все другие поля, что мы посчитали нужным включить в заказ. И отправляет заполненную форму на сохранение и исполнение.

Страница оформления заказа

В случае успешного сохранения заказа(и правильного заполнения полей) на месте формы появляется торжественное статусное сообщение о сим событии. И письма о новом заказе, с настроенными ранее для него данными, разлетаются по ящикам получателей. При обратном раскладе - пользователь видит сообщение об ошибке.

Сообщение об успешном сохранении нового заказа.

Скачать и примечания

Возможно в будущем написанное выше будет разбавлено новыми деталями или изменениями, если что упустил помянуть.

В планах: более основательный рефакторинг, оптимизация и документирование кода.

ВАЖНО!!!
При обновлении модуля с версии 7.1 прежде всего необходимо отключить и полностью деинсталлировать "старую" версию. Это два совершенно разных модуля.
Dalay

Комментарии

Таблица существует физически?
Что в серверных логах на момент отправки?

Внес важные дополнения в описание.

При выводе кнопки "отложить в корзину" через views всё функционирует, однако выдает ошибку "Strict warning: Declaration of nodebasket_handler_field_add_to_basket::element_type() should be compatible with views_handler_field::element_type($none_supported = false, $default_empty = false, $inline = false) in _registry_check_code() (line 3066 of C:\xampp\htdocs\drupal7\includes\bootstrap.inc)."

Спасибо, в ближайшее время исправлю.

Исправил, обновитесь.

=) Может стоит в случае когда нет цены не показывать кнопку просто или показывать кнопку с предварительного заказа с ссылкой на форму контактов? ...чем мигающая сообщение (которые даже не отключить)....

Это владельцу сайта стоит внимательней относится к корректности данных на своем ресурсе? Функции исправления последствий чьей-либо криворукости в обсуждаемой реализации модуля не будет.

...чем мигающая сообщение (которые даже не отключить)....

Опять, Олег, меня ребусами своими решил поизводить? Никаких "мигающая сообщение" в модуле нет, аднака. Ищи причины в стилях своей темы.

Так сообщения же выводятся и потом сами исчезают - добавили в корзину продукт такой-то. я не успеваю прочитать. Это оч раздражает и откл нельзя и прочитать не успеваешь... Плюс когда нет цены вылезает сообщение сорри там добавить в корзину не могу. при этом кнопка становится в корзине. Поэтому и предлагается чуточку к нормальному виду привести. Нет цены - нет кнопки.

Нет цены -> нет кнопки -> за каким хреном тогда на сайте опубликован этот товар(спрашивает себя покупатель, и гневно посылает в браузер комбинацию CTRL+W). Еще раз и закроем тему: следи за тем, что и как у тебя на сайте.

Время отображения сообщения до его сокрытия несложно увеличить. Но. Текущие параметры сего заданы не от балды, а путем неких личных выводов на практической основе. Претензий к этому никто, окромя уважаемого Олега, более не выказывал. Посему напрашиваются справедливые выводы, что проблема не в модуле, а в Олеге.

Так это каталог товаров или как тока цена меняется или товар заканчивается его удалять и потом заново все заводить. Вполне нормальная штука - нет цены - нет кнопки/или есть кнопка предзаказ. А так функционал хороший. Если все в нем устраивает разработчика - пусть так и будет.

) Зачем удалять, если можно просто снять с публикации.

Это если товар обсуждать не требуется. Да и снятое это больше для админа больше.

Абсурдная идея - комментировать то, чего, по сути, нет. Ну да дело не мое. В любом случае, на том же д.ру, с энтузиазмом, уверен, обрисуют ситуацию, что делать, когда функционала существующих "общественных" модулей для конкретного проекта не достаточно или он(функционал) "не совсем такой, как надо".

Dalay, Ваш модуль полный шик. Пишу к Вам за помощью или советом.

Делаю сайт по запчастям для разного рода техники.

Задумка: пользователь на странице интересующего его узла отправляет запчасти в корзину.

Реальность: у одной модели техники до тридцати узлов, у каждого узла до сотни запчастей. Запчасть представляет собой артикул и название. Заводить каждую запчасть как ноду не представляется возможным.

Вопрос: возможно ли вручную на странице добавлять кнопки, которые будут отправлять в корзину данные (артикул и название) и что для этого нужно изменить в модуле?

Невозможно. Для этого надо изменить модуль.

Походу придется все таки заводить каждую запчасть как ноду, тогда можно будет использовать модуль без изменений.

Походу так.)

на последнем этапе оформления заказа корзины перепутаны названия полей и их значения

Поправлено. Спасибо.

Добрый день! Установил модуль, но не появился пункт в админке с настройками модуля, ввёл url настроек - белая страница.

В error log:
PHP Parse error: syntax error, unexpected ':' in nodebasket.admin.inc on line 158

Модуль чистый, без изменений. Подскажите, пожалуйста, как можно решить проблему.

Да, там синтаксис в тернарных операторах php5.3-шный, у Вас, похоже, 5.2. Чуть позже поправлю. Пока можете сами. В nodebasket.admin.inc:
строка 158

// замените эту строку
'#default_value' => variable_get('nodebasket_mail_fields_site') ?  : array(),

// на эту
'#default_value' => variable_get('nodebasket_mail_fields_site', array()),

и еще ниже, строка 164

// замените эту строку
'#default_value' => variable_get('nodebasket_mail_fields_user') ?  : array(),

// на эту
'#default_value' => variable_get('nodebasket_mail_fields_user', array()),

Спасибо большое за ответ, поправил! Действительно, версия 5.2.

Ещё небольшой фидбек от меня: сейчас ссылки настроек нету в конфигурации сайта (не использую Administration menu), я решил для себя это следующим способом:

в hook_menu поправил пути страниц конфигурации модуля на "admin/config/system/nodebasket", соответственно после чистки кеша они появились в разделе "Система"

При первом нажатии на кнопку добавить в корзину товар в корзине визуально не видно (т.е ничего не меняется), меняется при перезагрузки страницы, после того как в корзине есть один товар дальше добавляется все нормально (т.е без перезагрузки страницы). Подскажите пожалуйста в чем может быть проблема?

Понятия не имею. Вы первый с такой бедой. Посмотрите, что в консоли браузера рисует после нажатия на кнопку.

Здравствуйте. Очень интересный модуль. Но возник вопрос, есть ли возможность вывести все товары таблицей и кнопку добавить в корзину тоже. в редактировании вьювса ненашел подобного поля :(

:) Извините за флуд. Плохо смотрел.

Добрый день, с праздником! =)
Заметил, что нету проверки значения поля количества, т.е. можно и отрицательное и дробное сделать - считать будет, наверное лучше на адекватность юзверей не надеяться =)
Плюс, ещё если поставить ноль, то лучше чтобы товар удалялся, а то сейчас он странно себя ведёт.

Последнее решил заменой в nodebasket.pages.inc 195 строчки:
if ($val['del']) {
на
if ($val['del'] || $val['qty'] == 0) {

С первым пока не поборолся.

Спасибо, Иван, за замечания. В ближайшее время пофиксю обозначенное.

Пожалуй, что Вашей строчкой можно решить сразу оба вопроса, чутка ее изменив:
if ($val['del'] || !is_int($val['qty'] ) && $val['qty'] <= 0) {

Точнее так(поле с количеством ведь может и не быть задействованным) :
if ($val['del'] || isset($val['qty']) && !is_int($val['qty'] ) && $val['qty'] <= 0) {

Но тогда он просто будет удалять эти строки из корзины, пытаюсь сделать это отдельной проверкой, но что-то не ладится пока у меня с ней. Вечером выложу её, мб подскажите где не прав.

Выше я просто, как вариант в контексте вашего кода, предложил. В модуле, конечно же, добавлю доп. проверку в функцию валидации. С возвратом ошибки о некорректном вводе, удалять действительно будет не логично.

Модуль обновил, с учетом пожеланий. При вводе нуля будет все ж возврат формы с ошибкой о неверном вводе, удалять такой пункт не будем. Ну и проверка на целое число: хоть и спорный момент(возможна ситуация, когда количество надо измерять в частях от целого), но пусть будет так, изменить это несложно.

Спасибо, что учитываете такие пожелания!

Подскажите пожалуйста, возможно ли при действиях с формой перезагружать только её часть, в моём варианте - только таблица с товарами?

Чуть подробнее: заменил чекбоксы на submit, при нажатии на него - удаляется соответствующая ей строка с товаром без необходимости клацания кнопки "Обновить".

Добавил в форму:
$form['items'][$nid]['del'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#name' => $nid,
'#submit' => array('nodebasket_item_delete_submit'),
'#ajax' => array(
'callback' => 'nodebasket_item_delete',
'path' => 'nodebasket/ajax',
'wrapper' => 'basket-items-table',
'method' => 'replace',
'effect' => 'fade',
),
'#prefix' => '

',
'#suffix' => '

',
);

+ сабмит и коллбек:

function nodebasket_item_delete_submit($form, &$form_state) {
$key = $form_state['clicked_button']['#name'];
drupal_set_message(t('Deleted:
%title', array(
'%title' => $_SESSION['basket']['items'][$key]['title'],
)));
unset($_SESSION['basket']['items'][$key]);
if (isset($_SESSION['basket']['store'][$key])) {
unset($_SESSION['basket']['store'][$key]);
}
_nodebasket_update_total();
}

function nodebasket_item_delete($form, &$form_state) {
return $form['items'];
}

Такая конструкция не работает адекватно без "$form_state['rebuild'] = TRUE": значение удаляется, сообщение выводится, но форма не обновляется.

Первое, Иван, что Вам следует сделать, если решили писать свой функционал на основе Nodebasket - переименовать модуль (и имена в коде, соответственно). То бишь, делать самостоятельный модуль, а не хардкордить имеющийся. В противном случае, будущее принесет Вам обязательный тяжелый головняк. Или проклятья тех, кто буде разбираться в Вашем коде за Вашим отсутствием.

По теме вопроса:

"...возможно ли при действиях с формой перезагружать только её часть, в моём варианте - только таблица с товарами"
А что такое, простите, есть форма, как не таблица с товарами?)

Ваш код без rebuild в тру бессмыслен. Я понимаю примерно, что требуется. Но для нормальной, рабочей реализации нужного пары новых функций и изменения пары строчек в существующих будет явно не достаточно.

Поставил новую версию. Теперь в корзину добавляются товары с пустыми ячейками цены и с нулевыми...

А кто-нибудь в курсе как добавить кнопку добавления в корзину через шаблон Contemplate? Пробовал по разному не получается :( Я так думаю должно быть что то типа $node->content['nodebasket_add2basket']['#markup'] ..

Теоретически, должно работать:

print render(drupal_get_form('nodebasket_add2basket_button', $node));

Но я бы Вам советовал забыть про Contemplate. Уже хотя бы из-за предупреждения жирным шрифтом на самом верху страницы модуля:
NOTE: This module *may* be deprecated in Drupal 7 !

Иначе говоря - этот модуль устарел. И судя по датам релизов - не только морально.

спасибо вам огромное! я был близок к решению) я пытался просто print drupal_get_form('nodebasket_add2basket_button', $node) :)))) А что вы посоветуете заместо Contemplate? темизацию нод через tpl файлы?

Да, именно это.
И, кстати, модуль обновился.

а каким способом можно сделать кнопки +/- добавления товара? и можно ли заместо чекбоксов просто поставить крестики, которые будут удалять товар?)

1. Перечислите способы, которые знаете, и я укажу на наиболее подходящий.
2. Можно, ставьте.

Вопрос. Обновил модуль с версии 2.2 на 2.6. В заказ стали попадать обрезанные товары (цены округляются). Есть поле с ценой - field_product_price / Плавающий / Текстовая строка. При добавлении в корзину товара с ценой 15.88 - получается 15 (вместо 10.5 получается 10). В старой версии всё работало нормально.

Причина обновления модуля: в старой версии в письме о новом заказе отсутствовал e-mail заказчика, при этом на странице заказа этот e-mail был виден, письмо покупателю приходило.

Это мой косяк (не правильно что-то настроил) или бага?

З.Ы. В любом случае хочу сказать спасибо за модуль.

... В заказ стали попадать обрезанные товары (цены округляются) ...

Да, это я чутка перестарался в коде. В ближайшее время поправлю. Если ждать не хотите, то:
В файле nodebasket.module, строка 194, замените

$price = (int) $form['price']['#value'];

на
$price = $form['price']['#value'];

... в письме о новом заказе отсутствовал e-mail заказчика ...

Уже и не помню, чем руководствовался, но да, адрес заказчика в уведомлении о заказе нигде не фигурирует. Пожалуй, стоит его в отправители поставить. Как и выше, если надо срочно, то:
В файле nodebasket.module, строка 646, замените

$from = variable_get('site_mail', ini_get('sendmail_from'));

на
$from = ($key == 'user') ? variable_get('nodebasket_basket_email', variable_get('site_mail', ini_get('sendmail_from'))) : $node->order_mail;

Спасибо большое!

Здравствуйте, спасибо за модуль. У меня вопрос, можно ли добавить поле артикул к таблице с заголовком, ценой и кол-вом (не всегда удобно искать по названию товара). И как вывести поле заголовок ссылкой на ноду. (в корзине она ссылкой, а в письме заказа и созданной ноде заказа только текст)

1. Можно.
2. Формат писем - простой текст. "Заголовок ссылкой" это немного о другом).

2. А "Заголовок ссылкой" из ноды (заказ) - можно там реализовать ссылку?

Присоединяюсь ко второму вопросу (ссылка на товар из письма), тоже интересно как это реализовать. Давече пришлось импортировать 50К товаров (названия схожих товаров на 99% одинаковые), и найти нужные Кроссовки Nike среди 5К с таким же названием - практически нереально. Пришлось при импорте к названия клеить артикул - это позволяет из письма найти нужный товар... но по ссылке попасть на нужный товар было бы лучше. Заранее спасибо за любой ответ (кодом или напутственным словом где искать решение)...

В формате "простого текста"(plain text) только как текст.
В template.php темы вставьте:

function НАЗВАНИЕ-ВАШЕЙ-ТЕМЫ_preprocess_lightshop_mail_notice(&$vars) {
  $node = $vars['node'];
  $vars['order_items'] = '';
  foreach ($node->order_items['items'] as $item) {
    $output = $item['title'];
    if (isset($item['qty'])) {
      $output .= ' | ' . t('qty: @qty', array('@qty' => $item['qty']));
    }
    if (isset($item['price'])) {
      $output .= ' | ' . t('price: @price ', array('@price' => _lightshop_get_price_numberformat($item['price'])));
    }
    $output .= ' | ' . t('Link: @link', array('@link' => url('node/' . $node->nid, array('absolute' => TRUE))));
    $vars['order_items'] .= $output . "\n";
  }
}