Реализуем корзину в miniShop2

Начинаем 12 часть нашего курса создания интернет-магазина на MODx Revo. В данном уроке мы с вами реализуем корзину. Если прочитать документацию к miniShop2, то мы увидим, что корзина в данном компоненте два вида корзины:

  • Мини-корзина (за это отвечает сниппет msMiniCart).
  • Большая корзина (ее выводит сниппет msCart).

В нашем же случае видов корзины будет 3:

  • Корзина в шапке
  • Выпадающая корзина
  • Страница с корзиной

Начнем с большой, потому что так нам будет проще. Советую посмотреть дефолтный чанк корзины miniShop2 – tpl.msCart. Те, кто смотрел мой предыдущий курс, сразу найдут разницу – здесь мы впервые столкнемся с шаблонизатором Fenom (документация Fenom).

Для того, чтобы начать, нам необходимо интегрировать шаблон корзины (cart.html). Я надеюсь, вы помните, как мы это делали на прошлых уроках. Также необходимо создать ресурс “корзина” с данным шаблоном. У меня получилось вот так:

Рализация корзины в miniShop2

Дальше мы приступим к созданию чанка. В данном чанке у нас будет полностью все, что находится в элементе “section#cart-page”. Для того, чтобы посмотреть все доступные плейсхолдеры, нужно вызвать сниппет msCart с пустым параметром tpl:


<pre>[[!msCart?tpl=`]]</pre>

Рализация корзины в miniShop2

Теперь соединим дефолтный чанк с нашим и у нас получится следующее:


<div class="container" id="msCart">
  {if !count($products)}
  <div class="alert alert-info" role="alert">{'ms2_cart_is_empty' | lexicon}</div>
  {else}
  <div class="col-xs-12 col-md-9 items-holder no-margin">	
    {foreach $products as $product}
	<div class="row no-margin cart-item" id="{$product.key}">
	  <div class="col-xs-12 col-sm-2 no-margin">
		<a href="{$product.id | url}" class="thumb-holder">
		  {if $product.image?}
		  <img class="lazy" src="{$product.image}" alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
		  {else}
		  <img class="lazy" src="{'assets_url' | option}components/minishop2/img/web/ms2_small.png"
		  srcset="{'assets_url' | option}components/minishop2/img/web/ms2_small@2x.png 2x"
		  alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
		  {/if}
		</a>
	  </div>	  
	  <div class="col-xs-12 col-sm-5 ">
		<div class="title">
		  <a href="{$product.id | url}">{$product.pagetitle}</a>
		</div>
		<div class="brand">{$product['vendor.name']}</div>
		{if $product.options?}
		<div class="small">
		  {$product.options | join : '; '}
		</div>
		{/if}
	  </div>	  
	  <div class="col-xs-12 col-sm-3 no-margin count">
		<div class="quantity">
		  <div class="le-quantity">
			<form method="post" class="ms2_form form-inline" role="form">
			  <input type="hidden" name="key" value="{$product.key}"/>
			  <a class="minus" href="#reduce"></a>
			  <input type="number" name="count" class="counter" value="{$product.count}"/>
			  <a class="plus" href="#add"></a>
			  <button class="btn btn-default" type="submit" name="ms2_action" value="cart/change">
				<i class="glyphicon glyphicon-refresh"></i>
			  </button>
			</form>
		  </div>
		</div>
	  </div>	  
	  <div class="col-xs-12 col-sm-2 no-margin">
		<div class="price">
		  {$product.price} руб.
		</div>
		<form method="post" class="ms2_form">
		  <input type="hidden" name="key" value="{$product.key}">
		  <button class="close-btn" type="submit" name="ms2_action" value="cart/remove">
		  </button>
		</form>
	  </div>
	</div>
	{/foreach}
  </div>
  <div class="col-xs-12 col-md-3 no-margin sidebar ">
	<div class="widget cart-summary">
	  <h1 class="border">Корзина</h1>
	  <div class="body">
		<ul id="total-price" class="tabled-data inverse-bold no-border">
		  <li>
			<label>Стоимость</label>
			<div class="value pull-right">
			  <span class="ms2_total_cost">{$total.cost}</span> руб.
			</div>
		  </li>
		</ul>
		<div class="buttons-holder">
		  <a class="le-button big" href="checkout.html" >Оформить заказ</a>
		  <a class="simple-link block" href="category-grid.html" >Продолжить покупки</a>
		</div>
	  </div>
	</div>
  </div>
  {/if}
</div>

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

Теперь шаблон корзины у меня выглядит следующим образом:


<!DOCTYPE html>
<html lang="ru">
    <head>
        [[$meta]]
    </head>

    <body>
        <div class="wrapper">
            [[$headerInner]]
            <section id="cart-page">
                [[!msCart?tpl=`cartTpl`]]
            </section>
            [[$footer]]
        </div>
        [[$scripts]]
    </body>
</html>

Неплохо не правда ли? Приступим к реализации маленькой корзины. Поступим тем же самым способом, как и с большой – будем сравнивать дефолтный чанк (tpl.msMiniCart) с нашим и будем вставлять необходимые классы и плейсхолдеры. Также я немного опередил события и добавил классы, для нашей выпадающей корзины (подробнее смотрите на видео). Не забывайте, что мини корзина у нас находится в двух чанках: header и headerInner. У меня чанк получился такой:


<a class="dropdown-toggle {$total_count > 0 ? 'full' : ''}" data-toggle="dropdown" href="[[~10]]" id="msMiniCart">
  <div class="empty">
	<div class="basket-item-count">
	  <span class="count ms2_total_count">0</span>
	  <img src="assets/images/icon-cart.png" alt="" />
	</div>	
	<div class="total-price-basket">
	  <span class="lbl">Корзина:</span>
	  <span class="total-price">
		<span class="value ms2_total_cost">0</span><i class="fa fa-ruble"></i>
	  </span>
	</div>
  </div>
  <div class="not_empty">
	<div class="basket-item-count">
	  <span class="count ms2_total_count">{$total_count}</span>
	  <img src="assets/images/icon-cart.png" alt="" />
	</div>	
	<div class="total-price-basket">
	  <span class="lbl">Корзина:</span>
	  <span class="total-price">
		<span class="value ms2_total_cost">{$total_cost}</span><i class="fa fa-ruble"></i>
	  </span>
	</div>
  </div>
</a>

Теперь вызываем нашу мини корзину в двух чанках шапки нашу корзину:


[[!msMiniCart?tpl=`miniCartTpl`]]

Теперь у нас есть мини корзина! Поздравляю!

И в данной части будет глава, которую достаточно много человек у меня просили в прошлом курсе – выпадающая корзина. Реализовывать будем с помощью технологии AJAX. Для начала нам нужно сделать чанк подобный большой корзине. У меня он получился следующий:


{if !count($products)}
<div class="alert alert-info" role="alert">{'ms2_cart_is_empty' | lexicon}</div>
{else}
{foreach $products as $product}
<li>
  <div class="basket-item">
	<div class="row">
	  <div class="col-xs-4 col-sm-4 no-margin text-center">
		<div class="thumb">
		  <a href="{$product.id | url}" class="thumb-holder">
			{if $product.image?}
			<img class="lazy" src="{$product.image}" alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
			{else}
			<img class="lazy" src="{'assets_url' | option}components/minishop2/img/web/ms2_small.png"
			srcset="{'assets_url' | option}components/minishop2/img/web/ms2_small@2x.png 2x"
			alt="{$product.pagetitle}" title="{$product.pagetitle}"/>
			{/if}
		  </a>
		</div>
	  </div>
	  <div class="col-xs-8 col-sm-8 no-margin">
		<div class="title">{$product.pagetitle}</div>
		<div class="price">{$product.price} <i class="fa fa-ruble"></i></div>
	  </div>
	</div>
	<form method="post" class="ms2_form">
	  <input type="hidden" name="key" value="{$product.key}">
	  <button class="close-btn" type="submit" name="ms2_action" value="cart/remove">
	  </button>
	</form>
  </div>
</li>
{/foreach}
<li class="checkout">
  <div class="basket-item">
	<div class="row">
	  <div class="col-xs-12 col-sm-6">
		<a href="[[~10]]" class="le-button inverse">Корзина</a>
	  </div>
	  <div class="col-xs-12 col-sm-6">
		<a href="checkout.html" class="le-button">Оформить заказ</a>
	  </div>
	</div>
  </div>
</li>	
{/if}

Заметили, что он получается подобный крупной корзине? Теперь на нужно создать несколько ресурсов, к которым мы будем обращаться по ajax. Обычно для таких ресурсов я создаю контейнер “технические ресурсы”/”ajax” и создаем ресурс с пустым шаблоном, отключаем текстовый редактор на вкладке настройки.

Рализация корзины в miniShop2

В содержимом ресурса вызываем уже знакомы сниппет:


[[!msCart?tpl=`toggleCartTpl` &sortdir=`DESC`]]

Только мы сменили сортировку, последние добавленные товары будут первыми. И нам осталось написать скрипт JS, который мы разместим в чанке “scripts”:


<script>
  $(document).on('click', '#msMiniCart', function(e) {
	e.preventDefault();
	$.ajax({  
	  type: "POST",  
	  url: '[[~13]]', 
	  data: {parent: '[[*id]]'},
	  success:  function(data) { 
		if (data){
		  $('.basket .ajax-data').html(data);
		}else{
		  miniShop2.Message.error('Что-то пошло не так, попробуйте позже!');
		}
	  } 
	}); 
  });
</script>

Ура! У нас работает всплывающая корзина! Делается все очень просто, не правда ли? На этом мы закончили реализацию наших корзин. Осталось дело за малым – настроить стили, а то они немного подслетели. До следующих уроков!

Оцените статью:

Проголосовало: 46

Комментарии (24)

  1. Muxa 20 апреля 2017, 13:47 # 0
    Подскажите, как в в пустой корзине (при ее очистке пользователем) сделать редирект на определенную страницу, вместо сообщения «ваша корзина пуста»?
    1. Петропавловский Артем 20 апреля 2017, 14:02 # 0
      Смотрите, у miniShop2 есть события, на которые вы можете поставить свой обработчик. В вашем случае будет примерно такой:
      miniShop2.Callbacks.add('Cart.clean.success', 'restrict_cart', function() {
          location.href('ссылка для переадресации');
          return false;
      });
    2. Сергей 24 апреля 2017, 21:29 # 0
      как только дело к корзине подходит и у меня проблемы начинаются.Ничего, кто объясняет не пойму.Может мне лично как-то помножите?У вас видео со статьями не соответствует. И minichop2 теперь в снипетах находится.
      1. Сергей 24 апреля 2017, 23:40 # 0
        Можете мне по поэтапно объяснить.Я только учусь.Первое: какой чанк создать и что в него поместить?Второе: что входит в дефолтный чанк?Третье: какие чанки соединить?Мне просто не кому обратиться?Заранее благодарен!
        1. Петропавловский Артем 25 апреля 2017, 08:35 # 0
          Возможны платные консультации в скайпе.
          1. Сергей 25 апреля 2017, 10:41 # 0
            я не против оплаты. если цена разумна.сколько стоит консультация?
            1. Петропавловский Артем 25 апреля 2017, 11:06 # 0
              Ответил на почту.
              1. Денис 19 октября 2017, 12:55 # 0
                Здравствуйте! Скажите пожалуйста, можете скинуть на почту стоимость консультации. Спасибо!
        2. Роман 27 апреля 2017, 12:52 # 0
          Артем, добрый день! А как можно очистить мини корзинку по клику? Буду очень благодарен =)
          1. Николай 08 июня 2017, 15:43 # 0
            <form method="post">
                        <button class="btn btn-default" type="submit" name="ms2_action" value="cart/clean">
                            <i class="glyphicon glyphicon-remove"></i> {'ms2_cart_clean' | lexicon}
                        </button>
                    </form>
          2. Александр Булатов 01 июня 2017, 11:42(Комментарий был изменён) # 0
            Артём, добрый день!А у меня почему то пишет в консоли Хрома такую ошибку Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. И при этом показывает белый экран через 5 секунд.
            1. Николай 13 июня 2017, 18:06 # 0
              Подскажите, пожалуйста, по реализации кнопок ± в корзине
              1. Петропавловский Артем 13 июня 2017, 18:20 # 0
                У меня за это отвечает такой код:
                // Quantity Spinner
                        $('.le-quantity a').click(function(e){
                            e.preventDefault();
                			var elem = $(this).parent().parent().find('input.counter');
                			var currentQty= elem.val();
                
                            if( $(this).hasClass('minus') && currentQty>0){
                                elem.val(parseInt(currentQty, 10) - 1);
                				elem.trigger("change");
                            }else{
                                if( $(this).hasClass('plus')){
                                    elem.val(parseInt(currentQty, 10) + 1);
                					elem.trigger("change");
                                }
                            }
                        });
                Проверьте соответствие в файле assets/js/scripts.js примерно 316 строка
                1. Николай 15 июня 2017, 11:43 # 0
                  Спасибо, путем некоторых правок починил
              2. Александр 25 июля 2017, 11:56 # 0
                $('.basket .ajax-data').html(data);
                не совсем понятно куда пишется содержимое ответа, селекторы .basket .ajax-data нигде не встречаются.
                1. Серёня 02 сентября 2017, 19:43 # 0
                  Автор немного лукавит, приводя неполный код, но в видео все есть!!))см 3:34
                2. Вадим 28 июля 2017, 16:05 # 0
                  Добрый день! У меня на сайте после нажатия кнопки «купить», до обновления страницы, пишет (при клике на корзину), что корзина пуста. Как исправить, подскажите, пожалуйста. Сайт — http://shop.remonter.by/
                  1. Aleksey 09 сентября 2017, 15:25 # 0
                    Артем спасибо за уроки! Делал все по урокам все получилось, только вот выпадающая корзина не выпадает) Все проверил не работает scripts, а вот как его правильно настроить не знаю. Было бы хорошо если Вы более подробно рассказали, что от куда и куда берется). Думаю не только мне одному будет интересно.
                    1. Петропавловский Артем 09 сентября 2017, 18:32 # 0
                      Там должна быть связка JS — PHP посмотрите внимательней видео.
                      1. Aleksey 10 сентября 2017, 16:58 # 0
                        Понял в чем была причина! Мой файл jquery.js содержал ошибки, поэтому не работала выпадающая корзина. Спасибо Вам Артем ОГРОМНОЕ за наводку и за Ваши уроки!
                    2. Владимир 09 сентября 2017, 18:29 # 0
                      Артем, как можно к вам обратиться за платной технической помощью?
                      1. Петропавловский Артем 09 сентября 2017, 18:32 # 0
                        Да, конечно. Скайп — mot9i_ami или почта avp@dart.agency
                        1. Владимир 09 сентября 2017, 18:45 # 0
                          Отправил запрос в skype.
                      2. Алексей 04 октября 2017, 18:40 # 0
                        Здравствуйте Артем! Подскажите, как сделать так чтоб мини корзина выпадала не по клику а по наведению мыши. Спасибо!