Обучаю битриксу программистов, интеграторов. Подробнee ⇒

Корзина, BX.Vue.component, BX.ajax.runComponentAction



У Битрикса есть хороший стандартный компонент и шаблон корзины
Если нет какой-то супер сложной логики, то рекомендую использовать его (или шаблон корзины в вашем шаблонном решении).
Можно его в свое пространство имен скопировать, убрать лишнее (например, подарки), навесить свои стили, добавить функционал


Если есть суперсложная логика в корзине (например, Вы продаете лицензии или страховки), то удобно делать корзину на bx.vue


Реализация корзины на BX.Vue
Ниже пример очень простой корзины и простой верстки для понимания BX.Vue
компонент test:basket
  • test/basket/class.php
  • test/basket/templates/.default/template.php
  • test/basket/templates/.default/script.js
.options .desсription - надо бы тоже написать (стандартные)

class.php
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

use Bitrix\Main\Engine\Contract\Controllerable;
use Bitrix\Main\Engine\ActionFilter\Csrf;
use Bitrix\Main\Engine\ActionFilter\HttpMethod;
use Bitrix\Main\Loader;
use Bitrix\Sale\Basket;



class CMyBasket extends CBitrixComponent implements Controllerable{

   public function configureActions()
    {
        return [ 
            'GetBasket' => [ // Ajax-метод
                'prefilters' => [],
                'postfilters' => []
            ],
            'UpdateItem'=> [ // Ajax-метод
                'prefilters' => [],
                'postfilters' => []
            ],
            'DeleteItem'=> [ // Ajax-метод
                'prefilters' => [],
                'postfilters' => []
            ],
        ];
    }
   public function __construct(CBitrixComponent $component = null)
   {
        parent::__construct($component);       
        Loader::includeModule("sale"); //сразу подключаем модуль sale

   }    

   public function executeComponent(){ 
      $this->GetBasket(); 
      $this->includeComponentTemplate(); 
   }

   public function GetBasket(){ //получение товаров в корзине
        
        $fUser = Fuser::getId();
        $basket = Basket::loadItemsForFUser($fUser, SITE_ID);       
        $this->arResult = [
            "totalPrice" => $basket->getPrice(),             
            "items" => [],        
        ];      
        $items = $basket->getBasketItems();        
        foreach ($items as $item) {            
            $this->arResult["items"][] = [
                "id" => $item->getID(),
                "title" => $item->getField("NAME"),
                "priceItem"=> $item->getPrice(),
                "price" => ceil($item->getPrice() * $item->getField("QUANTITY")*100)/100,                
                "quantity" => intval($item->getField("QUANTITY")),
            ];
        }
   }
   
   public function GetBasketAction($sessid){ 
      $this->GetBasket();
      return $this->arResult;
   } 
   
   public function UpdateItemAction($id,$q,$sessid){ 
      if (!check_bitrix_sessid()) {
         throw new \Exception("SESSION_EXPIRED", 1);
      }
      $fUser = Fuser::getId();
      $basket = Basket::loadItemsForFUser($fUser, SITE_ID);
      $items = $basket->getBasketItems();
      foreach ($items as $item) {  
         if($item->getID() == $id){
            $item->setField('QUANTITY', $q);
            $item->save();
            break;
         }
      } 
      return true;  

   }

   public function DeleteItemAction($id,$sessid){ 
      if (!check_bitrix_sessid()) {
         throw new \Exception("SESSION_EXPIRED", 1);
      }
      $fUser = Fuser::getId();
      $basket = Basket::loadItemsForFUser($fUser, SITE_ID);
      $items = $basket->getBasketItems();
      foreach ($items as $item) {  
         if($item->getID() == $id){
            $item->delete();
            $basket->save();
            break;
         }
      } 
      return true; 
   }
}
template.php
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
\Bitrix\Main\UI\Extension::load("ui.vue");
\Bitrix\Main\UI\Extension::load("ui.vue.vuex");
?>
<div id="basket"></div>
<script>
  var store = BX.Vuex.store({
      
    state: {
      loaded: false,
      items: <?= CUtil::PhpToJSObject($arResult['items']) ?>,      
      totalPrice: <?=$arResult['totalPrice']?> 
    },
    actions: {
    },
    mutations: { //мутаторы, при их вызове будет перерисовываться корзина
      setLoad(state, flag) {
        state.loaded = flag;       
      },
      setbasketItems(state, items) {
        state.items = items;        
      },
      setTotalprice(state, totalPrice) {
        state.totalPrice = totalPrice;        
      }
    }
  })

  BX.Vue.create({
    el: '#basket',
    store: store,
    template: `<basket/>`,
  })
</script>
script.js
;(function (window) {   
   "use strict"

   const BX = window.BX;

   BX.Vue.component("basket", { 
      props:{},
      data(){ 
         return { //данные для первоначальной отрисовки
            items: this.$store.state.items,
            loaded: false,
            totalPrice: this.$store.state.totalPrice,            
         }
      },            
      created() {
         BX.Vue.event.$on("updateBasket", this.getBasket); //подписываемся на событие updateBasket (если это корзина в шапке и надо обновлять при добавлении нового товара в корзину)
      },      
      updated: function () {
         //при изменении обработчик
      },
      beforeDestroy() {
         //обработчик перед destroy
      },
      methods: {         
         getBasket: function () {
            store.commit("setLoad", true);
            var request = BX.ajax.runComponentAction("test:basket", "GetBasket", { //запустится метод GetBasketAction из class.php
               mode: "class",              
               data: {                  
                  sessid: BX.message("bitrix_sessid"),
               },
            })

            request.catch(function (response) {
               console.log(response);
               store.commit("setLoad", false);
            })

            request.then(function (response) {               
               if (response.status == "success") {                                    
                  store.commit("setbasketItems", response.data.items);
                  store.commit("setTotalprice", response.data.totalPrice);                     
                  store.commit("setLoad", false);                  
               }
            })
         },

         remove: function (id) {           
            store.commit("setLoad", true);
            var t = this;
            var request = BX.ajax.runComponentAction("test:basket", "DeleteItem", { //запустится метод DeleteItemAction из class.php

               mode: "class",
               data: {
                  id: id,
                  sessid: BX.message("bitrix_sessid"),
               },
            })

            request.catch(function (response) {
               console.log(response);
               store.commit("setLoad", false);
            });
            request.then(function (response) {
               if (response.status == "success") {
                  t.getBasket();
               }
            });
         },
         minus: function (id,q) {            
            if(q==1){//удаляем
               this.remove(id);
            }
            else{
               q = q -1;
            }
            this.update(id,q);

         }, 
         plus: function (id,q) {
            q = q*1+1;
            this.update(id,q);
         },
        
         update: function (id,q) {
            var t = this;
            store.commit("setLoad", true);
            var request = BX.ajax.runComponentAction("test:basket", "UpdateItem", { //запустится метод UpdateItemAction из class.php

               mode: "class",
               data: {
                  id: id,
                  q: q,
                  sessid: BX.message("bitrix_sessid"),
               },
            });

            request.catch(function (response) {
               console.log(response);
               store.commit("setLoad", false);
            });

            request.then(function (response) {
               if (response.status == "success") {
                  t.getBasket();
               }
            });
         },   
         
         save: function () {},
      },
      computed: {},
      template: `
         <div>
            <h1>Корзина</h1>       
            <div v-if="this.$store.state.loaded">
               ожидайте... 
            </div>
            <div v-if="!this.$store.state.loaded">            
               <div v-if="this.$store.state.items.length==0">               
                     корзина пустая             
               </div>
               <div v-else>                
                  <div v-for="item in this.$store.state.items" :key="item.id">                     
                     <b>{{item.title}}</b>  {{item.priceItem}} руб/шт.  
                     <button  v-on:click="minus(item.id,item.quantity)">-</button>
                     {{item.quantity}} шт.
                     <button v-on:click="plus(item.id,item.quantity)">+</button>
                     => {{item.price}} руб.                        
                     <button v-on:click="remove(item.id)">Удалить</button>                     
                                         
                  </div>  
                  <br><br>                                  
                  <div>Всего цена: {{this.$store.state.totalPrice}} руб.</div>                  
                  <div><a href="/personal/order/make/">Оформить заказ</a></div>                     
               </div>
            </div>
         </div>       
        `,
   })
})(window);



вызываем событие (например, при добавлении товара в корзину)
и наша корзина обновится
BX.Vue.event.$emit("updateBasket", {});
страница, где корзина /cart/index.php
вызываем компонент
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->SetTitle("Корзина");?>
<?$APPLICATION->IncludeComponent("test:basket", "", []);?>
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>

вот так выглядит (страшно, но зато элементарно по коду все)



Директивы vue

v-show
отображение элемента в зависимости от переданного значения
showH1 - true/false
элемент всегда в DOM, менятся display
<h1 v-show="showH1">Заголовок</h1>
v-if
как v-show, но элемента не будет  в DOM
showH1 - true/false
<h1 v-if="showH1">Заголовок</h1>
v-else
иначе
после v-if
<h1 v-if="showH1">Заголовок 1</h1>
<h1 v-else>Заголовок 2</h1>
но надежнее 2 v-if
<h1 v-if="showH1">Заголовок 1</h1>
<h1 v-if="!showH1">Заголовок 2</h1>
v-else-if
<h1 v-if="showH1">Заголовок 1</h1>
<h1 v-else-if="showH2">Заголовок 2</h1>
<h1 v-else>Заголовок 3</h1>

v-bind
для вывода динамических данных внутри html атрибута
например, href будет меняться от myUrl
<а v-bind:href="myUrl">Ссылка</а>

v-model
как v-bind, но связка двухсторонняя
например, при изменении value инпута меняется someText
<input type="text" v-model="someText">

v-on
вешаем событие
myMethod описать в methods:
<button v-on:click="myMethod">Click me</button>

с версии vue 2.4.0 доступна возможность навесить сразу несколько событий на элемент.

<button v-on="{ mousedown: myMethod1, mouseup: myMethod2 }">Click me</button>

v-for
цикл
<div v-for="item in this.$store.state.items" :key="item.id">   
  {{item.title}} -  {{item.id}}
</div>  

v-html
для вставки html
переменная myHtml будет версткой
<div v-html="myHtml"></div>




Если блог был полезным, можете угостить меня "чашечкой кофе" :)

Сбер по номеру телефона +7 (953) 585-13-09 Вероника.
Спасибо!