Ajaxify Node Basket -2012: Apocalypse Edition

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

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

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

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

Страница настроек

После включения модуля, в меню настроек сайта появится новый пункт: Настройки Node Basket(не дословно так, но мимо не проскочите)). При заходе на страничку по данному пункту(admin/settings/nodebasket) откроется вам примерно такой вот вид:

Node Basket: страница настроек.

Тут вам будет предложено отметить галочками те из существующих типом нод, которые модуль будет считать "своими".

Выбрали. Сохранили. Видим такое:

Node Basket: страница настроек с уже выбранноми типами нод для модуля.

Предлагается следующее, в порядке отображения на странице (после выбора типов):

1. Снять галочку или оставить отмеченным чекбокс с опцией варианта отображения кнопки добавления ноды в корзину. Если отмечено, то кнопка появиться под основным текстом у каждой ноды(настроенного типа, разумеется). Если же в планах выводить эту кнопку своим хитрым методом (через вызов в файле шаблона ноды конструкции drupal_get_form('nodebasket_nodeviewform', $node)), то опцию следует отключить.

2. Решить - нужно ли вам отображение на странице редактирования пользователем своей корзины поле с изменяемым количеством для каждой из отложенных в корзину нод. Если решили, что надо, то оставляем, как есть, в противном случае - совершаем противоположное действие.

3. Выбор поля(модуля Content Construction Kit (CCK)) с ценой продукта. Из уже существующих. Отобразиться, если в выбранных типах нод такие поля присутствуют и подходят в качестве "ценника". Что делать понятно, да? Идем дальше.

4. Последним пунктом настроек: поля для ввода суффикса цен. Поясню: этот суффикс будет добавлен к цифрам "ценника". Если при настройке поля на страницах редактирования типов нод вы уже подобное указывали, то для нашего модуля надо сделать это еще раз. При взаимодействии с CCK-полями Node Basket оперирует лишь их значениями('value'), не отформатированным видом, от того и нужда в дублировании суффикса.

Вот с настройками и все.

Кнопка добавления в корзину при построении "вида" (модулем Views)

Если возникнут вопросы по кнопке для Views, читаем тут. Не вижу с сим сложностей, дабы пересказывать по-новой.

Демонстрация работы модуля

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

Dalay

Комментарии

Посоветуйте, пожалуйста, как бороться с notice в модуле: Undefined variable: subtotal_price in theme_nodebasket_basketview()

$subtotal_price = ''; - сами понимаете не подошло..)

Понимаю что это не критично, но покоя эта фигня не даёт =)

Какая у Вас версия модуля?
В коде ковырялись?
Поле с ценой используете?

Версия последняя 6.x-3.0
Грешен, ковырялся, но всё что касаемо объявления/не объявления переменной - не трогал.

Ругается devel с включённым обработчиком Backtrace на строчки
334 $subtotal_price += $v['price'];
335 $subtotal_count += $v['count'];

Поле с ценой использую.

Отковыривайтесь на оригинальный код.

Посмотрел - да, может предупреждение выскакивать. Позже обновлю, а пока, если сильно мешает, оберните $subtotal_price в isset() в условии.

Строка 340(nodebasket.module):
if ($subtotal_price) {
замените на
if (isset($subtotal_price)) {

Спасибо Вам за отклик!
Но к сожалению не помогло, всё таки эти две переменные находятся ДО условия if и обработчик не видит что их объявили.

Да какая разница, где они объявлены. Их первый вызов происходит в условии с проверкой на существование оных.

Походу, код-таки "перековырян" больше нужного. Никто более с такими косяками не обьявлялся. Да и мной все весьма тщательно тестируется до того, как выложить файлы в шару.

Зря вы оскарбились. Я понял, у вас сообщение перестало выводиться, буду чистить свои "перековырки". Да и до косяка это не дотягивает, т.к. это сообщение кроме администратора с включённым devel с backtrace нигде больше не выводится и никуда не пишется.

С чего обскорбляться, не понял это место?

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

Огромное спасибо за модуль! Давно искал нечто подобное.. скажите, не планируете лисвы делать такой-же для 7ки?

Здравствуйте, а для друпал 7 не портировали?

Так для 7ки есть свой модуль с чуть большим функционалом. Там создаются еще все заказы...

Это вы про basic cart или вы еще про какой?

про него. но он на порядок хуже... очень жду что бы его портировали на 7ку и добавили заказы для зарег пользователей и поле для скидочной цены...

ск будет стоить портировать если за денюшку.

Firefox 15.0.1 не работает обновление цены в корзине при увеличении количества.

Да, Жень, засек уже это дело. Обновись.

Супер! спасибо!

Что там с 7 кой. Друпал коммерц уже достал... своими багами!

Надобности такой пока не возникало. Как только, так сразу.

Здравствуйте, очень полезный модуль спасибо. но у меня возник вопрос

все работает как нужно, только вот после того как я в node basket settings выбрал галочку что именно будет обрабатывать модуль , в моем случае это "Товар", кнопка добавления в корзину появилась, но вижу ее только я как авторизированный пользователь, а если зайти без авторизации то кнопку не видно( как сделать чтобы она была видимой для всех? помогите пожалуйста

Права доступа настройте (/admin/user/permissions#module-nodebasket).

с этим разобрался спасибо, все хорошо работает, но возник новый вопрос, а точнее два.

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

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

А у меня почему то не появляется этого всплывающего окна с информацией о добавленном предмете, и в момент добавления кнопка add to basket просто становиться не активной, и блок с содержимым не обновляется... НО, если обновить страницу то в блоке с содержимым корзины появляются добавленные предметы.

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

Если после установки кэш не почистили, то проблема неотображения в этом. В противном случае - конфликт с иными модулями, мне не ведомыми, естественно.

С версиями, да, возможно попутал, поправлю, спасибо, что заметили.

Здравствуйте уважаемый Dalay. По прежнему пользуюсь вашим модулем, и до сегодняшнего дня был уверен что все отлажено идеально, и работает как часы.
Дело в том что я пользуюсь браузером Opera и при пользовании вашим модулем через этот браузер не испытываю совершенно никаких проблем. Товар хорошо добавляется в корзину, нет проблем и с оформлением товаров. Но, вот так сложилось что мне пришлось посетить сайт с другого компьютера на котором установлен Google Chrome. Так вот там проблема возникла следующая:
Во время добавления товара в корзину, вылезает всплывающее окно с информацией о том что товар был успешно добавлен в корзину, НО, если я добавляю следующий товар, то в блоке с корзиной предыщий товар просто заменяется вновь добавленным, написано что в корзине 1 предмет. И так сколько не добавляй, они будут друг друга заменять. При этом если перейти site.ru/basket , то там я вижу "Ваша корзина пуста".

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

Итог: В опере - все идеально. В хроме и ИЕ - товар заменяется следующим, а при переходе /basket - "корзина пуста". В мозиле не проверял.

Помогите разобраться пожалуйста.

А и еще вопрос ) подскажите пожалуйста как мне на почту отправлять еще и поля с ценой каждого товара и общуюю сумму, что именно сдесь нужно дописать подскажите пожалуйста, если вас не затруднит )

function nodebasket_mail($key, &$message) {
$subject = t("Node Basket order from @sitename", array('@sitename' => variable_get(site_name, "t('Your site')")));
$data = $_SESSION['nodebasket_data'];
$rows = t('Node Basket order') ."\n";
$rows .= t('Sender IP') .': '. $data['sender_ip'] ."\n\n";
$rows .= t('Order items') ."\n";
foreach ($_SESSION['basket'] as $k => $v) {
$rows .= check_plain($v['title']) .' | '. t('Quantity') .': '. $v['count'] ."\n";
}

$rows .= "\n". t('Contact Info') ."\n";
$rows .= t('Sender name') .': '. $data['name'] ."\n";
$rows .= t('Conact phone') .': '. $data['phone'] ."\n";
$rows .= t('E-mail') .': '. $data['email'] ."\n";
if (!empty($data['descr'])) {
$rows .= t('Notes') .': '. $data['descr'];
}
switch ($key) {
case 'order':
$message['subject'] = $subject;
$message['body'] = $rows;
break;
}
}

Здравствуйте, Владимир.

По поводу "в хроме и ИЕ". Зайдите на демо-страницу модуля данными браузерами. Если ошибок не возникнет, то, следовательно, суть проблемы на Вашей стороне. В противном случае будем разбираться.

По коду. Примерно так:

function nodebasket_mail($key, &$message) {
  $subject = t("Node Basket order from @sitename", array('@sitename' => variable_get(site_name, "t('Your site')")));
  $data = $_SESSION['nodebasket_data'];
  $rows = t('Node Basket order') . "\n";
  $rows .= t('Sender IP') . ': ' . $data['sender_ip'] . "\n\n";
  $rows .= t('Order items') . "\n";

  // В обсуждаемой реализации модуля значение общей суммы заказа статично нигде не хранится.
  // Посему считаем "на живую" в переменную $total_sum.
  $total_sum = 0;

  foreach ($_SESSION['basket'] as $k => $v) {

    // При обходе массива содержимого корзины увеличиваем значение переменной
    // $total_sum на сумму стоимости каждого пункта заказа.
    $total_sum += $v['price'];

    // Формируем переменную $row, которая будет содержать строку товарной единицы заказа
    $row = '';
    // Заголовок
    $row .= check_plain($v['title']);
    // Количество
    $row .= ' (' . t('Quantity') . ': ' . $v['count'] . ', ';
    // Стоимость
    $row .= t('Price') . ': ' . $v['price'] . ')';

    // Страница товара, думаю, нафиг не нужна, но если вдруг, то раскомментируйте.
    //$row .= ' | ' . t('Item page') .': '. url('node/'. $k, array('absolute' => TRUE));

    $rows .= $row . "\n";
  }

  // Добавляем новое поле с общей суммой заказа.
  $rows .= "\n\n" . t('Total sum: ') . $total_sum . "\n";

  $rows .= "\n" . t('Contact Info') . "\n";
  $rows .= t('Sender name') . ': ' . $data['name'] . "\n";
  $rows .= t('Conact phone') . ': ' . $data['phone'] . "\n";
  $rows .= t('E-mail') . ': ' . $data['email'] . "\n";
  if (!empty($data['descr'])) {
    $rows .= t('Notes') . ': ' . $data['descr'];
  }
  switch ($key) {
    case 'order':
      $message['subject'] = $subject;
      $message['body'] = $rows;
      break;
  }
}

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

По второму вопросу:
Большое спасибо за ответ и помощь, я сам не програмист и врядли бы справился бы сам. Вобщем заменил ваш код, и вобщем то как и хотел получил на почту цену на каждый товар.. Только вот общаяя сумма не посчиталась, в конце письма "Total sum" а после ничего не написано)

Не подскажете что мне исправить, чтобы и общая сумма тоже приходила ?
Спасибо)

Попробуйте тот код, что в моем ответе сейчас, до этого переменную неверно обозвал.

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

Здравствуйте Dalay. Возникли не большие проблемы с модулем. Оформляю заказ на сайте дохожу до заполнения форм с фамилией и прочими данными. Все заполняю, нажимаю кнопку "Совершить заказ". Страница обновляется и в верхней части появляется надпись красными буквами "Поле Payment method обязательно для заполнения." Но поле метода оплаты совершенно не активно, в моем случае там находится общая сумма заказа. Я что то сделал не правильно при установки и настройки? Подскажите как разрешить данную задачу. Заранее благодарен. Версия модуля 6.x-3.2_0

Здравствуйте. В моем модуле при оформлении заказа присутствуют только три поля: Имя, Мыло, Комменты к заказу. Никаких методов оплаты там по дефолту и в помине нет. Потому, вообще не понимаю, о чем идет речь.

Запостил порт под седьмой Drupal. Велком попробовать.

Огромное спасибо за модуль.

Не силен в PHP. Правильно ли я делаю? В function nodebasket_orderform добавляю

$form['address'] = array(
'#type' => 'textarea',
'#title' => t('Address'),
'#required' => FALSE,
'#default_value' => '',
'#rows' => 5,
);

и далее в function nodebasket_mail

$rows .= t('Address') .': '. $data['address'] ."\n";

Этого достаточно?

Да, все верно. Только новое поле желательно профильтровать в целях безопасности. Например, через check_plain().
Допишите в функции nodebasket_orderform_submit() после строки:

$values = $form_state['values'];

это:

$values['address'] = check_plain($values['address']);

Спасибо за модуль! Отличная штука

Все в этом модуле прекрасно. Не хватает одного - статистики по добавлению нод в корзину, с выводом этих данных во views.

Очень полезная фишка, чтобы смотреть как часто и какие ноды добавляют...

У меня после нажатия кнопки отправить в order, выдаёт белый экран и ничего не происходит, как с этим бороться?

Путем просмотра логов на предмет того, что вызывает ошибку.

Нашел здесь .../sites/all/modules/smtp/phpmailer/language/phpmailer.lang-ru.php:27) убрал лишнее строки в конце и все заработало!

Возможно, имеет смысл поведать об ошибке автору модуля smtp. Хотя, если судить по количеству открытых багов, смысла в том не много. Может и Ваша ошибка среди них. Не стремно такие модули использовать? Или просто сайт не Ваш?)

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

Вообще спасибо, что не поленились отписаться. Наверняка кому-то будет полезно об этом знать.

Добрый вечер Dalay,
Во первых благодарю вас за столь прекрасный модуль.
Прошу вас подсказать мне как, или где подпилить, что бы в блоке корзины отсутствовал превью товара?
Говорю заранее мои познания в php не очень велики.
На сколько я смог разобраться у вас написана общая функция для вывода содержимого корзины, как для страницы, так и для блока. В попытке написать отдельную функцию для блока с выводом только количества товара, потерпел неудачу.
Помогите, или хотя бы подскажите правильное направление.
Спасибо.

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

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

Для версии 6.х.
Вставьте в template.php своей темы функцию:

// Не забудьте заменить НАЗВАНИЕ_ВАШЕЙ_ТЕМЫ на название Вашей темы.
// Если такая функция в template.php уже есть, то замените на эту.

function НАЗВАНИЕ_ВАШЕЙ_ТЕМЫ_nodebasket_basketview($is_block = NULL) {
  drupal_add_css(drupal_get_path('module', 'nodebasket') . '/nodebasket.css');
  $output = !$is_block ? '<div id="nodebasket-ajax-content">' : '<div>';
  if (!empty($_SESSION['basket']) || !empty($_SESSION['basket_history'])) {
    $count = count($_SESSION['basket']);
    if (isset($_SESSION['basket']) && ($count > 0)) {
      if ($is_block) {
        $output .= l(t('Your Basket Items') . ' (' . $count . ')', 'basket', array(
            'attributes' => array(
              'class' => 'basket-count block',
              'id' => 'basket-count-link',
              'title' => t('Go to your the Basket items list page'),
            ),
          ));
      }
      elseif (!is_null($is_block)) {
        $output .= l(t('Clear basket'), 'basket/clear', array(
            'query' => drupal_get_destination(),
            'attributes' => array(
              'class' => 'basket-actions clear',
              'title' => t('Clear basket completely'),
            ),
          ));
      }

      foreach ($_SESSION['basket'] as $k => $v) {
        $row = array();
        if (!$is_block) {
          $row[] = l($v['title'], 'node/' . $k);
          $header = array();
          $header[] = array(
            'data' => t('Item title'),
          );
          if (variable_get('nodebasket_show_count_field', TRUE)) {
            $row[] = !is_null($is_block) ? array(
              'data' => drupal_get_form('nodebasket_countupdateform', $k, $v['count']),
              'class' => 'count-cell',
            ) : $v['count'];
            $header[] = array(
              'data' => t('Quantity'),
              'class' => 'count-cell',
            );
          }
          if (variable_get('nodebasket_price_field', array())) {
            $header[] = array(
              'data' => t('Price'),
              'class' => 'price-count-cell',
            );
            $row[] = array(
              'data' => _nodebasket_get_price_numberformat($v['price']),
              'class' => 'price-count-cell',
            );
          }
          $row[] = l('<img src="' . base_path() . drupal_get_path('module', 'nodebasket') . '/img/remove.gif">', 'basket/' . $k . '/del', array(
              'query' => drupal_get_destination(),
              'html' => TRUE, 'attributes' => array(
                'title' => t('Remove this item from basket'),
                'class' => 'basket-actions del-from-basket',
              ),
            ));
        }


        $rows[] = $row;
        $subtotal_price += $v['price'];
        $subtotal_count += $v['count'];
      }
      if (count($rows)) {
        if ($subtotal_price) {
          $subtotal_price = _nodebasket_get_price_numberformat($subtotal_price);
          $rows[] = !$is_block ? array(
            'data' => array(
              t('Subtotal: '),
              $subtotal_count,
              array(
                'data' => $subtotal_price,
                'colspan' => 2,
                'class' => 'subtotal_price',
              ),
            ),
            'class' => 'subtotal page',
          ) : array(
            array(
              'data' => t('Subtotal price: ') . $subtotal_price,
              'colspan' => 2,
              'class' => 'subtotal block',
            ),
          );
        }
        $output .= theme('table', $header, $rows, array(
            'id' => 'basket_list',
          )
        );
        if (!$is_block && !is_null($is_block)) {
          $output .= l(t('Ordering'), 'basket/order', array(
              'attributes' => array(
                'class' => 'basket-links order',
                'title' => t('Go to ordering'),
              ),
            ));
        }
      }
    }
    if (isset($_SESSION['basket_history'])) {
      $output .= l(t('History of ordering'), 'basket/history', array(
          'attributes' => array(
            'class' => 'basket-links history',
            'title' => t('Go to history of ordering page'),
          ),
        ));
    }
  }
  else {
    $output .= t('Your basket is emty');
  }
  $output .= '</div>';

  return $output;
}

Благодарю вас!

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

Модуль не заточен для работы с несколькими значениями стоимости.

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

Или попробовать добавить еще один выпадающий список в настройках