├── .gitignore ├── Cart.php ├── CartItem.php ├── LICENSE ├── README.md ├── calculators ├── CalculatorInterface.php └── SimpleCalculator.php ├── composer.json ├── docs └── guide-ru.md ├── migrations └── m180604_202836_create_cart_items_table.php ├── phpunit.xml.dist ├── storage ├── CookieStorage.php ├── DbSessionStorage.php ├── SessionStorage.php └── StorageInterface.php └── tests ├── CartTest.php ├── CookieStorageTest.php ├── SessionStorageTest.php ├── SimpleCalculatorTest.php ├── TestCase.php ├── bootstrap.php └── data ├── DummyProduct.php └── DummyStorage.php /.gitignore: -------------------------------------------------------------------------------- 1 | # cache directories 2 | Thumbs.db 3 | *.DS_Store 4 | *.empty 5 | 6 | #phpstorm project files 7 | .idea 8 | 9 | #netbeans project files 10 | nbproject 11 | 12 | #eclipse, zend studio, aptana or other eclipse like project files 13 | .buildpath 14 | .project 15 | .settings 16 | 17 | # mac deployment helpers 18 | switch 19 | index 20 | 21 | vendor/ 22 | composer.lock 23 | -------------------------------------------------------------------------------- /Cart.php: -------------------------------------------------------------------------------- 1 | 'cart', 30 | 'expire' => 604800, 31 | 'productClass' => 'app\model\Product', 32 | 'productFieldId' => 'id', 33 | 'productFieldPrice' => 'price', 34 | ]; 35 | 36 | /** 37 | * @var CartItem[] 38 | */ 39 | private $items; 40 | 41 | /** 42 | * @var \devanych\cart\storage\StorageInterface 43 | */ 44 | private $storage; 45 | 46 | /** 47 | * @var \devanych\cart\calculators\CalculatorInterface 48 | */ 49 | private $calculator; 50 | 51 | /** 52 | * @inheritdoc 53 | */ 54 | public function init() 55 | { 56 | parent::init(); 57 | 58 | $this->params = array_merge($this->defaultParams, $this->params); 59 | 60 | if (!class_exists($this->params['productClass'])) { 61 | throw new InvalidConfigException('productClass `' . $this->params['productClass'] . '` not found'); 62 | } 63 | if (!class_exists($this->storageClass)) { 64 | throw new InvalidConfigException('storageClass `' . $this->storageClass . '` not found'); 65 | } 66 | if (!class_exists($this->calculatorClass)) { 67 | throw new InvalidConfigException('calculatorClass `' . $this->calculatorClass . '` not found'); 68 | } 69 | 70 | $this->storage = new $this->storageClass($this->params); 71 | $this->calculator = new $this->calculatorClass(); 72 | } 73 | 74 | /** 75 | * Add an item to the cart 76 | * @param object $product 77 | * @param integer $quantity 78 | * @return void 79 | */ 80 | public function add($product, $quantity) 81 | { 82 | $this->loadItems(); 83 | if (isset($this->items[$product->{$this->params['productFieldId']}])) { 84 | $this->plus($product->{$this->params['productFieldId']}, $quantity); 85 | } else { 86 | $this->items[$product->{$this->params['productFieldId']}] = new CartItem($product, $quantity, $this->params); 87 | ksort($this->items, SORT_NUMERIC); 88 | $this->saveItems(); 89 | } 90 | } 91 | 92 | /** 93 | * Adding item quantity in the cart 94 | * @param integer $id 95 | * @param integer $quantity 96 | * @return void 97 | */ 98 | public function plus($id, $quantity) 99 | { 100 | $this->loadItems(); 101 | if (isset($this->items[$id])) { 102 | $this->items[$id]->setQuantity($quantity + $this->items[$id]->getQuantity()); 103 | } 104 | $this->saveItems(); 105 | } 106 | 107 | /** 108 | * Change item quantity in the cart 109 | * @param integer $id 110 | * @param integer $quantity 111 | * @return void 112 | */ 113 | public function change($id, $quantity) 114 | { 115 | $this->loadItems(); 116 | if (isset($this->items[$id])) { 117 | $this->items[$id]->setQuantity($quantity); 118 | } 119 | $this->saveItems(); 120 | } 121 | 122 | /** 123 | * Removes an items from the cart 124 | * @param integer $id 125 | * @return void 126 | */ 127 | public function remove($id) 128 | { 129 | $this->loadItems(); 130 | if (array_key_exists($id, $this->items)) { 131 | unset($this->items[$id]); 132 | } 133 | $this->saveItems(); 134 | } 135 | 136 | /** 137 | * Removes all items from the cart 138 | * @return void 139 | */ 140 | public function clear() 141 | { 142 | $this->items = []; 143 | $this->saveItems(); 144 | } 145 | 146 | /** 147 | * Returns all items from the cart 148 | * @return CartItem[] 149 | */ 150 | public function getItems() 151 | { 152 | $this->loadItems(); 153 | return $this->items; 154 | } 155 | 156 | /** 157 | * Returns an item from the cart 158 | * @param integer $id 159 | * @return CartItem 160 | */ 161 | public function getItem($id) 162 | { 163 | $this->loadItems(); 164 | return isset($this->items[$id]) ? $this->items[$id] : null; 165 | } 166 | 167 | /** 168 | * Returns ids array all items from the cart 169 | * @return array 170 | */ 171 | public function getItemIds() 172 | { 173 | $this->loadItems(); 174 | $items = []; 175 | foreach ($this->items as $item) { 176 | $items[] = $item->getId(); 177 | } 178 | return $items; 179 | } 180 | 181 | /** 182 | * Returns total cost all items from the cart 183 | * @return integer 184 | */ 185 | public function getTotalCost() 186 | { 187 | $this->loadItems(); 188 | return $this->calculator->getCost($this->items); 189 | } 190 | 191 | /** 192 | * Returns total count all items from the cart 193 | * @return integer 194 | */ 195 | public function getTotalCount() 196 | { 197 | $this->loadItems(); 198 | return $this->calculator->getCount($this->items); 199 | } 200 | 201 | /** 202 | * Load all items from the cart 203 | * @return void 204 | */ 205 | private function loadItems() 206 | { 207 | if ($this->items === null) { 208 | $this->items = $this->storage->load(); 209 | } 210 | } 211 | 212 | /** 213 | * Save all items to the cart 214 | * @return void 215 | */ 216 | private function saveItems() 217 | { 218 | $this->storage->save($this->items); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /CartItem.php: -------------------------------------------------------------------------------- 1 | product = $product; 23 | $this->quantity = $quantity; 24 | $this->params = $params; 25 | } 26 | 27 | /** 28 | * Returns the id of the item 29 | * @return integer 30 | */ 31 | public function getId() 32 | { 33 | return $this->product->{$this->params['productFieldId']}; 34 | } 35 | 36 | /** 37 | * Returns the price of the item 38 | * @return integer|float 39 | */ 40 | public function getPrice() 41 | { 42 | return $this->product->{$this->params['productFieldPrice']}; 43 | } 44 | 45 | /** 46 | * Returns the product, AR model 47 | * @return object 48 | */ 49 | public function getProduct() 50 | { 51 | return $this->product; 52 | } 53 | 54 | /** 55 | * Returns the cost of the item 56 | * @return integer|float 57 | */ 58 | public function getCost() 59 | { 60 | return ceil($this->getPrice() * $this->quantity); 61 | } 62 | 63 | /** 64 | * Returns the quantity of the item 65 | * @return integer 66 | */ 67 | public function getQuantity() 68 | { 69 | return $this->quantity; 70 | } 71 | 72 | /** 73 | * Sets the quantity of the item 74 | * @param integer $quantity 75 | * @return void 76 | */ 77 | public function setQuantity($quantity) 78 | { 79 | $this->quantity = $quantity; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Evgeniy Zyubin (Devanych) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of sitemap nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 shopping cart 2 | 3 | This extension adds shopping cart for Yii framework 2.0 4 | 5 | Guide with a detailed description in Russian language [here](https://github.com/devanych/yii2-cart/blob/master/docs/guide-ru.md). 6 | 7 | ## Installation 8 | 9 | The preferred way to install this extension is through [Composer](https://getcomposer.org/download/) 10 | 11 | Either run 12 | 13 | ``` 14 | php composer.phar require devanych/yii2-cart "*" 15 | ``` 16 | 17 | or add 18 | 19 | ``` 20 | devanych/yii2-cart: "*" 21 | ``` 22 | 23 | to the `require` section of your `composer.json` file. 24 | 25 | ## Configuration 26 | 27 | Configure the `cart` component (default values are shown): 28 | 29 | ```php 30 | return [ 31 | //... 32 | 'components' => [ 33 | //... 34 | 'cart' => [ 35 | 'class' => 'devanych\cart\Cart', 36 | 'storageClass' => 'devanych\cart\storage\SessionStorage', 37 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 38 | 'params' => [ 39 | 'key' => 'cart', 40 | 'expire' => 604800, 41 | 'productClass' => 'app\model\Product', 42 | 'productFieldId' => 'id', 43 | 'productFieldPrice' => 'price', 44 | ], 45 | ], 46 | ] 47 | //... 48 | ]; 49 | ``` 50 | 51 | In addition to `devanych\cart\storage\SessionStorage`, there is also `devanych\cart\storage\CookieStorage` and `devanych\cart\storage\DbSessionStorage`. It is possible to create your own storage, you need to implement the interface `devanych\cart\storage\StorageInterface`. 52 | 53 | `DbSessionStorage` uses `SessionStorage` for unauthorized users and database for authorized. 54 | 55 | > If you use the `devanych\cart\storage\DbSessionStorage` as `storageClass` then you need to apply the following migration: 56 | 57 | ```php 58 | php yii migrate --migrationPath=@vendor/devanych/yii2-cart/migrations 59 | ``` 60 | 61 | `devanych\cart\calculators\SimpleCalculator` produces the usual calculation of the total cost and total quantity of items in the cart. If you need to make a calculation with discounts or something else, you can create your own calculator by implementing the interface `devanych\cart\calculators\CalculatorInterface`. 62 | 63 | Setting up the `params` array: 64 | 65 | * `key` - For Session and Cookie. 66 | 67 | * `expire` - Cookie life time. 68 | 69 | * `productClass` - Product class is an ActiveRecord model. 70 | 71 | * `productFieldId` - Name of the product model `id` field. 72 | 73 | * `productFieldPrice` - Name of the product model `price` field. 74 | 75 | #### Supporting multiple shopping carts to same website: 76 | 77 | ```php 78 | //... 79 | 'cart' => [ 80 | 'class' => 'devanych\cart\Cart', 81 | 'storageClass' => 'devanych\cart\storage\SessionStorage', 82 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 83 | 'params' => [ 84 | 'key' => 'cart', 85 | 'expire' => 604800, 86 | 'productClass' => 'app\model\Product', 87 | 'productFieldId' => 'id', 88 | 'productFieldPrice' => 'price', 89 | ], 90 | ], 91 | 'favorite' => [ 92 | 'class' => 'devanych\cart\Cart', 93 | 'storageClass' => 'devanych\cart\storage\DbSessionStorage', 94 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 95 | 'params' => [ 96 | 'key' => 'favorite', 97 | 'expire' => 604800, 98 | 'productClass' => 'app\models\Product', 99 | 'productFieldId' => 'id', 100 | 'productFieldPrice' => 'price', 101 | ], 102 | ], 103 | //... 104 | ``` 105 | 106 | ## Usage 107 | 108 | You can get the shopping cart component anywhere in the app using `Yii::$app->cart`. 109 | 110 | Using cart: 111 | 112 | ```php 113 | // Product is an AR model 114 | $product = Product::findOne(1); 115 | 116 | // Get component of the cart 117 | $cart = \Yii::$app->cart; 118 | 119 | // Add an item to the cart 120 | $cart->add($product, $quantity); 121 | 122 | // Adding item quantity in the cart 123 | $cart->plus($product->id, $quantity); 124 | 125 | // Change item quantity in the cart 126 | $cart->change($product->id, $quantity); 127 | 128 | // Removes an items from the cart 129 | $cart->remove($product->id); 130 | 131 | // Removes all items from the cart 132 | $cart->clear(); 133 | 134 | // Get all items from the cart 135 | $cart->getItems(); 136 | 137 | // Get an item from the cart 138 | $cart->getItem($product->id); 139 | 140 | // Get ids array all items from the cart 141 | $cart->getItemIds(); 142 | 143 | // Get total cost all items from the cart 144 | $cart->getTotalCost(); 145 | 146 | // Get total count all items from the cart 147 | $cart->getTotalCount(); 148 | ``` 149 | 150 | #### Using cart items: 151 | 152 | ```php 153 | // Product is an AR model 154 | $product = Product::findOne(1); 155 | 156 | // Get component of the cart 157 | $cart = \Yii::$app->cart; 158 | 159 | // Get an item from the cart 160 | $item = $cart->getItem($product->id); 161 | 162 | // Get the id of the item 163 | $item->getId(); 164 | 165 | // Get the price of the item 166 | $item->getPrice(); 167 | 168 | // Get the product, AR model 169 | $item->getProduct(); 170 | 171 | // Get the cost of the item 172 | $item->getCost(); 173 | 174 | // Get the quantity of the item 175 | $item->getQuantity(); 176 | 177 | // Set the quantity of the item 178 | $item->setQuantity($quantity); 179 | ``` 180 | 181 | > By using method `getProduct()`, you have access to all the properties and methods of the product. 182 | 183 | ```php 184 | $product = $item->getProduct(); 185 | 186 | echo $product->name; 187 | ``` 188 | -------------------------------------------------------------------------------- /calculators/CalculatorInterface.php: -------------------------------------------------------------------------------- 1 | getCost(); 16 | } 17 | return $cost; 18 | } 19 | 20 | /** 21 | * @param \devanych\cart\CartItem[] $items 22 | * @return integer 23 | */ 24 | public function getCount(array $items) 25 | { 26 | $count = 0; 27 | foreach ($items as $item) { 28 | $count += $item->getQuantity(); 29 | } 30 | return $count; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devanych/yii2-cart", 3 | "description": "Shopping cart for Yii2", 4 | "keywords": ["yii2-cart", "yii2-shopping-cart", "yii2-extension", "yii2"], 5 | "type": "yii2-extension", 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Evgeniy Zyubin", 10 | "email": "info@zyubin.ru", 11 | "homepage": "https://zyubin.ru" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/devanych/yii2-cart/issues?state=open", 16 | "source": "https://github.com/devanych/yii2-cart" 17 | }, 18 | "require": { 19 | "php": ">=5.6", 20 | "yiisoft/yii2": "*" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "~6.0" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "devanych\\cart\\": "" 28 | } 29 | }, 30 | "repositories": [ 31 | { 32 | "type": "composer", 33 | "url": "https://asset-packagist.org" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /docs/guide-ru.md: -------------------------------------------------------------------------------- 1 | # Расширение корзина для Yii2 2 | 3 | Корзина — это обязательный компонент для любого интернет-магазина, но ее функциональность в разных проектах может различаться. На одном сайте корзина работает с сессией, на втором — с кукисами, а на третьем — с базой данных, также не исключено, что со временем хранилище может меняться. 4 | 5 | Тоже касается и подсчета стоимости, например: нужно считать цену товара со скидкой в определенный день недели или в какой-то праздник, но это все невозможно предусмотреть при разработке корзины, поэтому должна быть возможность удобной кастомизации в будущем. 6 | 7 | Расширение «[devanych/yii2-cart](https://github.com/devanych/yii2-cart)» решает эти проблемы и позволяет очень легко менять хранилища и калькуляторы, дает возможность подключать собственные решения. 8 | 9 | Установить расширение через «[Composer](https://getcomposer.org/download/)»: 10 | 11 | ``` 12 | php composer.phar require devanych/yii2-cart "*" 13 | ``` 14 | 15 | или прописать зависимость в разделе `require` в файле `composer.json`: 16 | 17 | ``` 18 | devanych/yii2-cart: "*" 19 | ``` 20 | 21 | и выполнить в терминале `composer update`. 22 | 23 | ## Конфигурация 24 | 25 | В конфигурационном файле Yii2 приложения (`web.php` в «***basic***» и `main.php` в «***advanced***») в секцию `components` помещаем следующий код. 26 | 27 | ```php 28 | return [ 29 | //... 30 | 'components' => [ 31 | //... 32 | 'cart' => [ 33 | 'class' => 'devanych\cart\Cart', 34 | 'storageClass' => 'devanych\cart\storage\SessionStorage', 35 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 36 | 'params' => [ 37 | 'key' => 'cart', 38 | 'expire' => 604800, 39 | 'productClass' => 'app\model\Product', 40 | 'productFieldId' => 'id', 41 | 'productFieldPrice' => 'price', 42 | ], 43 | ], 44 | ] 45 | //... 46 | ]; 47 | ``` 48 | 49 | Свойство `storageClass` отвечает за хранилище, используемое корзиной, по умолчанию это `devanych\cart\storage\SessionStorage`. Сессионное хранилище можно поменять на `devanych\cart\storage\CookieStorage` или на `devanych\cart\storage\DbSessionStorage`. 50 | 51 | `DbSessionStorage` использует сессионное хранилище для не авторизованных пользователей и базу данных для авторизованных. Для его использования необходимо применить следующую миграцию. 52 | 53 | ``` 54 | php yii migrate --migrationPath=@vendor/devanych/yii2-cart/migrations 55 | ``` 56 | 57 | Если этих хранилищ окажется недостаточно, то вы можете создать собственное. Для этого необходимо реализовать интерфейс `devanych\cart\storage\StorageInterface` и указать свой созданный класс значением свойства `storageClass`. 58 | 59 | Свойству `calculatorClass` присвоено имя класса калькулятора `devanych\cart\calculators\SimpleCalculator`, этот класс подсчитывает общую стоимость и количество всех элементов корзины. Для реализации собственного калькулятора нужно реализовать интерфейс `devanych\cart\calculators\CalculatorInterface`. 60 | 61 | Разберем все дополнительные настройки, находящиеся в подмассиве `params`: 62 | 63 | * `key` — имя необходимое для сессии и куки (по умолчанию — `cart`); 64 | * `expire` — срок жизни cookie (по умолчанию — `604800`, т.е. неделя); 65 | * `productClass` — класс товара ActiveRecord модели (по умолчанию — `app\model\Product`); 66 | * `productFieldId` — первичный ключ модели товара (по умолчанию — `id`); 67 | * `productFieldId` — свойство (поле в БД) цены модели товара (по умолчанию — `price`). 68 | 69 | Вы можете использовать несколько компонентов корзины, например, хранить еще избранные товары: 70 | 71 | ```php 72 | //... 73 | 'cart' => [ 74 | 'class' => 'devanych\cart\Cart', 75 | 'storageClass' => 'devanych\cart\storage\SessionStorage', 76 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 77 | 'params' => [ 78 | 'key' => 'cart', 79 | 'expire' => 604800, 80 | 'productClass' => 'app\model\Product', 81 | 'productFieldId' => 'id', 82 | 'productFieldPrice' => 'price', 83 | ], 84 | ], 85 | 'favorite' => [ 86 | 'class' => 'devanych\cart\Cart', 87 | 'storageClass' => 'devanych\cart\storage\DbSessionStorage', 88 | 'calculatorClass' => 'devanych\cart\calculators\SimpleCalculator', 89 | 'params' => [ 90 | 'key' => 'favorite', 91 | 'expire' => 604800, 92 | 'productClass' => 'app\models\Product', 93 | 'productFieldId' => 'id', 94 | 'productFieldPrice' => 'price', 95 | ], 96 | ], 97 | //... 98 | ``` 99 | 100 | ## Использование 101 | 102 | Подключение корзины как компонента дает возможность обращения к ней практически из любого места приложения, используя сервис локатор `Yii::$app`. 103 | 104 | Использование корзины: 105 | 106 | ```php 107 | // Товар, объект AR модели 108 | $product = Product::findOne(1); 109 | 110 | // Компонент корзины 111 | $cart = \Yii::$app->cart; 112 | 113 | // Создает элемент корзины из переданного товара и его кол-ва 114 | $cart->add($product, $quantity); 115 | 116 | // Добавляет кол-во существующего элемента корзины 117 | $cart->plus($product->id, $quantity); 118 | 119 | // Изменяет кол-во существующего элемента корзины 120 | $cart->change($product->id, $quantity); 121 | 122 | // Удаляет конкретный элемент из корзины, объект `devanych\cart\CartItem` 123 | $cart->remove($product->id); 124 | 125 | // Удаляет все элемент из корзины 126 | $cart->clear(); 127 | 128 | // Получает все элемент из корзины 129 | $cart->getItems(); 130 | 131 | // Получает конкретный элемент из корзины 132 | $cart->getItem($product->id); 133 | 134 | // Получает идентификаторы всех элементов 135 | $cart->getItemIds(); 136 | 137 | // Получает общую стоимость всех элементов 138 | $cart->getTotalCost(); 139 | 140 | // Получает общее количество всех элементов 141 | $cart->getTotalCount(); 142 | ``` 143 | 144 | Использование элементов корзины: 145 | 146 | ```php 147 | // Товар, объект AR модели 148 | $product = Product::findOne(1); 149 | 150 | // Компонент корзины 151 | $cart = \Yii::$app->cart; 152 | 153 | // Получает конкретный элемент из корзины, объект `devanych\cart\CartItem` 154 | $item = $cart->getItem($product->id); 155 | 156 | // Получает идентификатор элемента равному идентификатор товара 157 | $item->getId(); 158 | 159 | // Получает цену элемента равную цене товара 160 | $item->getPrice(); 161 | 162 | // Получает товар, объект AR модели 163 | $item->getProduct(); 164 | 165 | // Получает общую стоимость товара хранящегося в элементе по его количеству 166 | $item->getCost(); 167 | 168 | // Получает общее кол-во товара хранящегося в элементе корзины 169 | $item->getQuantity(); 170 | 171 | // Устанавливает кол-во товара хранящегося в элементе корзины 172 | $item->setQuantity($quantity); 173 | ``` 174 | 175 | Стоит отметить, что метод `getProduct()` элемента корзины (`devanych\cart\CartItem`) возвращает полноценный объект ActiveRecord модели, это очень удобно, если нужно вывести какую-то информацию о хранящемся в корзине товаре. 176 | 177 | ## Простая реализация контроллера и представления 178 | 179 | Данное расширение дает возможность реализации любого представления для корзины. Я специально не стал делать дефолтный контроллер и представление, так как в каждом проекте корзина реализовывается по-разному: где-то просто, где-то без перезагрузки страницы, где-то в модальном окне, и т.д., а это значит, что и код будет отличаться. 180 | 181 | В самом примитивном варианте, если «запихать» все в контроллер (хотя так делать не нужно `;-))`, класс контроллера будет выглядеть так. 182 | 183 | ```php 184 | namespace app\controllers; 185 | 186 | use Yii; 187 | use yii\helpers\Html; 188 | use yii\web\Controller; 189 | use app\models\Product; 190 | 191 | class CartController extends Controller 192 | { 193 | /** 194 | * @var \devanych\cart\Cart $cart 195 | */ 196 | private $cart; 197 | 198 | public function __construct($id, $module, $config = []) 199 | { 200 | parent::__construct($id, $module, $config); 201 | $this->cart = Yii::$app->cart; 202 | } 203 | 204 | public function actionIndex() 205 | { 206 | return $this->render('index', [ 207 | 'cart' => $this->cart, 208 | ]); 209 | } 210 | 211 | public function actionAdd($id, $qty = 1) 212 | { 213 | try { 214 | $product = $this->getProduct($id); 215 | $quantity = $this->getQuantity($qty, $product->quantity); 216 | if ($item = $this->cart->getItem($product->id)) { 217 | $this->cart->plus($item->getId(), $quantity); 218 | } else { 219 | $this->cart->add($product, $quantity); 220 | } 221 | } catch (\DomainException $e) { 222 | Yii::$app->errorHandler->logException($e); 223 | Yii::$app->session->setFlash('error', $e->getMessage()); 224 | } 225 | return $this->redirect(['index']); 226 | } 227 | 228 | public function actionChange($id, $qty = 1) 229 | { 230 | try { 231 | $product = $this->getProduct($id); 232 | $quantity = $this->getQuantity($qty, $product->quantity); 233 | if ($item = $this->cart->getItem($product->id)) { 234 | $this->cart->change($item->getId(), $quantity); 235 | } 236 | } catch (\DomainException $e) { 237 | Yii::$app->errorHandler->logException($e); 238 | Yii::$app->session->setFlash('error', $e->getMessage()); 239 | } 240 | return $this->redirect(['index']); 241 | } 242 | 243 | public function actionRemove($id) 244 | { 245 | try { 246 | $product = $this->getProduct($id); 247 | $this->cart->remove($product->id); 248 | } catch (\DomainException $e) { 249 | Yii::$app->errorHandler->logException($e); 250 | Yii::$app->session->setFlash('error', $e->getMessage()); 251 | } 252 | return $this->redirect(['index']); 253 | } 254 | 255 | public function actionClear() 256 | { 257 | $this->cart->clear(); 258 | return $this->redirect(['index']); 259 | } 260 | 261 | /** 262 | * @param integer $id 263 | * @return Product the loaded model 264 | * @throws \DomainException if the product cannot be found 265 | */ 266 | private function getProduct($id) 267 | { 268 | if (($product = Product::findOne((int)$id)) !== null) { 269 | return $product; 270 | } 271 | throw new \DomainException('Товар не найден'); 272 | } 273 | 274 | /** 275 | * @param integer $qty 276 | * @param integer $maxQty 277 | * @return integer 278 | * @throws \DomainException if the product cannot be found 279 | */ 280 | private function getQuantity($qty, $maxQty) 281 | { 282 | $quantity = (int)$qty > 0 ? (int)$qty : 1; 283 | if ($quantity > $maxQty) { 284 | throw new \DomainException('Товара в наличии всего ' . Html::encode($maxQty) . ' шт.'); 285 | } 286 | return $quantity; 287 | } 288 | } 289 | ``` 290 | 291 | Ну и осталось создать представление `index.php`, в которое передается корзина, пройтись по ней в цикле и вывести информацию о добавленных товарах либо использовать `GridView`. 292 | 293 | ```html 294 | 301 | getItems())): ?> 302 |
Фото | 307 |Наименование | 308 |Кол-во | 309 |Цена | 310 |Сумма | 311 |× | 312 |
---|---|---|---|---|---|
=Html::img("@web{$item->getProduct()->photo}", ['alt' => $item->getProduct()->name, 'width' => 50])?> | 318 |= $item->getProduct()->name ?> | 319 |=$item->getQuantity()?> | 320 |=$item->getPrice()?> | 321 |=$item->getCost()?> | 322 |Удалить | 323 |
Общее кол-во: | 327 |= $cart->getTotalCount()?> | 328 |||||
Общая сумма: | 331 |=$cart->getTotalCost() ?> | 332 |