├── styleci.yml ├── .gitignore ├── src ├── Exceptions │ ├── InvalidRowIDException.php │ ├── UnknownModelException.php │ ├── CartAlreadyStoredException.php │ └── InvalidCalculatorException.php ├── Contracts │ ├── Calculator.php │ ├── InstanceIdentifier.php │ └── Buyable.php ├── CartItemOptions.php ├── Database │ └── migrations │ │ └── 2018_12_23_120000_create_shoppingcart_table.php ├── ShoppingcartServiceProvider.php ├── CanBeBought.php ├── Calculation │ ├── DefaultCalculator.php │ └── GrossPrice.php ├── Config │ └── cart.php ├── Facades │ └── Cart.php ├── CartItem.php └── Cart.php ├── tests ├── Fixtures │ ├── ProductModel.php │ ├── BuyableProductTrait.php │ ├── Identifiable.php │ └── BuyableProduct.php ├── CartAssertions.php └── CartItemTest.php ├── .github └── workflows │ ├── contributors.yml │ ├── stale.yml │ └── php.yml ├── phpunit.xml ├── LICENSE ├── composer.json ├── README_Idn.md ├── README_uk-UA.md ├── README.md └── README_es.md /styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | coverage.xml 6 | .phpunit.result.cache -------------------------------------------------------------------------------- /src/Exceptions/InvalidRowIDException.php: -------------------------------------------------------------------------------- 1 | get($key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Contracts/InstanceIdentifier.php: -------------------------------------------------------------------------------- 1 | 1, 28 | 'name' => 'Item name', 29 | 'price' => 10.00, 30 | 'weight' => 0, 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /src/Contracts/Buyable.php: -------------------------------------------------------------------------------- 1 | string('identifier'); 16 | $table->string('instance'); 17 | $table->longText('content'); 18 | $table->nullableTimestamps(); 19 | 20 | $table->primary(['identifier', 'instance']); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | */ 27 | public function down() 28 | { 29 | Schema::drop(config('cart.database.table')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | 15 | 16 | 17 | 18 | ./src 19 | 20 | ./src/CanBeBought.php 21 | ./src/Facades 22 | ./src/Database/migrations 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/CartAssertions.php: -------------------------------------------------------------------------------- 1 | count(); 19 | 20 | PHPUnit::assertEquals($items, $cart->count(), "Expected the cart to contain {$items} items, but got {$actual}."); 21 | } 22 | 23 | /** 24 | * Assert that the cart contains the given number of rows. 25 | * 26 | * @param int $rows 27 | * @param \Gloudemans\Shoppingcart\Cart $cart 28 | */ 29 | public function assertRowsInCart($rows, Cart $cart) 30 | { 31 | $actual = $cart->content()->count(); 32 | 33 | PHPUnit::assertCount($rows, $cart->content(), "Expected the cart to contain {$rows} rows, but got {$actual}."); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ShoppingcartServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->bind('cart', 'Gloudemans\Shoppingcart\Cart'); 19 | 20 | $config = __DIR__.'/Config/cart.php'; 21 | $this->mergeConfigFrom($config, 'cart'); 22 | 23 | $this->publishes([__DIR__.'/Config/cart.php' => config_path('cart.php')], 'config'); 24 | 25 | $this->app['events']->listen(Logout::class, function () { 26 | if ($this->app['config']->get('cart.destroy_on_logout')) { 27 | $this->app->make(SessionManager::class)->forget('cart'); 28 | } 29 | }); 30 | 31 | $this->publishes([ 32 | realpath(__DIR__.'/Database/migrations') => $this->app->databasePath().'/migrations', 33 | ], 'migrations'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Rob Gloudemans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/Fixtures/Identifiable.php: -------------------------------------------------------------------------------- 1 | identifier = $identifier; 29 | $this->discountRate = $discountRate; 30 | } 31 | 32 | /** 33 | * Get the unique identifier to load the Cart from. 34 | * 35 | * @return int|string 36 | */ 37 | public function getInstanceIdentifier($options = null) 38 | { 39 | return $this->identifier; 40 | } 41 | 42 | /** 43 | * Get the unique identifier to load the Cart from. 44 | * 45 | * @return int|string 46 | */ 47 | public function getInstanceGlobalDiscount($options = null) 48 | { 49 | return $this->discountRate; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/CanBeBought.php: -------------------------------------------------------------------------------- 1 | getKey() : $this->id; 15 | } 16 | 17 | /** 18 | * Get the name, title or description of the Buyable item. 19 | * 20 | * @return string 21 | */ 22 | public function getBuyableDescription($options = null) 23 | { 24 | if (($name = $this->getAttribute('name'))) { 25 | return $name; 26 | } 27 | 28 | if (($title = $this->getAttribute('title'))) { 29 | return $title; 30 | } 31 | 32 | if (($description = $this->getAttribute('description'))) { 33 | return $description; 34 | } 35 | } 36 | 37 | /** 38 | * Get the price of the Buyable item. 39 | * 40 | * @return float 41 | */ 42 | public function getBuyablePrice($options = null) 43 | { 44 | if (($price = $this->getAttribute('price'))) { 45 | return $price; 46 | } 47 | } 48 | 49 | /** 50 | * Get the weight of the Buyable item. 51 | * 52 | * @return float 53 | */ 54 | public function getBuyableWeight($options = null) 55 | { 56 | if (($weight = $this->getAttribute('weight'))) { 57 | return $weight; 58 | } 59 | 60 | return 0; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Calculation/DefaultCalculator.php: -------------------------------------------------------------------------------- 1 | price * ($cartItem->getDiscountRate() / 100); 17 | case 'tax': 18 | return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals); 19 | case 'priceTax': 20 | return round($cartItem->priceTarget + $cartItem->tax, $decimals); 21 | case 'discountTotal': 22 | return round($cartItem->discount * $cartItem->qty, $decimals); 23 | case 'priceTotal': 24 | return round($cartItem->price * $cartItem->qty, $decimals); 25 | case 'subtotal': 26 | return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0); 27 | case 'priceTarget': 28 | return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals); 29 | case 'taxTotal': 30 | return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals); 31 | case 'total': 32 | return round($cartItem->subtotal + $cartItem->taxTotal, $decimals); 33 | default: 34 | return; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Fixtures/BuyableProduct.php: -------------------------------------------------------------------------------- 1 | 1, 26 | 'name' => 'Item name', 27 | 'price' => 10.00, 28 | 'weight' => 0, 29 | ]; 30 | 31 | /** 32 | * Get the identifier of the Buyable item. 33 | * 34 | * @return int|string 35 | */ 36 | public function getBuyableIdentifier($options = null) 37 | { 38 | return $this->id; 39 | } 40 | 41 | /** 42 | * Get the description or title of the Buyable item. 43 | * 44 | * @return string 45 | */ 46 | public function getBuyableDescription($options = null) 47 | { 48 | return $this->name; 49 | } 50 | 51 | /** 52 | * Get the price of the Buyable item. 53 | * 54 | * @return float 55 | */ 56 | public function getBuyablePrice($options = null) 57 | { 58 | return $this->price; 59 | } 60 | 61 | /** 62 | * Get the price of the Buyable item. 63 | * 64 | * @return float 65 | */ 66 | public function getBuyableWeight($options = null) 67 | { 68 | return $this->weight; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Calculation/GrossPrice.php: -------------------------------------------------------------------------------- 1 | price / (1 + ($cartItem->taxRate / 100)), $decimals); 17 | case 'discount': 18 | return $cartItem->priceNet * ($cartItem->getDiscountRate() / 100); 19 | case 'tax': 20 | return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals); 21 | case 'priceTax': 22 | return round($cartItem->priceTarget + $cartItem->tax, $decimals); 23 | case 'discountTotal': 24 | return round($cartItem->discount * $cartItem->qty, $decimals); 25 | case 'priceTotal': 26 | return round($cartItem->priceNet * $cartItem->qty, $decimals); 27 | case 'subtotal': 28 | return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0); 29 | case 'priceTarget': 30 | return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals); 31 | case 'taxTotal': 32 | return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals); 33 | case 'total': 34 | return round($cartItem->subtotal + $cartItem->taxTotal, $decimals); 35 | default: 36 | return; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: CI Code Checks 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ master ] 7 | 8 | jobs: 9 | run: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | php-versions: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] 15 | env: 16 | PHP_VERSION: ${{ matrix.php-versions }} 17 | name: PHP ${{ matrix.php-versions }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: ${{ matrix.php-versions }} 26 | env: 27 | runner: ubuntu-18.04 28 | 29 | - name: Validate composer.json and composer.lock 30 | run: composer validate 31 | 32 | - name: Get composer cache directory 33 | id: composercache 34 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 35 | 36 | - name: Cache Composer packages 37 | id: composer-cache 38 | uses: actions/cache@v3 39 | with: 40 | path: ${{ steps.composercache.outputs.dir }} 41 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 42 | restore-keys: ${{ runner.os }}-composer- 43 | 44 | - name: Install composer packages 45 | run: composer install --prefer-dist --no-interaction 46 | 47 | - name: Tests Coverage 48 | run: vendor/bin/phpunit --bootstrap ./vendor/autoload.php --coverage-clover=coverage.xml 49 | 50 | - name: Upload coverage to Codecov 51 | uses: codecov/codecov-action@v5 52 | with: 53 | file: ./coverage.xml 54 | flags: unittests 55 | env_vars: PHP_VERSION 56 | token: ${{ secrets.CODECOV_TOKEN }} 57 | fail_ci_if_error: true 58 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anayarojo/shoppingcart", 3 | "description": "Laravel Shoppingcart", 4 | "keywords": ["laravel", "shoppingcart"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Rob Gloudemans", 9 | "email": "info@robgloudemans.nl", 10 | "homepage": "http://robgloudemans.nl/" 11 | }, 12 | { 13 | "name": "Patrick Henninger", 14 | "email": "privat@skyraptor.eu", 15 | "homepage": "https://skyraptor.eu/" 16 | }, 17 | { 18 | "name": "Raul Anaya Rojo", 19 | "email": "isw.anayarojo@gmail.com", 20 | "homepage": "https://anayarojo.net/" 21 | } 22 | ], 23 | "require": { 24 | "illuminate/support": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0||^9.0||^10||^11||^12.0", 25 | "illuminate/session": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0||^9.0||^10||^11||^12.0", 26 | "illuminate/events": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0||^8.0||^9.0||^10||^11||^12.0", 27 | "nesbot/carbon": "~1.20||^1.26.3||^2.0||^3.0" 28 | }, 29 | "require-dev": { 30 | "phpunit/phpunit": "~5.0||~6.0||~7.0||~8.0||~9.0||~10.0||~11.0", 31 | "mockery/mockery": "^1.0", 32 | "orchestra/testbench": "^3.4|^4.0|^5.0|^6.0|^9.0|^10.0" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Gloudemans\\Shoppingcart\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Gloudemans\\Tests\\Shoppingcart\\": "tests/" 42 | } 43 | }, 44 | "suggest": { 45 | "gloudemans/notify": "Simple flash notifications for Laravel" 46 | }, 47 | "minimum-stability": "stable", 48 | "extra": { 49 | "laravel": { 50 | "providers": [ 51 | "Gloudemans\\Shoppingcart\\ShoppingcartServiceProvider" 52 | ], 53 | "aliases": { 54 | "Cart": "Gloudemans\\Shoppingcart\\Facades\\Cart" 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/CartItemTest.php: -------------------------------------------------------------------------------- 1 | 'XL', 'color' => 'red']); 27 | $cartItem->setQuantity(2); 28 | 29 | $this->assertEquals([ 30 | 'id' => 1, 31 | 'name' => 'Some item', 32 | 'price' => 10.00, 33 | 'rowId' => '07d5da5550494c62daf9993cf954303f', 34 | 'qty' => 2, 35 | 'options' => [ 36 | 'size' => 'XL', 37 | 'color' => 'red', 38 | ], 39 | 'tax' => 0, 40 | 'subtotal' => 20.00, 41 | 'discount' => 0.0, 42 | 'weight' => 550.0, 43 | ], $cartItem->toArray()); 44 | } 45 | 46 | /** @test */ 47 | public function it_can_be_cast_to_json() 48 | { 49 | $cartItem = new CartItem(1, 'Some item', 10.00, 550, ['size' => 'XL', 'color' => 'red']); 50 | $cartItem->setQuantity(2); 51 | 52 | $this->assertJson($cartItem->toJson()); 53 | 54 | $json = '{"rowId":"07d5da5550494c62daf9993cf954303f","id":1,"name":"Some item","qty":2,"price":10,"weight":550,"options":{"size":"XL","color":"red"},"discount":0,"tax":0,"subtotal":20}'; 55 | 56 | $this->assertEquals($json, $cartItem->toJson()); 57 | } 58 | 59 | /** @test */ 60 | public function it_formats_price_total_correctly() 61 | { 62 | $cartItem = new CartItem(1, 'Some item', 10.00, 550, ['size' => 'XL', 'color' => 'red']); 63 | $cartItem->setQuantity(2); 64 | 65 | $this->assertSame('20.00', $cartItem->priceTotal()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Config/cart.php: -------------------------------------------------------------------------------- 1 | \Gloudemans\Shoppingcart\Calculation\DefaultCalculator::class, 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Default tax rate 20 | |-------------------------------------------------------------------------- 21 | | 22 | | This default tax rate will be used when you make a class implement the 23 | | Taxable interface and use the HasTax trait. 24 | | 25 | */ 26 | 27 | 'tax' => 21, 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Shoppingcart database settings 32 | |-------------------------------------------------------------------------- 33 | | 34 | | Here you can set the connection that the shoppingcart should use when 35 | | storing and restoring a cart. 36 | | 37 | */ 38 | 39 | 'database' => [ 40 | 41 | 'connection' => null, 42 | 43 | 'table' => 'shoppingcart', 44 | 45 | ], 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Destroy the cart on user logout 50 | |-------------------------------------------------------------------------- 51 | | 52 | | When this option is set to 'true' the cart will automatically 53 | | destroy all cart instances when the user logs out. 54 | | 55 | */ 56 | 57 | 'destroy_on_logout' => false, 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | Default number format 62 | |-------------------------------------------------------------------------- 63 | | 64 | | This defaults will be used for the formatted numbers if you don't 65 | | set them in the method call. 66 | | 67 | */ 68 | 69 | 'format' => [ 70 | 71 | 'decimals' => 2, 72 | 73 | 'decimal_point' => '.', 74 | 75 | 'thousand_separator' => ',', 76 | 77 | ], 78 | 79 | ]; 80 | -------------------------------------------------------------------------------- /src/Facades/Cart.php: -------------------------------------------------------------------------------- 1 | id = $id; 130 | $this->name = $name; 131 | $this->price = floatval($price); 132 | $this->weight = floatval($weight); 133 | $this->options = new CartItemOptions($options); 134 | $this->rowId = $this->generateRowId($id, $options); 135 | } 136 | 137 | /** 138 | * Returns the formatted weight. 139 | * 140 | * @param int $decimals 141 | * @param string $decimalPoint 142 | * @param string $thousandSeperator 143 | * 144 | * @return string 145 | */ 146 | public function weight($decimals = null, $decimalPoint = null, $thousandSeperator = null) 147 | { 148 | return $this->numberFormat($this->weight, $decimals, $decimalPoint, $thousandSeperator); 149 | } 150 | 151 | /** 152 | * Returns the formatted price without TAX. 153 | * 154 | * @param int $decimals 155 | * @param string $decimalPoint 156 | * @param string $thousandSeperator 157 | * 158 | * @return string 159 | */ 160 | public function price($decimals = null, $decimalPoint = null, $thousandSeperator = null) 161 | { 162 | return $this->numberFormat($this->price, $decimals, $decimalPoint, $thousandSeperator); 163 | } 164 | 165 | /** 166 | * Returns the formatted price with discount applied. 167 | * 168 | * @param int $decimals 169 | * @param string $decimalPoint 170 | * @param string $thousandSeperator 171 | * 172 | * @return string 173 | */ 174 | public function priceTarget($decimals = null, $decimalPoint = null, $thousandSeperator = null) 175 | { 176 | return $this->numberFormat($this->priceTarget, $decimals, $decimalPoint, $thousandSeperator); 177 | } 178 | 179 | /** 180 | * Returns the formatted price with TAX. 181 | * 182 | * @param int $decimals 183 | * @param string $decimalPoint 184 | * @param string $thousandSeperator 185 | * 186 | * @return string 187 | */ 188 | public function priceTax($decimals = null, $decimalPoint = null, $thousandSeperator = null) 189 | { 190 | return $this->numberFormat($this->priceTax, $decimals, $decimalPoint, $thousandSeperator); 191 | } 192 | 193 | /** 194 | * Returns the formatted subtotal. 195 | * Subtotal is price for whole CartItem without TAX. 196 | * 197 | * @param int $decimals 198 | * @param string $decimalPoint 199 | * @param string $thousandSeperator 200 | * 201 | * @return string 202 | */ 203 | public function subtotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 204 | { 205 | return $this->numberFormat($this->subtotal, $decimals, $decimalPoint, $thousandSeperator); 206 | } 207 | 208 | /** 209 | * Returns the formatted total. 210 | * Total is price for whole CartItem with TAX. 211 | * 212 | * @param int $decimals 213 | * @param string $decimalPoint 214 | * @param string $thousandSeperator 215 | * 216 | * @return string 217 | */ 218 | public function total($decimals = null, $decimalPoint = null, $thousandSeperator = null) 219 | { 220 | return $this->numberFormat($this->total, $decimals, $decimalPoint, $thousandSeperator); 221 | } 222 | 223 | /** 224 | * Returns the formatted tax. 225 | * 226 | * @param int $decimals 227 | * @param string $decimalPoint 228 | * @param string $thousandSeperator 229 | * 230 | * @return string 231 | */ 232 | public function tax($decimals = null, $decimalPoint = null, $thousandSeperator = null) 233 | { 234 | return $this->numberFormat($this->tax, $decimals, $decimalPoint, $thousandSeperator); 235 | } 236 | 237 | /** 238 | * Returns the formatted tax. 239 | * 240 | * @param int $decimals 241 | * @param string $decimalPoint 242 | * @param string $thousandSeperator 243 | * 244 | * @return string 245 | */ 246 | public function taxTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 247 | { 248 | return $this->numberFormat($this->taxTotal, $decimals, $decimalPoint, $thousandSeperator); 249 | } 250 | 251 | /** 252 | * Returns the formatted discount. 253 | * 254 | * @param int $decimals 255 | * @param string $decimalPoint 256 | * @param string $thousandSeperator 257 | * 258 | * @return string 259 | */ 260 | public function discount($decimals = null, $decimalPoint = null, $thousandSeperator = null) 261 | { 262 | return $this->numberFormat($this->discount, $decimals, $decimalPoint, $thousandSeperator); 263 | } 264 | 265 | /** 266 | * Returns the formatted total discount for this cart item. 267 | * 268 | * @param int $decimals 269 | * @param string $decimalPoint 270 | * @param string $thousandSeperator 271 | * 272 | * @return string 273 | */ 274 | public function discountTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 275 | { 276 | return $this->numberFormat($this->discountTotal, $decimals, $decimalPoint, $thousandSeperator); 277 | } 278 | 279 | /** 280 | * Returns the formatted total price for this cart item. 281 | * 282 | * @param int $decimals 283 | * @param string $decimalPoint 284 | * @param string $thousandSeperator 285 | * 286 | * @return string 287 | */ 288 | public function priceTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 289 | { 290 | return $this->numberFormat($this->priceTotal, $decimals, $decimalPoint, $thousandSeperator); 291 | } 292 | 293 | /** 294 | * Set the quantity for this cart item. 295 | * 296 | * @param int|float $qty 297 | */ 298 | public function setQuantity($qty) 299 | { 300 | if (empty($qty) || !is_numeric($qty)) { 301 | throw new \InvalidArgumentException('Please supply a valid quantity.'); 302 | } 303 | 304 | $this->qty = $qty; 305 | } 306 | 307 | /** 308 | * Update the cart item from a Buyable. 309 | * 310 | * @param \Gloudemans\Shoppingcart\Contracts\Buyable $item 311 | * 312 | * @return void 313 | */ 314 | public function updateFromBuyable(Buyable $item) 315 | { 316 | $this->id = $item->getBuyableIdentifier($this->options); 317 | $this->name = $item->getBuyableDescription($this->options); 318 | $this->price = $item->getBuyablePrice($this->options); 319 | } 320 | 321 | /** 322 | * Update the cart item from an array. 323 | * 324 | * @param array $attributes 325 | * 326 | * @return void 327 | */ 328 | public function updateFromArray(array $attributes) 329 | { 330 | $this->id = Arr::get($attributes, 'id', $this->id); 331 | $this->qty = Arr::get($attributes, 'qty', $this->qty); 332 | $this->name = Arr::get($attributes, 'name', $this->name); 333 | $this->price = Arr::get($attributes, 'price', $this->price); 334 | $this->weight = Arr::get($attributes, 'weight', $this->weight); 335 | $this->options = new CartItemOptions(Arr::get($attributes, 'options', $this->options)); 336 | 337 | $this->rowId = $this->generateRowId($this->id, $this->options->all()); 338 | } 339 | 340 | /** 341 | * Associate the cart item with the given model. 342 | * 343 | * @param mixed $model 344 | * 345 | * @return \Gloudemans\Shoppingcart\CartItem 346 | */ 347 | public function associate($model) 348 | { 349 | $this->associatedModel = is_string($model) ? $model : get_class($model); 350 | 351 | return $this; 352 | } 353 | 354 | /** 355 | * Set the tax rate. 356 | * 357 | * @param int|float $taxRate 358 | * 359 | * @return \Gloudemans\Shoppingcart\CartItem 360 | */ 361 | public function setTaxRate($taxRate) 362 | { 363 | $this->taxRate = $taxRate; 364 | 365 | return $this; 366 | } 367 | 368 | /** 369 | * Set the discount rate. 370 | * 371 | * @param int|float $discountRate 372 | * 373 | * @return \Gloudemans\Shoppingcart\CartItem 374 | */ 375 | public function setDiscountRate($discountRate) 376 | { 377 | $this->discountRate = $discountRate; 378 | 379 | return $this; 380 | } 381 | 382 | /** 383 | * Set cart instance. 384 | * 385 | * @param null|string $instance 386 | * 387 | * @return \Gloudemans\Shoppingcart\CartItem 388 | */ 389 | public function setInstance($instance) 390 | { 391 | $this->instance = $instance; 392 | 393 | return $this; 394 | } 395 | 396 | /** 397 | * Get an attribute from the cart item or get the associated model. 398 | * 399 | * @param string $attribute 400 | * 401 | * @return mixed 402 | */ 403 | public function __get($attribute) 404 | { 405 | if (property_exists($this, $attribute)) { 406 | return $this->{$attribute}; 407 | } 408 | $decimals = config('cart.format.decimals', 2); 409 | 410 | switch ($attribute) { 411 | case 'model': 412 | if (isset($this->associatedModel)) { 413 | return with(new $this->associatedModel())->find($this->id); 414 | } 415 | // no break 416 | case 'modelFQCN': 417 | if (isset($this->associatedModel)) { 418 | return $this->associatedModel; 419 | } 420 | // no break 421 | case 'weightTotal': 422 | return round($this->weight * $this->qty, $decimals); 423 | } 424 | 425 | $class = new ReflectionClass(config('cart.calculator', DefaultCalculator::class)); 426 | if (!$class->implementsInterface(Calculator::class)) { 427 | throw new InvalidCalculatorException('The configured Calculator seems to be invalid. Calculators have to implement the Calculator Contract.'); 428 | } 429 | 430 | return call_user_func($class->getName().'::getAttribute', $attribute, $this); 431 | } 432 | 433 | /** 434 | * Create a new instance from a Buyable. 435 | * 436 | * @param \Gloudemans\Shoppingcart\Contracts\Buyable $item 437 | * @param array $options 438 | * 439 | * @return \Gloudemans\Shoppingcart\CartItem 440 | */ 441 | public static function fromBuyable(Buyable $item, array $options = []) 442 | { 443 | return new self($item->getBuyableIdentifier($options), $item->getBuyableDescription($options), $item->getBuyablePrice($options), $item->getBuyableWeight($options), $options); 444 | } 445 | 446 | /** 447 | * Create a new instance from the given array. 448 | * 449 | * @param array $attributes 450 | * 451 | * @return \Gloudemans\Shoppingcart\CartItem 452 | */ 453 | public static function fromArray(array $attributes) 454 | { 455 | $options = Arr::get($attributes, 'options', []); 456 | 457 | return new self($attributes['id'], $attributes['name'], $attributes['price'], $attributes['weight'], $options); 458 | } 459 | 460 | /** 461 | * Create a new instance from the given attributes. 462 | * 463 | * @param int|string $id 464 | * @param string $name 465 | * @param float $price 466 | * @param array $options 467 | * 468 | * @return \Gloudemans\Shoppingcart\CartItem 469 | */ 470 | public static function fromAttributes($id, $name, $price, $weight, array $options = []) 471 | { 472 | return new self($id, $name, $price, $weight, $options); 473 | } 474 | 475 | /** 476 | * Generate a unique id for the cart item. 477 | * 478 | * @param string $id 479 | * @param array $options 480 | * 481 | * @return string 482 | */ 483 | protected function generateRowId($id, array $options) 484 | { 485 | ksort($options); 486 | 487 | return md5($id.serialize($options)); 488 | } 489 | 490 | /** 491 | * Get the instance as an array. 492 | * 493 | * @return array 494 | */ 495 | public function toArray() 496 | { 497 | return [ 498 | 'rowId' => $this->rowId, 499 | 'id' => $this->id, 500 | 'name' => $this->name, 501 | 'qty' => $this->qty, 502 | 'price' => $this->price, 503 | 'weight' => $this->weight, 504 | 'options' => is_object($this->options) 505 | ? $this->options->toArray() 506 | : $this->options, 507 | 'discount' => $this->discount, 508 | 'tax' => $this->tax, 509 | 'subtotal' => $this->subtotal, 510 | ]; 511 | } 512 | 513 | /** 514 | * Convert the object to its JSON representation. 515 | * 516 | * @param int $options 517 | * 518 | * @return string 519 | */ 520 | public function toJson($options = 0) 521 | { 522 | return json_encode($this->toArray(), $options); 523 | } 524 | 525 | /** 526 | * Get the formatted number. 527 | * 528 | * @param float $value 529 | * @param int $decimals 530 | * @param string $decimalPoint 531 | * @param string $thousandSeperator 532 | * 533 | * @return string 534 | */ 535 | private function numberFormat($value, $decimals, $decimalPoint, $thousandSeperator) 536 | { 537 | if (is_null($decimals)) { 538 | $decimals = config('cart.format.decimals', 2); 539 | } 540 | 541 | if (is_null($decimalPoint)) { 542 | $decimalPoint = config('cart.format.decimal_point', '.'); 543 | } 544 | 545 | if (is_null($thousandSeperator)) { 546 | $thousandSeperator = config('cart.format.thousand_separator', ','); 547 | } 548 | 549 | return number_format($value, $decimals, $decimalPoint, $thousandSeperator); 550 | } 551 | 552 | /** 553 | * Getter for the raw internal discount rate. 554 | * Should be used in calculators. 555 | * 556 | * @return float 557 | */ 558 | public function getDiscountRate() 559 | { 560 | return $this->discountRate; 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /README_Idn.md: -------------------------------------------------------------------------------- 1 | ## LaravelShoppingcart 2 | ![CI Code Checks](https://github.com/bumbummen99/LaravelShoppingcart/workflows/CI%20Code%20Checks/badge.svg?branch=master) 3 | [![codecov](https://codecov.io/gh/bumbummen99/LaravelShoppingcart/branch/master/graph/badge.svg)](https://codecov.io/gh/bumbummen99/LaravelShoppingcart) 4 | [![StyleCI](https://styleci.io/repos/152610878/shield?branch=master)](https://styleci.io/repos/152610878) 5 | [![Total Downloads](https://poser.pugx.org/bumbummen99/shoppingcart/downloads.png)](https://packagist.org/packages/bumbummen99/shoppingcart) 6 | [![Latest Stable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/stable)](https://packagist.org/packages/bumbummen99/shoppingcart) 7 | [![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](https://packagist.org/packages/bumbummen99/shoppingcart) 8 | [![License](https://poser.pugx.org/bumbummen99/shoppingcart/license)](https://packagist.org/packages/bumbummen99/shoppingcart) 9 | 10 | Ini adalah percabangan dari [Crinsane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) dikembangkan dengan fitur-fitur minor yang kompatibel dengan Laravel 6 11 | 12 | ## Instalasi 13 | 14 | Install paket(https://packagist.org/packages/bumbummen99/shoppingcart) menggunakan [Composer](http://getcomposer.org/). 15 | 16 | Jalankan Composer dengan menggunakan perintah berikut: 17 | 18 | composer require bumbummen99/shoppingcart 19 | 20 | Sekarang Anda siap untuk mulai menggunakan shoppingcart di aplikasi Anda. 21 | 22 | **Pada versi 2 dari paket ini memungkinkan untuk menggunakan injeksi dependensi untuk memasukkan instance Class Cart ke controller Anda atau Class lain** 23 | 24 | ## Gambaran 25 | Lihat salah satu topik berikut untuk mempelajari lebih lanjut tentang LaravelShoppingcart 26 | 27 | * [Usage](#usage) 28 | * [Collections](#collections) 29 | * [Instances](#instances) 30 | * [Models](#models) 31 | * [Database](#database) 32 | * [Exceptions](#exceptions) 33 | * [Events](#events) 34 | * [Example](#example) 35 | 36 | ## Penggunaan 37 | 38 | Shoppingcart memberi Anda metode berikut untuk digunakan: 39 | 40 | ### Cart::add() 41 | 42 | Menambahkan item ke troli sangat sederhana, Anda cukup menggunakan metode `add ()`, yang menerima berbagai parameter. 43 | 44 | Dalam bentuknya yang paling mendasar, Anda dapat menentukan id, nama, jumlah, harga, dan berat produk yang ingin Anda tambahkan ke troli. 45 | 46 | ```php 47 | Cart::add('293ad', 'Product 1', 1, 9.99, 550); 48 | ``` 49 | 50 | Sebagai opsional parameter kelima, Anda dapat memberikan opsi, sehingga Anda dapat menambahkan beberapa item dengan id yang sama, tetapi dengan (instance) ukuran yang berbeda. 51 | 52 | ```php 53 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 54 | ``` 55 | 56 | **Metode `add ()` akan mengembalikan instance CartItem dari item yang baru saja Anda tambahkan ke troli.** 57 | 58 | Mungkin Anda lebih suka menambahkan item menggunakan array? Selama array berisi kunci yang diperlukan, Anda bisa meneruskannya ke metode. Tombol opsi adalah opsional. 59 | 60 | ```php 61 | Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); 62 | ``` 63 | 64 | Baru dalam versi 2 paket ini adalah kemungkinan untuk bekerja dengan antarmuka [Buyable] (#buyable). Cara kerjanya adalah bahwa Anda memiliki model yang mengimplementasikan antarmuka [Buyable] (#buyable), yang akan membuat Anda menerapkan beberapa metode sehingga paket tahu bagaimana cara mendapatkan id, nama, dan harga dari model Anda. 65 | Dengan cara ini Anda bisa meneruskan metode `add ()` model dan kuantitas dan secara otomatis akan menambahkannya ke troli. 66 | 67 | **Sebagai bonus tambahan, itu akan secara otomatis mengaitkan model dengan CartItem** 68 | 69 | ```php 70 | Cart::add($product, 1, ['size' => 'large']); 71 | ``` 72 | Sebagai parameter ketiga opsional, Anda dapat menambahkan opsi. 73 | ```php 74 | Cart::add($product, 1, ['size' => 'large']); 75 | ``` 76 | 77 | Terakhir, Anda juga dapat menambahkan banyak item ke troli sekaligus. 78 | Anda bisa meneruskan metode `add ()` sebuah array array, atau array yang dapat dibeli dan mereka akan ditambahkan ke troli. 79 | 80 | **Saat menambahkan beberapa item ke troli, metode `add ()` akan mengembalikan array CartItems.** 81 | 82 | ```php 83 | Cart::add([ 84 | ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], 85 | ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] 86 | ]); 87 | 88 | Cart::add([$product1, $product2]); 89 | 90 | ``` 91 | 92 | ### Cart::update() 93 | 94 | Untuk memperbarui item di troli, Anda harus terlebih dahulu membutuhkan rowId item. 95 | Selanjutnya Anda dapat menggunakan metode `update ()` untuk memperbaruinya. 96 | 97 | Jika Anda hanya ingin memperbarui kuantitas, Anda akan melewati metode pembaruan rowId dan kuantitas baru: 98 | 99 | ```php 100 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 101 | 102 | Cart::update($rowId, 2); // Will update the quantity 103 | ``` 104 | Jika Anda ingin memperbarui lebih banyak atribut dari item, Anda dapat melewati metode pembaruan array atau `Dapat Dibeli` sebagai parameter kedua. Dengan cara ini Anda dapat memperbarui semua informasi item dengan rowId yang diberikan. 105 | 106 | ```php 107 | Cart::update($rowId, ['name' => 'Product 1']); // Will update the name 108 | 109 | Cart::update($rowId, $product); // Will update the id, name and price 110 | 111 | ``` 112 | 113 | ### Cart::remove() 114 | 115 | Untuk menghapus item untuk keranjang, Anda akan membutuhkan rowId lagi. Baris ini. Apakah Anda hanya meneruskan ke metode `hapus ()` dan itu akan menghapus item dari keranjang. 116 | 117 | ```php 118 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 119 | 120 | Cart::remove($rowId); 121 | ``` 122 | 123 | ### Cart::get() 124 | 125 | Jika Anda ingin mendapatkan item dari troli menggunakan rowId-nya, Anda bisa memanggil metode `get ()` di troli dan meneruskannya dengan rowId. 126 | 127 | ```php 128 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 129 | 130 | Cart::get($rowId); 131 | ``` 132 | 133 | ### Cart::content() 134 | 135 | Tentu saja Anda juga ingin mendapatkan konten gerobak. Di sinilah Anda akan menggunakan metode `konten`. Metode ini akan mengembalikan Koleksi CartItems yang dapat Anda ulangi dan tampilkan kontennya kepada pelanggan Anda. 136 | 137 | ```php 138 | Cart::content(); 139 | ``` 140 | Metode ini akan mengembalikan konten instance keranjang saat ini, jika Anda ingin konten instance lain, cukup lakukan panggilan. 141 | 142 | ```php 143 | Cart::instance('wishlist')->content(); 144 | ``` 145 | 146 | ### Cart::destroy() 147 | 148 | Jika Anda ingin menghapus konten keranjang sepenuhnya, Anda dapat memanggil metode penghancuran di kereta. Ini akan menghapus semua CartItems dari troli untuk instance troli saat ini. 149 | 150 | ```php 151 | Cart::destroy(); 152 | ``` 153 | 154 | ### Cart::weight() 155 | 156 | Metode `weight ()` dapat digunakan untuk mendapatkan total berat semua item di troli, mengingat berat dan kuantitasnya. 157 | 158 | 159 | ```php 160 | Cart::weight(); 161 | ``` 162 | 163 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 164 | 165 | ```php 166 | Cart::weight($decimals, $decimalSeperator, $thousandSeperator); 167 | ``` 168 | 169 | Anda dapat mengatur format angka default dalam file konfigurasi. 170 | 171 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> weight`** 172 | 173 | ### Cart::total() 174 | 175 | Maka `total ()` dapat digunakan untuk mendapatkan total yang dihitung dari semua item dalam troli, mengingat ada harga dan kuantitas. 176 | 177 | ```php 178 | Cart::total(); 179 | ``` 180 | 181 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 182 | 183 | ```php 184 | Cart::total($decimals, $decimalSeparator, $thousandSeparator); 185 | ``` 186 | 187 | Anda dapat mengatur format angka default dalam file konfigurasi. 188 | 189 | 190 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> total`** 191 | 192 | ### Cart::tax() 193 | 194 | Maka `tax ()` dapat digunakan untuk mendapatkan jumlah pajak yang dihitung untuk semua item di troli, mengingat ada harga dan kuantitas. 195 | 196 | ```php 197 | Cart::tax(); 198 | ``` 199 | 200 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 201 | 202 | ```php 203 | Cart::tax($decimals, $decimalSeparator, $thousandSeparator); 204 | ``` 205 | 206 | Anda dapat mengatur format angka default dalam file konfigurasi. 207 | 208 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> tax`** 209 | 210 | ### Cart::subtotal() 211 | 212 | Maka `subtotal ()` dapat digunakan untuk mendapatkan total semua item dalam troli, dikurangi jumlah total pajak. 213 | 214 | ```php 215 | Cart::subtotal(); 216 | ``` 217 | 218 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 219 | 220 | ```php 221 | Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); 222 | ``` 223 | 224 | Anda dapat mengatur format angka default dalam file konfigurasi. 225 | 226 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> subtotal`** 227 | 228 | ### Cart::discount() 229 | 230 | Maka `diskon ()` dapat digunakan untuk mendapatkan diskon total semua item di troli. 231 | 232 | ```php 233 | Cart::discount(); 234 | ``` 235 | 236 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 237 | 238 | ```php 239 | Cart::discount($decimals, $decimalSeparator, $thousandSeparator); 240 | ``` 241 | 242 | Anda dapat mengatur format angka default dalam file konfigurasi. 243 | 244 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> discount`** 245 | 246 | ### Cart::initial() 247 | 248 | maka `initial ()` dapat digunakan untuk mendapatkan harga total semua item di troli sebelum diskon. 249 | 250 | ```php 251 | Cart::initial(); 252 | ``` 253 | 254 | Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional 255 | 256 | ```php 257 | Cart::initial($decimals, $decimalSeparator, $thousandSeparator); 258 | ``` 259 | 260 | Anda dapat mengatur format angka default dalam file konfigurasi. 261 | 262 | **Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> initial`** 263 | 264 | ### Cart::count() 265 | 266 | Jika Anda ingin tahu berapa banyak item yang ada di troli Anda, Anda dapat menggunakan metode `count ()`. Metode ini akan mengembalikan jumlah total barang dalam kereta. Jadi, jika Anda telah menambahkan 2 buku dan 1 kemeja, itu akan mengembalikan 3 item. 267 | 268 | ```php 269 | Cart::count(); 270 | $cart->count(); 271 | ``` 272 | 273 | ### Cart::search() 274 | 275 | Untuk menemukan item di troli, Anda dapat menggunakan metode `search ()`. 276 | 277 | **Metode ini diubah pada versi 2** 278 | 279 | Di belakang layar, metode ini hanya menggunakan metode filter dari kelas Laravel Collection. Ini berarti Anda harus memberikannya suatu Penutupan di mana Anda akan menentukan istilah pencarian Anda. 280 | 281 | Jika Anda misalnya ingin menemukan semua item dengan id 1: 282 | 283 | ```php 284 | $cart->search(function ($cartItem, $rowId) { 285 | return $cartItem->id === 1; 286 | }); 287 | ``` 288 | 289 | Seperti yang Anda lihat, Penutupan akan menerima dua parameter. Yang pertama adalah Item Keranjang untuk melakukan pemeriksaan terhadap. Parameter kedua adalah rowId dari CartItem ini. 290 | 291 | ** Metode ini akan mengembalikan Koleksi yang berisi semua CartItems yang ditemukan ** 292 | 293 | Cara pencarian ini memberi Anda kontrol total atas proses pencarian dan memberi Anda kemampuan untuk membuat pencarian yang sangat tepat dan spesifik. 294 | 295 | ### Cart :: setTax ($ rowId, $ taxRate) 296 | 297 | Anda dapat menggunakan metode `setTax ()` untuk mengubah tarif pajak yang berlaku untuk CartItem. Ini akan menimpa nilai yang ditetapkan dalam file konfigurasi. 298 | 299 | ```php 300 | Cart::setTax($rowId, 21); 301 | $cart->setTax($rowId, 21); 302 | ``` 303 | 304 | ### Cart::setGlobalTax($taxRate) 305 | 306 | Anda dapat menggunakan metode `setGlobalTax ()` untuk mengubah tarif pajak untuk semua item di troli. Item baru juga akan menerima setGlobalTax. 307 | 308 | ```php 309 | Cart::setGlobalTax(21); 310 | $cart->setGlobalTax(21); 311 | ``` 312 | 313 | ### Cart::setGlobalDiscount($discountRate) 314 | 315 | Anda dapat menggunakan metode `setGlobalDiscount ()` untuk mengubah tingkat diskonto untuk semua item di troli. Barang baru akan menerima diskon juga. 316 | 317 | ```php 318 | Cart::setGlobalDiscount(50); 319 | $cart->setGlobalDiscount(50); 320 | ``` 321 | 322 | ### Cart::setDiscount($rowId, $taxRate) 323 | 324 | Anda dapat menggunakan metode `setDiscount ()` untuk mengubah tingkat diskonto yang menerapkan CartItem. Perlu diingat bahwa nilai ini akan berubah jika Anda menetapkan diskon global untuk Keranjang sesudahnya. 325 | 326 | ```php 327 | Cart::setDiscount($rowId, 21); 328 | $cart->setDiscount($rowId, 21); 329 | ``` 330 | 331 | ### Buyable 332 | 333 | Untuk kenyamanan menambahkan item yang lebih cepat ke troli dan asosiasi otomatisnya, model Anda harus mengimplementasikan antarmuka `Dapat Dibeli` Anda dapat menggunakan sifat `CanBeBought` untuk mengimplementasikan metode yang diperlukan tetapi perlu diingat bahwa ini akan menggunakan bidang yang telah ditentukan pada model Anda untuk nilai yang diperlukan. 334 | 335 | ```php 336 | id; 352 | } 353 | public function getBuyableDescription(){ 354 | return $this->name; 355 | } 356 | public function getBuyablePrice(){ 357 | return $this->price; 358 | } 359 | public function getBuyableWeight(){ 360 | return $this->weight; 361 | } 362 | ``` 363 | 364 | Contoh: 365 | 366 | ```php 367 | id; 376 | } 377 | public function getBuyableDescription($options = null) { 378 | return $this->name; 379 | } 380 | public function getBuyablePrice($options = null) { 381 | return $this->price; 382 | } 383 | } 384 | ``` 385 | 386 | ## Collections 387 | 388 | Dalam beberapa kasus, Keranjang akan mengembalikan kepada Anda Koleksi. Ini hanya Koleksi Laravel sederhana, sehingga semua metode yang dapat Anda panggil pada Koleksi Laravel juga tersedia pada hasilnya. 389 | 390 | Sebagai contoh, Anda dapat dengan cepat mendapatkan jumlah produk unik dalam keranjang: 391 | 392 | ```php 393 | Cart::content()->count(); 394 | ``` 395 | 396 | Atau Anda dapat mengelompokkan konten berdasarkan id produk: 397 | 398 | ```php 399 | Cart::content()->groupBy('id'); 400 | ``` 401 | 402 | ## Instances 403 | 404 | Paket-paket mendukung beberapa instance dari kereta. Cara kerjanya seperti ini: 405 | 406 | Anda dapat mengatur instance keranjang saat ini dengan memanggil `Cart :: instance ('newInstance')`. Mulai saat ini, instance aktif dari cart adalah `newInstance`, jadi ketika Anda menambah, menghapus, atau mendapatkan konten dari cart, Anda bekerja dengan instance` newInstance` dari cart. 407 | Jika Anda ingin mengganti instance, Anda cukup memanggil `Cart :: instance ('otherInstance')` lagi, dan Anda bekerja dengan `otherInstance` lagi. 408 | 409 | Contoh Kecil: 410 | 411 | ```php 412 | Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); 413 | 414 | // Get the content of the 'shopping' cart 415 | Cart::content(); 416 | 417 | Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); 418 | 419 | // Get the content of the 'wishlist' cart 420 | Cart::content(); 421 | 422 | // If you want to get the content of the 'shopping' cart again 423 | Cart::instance('shopping')->content(); 424 | 425 | // And the count of the 'wishlist' cart again 426 | Cart::instance('wishlist')->count(); 427 | ``` 428 | 429 | Anda juga dapat menggunakan Kontrak `InstanceIdentifier` untuk memperpanjang Model yang diinginkan untuk menetapkan / membuat instance Cart untuknya. Ini juga memungkinkan untuk secara langsung mengatur diskon global. 430 | ``` 431 | email; 450 | } 451 | 452 | /** 453 | * Get the unique identifier to load the Cart from 454 | * 455 | * @return int|string 456 | */ 457 | public function getInstanceGlobalDiscount($options = null) 458 | { 459 | return $this->discountRate ?: 0; 460 | } 461 | } 462 | 463 | // Inside Controller 464 | $user = \Auth::user(); 465 | $cart = Cart::instance($user); 466 | 467 | 468 | 469 | ``` 470 | 471 | **N.B. Ingatlah bahwa troli tetap berada di set instance terakhir selama Anda tidak menyetel yang berbeda selama eksekusi skrip.** 472 | 473 | **N.B.2 Contoh cart default disebut `default`, jadi ketika Anda tidak menggunakan instance,` Cart :: konten (); `sama dengan` Cart :: instance ('default') -> konten () `.** 474 | 475 | ## Models 476 | 477 | Karena sangat nyaman untuk dapat secara langsung mengakses model dari CartItem, apakah mungkin untuk mengaitkan model dengan barang-barang di dalam kereta. Katakanlah Anda memiliki model `Produk` di aplikasi Anda. Dengan metode `associate ()`, Anda dapat memberi tahu troli bahwa item di troli, terkait dengan model `Product`. 478 | 479 | Dengan begitu Anda dapat mengakses model Anda langsung dari `CartItem`! 480 | 481 | Model ini dapat diakses melalui properti `model` di CartItem. 482 | 483 | **Jika model Anda mengimplementasikan antarmuka `Buy Able` dan Anda menggunakan model Anda untuk menambahkan item ke troli, itu akan dikaitkan secara otomatis.** 484 | 485 | Berikut adalah contoh: 486 | 487 | ```php 488 | 489 | // First we'll add the item to the cart. 490 | $cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 491 | 492 | // Next we associate a model with the item. 493 | Cart::associate($cartItem->rowId, 'Product'); 494 | 495 | // Or even easier, call the associate method on the CartItem! 496 | $cartItem->associate('Product'); 497 | 498 | // You can even make it a one-liner 499 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); 500 | 501 | // Now, when iterating over the content of the cart, you can access the model. 502 | foreach(Cart::content() as $row) { 503 | echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; 504 | } 505 | ``` 506 | ## Database 507 | 508 | - [Config](#configuration) 509 | - [Storing the cart](#storing-the-cart) 510 | - [Restoring the cart](#restoring-the-cart) 511 | 512 | ### Konfigurasi 513 | Untuk menyimpan keranjang ke dalam basis data sehingga Anda dapat mengambilnya nanti, paket perlu mengetahui koneksi basis data yang digunakan dan apa nama tabelnya. 514 | Secara default paket akan menggunakan koneksi database default dan menggunakan tabel bernama `shoppingcart`. 515 | Jika Anda ingin mengubah opsi ini, Anda harus menerbitkan file `config`. 516 | 517 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" 518 | 519 | Ini akan memberi Anda file konfigurasi `cart.php` di mana Anda dapat melakukan perubahan. 520 | 521 | Untuk memudahkan hidup Anda, paket ini juga menyertakan `migration` yang siap digunakan yang dapat Anda terbitkan dengan menjalankan: 522 | 523 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" 524 | 525 | Ini akan menempatkan file migrasi tabel `shoppingcart` ke direktori` database / migrations`. Sekarang yang harus Anda lakukan adalah menjalankan `php artisan migrate` untuk memigrasi basis data Anda. 526 | 527 | ### Menyimpan ke Troli 528 | Untuk menyimpan instance kereta ke dalam database, Anda harus memanggil metode `store ($ identifier)`. Di mana `$ identifier` adalah kunci acak, misalnya id atau nama pengguna pengguna. 529 | 530 | Cart::store('username'); 531 | 532 | // To store a cart instance named 'wishlist' 533 | Cart::instance('wishlist')->store('username'); 534 | 535 | ### Mengembalikan ke Troli 536 | Jika Anda ingin mengambil keranjang dari database dan mengembalikannya, yang harus Anda lakukan adalah memanggil `restore ($ identifier)` di mana `$ identifier` adalah kunci yang Anda tentukan untuk metode` store`. 537 | 538 | Cart::restore('username'); 539 | 540 | // To restore a cart instance named 'wishlist' 541 | Cart::instance('wishlist')->restore('username'); 542 | 543 | ### Menggabungkan Troli 544 | Jika Anda ingin menggabungkan keranjang dengan keranjang lain dari basis data, yang harus Anda lakukan adalah memanggil `gabungan ($ identifier)` di mana `$ identifier` adalah kunci yang Anda tentukan untuk metode` store`. Anda juga dapat menentukan apakah Anda ingin mempertahankan potongan harga dan tarif pajak item. 545 | 546 | // Merge the contents of 'savedcart' into 'username'. 547 | Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, 'savedcartinstance'); 548 | 549 | ## Pengecualian 550 | 551 | Paket Cart akan mengeluarkan pengecualian jika terjadi kesalahan. Dengan cara ini lebih mudah untuk men-debug kode Anda menggunakan paket Cart atau untuk menangani kesalahan berdasarkan pada jenis pengecualian. Paket-paket Cart dapat membuang pengecualian berikut: 552 | 553 | | Exception | Reason | 554 | | ---------------------------- | ---------------------------------------------------------------------------------- | 555 | | *CartAlreadyStoredException* | Saat mencoba menyimpan keranjang yang sudah disimpan menggunakan pengenal yang ditentukan | 556 | | *InvalidRowIDException* | Ketika rowId yang diteruskan tidak ada dalam instance troli saat ini | 557 | | *UnknownModelException* | Saat Anda mencoba mengaitkan model yang tidak ada dengan Item Keranjang. | 558 | 559 | ## Events 560 | 561 | Troli juga memiliki event. Ada lima event yang bisa Anda lakukan. 562 | 563 | | Event | Fired | Parameter | 564 | | ------------- | ---------------------------------------- | -------------------------------- | 565 | | cart.added | Saat item ditambahkan ke troli. | The `CartItem` that was added. | 566 | | cart.updated | Ketika item dalam troli diperbarui. | The `CartItem` that was updated. | 567 | | cart.removed | Ketika item dalam troli dihapus. | The `CartItem` that was removed. | 568 | | cart.stored | Ketika isi trol disimpan. | - | 569 | | cart.restored | Ketika konten keranjang Dikembalikan. | - | 570 | 571 | ## Contoh 572 | 573 | Di bawah ini adalah sedikit contoh cara membuat daftar isi keranjang dalam sebuah tabel: 574 | 575 | ```php 576 | 577 | // Tambahkan beberapa item di Kontroler Anda. 578 | Cart::add('192ao12', 'Product 1', 1, 9.99); 579 | Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); 580 | 581 | // Tampilkan konten dalam Tampilan. 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 |
ProductQtyPriceSubtotal
598 |

name; ?>

599 |

options->has('size') ? $row->options->size : ''); ?>

600 |
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
628 | ``` 629 | -------------------------------------------------------------------------------- /README_uk-UA.md: -------------------------------------------------------------------------------- 1 | ## LaravelShoppingcart 2 | ![CI Code Checks](https://github.com/bumbummen99/LaravelShoppingcart/workflows/CI%20Code%20Checks/badge.svg?branch=master) 3 | [![codecov](https://codecov.io/gh/bumbummen99/LaravelShoppingcart/branch/master/graph/badge.svg)](https://codecov.io/gh/bumbummen99/LaravelShoppingcart) 4 | [![StyleCI](https://styleci.io/repos/152610878/shield?branch=master)](https://styleci.io/repos/152610878) 5 | [![Total Downloads](https://poser.pugx.org/bumbummen99/shoppingcart/downloads.png)](https://packagist.org/packages/bumbummen99/shoppingcart) 6 | [![Latest Stable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/stable)](https://packagist.org/packages/bumbummen99/shoppingcart) 7 | [![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](https://packagist.org/packages/bumbummen99/shoppingcart) 8 | [![License](https://poser.pugx.org/bumbummen99/shoppingcart/license)](https://packagist.org/packages/bumbummen99/shoppingcart) 9 | 10 | Цей репозиторій є відгалуженням [Crinsane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) та містить додаткові незначні доповнення, сумісні з Laravel 6. 11 | 12 | ## Встановлення 13 | 14 | Встановіть [пакет](https://packagist.org/packages/bumbummen99/shoppingcart) скориставшись [Завантажувачем](http://getcomposer.org/). 15 | 16 | Для запуску Завантажувача, скористайтеся командою у Терміналі: 17 | 18 | composer require bumbummen99/shoppingcart 19 | 20 | Тепер ви готові розпочати користуватися кошиком у вашому застосунку. 21 | 22 | **Починаючи з версії 2 даного пакету з'явилася можливість впровадження залежності для впровадження екземпляра класу Кошик (Cart) до вашого контролера або іншого класу** 23 | 24 | ## Огляд 25 | Щоб детальніше ознайомитися LaravelShoppingcart, можете пройти за посиланнями 26 | 27 | * [Застосування](#usage) 28 | * [Колекції](#collections) 29 | * [Екземпляри](#instances) 30 | * [Моделі](#models) 31 | * [База даних](#database) 32 | * [Винятки](#exceptions) 33 | * [Події](#events) 34 | * [Приклад](#example) 35 | 36 | ## Застосування 37 | 38 | Кошик (Cart) дозволяє вам скористатися наступними методами: 39 | 40 | ### Cart::add() 41 | 42 | Додавати покупки у кошик дуже зручно - достатньо скористатися методом `add()`, який приймає різноманітні параметри. 43 | 44 | У найпростішій формі метода достатньо вказати ідентифікатор товару, назву, кількість, ціну та вагу товару, який ви хочете додати у кошик. 45 | 46 | ```php 47 | Cart::add('293ad', 'Product 1', 1, 9.99, 550); 48 | ``` 49 | 50 | У якості додаткового п'ятого параметра можна задати додаткові опції, наприклад, щоб додати декілька одиниць з однаковим ідентифікатором, але, наприклад, різного розміру. 51 | 52 | ```php 53 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 54 | ``` 55 | 56 | **Метод `add()` повертає екземпляр CartItems того товару, який ви щойно додали у кошик.** 57 | 58 | Можливо, вам більше до вподоби додавати товари, використовуючи масив? Якщо масив містить усі необхідні поля, ви можете передавати масив у цей метод. Поле із додатковими опціями є необов'язковим. 59 | 60 | ```php 61 | Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); 62 | ``` 63 | 64 | У версії 2 пакета з'явилася нова можливість для роботи з інтерфейсом [Buyable](#buyable). Такий функціонал з'являється за рахунок того, що модель запускає інтерфейс [Buyable](#buyable), який дозволить імплементувати декілька методів, з яких пакет знатиме як отримати ідентифікатор, назву та ціну з вашої моделі. 65 | Таким чином, ви можете передати метод `add()` та кількість одиниць товару до моделі, а вона автоматично додасть їх до кошика. 66 | 67 | **Додатковий бонус інтерфейсу - автоматичне об'єднання моделі з CartItems** 68 | 69 | ```php 70 | Cart::add($product, 1, ['size' => 'large']); 71 | ``` 72 | У якості додаткового параметра, ви можете внести опції. 73 | ```php 74 | Cart::add($product, 1, ['size' => 'large']); 75 | ``` 76 | 77 | Нарешті, ви також можете додавати до кошика декілька одиниць водночас. Для цього потрібно передати у `add()` масив масивів або масив Buyables, і їх буде додано в кошик. 78 | 79 | **Під час додавання декількох одиниць товару в кошик, метод `add()` повертає масив CartItems.** 80 | 81 | ```php 82 | Cart::add([ 83 | ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], 84 | ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] 85 | ]); 86 | 87 | Cart::add([$product1, $product2]); 88 | 89 | ``` 90 | 91 | ### Cart::update() 92 | 93 | Щоб оновити товар у кошику, вам знадобиться ідентифікатор рядка (rowId) даного товару. 94 | Далі ви можете скористатися методом `update()` для того, щоб оновити його. 95 | 96 | Якщо ви просто хочете оновити кількість товару, вам необхідно передати у метод `update()` rowId і оновлену кількість: 97 | 98 | ```php 99 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 100 | 101 | Cart::update($rowId, 2); // Will update the quantity 102 | ``` 103 | 104 | Якщо ви хочете оновити більше атрибутів товару, вам потрібно або передати у метод `update()` масив або `Buyable` у якості другого параметра. Таким чином, ви можете оновити всю інформацію про товар за заданим rowId. 105 | 106 | ```php 107 | Cart::update($rowId, ['name' => 'Product 1']); // Will update the name 108 | 109 | Cart::update($rowId, $product); // Will update the id, name and price 110 | 111 | ``` 112 | 113 | ### Cart::remove() 114 | 115 | Щоб вилучити товар з кошика, вам знову знадобиться rowId. Такий rowId потрібно передати у метод `remove()`, який автоматично вилучить товар із кошика. 116 | 117 | ```php 118 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 119 | 120 | Cart::remove($rowId); 121 | ``` 122 | 123 | ### Cart::get() 124 | 125 | Якщо ви хочете отримати товар із кошика, використовуючи його rowId, вам потрібно застосувати метод `get()` щодо кошика і передати в нього rowId. 126 | 127 | ```php 128 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 129 | 130 | Cart::get($rowId); 131 | ``` 132 | 133 | ### Cart::content() 134 | 135 | Вам також може знадобитися можливість отримати інформацію про вміст кошика. Для цього вам потрібно скористатися методом `content`. Такий метод повертає колекцію CartItems, ви можете перебирати вміст такої колекції і відобразити вміст кошика для ваших клієнтів. 136 | 137 | ```php 138 | Cart::content(); 139 | ``` 140 | 141 | Даний метод повертає вміст поточного екземпляра кошика, якщо ви хочете вміст іншого екземпляра, вам потрібно зв'язати виклики. 142 | 143 | ```php 144 | Cart::instance('wishlist')->content(); 145 | ``` 146 | 147 | ### Cart::destroy() 148 | 149 | Якщо ви хочете остаточно вилучити вміст кошика, ви можете застосувати метод `destroy()` щодо кошика. Даний метод вилучить всі CartItems з кошика для поточного екземпляра кошика. 150 | 151 | ```php 152 | Cart::destroy(); 153 | ``` 154 | 155 | ### Cart::weight() 156 | 157 | Метод `weight()` можна застосувати, щоб отримати розрахунок ваги усіх товарів у кошику, за умови, що задано вагу і кількість одиниць. 158 | 159 | ```php 160 | Cart::weight(); 161 | ``` 162 | 163 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 164 | 165 | ```php 166 | Cart::weight($decimals, $decimalSeperator, $thousandSeperator); 167 | ``` 168 | 169 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 170 | 171 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вагу товарів через `$cart->weight`** 172 | 173 | ### Cart::total() 174 | 175 | Метод `total()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів у кошику, за умови, що задані ціна і кількість одиниць. 176 | 177 | ```php 178 | Cart::total(); 179 | ``` 180 | 181 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 182 | 183 | ```php 184 | Cart::total($decimals, $decimalSeparator, $thousandSeparator); 185 | ``` 186 | 187 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 188 | 189 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість товарів через `$cart->total`** 190 | 191 | ### Cart::tax() 192 | 193 | Метод `tax()` можна застосовувати, щоб отримати розрахунок суми податків для усіх товарів у кошику, за умови, що задані ціна і кількість одиниць. 194 | 195 | ```php 196 | Cart::tax(); 197 | ``` 198 | 199 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 200 | 201 | ```php 202 | Cart::tax($decimals, $decimalSeparator, $thousandSeparator); 203 | ``` 204 | 205 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 206 | 207 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про суму податку на товари через `$cart->tax`** 208 | 209 | ### Cart::subtotal() 210 | 211 | Метод `subtotal()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів у кошику, без урахування суми податку. 212 | 213 | ```php 214 | Cart::subtotal(); 215 | ``` 216 | 217 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 218 | 219 | ```php 220 | Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); 221 | ``` 222 | 223 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 224 | 225 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів без урахування суми податків через `$cart->subtotal`** 226 | 227 | ### Cart::discount() 228 | 229 | Метод `discount()` можна застосовувати, щоб отримати розрахунок знижки на усі товари у кошику. 230 | 231 | ```php 232 | Cart::discount(); 233 | ``` 234 | 235 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 236 | 237 | ```php 238 | Cart::discount($decimals, $decimalSeparator, $thousandSeparator); 239 | ``` 240 | 241 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 242 | 243 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів з урахуванням знижки `$cart->discount`** 244 | 245 | ### Cart::initial() 246 | 247 | Метод `initial()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів до застосування знижки. 248 | 249 | ```php 250 | Cart::initial(); 251 | ``` 252 | 253 | Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. 254 | 255 | ```php 256 | Cart::initial($decimals, $decimalSeparator, $thousandSeparator); 257 | ``` 258 | 259 | Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. 260 | 261 | **Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів до застосування знижки `$cart->initial`** 262 | 263 | ### Cart::count() 264 | 265 | Метод `count()` можна застосовувати, щоб дізнатися кількість одиниць товарів у кошику. Даний метод повертає загальну кількість одиниць товарів у кошику. Тобто якщо ви додали 2 книжки і 1 сорочку, цей метод поверне 3 одиниці. 266 | 267 | ```php 268 | Cart::count(); 269 | $cart->count(); 270 | ``` 271 | 272 | ### Cart::search() 273 | 274 | Метод `search()` можна застосовувати, щоб знайти одиницю товару у кошику. 275 | 276 | **Даний метод було змінено у версії 2** 277 | 278 | У своїй імплементації, цей метод застосовує метод фільтрування з класу Laravel Collection. Це означає, що вам потрібно передати замикання (Closure) для даного методу, де ви зазначите умови для пошуку. 279 | 280 | Наприклад, якщо ви хочете знайти всі одиниці товару з ідентифікатором 1: 281 | 282 | ```php 283 | $cart->search(function ($cartItem, $rowId) { 284 | return $cartItem->id === 1; 285 | }); 286 | ``` 287 | 288 | Як ви можете побачити, замикання отримає 2 параметра. Перший - CartItem для здійснення перевірки щодо нього. Другий параметр - rowId даного CartItem. 289 | 290 | **Даний метод повертає колекцію, яка вміщує всі CartItems, які було знайдено** 291 | 292 | Такий спосіб пошуку надає вам повний контроль над процесом пошуку та дозволяє здійснювати дуже точні та конкретні пошуки. 293 | 294 | ### Cart::setTax($rowId, $taxRate) 295 | 296 | Метод `setTax()` можна застосовувати, щоб змінювати ставку оподаткування, яка застосовується до CartItem. Така операція перезапише значення встановлене у файлі з конфігураціями. 297 | 298 | ```php 299 | Cart::setTax($rowId, 21); 300 | $cart->setTax($rowId, 21); 301 | ``` 302 | 303 | ### Cart::setGlobalTax($taxRate) 304 | 305 | Метод `setGlobalTax()` можна застосовувати, щоб змінити ставку оподаткування для усіх найменувать у кошику. Нові найменування отримають значення setGlobalTax також. 306 | 307 | ```php 308 | Cart::setGlobalTax(21); 309 | $cart->setGlobalTax(21); 310 | ``` 311 | 312 | ### Cart::setGlobalDiscount($discountRate) 313 | 314 | Метод `setGlobalDiscount()` можна застосовувати для заміни ставки знижки щодо усіх найменувань у кошику. Нові найменування також отримуватимуть таку знижку. 315 | 316 | ```php 317 | Cart::setGlobalDiscount(50); 318 | $cart->setGlobalDiscount(50); 319 | ``` 320 | 321 | ### Cart::setDiscount($rowId, $taxRate) 322 | 323 | Застосування методу `setDiscount()` полягає у заміні ставки знижки, яка застосовується до CartItem. Зверніть увагу, що дане значення ставки знижки буде змінено, якщо ви згодом встановите глобальну знижку для Кошика (Cart). 324 | 325 | ```php 326 | Cart::setDiscount($rowId, 21); 327 | $cart->setDiscount($rowId, 21); 328 | ``` 329 | 330 | ### Buyable 331 | 332 | Для зручного швидкого додавання товарів до кошика та їхнього автоматичного об'єднання, ваша модель повинна запустити інтерфейс `Buyable`. Ви можете застосовувати `CanBeBought` трейт для імплементації необхідних методів, але зверніть увагу, що такі методи застосовуватимуть попередньо визначені поля у вашій моделі для необхідних значень. 333 | ```php 334 | id; 350 | } 351 | public function getBuyableDescription(){ 352 | return $this->name; 353 | } 354 | public function getBuyablePrice(){ 355 | return $this->price; 356 | } 357 | public function getBuyableWeight(){ 358 | return $this->weight; 359 | } 360 | ``` 361 | 362 | Приклад: 363 | 364 | ```php 365 | id; 374 | } 375 | public function getBuyableDescription($options = null) { 376 | return $this->name; 377 | } 378 | public function getBuyablePrice($options = null) { 379 | return $this->price; 380 | } 381 | } 382 | ``` 383 | 384 | ## Колекції 385 | 386 | Щодо багатьох екземплярів Кошик (Cart) повертає Колекцію, яка є простим видом Laravel Collection. Таким чином усі методи, які ви можете застосовувати щодо Laravel Collection, є також доступними у результаті операції. 387 | 388 | Наприклад, ви можете швидко отримати кількість унікальних товарів у кошику: 389 | 390 | ```php 391 | Cart::content()->count(); 392 | ``` 393 | 394 | Або групувати вміст за ідентифікатором товару: 395 | 396 | ```php 397 | Cart::content()->groupBy('id'); 398 | ``` 399 | 400 | ## Екземпляри 401 | 402 | Пакет підтримує декілька екземплярів кошика. Як це працює: 403 | 404 | Ви можете встановити поточний екземпляр кошика через виклик `Cart::instance('newInstance')`. З цього моменту, активний екземляр кошика буде `newInstance`, тому коли ви додаєте, вилучаєте або отримуєте інформацію щодо вмісту кошика, ви працюєте з екземпляром `newInstance` кошика. 405 | Якщо ви хочете переключитися між екзмеплярами, ви можете викликати `Cart::instance('otherInstance')` ще раз, і ви знову працюватимете з `otherInstance`. 406 | 407 | Короткий приклад: 408 | 409 | ```php 410 | Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); 411 | 412 | // Get the content of the 'shopping' cart 413 | Cart::content(); 414 | 415 | Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); 416 | 417 | // Get the content of the 'wishlist' cart 418 | Cart::content(); 419 | 420 | // If you want to get the content of the 'shopping' cart again 421 | Cart::instance('shopping')->content(); 422 | 423 | // And the count of the 'wishlist' cart again 424 | Cart::instance('wishlist')->count(); 425 | ``` 426 | 427 | Ви також можете застосувати Контракт `InstanceIdentifier` для розширення бажаної моделі через призначення / створення екземпляру Кошика (Cart) для неї. Така дія також дозволить напряму встановлювати глобальну знижку. 428 | ``` 429 | email; 448 | } 449 | 450 | /** 451 | * Get the unique identifier to load the Cart from 452 | * 453 | * @return int|string 454 | */ 455 | public function getInstanceGlobalDiscount($options = null) 456 | { 457 | return $this->discountRate ?: 0; 458 | } 459 | } 460 | 461 | // Inside Controller 462 | $user = \Auth::user(); 463 | $cart = Cart::instance($user); 464 | 465 | 466 | 467 | ``` 468 | 469 | **N.B. Зверніть увагу, що кошик залишається у стані останнього призначеного екземпляра, доки ви не встановите інший екземпляр протягом виконання скрипта.** 470 | 471 | **N.B.2 За замовчуванням екземпляр кошика називається `default`, тому коли ви не використовуєте екземпляри, `Cart::content();` залишається таким самим як і `Cart::instance('default')->content()`.** 472 | 473 | ## Моделі 474 | 475 | Через те, що можливість прямого доступу до моделі з CartItem може бути дуже зручною, виникає питання чи можливо об'єднати модель із товарами у кошику. Скажімо, у вашому застосунку є модель `Product`. Завдяки методу `associate()` ви можете вказати кошику, що товар у кошику об'єднаний з моделлю `Product`. 476 | 477 | Таким чином ви можете отримати доступ до вашої моделі одразу з `CartItem`! 478 | 479 | Доступ до моделі також можна отримати через властивість CartItem `model`. 480 | 481 | **Якщо ваша модель запускає інтерфейс `Buyable` і ви використовували вашу модель для додавання товару до кошика, вони будуть об'єднані автоматично.** 482 | 483 | Ось приклад: 484 | 485 | ```php 486 | 487 | // First we'll add the item to the cart. 488 | $cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 489 | 490 | // Next we associate a model with the item. 491 | Cart::associate($cartItem->rowId, 'Product'); 492 | 493 | // Or even easier, call the associate method on the CartItem! 494 | $cartItem->associate('Product'); 495 | 496 | // You can even make it a one-liner 497 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); 498 | 499 | // Now, when iterating over the content of the cart, you can access the model. 500 | foreach(Cart::content() as $row) { 501 | echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; 502 | } 503 | ``` 504 | ## База даних 505 | 506 | - [Конфігурації](#configuration) 507 | - [Збереження кошика](#storing-the-cart) 508 | - [Відновлення кошика](#restoring-the-cart) 509 | 510 | ### Конфігурації 511 | Для збереження кошика до бази даних, щоб ви могли отримати його пізніше, пакет повинен знати яке підключення до бази даних використовувати і яка назва окремої таблиці. 512 | За замовчуванням, пакет використовуватиме підключення до бази даних, яке вказане за замовчуванням, та використовуватиме таблицію `shoppingcart`. 513 | Якщо ви хочете змінити ці значення, вам потрібно буде опублікувати файл з конфігураціями `config`. 514 | 515 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" 516 | 517 | Така дія створить вам файл з конфігураціями `cart.php`, в якому ви можете внести бажані зміни. 518 | 519 | Щоб спростити ваше життя, пакет також включає готову до вжитку `migration`, яку можна опублікувати через запуск наступної команди: 520 | 521 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" 522 | 523 | Така дія розмістить файл з міграцією таблиці `shoppingcart` в директорію `database/migrations`. Все що вам залишається зробити, це запустити `php artisan migrate` для міграції вашої бази даних. 524 | 525 | ### Збереження кошика 526 | Для збереження екземпляра кошика до бази даних, вам потрібно викликати метод `store($identifier) `. Де `$identifier` є випадковим ключем, наприклад, ідентифікатор або ім'я користувача. 527 | 528 | Cart::store('username'); 529 | 530 | // To store a cart instance named 'wishlist' 531 | Cart::instance('wishlist')->store('username'); 532 | 533 | ### Відновлення кошика 534 | Якщо ви хочете отримати кошик із бази даних і відновити його, вам знадобиться викликати метод `restore($identifier)`, де `$identifier` - це ключ, який ви зазначили у методі `store`. 535 | 536 | Cart::restore('username'); 537 | 538 | // To restore a cart instance named 'wishlist' 539 | Cart::instance('wishlist')->restore('username'); 540 | 541 | ### Злиття кошиків 542 | Якщо ви хочете злити кошик із іншим кошиком, збереженим у базі даних, вам знадобиться викликати метод `merge($identifier)`, де `$identifier` - це ключ, який ви зазначили у методі`store`. Ви також можете визначити чи хочете ви зберегти знижку і ставку оподаткування для товарів. 543 | 544 | // Merge the contents of 'savedcart' into 'username'. 545 | Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, 'savedcartinstance'); 546 | 547 | ## Перехоплення 548 | 549 | Пакет Кошик (Cart) видаватиме винятки/перехоплення у разі, якщо щось йде не за планом. Таким чином, вам буде простіше відлагоджувати (debug) ваш код, використовуючи пакет Кошик, або обробляти помилку за типом перехоплення. Пакети Кошика можуть видавати наступні перехоплення: 550 | 551 | | Перехоплення | Пояснення | 552 | | ---------------------------- | ---------------------------------------------------------------------------------- | 553 | | *CartAlreadyStoredException* | ПерехопленняКошикВжеЗбережено Коли ви намагаєтеся зберегти кошик, який вже було збережено, застосовуючи вказаний ідентифікатор | 554 | | *InvalidRowIDException* | ПерехопленняНеправильнийІдРядка Коли rowId, який було передано, не існує у поточному екземплярі кошика | 555 | | *UnknownModelException* | ПерехопленняНевідомаМодель Коли ви намагаєтеся об'єднати неіснуючу модель з CartItem. | 556 | 557 | ## Події 558 | 559 | Кошик також має вбудовані події. Існує п'ять подій, які можна очікувати. 560 | 561 | | Подія | Видано | Параметр | 562 | | ------------- | ---------------------------------------- | -------------------------------- | 563 | | cart.added | Коли товар додано до кошика. | `CartItem`, який було додано. | 564 | | cart.updated | Коли товар оновлено у кошику. | `CartItem`, який було оновлено. | 565 | | cart.removed | Коли товар вилучено з кошика. | `CartItem`, який було вилучено. | 566 | | cart.stored | Коли вміст кошика було збережено. | - | 567 | | cart.restored | Коли вміст кошика було відновлено. | - | 568 | 569 | ## Приклад 570 | 571 | Нижче наведено приклад як відобразити вміст кошика у таблиці: 572 | 573 | ```php 574 | 575 | // Add some items in your Controller. 576 | Cart::add('192ao12', 'Product 1', 1, 9.99); 577 | Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); 578 | 579 | // Display the content in a View. 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 |
ProductQtyPriceSubtotal
596 |

name; ?>

597 |

options->has('size') ? $row->options->size : ''); ?>

598 |
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
626 | ``` 627 | -------------------------------------------------------------------------------- /src/Cart.php: -------------------------------------------------------------------------------- 1 | session = $session; 82 | $this->events = $events; 83 | $this->taxRate = config('cart.tax'); 84 | 85 | $this->instance(self::DEFAULT_INSTANCE); 86 | } 87 | 88 | /** 89 | * Set the current cart instance. 90 | * 91 | * @param string|null $instance 92 | * 93 | * @return \Gloudemans\Shoppingcart\Cart 94 | */ 95 | public function instance($instance = null) 96 | { 97 | $instance = $instance ?: self::DEFAULT_INSTANCE; 98 | 99 | if ($instance instanceof InstanceIdentifier) { 100 | $this->discount = $instance->getInstanceGlobalDiscount(); 101 | $instance = $instance->getInstanceIdentifier(); 102 | } 103 | 104 | $this->instance = 'cart.'.$instance; 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Get the current cart instance. 111 | * 112 | * @return string 113 | */ 114 | public function currentInstance() 115 | { 116 | return str_replace('cart.', '', $this->instance); 117 | } 118 | 119 | /** 120 | * Add an item to the cart. 121 | * 122 | * @param mixed $id 123 | * @param mixed $name 124 | * @param int|float $qty 125 | * @param float $price 126 | * @param float $weight 127 | * @param array $options 128 | * 129 | * @return \Gloudemans\Shoppingcart\CartItem 130 | */ 131 | public function add($id, $name = null, $qty = null, $price = null, $weight = 0, array $options = []) 132 | { 133 | if ($this->isMulti($id)) { 134 | return array_map(function ($item) { 135 | return $this->add($item); 136 | }, $id); 137 | } 138 | 139 | $cartItem = $this->createCartItem($id, $name, $qty, $price, $weight, $options); 140 | 141 | return $this->addCartItem($cartItem); 142 | } 143 | 144 | /** 145 | * Add an item to the cart. 146 | * 147 | * @param \Gloudemans\Shoppingcart\CartItem $item Item to add to the Cart 148 | * @param bool $keepDiscount Keep the discount rate of the Item 149 | * @param bool $keepTax Keep the Tax rate of the Item 150 | * @param bool $dispatchEvent 151 | * 152 | * @return \Gloudemans\Shoppingcart\CartItem The CartItem 153 | */ 154 | public function addCartItem($item, $keepDiscount = false, $keepTax = false, $dispatchEvent = true) 155 | { 156 | if (!$keepDiscount) { 157 | $item->setDiscountRate($this->discount); 158 | } 159 | 160 | if (!$keepTax) { 161 | $item->setTaxRate($this->taxRate); 162 | } 163 | 164 | $content = $this->getContent(); 165 | 166 | if ($content->has($item->rowId)) { 167 | $item->qty += $content->get($item->rowId)->qty; 168 | } 169 | 170 | $content->put($item->rowId, $item); 171 | 172 | if ($dispatchEvent) { 173 | $this->events->dispatch('cart.adding', $item); 174 | } 175 | 176 | $this->session->put($this->instance, $content); 177 | 178 | if ($dispatchEvent) { 179 | $this->events->dispatch('cart.added', $item); 180 | } 181 | 182 | return $item; 183 | } 184 | 185 | /** 186 | * Update the cart item with the given rowId. 187 | * 188 | * @param string $rowId 189 | * @param mixed $qty 190 | * 191 | * @return \Gloudemans\Shoppingcart\CartItem 192 | */ 193 | public function update($rowId, $qty) 194 | { 195 | $cartItem = $this->get($rowId); 196 | 197 | if ($qty instanceof Buyable) { 198 | $cartItem->updateFromBuyable($qty); 199 | } elseif (is_array($qty)) { 200 | $cartItem->updateFromArray($qty); 201 | } else { 202 | $cartItem->qty = $qty; 203 | } 204 | 205 | $content = $this->getContent(); 206 | 207 | if ($rowId !== $cartItem->rowId) { 208 | $itemOldIndex = $content->keys()->search($rowId); 209 | 210 | $content->pull($rowId); 211 | 212 | if ($content->has($cartItem->rowId)) { 213 | $existingCartItem = $this->get($cartItem->rowId); 214 | $cartItem->setQuantity($existingCartItem->qty + $cartItem->qty); 215 | } 216 | } 217 | 218 | if ($cartItem->qty <= 0) { 219 | $this->remove($cartItem->rowId); 220 | 221 | return; 222 | } else { 223 | if (isset($itemOldIndex)) { 224 | $content = $content->slice(0, $itemOldIndex) 225 | ->merge([$cartItem->rowId => $cartItem]) 226 | ->merge($content->slice($itemOldIndex)); 227 | } else { 228 | $content->put($cartItem->rowId, $cartItem); 229 | } 230 | } 231 | 232 | $this->events->dispatch('cart.updating', $cartItem); 233 | 234 | $this->session->put($this->instance, $content); 235 | 236 | $this->events->dispatch('cart.updated', $cartItem); 237 | 238 | return $cartItem; 239 | } 240 | 241 | /** 242 | * Remove the cart item with the given rowId from the cart. 243 | * 244 | * @param string $rowId 245 | * 246 | * @return void 247 | */ 248 | public function remove($rowId) 249 | { 250 | $cartItem = $this->get($rowId); 251 | 252 | $content = $this->getContent(); 253 | 254 | $content->pull($cartItem->rowId); 255 | 256 | $this->events->dispatch('cart.removing', $cartItem); 257 | 258 | $this->session->put($this->instance, $content); 259 | 260 | $this->events->dispatch('cart.removed', $cartItem); 261 | } 262 | 263 | /** 264 | * Get a cart item from the cart by its rowId. 265 | * 266 | * @param string $rowId 267 | * 268 | * @return \Gloudemans\Shoppingcart\CartItem 269 | */ 270 | public function get($rowId) 271 | { 272 | $content = $this->getContent(); 273 | 274 | if (!$content->has($rowId)) { 275 | throw new InvalidRowIDException("The cart does not contain rowId {$rowId}."); 276 | } 277 | 278 | return $content->get($rowId); 279 | } 280 | 281 | /** 282 | * Destroy the current cart instance. 283 | * 284 | * @return void 285 | */ 286 | public function destroy() 287 | { 288 | $this->session->remove($this->instance); 289 | } 290 | 291 | /** 292 | * Get the content of the cart. 293 | * 294 | * @return \Illuminate\Support\Collection 295 | */ 296 | public function content() 297 | { 298 | if (is_null($this->session->get($this->instance))) { 299 | return new Collection([]); 300 | } 301 | 302 | return $this->session->get($this->instance); 303 | } 304 | 305 | /** 306 | * Get the total quantity of all CartItems in the cart. 307 | * 308 | * @return int|float 309 | */ 310 | public function count() 311 | { 312 | return $this->getContent()->sum('qty'); 313 | } 314 | 315 | /** 316 | * Get the amount of CartItems in the Cart. 317 | * Keep in mind that this does NOT count quantity. 318 | * 319 | * @return int|float 320 | */ 321 | public function countItems() 322 | { 323 | return $this->getContent()->count(); 324 | } 325 | 326 | /** 327 | * Get the total price of the items in the cart. 328 | * 329 | * @return float 330 | */ 331 | public function totalFloat() 332 | { 333 | return $this->getContent()->reduce(function ($total, CartItem $cartItem) { 334 | return $total + $cartItem->total; 335 | }, 0); 336 | } 337 | 338 | /** 339 | * Get the total price of the items in the cart as formatted string. 340 | * 341 | * @param int $decimals 342 | * @param string $decimalPoint 343 | * @param string $thousandSeperator 344 | * 345 | * @return string 346 | */ 347 | public function total($decimals = null, $decimalPoint = null, $thousandSeperator = null) 348 | { 349 | return $this->numberFormat($this->totalFloat(), $decimals, $decimalPoint, $thousandSeperator); 350 | } 351 | 352 | /** 353 | * Get the total tax of the items in the cart. 354 | * 355 | * @return float 356 | */ 357 | public function taxFloat() 358 | { 359 | return $this->getContent()->reduce(function ($tax, CartItem $cartItem) { 360 | return $tax + $cartItem->taxTotal; 361 | }, 0); 362 | } 363 | 364 | /** 365 | * Get the total tax of the items in the cart as formatted string. 366 | * 367 | * @param int $decimals 368 | * @param string $decimalPoint 369 | * @param string $thousandSeperator 370 | * 371 | * @return string 372 | */ 373 | public function tax($decimals = null, $decimalPoint = null, $thousandSeperator = null) 374 | { 375 | return $this->numberFormat($this->taxFloat(), $decimals, $decimalPoint, $thousandSeperator); 376 | } 377 | 378 | /** 379 | * Get the subtotal (total - tax) of the items in the cart. 380 | * 381 | * @return float 382 | */ 383 | public function subtotalFloat() 384 | { 385 | return $this->getContent()->reduce(function ($subTotal, CartItem $cartItem) { 386 | return $subTotal + $cartItem->subtotal; 387 | }, 0); 388 | } 389 | 390 | /** 391 | * Get the subtotal (total - tax) of the items in the cart as formatted string. 392 | * 393 | * @param int $decimals 394 | * @param string $decimalPoint 395 | * @param string $thousandSeperator 396 | * 397 | * @return string 398 | */ 399 | public function subtotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 400 | { 401 | return $this->numberFormat($this->subtotalFloat(), $decimals, $decimalPoint, $thousandSeperator); 402 | } 403 | 404 | /** 405 | * Get the discount of the items in the cart. 406 | * 407 | * @return float 408 | */ 409 | public function discountFloat() 410 | { 411 | return $this->getContent()->reduce(function ($discount, CartItem $cartItem) { 412 | return $discount + $cartItem->discountTotal; 413 | }, 0); 414 | } 415 | 416 | /** 417 | * Get the discount of the items in the cart as formatted string. 418 | * 419 | * @param int $decimals 420 | * @param string $decimalPoint 421 | * @param string $thousandSeperator 422 | * 423 | * @return string 424 | */ 425 | public function discount($decimals = null, $decimalPoint = null, $thousandSeperator = null) 426 | { 427 | return $this->numberFormat($this->discountFloat(), $decimals, $decimalPoint, $thousandSeperator); 428 | } 429 | 430 | /** 431 | * Get the price of the items in the cart (not rounded). 432 | * 433 | * @return float 434 | */ 435 | public function initialFloat() 436 | { 437 | return $this->getContent()->reduce(function ($initial, CartItem $cartItem) { 438 | return $initial + ($cartItem->qty * $cartItem->price); 439 | }, 0); 440 | } 441 | 442 | /** 443 | * Get the price of the items in the cart as formatted string. 444 | * 445 | * @param int $decimals 446 | * @param string $decimalPoint 447 | * @param string $thousandSeperator 448 | * 449 | * @return string 450 | */ 451 | public function initial($decimals = null, $decimalPoint = null, $thousandSeperator = null) 452 | { 453 | return $this->numberFormat($this->initialFloat(), $decimals, $decimalPoint, $thousandSeperator); 454 | } 455 | 456 | /** 457 | * Get the price of the items in the cart (previously rounded). 458 | * 459 | * @return float 460 | */ 461 | public function priceTotalFloat() 462 | { 463 | return $this->getContent()->reduce(function ($initial, CartItem $cartItem) { 464 | return $initial + $cartItem->priceTotal; 465 | }, 0); 466 | } 467 | 468 | /** 469 | * Get the price of the items in the cart as formatted string. 470 | * 471 | * @param int $decimals 472 | * @param string $decimalPoint 473 | * @param string $thousandSeperator 474 | * 475 | * @return string 476 | */ 477 | public function priceTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) 478 | { 479 | return $this->numberFormat($this->priceTotalFloat(), $decimals, $decimalPoint, $thousandSeperator); 480 | } 481 | 482 | /** 483 | * Get the total weight of the items in the cart. 484 | * 485 | * @return float 486 | */ 487 | public function weightFloat() 488 | { 489 | return $this->getContent()->reduce(function ($total, CartItem $cartItem) { 490 | return $total + ($cartItem->qty * $cartItem->weight); 491 | }, 0); 492 | } 493 | 494 | /** 495 | * Get the total weight of the items in the cart. 496 | * 497 | * @param int $decimals 498 | * @param string $decimalPoint 499 | * @param string $thousandSeperator 500 | * 501 | * @return string 502 | */ 503 | public function weight($decimals = null, $decimalPoint = null, $thousandSeperator = null) 504 | { 505 | return $this->numberFormat($this->weightFloat(), $decimals, $decimalPoint, $thousandSeperator); 506 | } 507 | 508 | /** 509 | * Search the cart content for a cart item matching the given search closure. 510 | * 511 | * @param \Closure $search 512 | * 513 | * @return \Illuminate\Support\Collection 514 | */ 515 | public function search(Closure $search) 516 | { 517 | return $this->getContent()->filter($search); 518 | } 519 | 520 | /** 521 | * Associate the cart item with the given rowId with the given model. 522 | * 523 | * @param string $rowId 524 | * @param mixed $model 525 | * 526 | * @return void 527 | */ 528 | public function associate($rowId, $model) 529 | { 530 | if (is_string($model) && !class_exists($model)) { 531 | throw new UnknownModelException("The supplied model {$model} does not exist."); 532 | } 533 | 534 | $cartItem = $this->get($rowId); 535 | 536 | $cartItem->associate($model); 537 | 538 | $content = $this->getContent(); 539 | 540 | $content->put($cartItem->rowId, $cartItem); 541 | 542 | $this->session->put($this->instance, $content); 543 | } 544 | 545 | /** 546 | * Set the tax rate for the cart item with the given rowId. 547 | * 548 | * @param string $rowId 549 | * @param int|float $taxRate 550 | * 551 | * @return void 552 | */ 553 | public function setTax($rowId, $taxRate) 554 | { 555 | $cartItem = $this->get($rowId); 556 | 557 | $cartItem->setTaxRate($taxRate); 558 | 559 | $content = $this->getContent(); 560 | 561 | $content->put($cartItem->rowId, $cartItem); 562 | 563 | $this->session->put($this->instance, $content); 564 | } 565 | 566 | /** 567 | * Set the global tax rate for the cart. 568 | * This will set the tax rate for all items. 569 | * 570 | * @param float $discount 571 | */ 572 | public function setGlobalTax($taxRate) 573 | { 574 | $this->taxRate = $taxRate; 575 | 576 | $content = $this->getContent(); 577 | if ($content && $content->count()) { 578 | $content->each(function ($item, $key) { 579 | $item->setTaxRate($this->taxRate); 580 | }); 581 | } 582 | } 583 | 584 | /** 585 | * Set the discount rate for the cart item with the given rowId. 586 | * 587 | * @param string $rowId 588 | * @param int|float $taxRate 589 | * 590 | * @return void 591 | */ 592 | public function setDiscount($rowId, $discount) 593 | { 594 | $cartItem = $this->get($rowId); 595 | 596 | $cartItem->setDiscountRate($discount); 597 | 598 | $content = $this->getContent(); 599 | 600 | $content->put($cartItem->rowId, $cartItem); 601 | 602 | $this->session->put($this->instance, $content); 603 | } 604 | 605 | /** 606 | * Set the global discount percentage for the cart. 607 | * This will set the discount for all cart items. 608 | * 609 | * @param float $discount 610 | * 611 | * @return void 612 | */ 613 | public function setGlobalDiscount($discount) 614 | { 615 | $this->discount = $discount; 616 | 617 | $content = $this->getContent(); 618 | if ($content && $content->count()) { 619 | $content->each(function ($item, $key) { 620 | $item->setDiscountRate($this->discount); 621 | }); 622 | } 623 | } 624 | 625 | /** 626 | * Store an the current instance of the cart. 627 | * 628 | * @param mixed $identifier 629 | * 630 | * @return void 631 | */ 632 | public function store($identifier) 633 | { 634 | $content = $this->getContent(); 635 | 636 | if ($identifier instanceof InstanceIdentifier) { 637 | $identifier = $identifier->getInstanceIdentifier(); 638 | } 639 | 640 | $instance = $this->currentInstance(); 641 | 642 | if ($this->storedCartInstanceWithIdentifierExists($instance, $identifier)) { 643 | throw new CartAlreadyStoredException("A cart with identifier {$identifier} was already stored."); 644 | } 645 | 646 | if ($this->getConnection()->getDriverName() === 'pgsql') { 647 | $serializedContent = base64_encode(serialize($content)); 648 | } else { 649 | $serializedContent = serialize($content); 650 | } 651 | 652 | $this->getConnection()->table($this->getTableName())->insert([ 653 | 'identifier' => $identifier, 654 | 'instance' => $instance, 655 | 'content' => $serializedContent, 656 | 'created_at' => $this->createdAt ?: Carbon::now(), 657 | 'updated_at' => Carbon::now(), 658 | ]); 659 | 660 | $this->events->dispatch('cart.stored'); 661 | } 662 | 663 | /** 664 | * Restore the cart with the given identifier. 665 | * 666 | * @param mixed $identifier 667 | * 668 | * @return void 669 | */ 670 | public function restore($identifier) 671 | { 672 | if ($identifier instanceof InstanceIdentifier) { 673 | $identifier = $identifier->getInstanceIdentifier(); 674 | } 675 | 676 | $currentInstance = $this->currentInstance(); 677 | 678 | if (!$this->storedCartInstanceWithIdentifierExists($currentInstance, $identifier)) { 679 | return; 680 | } 681 | 682 | $stored = $this->getConnection()->table($this->getTableName()) 683 | ->where(['identifier'=> $identifier, 'instance' => $currentInstance])->first(); 684 | 685 | if ($this->getConnection()->getDriverName() === 'pgsql') { 686 | $storedContent = unserialize(base64_decode(data_get($stored, 'content'))); 687 | } else { 688 | $storedContent = unserialize(data_get($stored, 'content')); 689 | } 690 | 691 | $this->instance(data_get($stored, 'instance')); 692 | 693 | $content = $this->getContent(); 694 | 695 | foreach ($storedContent as $cartItem) { 696 | $content->put($cartItem->rowId, $cartItem); 697 | } 698 | 699 | $this->events->dispatch('cart.restored'); 700 | 701 | $this->session->put($this->instance, $content); 702 | 703 | $this->instance($currentInstance); 704 | 705 | $this->createdAt = Carbon::parse(data_get($stored, 'created_at')); 706 | $this->updatedAt = Carbon::parse(data_get($stored, 'updated_at')); 707 | 708 | $this->getConnection()->table($this->getTableName())->where(['identifier' => $identifier, 'instance' => $currentInstance])->delete(); 709 | } 710 | 711 | /** 712 | * Erase the cart with the given identifier. 713 | * 714 | * @param mixed $identifier 715 | * 716 | * @return void 717 | */ 718 | public function erase($identifier) 719 | { 720 | if ($identifier instanceof InstanceIdentifier) { 721 | $identifier = $identifier->getInstanceIdentifier(); 722 | } 723 | 724 | $instance = $this->currentInstance(); 725 | 726 | if (!$this->storedCartInstanceWithIdentifierExists($instance, $identifier)) { 727 | return; 728 | } 729 | 730 | $this->getConnection()->table($this->getTableName())->where(['identifier' => $identifier, 'instance' => $instance])->delete(); 731 | 732 | $this->events->dispatch('cart.erased'); 733 | } 734 | 735 | /** 736 | * Merges the contents of another cart into this cart. 737 | * 738 | * @param mixed $identifier Identifier of the Cart to merge with. 739 | * @param bool $keepDiscount Keep the discount of the CartItems. 740 | * @param bool $keepTax Keep the tax of the CartItems. 741 | * @param bool $dispatchAdd Flag to dispatch the add events. 742 | * 743 | * @return bool 744 | */ 745 | public function merge($identifier, $keepDiscount = false, $keepTax = false, $dispatchAdd = true, $instance = self::DEFAULT_INSTANCE) 746 | { 747 | if (!$this->storedCartInstanceWithIdentifierExists($instance, $identifier)) { 748 | return false; 749 | } 750 | 751 | $stored = $this->getConnection()->table($this->getTableName()) 752 | ->where(['identifier'=> $identifier, 'instance'=> $instance])->first(); 753 | 754 | if ($this->getConnection()->getDriverName() === 'pgsql') { 755 | $storedContent = unserialize(base64_decode($stored->content)); 756 | } else { 757 | $storedContent = unserialize($stored->content); 758 | } 759 | 760 | foreach ($storedContent as $cartItem) { 761 | $this->addCartItem($cartItem, $keepDiscount, $keepTax, $dispatchAdd); 762 | } 763 | 764 | $this->events->dispatch('cart.merged'); 765 | 766 | return true; 767 | } 768 | 769 | /** 770 | * Magic method to make accessing the total, tax and subtotal properties possible. 771 | * 772 | * @param string $attribute 773 | * 774 | * @return float|null 775 | */ 776 | public function __get($attribute) 777 | { 778 | switch ($attribute) { 779 | case 'total': 780 | return $this->total(); 781 | case 'tax': 782 | return $this->tax(); 783 | case 'subtotal': 784 | return $this->subtotal(); 785 | default: 786 | return; 787 | } 788 | } 789 | 790 | /** 791 | * Get the carts content, if there is no cart content set yet, return a new empty Collection. 792 | * 793 | * @return \Illuminate\Support\Collection 794 | */ 795 | protected function getContent() 796 | { 797 | if ($this->session->has($this->instance)) { 798 | return $this->session->get($this->instance); 799 | } 800 | 801 | return new Collection(); 802 | } 803 | 804 | /** 805 | * Create a new CartItem from the supplied attributes. 806 | * 807 | * @param mixed $id 808 | * @param mixed $name 809 | * @param int|float $qty 810 | * @param float $price 811 | * @param float $weight 812 | * @param array $options 813 | * 814 | * @return \Gloudemans\Shoppingcart\CartItem 815 | */ 816 | private function createCartItem($id, $name, $qty, $price, $weight, array $options) 817 | { 818 | if ($id instanceof Buyable) { 819 | $cartItem = CartItem::fromBuyable($id, $qty ?: []); 820 | $cartItem->setQuantity($name ?: 1); 821 | $cartItem->associate($id); 822 | } elseif (is_array($id)) { 823 | $cartItem = CartItem::fromArray($id); 824 | $cartItem->setQuantity($id['qty']); 825 | } else { 826 | $cartItem = CartItem::fromAttributes($id, $name, $price, $weight, $options); 827 | $cartItem->setQuantity($qty); 828 | } 829 | 830 | $cartItem->setInstance($this->currentInstance()); 831 | 832 | return $cartItem; 833 | } 834 | 835 | /** 836 | * Check if the item is a multidimensional array or an array of Buyables. 837 | * 838 | * @param mixed $item 839 | * 840 | * @return bool 841 | */ 842 | private function isMulti($item) 843 | { 844 | if (!is_array($item)) { 845 | return false; 846 | } 847 | 848 | return is_array(head($item)) || head($item) instanceof Buyable; 849 | } 850 | 851 | /** 852 | * @param $identifier 853 | * 854 | * @return bool 855 | */ 856 | private function storedCartInstanceWithIdentifierExists($instance, $identifier) 857 | { 858 | return $this->getConnection()->table($this->getTableName())->where(['identifier' => $identifier, 'instance'=> $instance])->exists(); 859 | } 860 | 861 | /** 862 | * Get the database connection. 863 | * 864 | * @return \Illuminate\Database\Connection 865 | */ 866 | private function getConnection() 867 | { 868 | return app(DatabaseManager::class)->connection($this->getConnectionName()); 869 | } 870 | 871 | /** 872 | * Get the database table name. 873 | * 874 | * @return string 875 | */ 876 | private function getTableName() 877 | { 878 | return config('cart.database.table', 'shoppingcart'); 879 | } 880 | 881 | /** 882 | * Get the database connection name. 883 | * 884 | * @return string 885 | */ 886 | private function getConnectionName() 887 | { 888 | $connection = config('cart.database.connection'); 889 | 890 | return is_null($connection) ? config('database.default') : $connection; 891 | } 892 | 893 | /** 894 | * Get the Formatted number. 895 | * 896 | * @param $value 897 | * @param $decimals 898 | * @param $decimalPoint 899 | * @param $thousandSeperator 900 | * 901 | * @return string 902 | */ 903 | private function numberFormat($value, $decimals, $decimalPoint, $thousandSeperator) 904 | { 905 | if (is_null($decimals)) { 906 | $decimals = config('cart.format.decimals', 2); 907 | } 908 | 909 | if (is_null($decimalPoint)) { 910 | $decimalPoint = config('cart.format.decimal_point', '.'); 911 | } 912 | 913 | if (is_null($thousandSeperator)) { 914 | $thousandSeperator = config('cart.format.thousand_separator', ','); 915 | } 916 | 917 | return number_format($value, $decimals, $decimalPoint, $thousandSeperator); 918 | } 919 | 920 | /** 921 | * Get the creation date of the cart (db context). 922 | * 923 | * @return \Carbon\Carbon|null 924 | */ 925 | public function createdAt() 926 | { 927 | return $this->createdAt; 928 | } 929 | 930 | /** 931 | * Get the lats update date of the cart (db context). 932 | * 933 | * @return \Carbon\Carbon|null 934 | */ 935 | public function updatedAt() 936 | { 937 | return $this->updatedAt; 938 | } 939 | } 940 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## LaravelShoppingcart 3 | [![codecov](https://codecov.io/gh/anayarojo/laravel-shopping-cart/branch/master/graph/badge.svg)](https://codecov.io/gh/anayarojo/laravel-shopping-cart) 4 | [![Latest Stable Version](https://poser.pugx.org/anayarojo/shoppingcart/v/stable)](https://packagist.org/packages/anayarojo/shoppingcart) 5 | [![Latest Unstable Version](https://poser.pugx.org/anayarojo/shoppingcart/v/unstable)](https://packagist.org/packages/anayarojo/shoppingcart) 6 | [![License](https://poser.pugx.org/anayarojo/shoppingcart/license)](https://packagist.org/packages/anayarojo/shoppingcart) 7 | 8 | This is a fork of [bumbummen99's LaravelShoppingcart](https://github.com/bumbummen99/LaravelShoppingcart) extended with minor features compatible with Laravel 10+. An example integration can be [found here](https://github.com/bumbummen99/LaravelShoppingcartDemo). 9 | 10 | ## Installation 11 | 12 | Install the [package](https://packagist.org/packages/anayarojo/shoppingcart) through [Composer](http://getcomposer.org/). 13 | 14 | Run the Composer require command from the Terminal: 15 | 16 | composer require anayarojo/shoppingcart 17 | 18 | Now you're ready to start using the Laravel Shopping Cart in your application. 19 | 20 | **As of version 2 of this package it's possibly to use dependency injection to inject an instance of the Cart class into your controller or other class** 21 | 22 | You definitely should publish the `config` file and take a look at it. 23 | 24 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" 25 | 26 | This will give you a `cart.php` config file in which you can make changes to the packages behaivor. 27 | 28 | ## Updates 29 | 30 | As of version **4.2.0** this package does, when being used with PostgreSQL, encode the cart content to base64 before storing into database due to an [issue with saving values including zero bytes](https://github.com/bumbummen99/LaravelShoppingcart/pull/167). Please consider clearing your cart table in case you are upgrading using PostgreSQL from a version **<4.2.0**. 31 | 32 | ## Table of Contents 33 | Look at one of the following topics to learn more about LaravelShoppingcart 34 | 35 | * [Important note](#important-note) 36 | * [Usage](#usage) 37 | * [Collections](#collections) 38 | * [Instances](#instances) 39 | * [Models](#models) 40 | * [Database](#database) 41 | * [Calculators](#calculators) 42 | * [Exceptions](#exceptions) 43 | * [Events](#events) 44 | * [Example](#example) 45 | * [Contributors](#contributors) 46 | 47 | ## Important note 48 | 49 | As all the shopping cart that calculate prices including taxes and discount, also this module could be affected by the "totals rounding issue" ([*](https://stackoverflow.com/questions/13529580/magento-tax-rounding-issue)) due to the decimal precision used for prices and for the results. 50 | In order to avoid (or at least minimize) this issue, in the Laravel shoppingcart package the totals are calculated using the method **"per Row"** and returned already rounded based on the number format set as default in the config file (cart.php). 51 | Due to this **WE DISCOURAGE TO SET HIGH PRECISION AS DEFAULT AND TO FORMAT THE OUTPUT RESULT USING LESS DECIMAL** Doing this can lead to the rounding issue. 52 | 53 | The base price (product price) is left not rounded. 54 | 55 | ## Usage 56 | 57 | The shoppingcart gives you the following methods to use: 58 | 59 | ### Cart::add() 60 | 61 | Adding an item to the cart is really simple, you just use the `add()` method, which accepts a variety of parameters. 62 | 63 | In its most basic form you can specify the id, name, quantity, price and weight of the product you'd like to add to the cart. 64 | 65 | ```php 66 | Cart::add('293ad', 'Product 1', 1, 9.99, 550); 67 | ``` 68 | 69 | As an optional fifth parameter you can pass it options, so you can add multiple items with the same id, but with (for instance) a different size. 70 | 71 | ```php 72 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 73 | ``` 74 | 75 | **The `add()` method will return an CartItem instance of the item you just added to the cart.** 76 | 77 | Maybe you prefer to add the item using an array? As long as the array contains the required keys, you can pass it to the method. The options key is optional. 78 | 79 | ```php 80 | Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); 81 | ``` 82 | 83 | New in version 2 of the package is the possibility to work with the [Buyable](#buyable) interface. The way this works is that you have a model implement the [Buyable](#buyable) interface, which will make you implement a few methods so the package knows how to get the id, name and price from your model. 84 | This way you can just pass the `add()` method a model and the quantity and it will automatically add it to the cart. 85 | 86 | **As an added bonus it will automatically associate the model with the CartItem** 87 | 88 | ```php 89 | Cart::add($product, 1, ['size' => 'large']); 90 | ``` 91 | As an optional third parameter you can add options. 92 | ```php 93 | Cart::add($product, 1, ['size' => 'large']); 94 | ``` 95 | 96 | Finally, you can also add multipe items to the cart at once. 97 | You can just pass the `add()` method an array of arrays, or an array of Buyables and they will be added to the cart. 98 | 99 | **When adding multiple items to the cart, the `add()` method will return an array of CartItems.** 100 | 101 | ```php 102 | Cart::add([ 103 | ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], 104 | ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] 105 | ]); 106 | 107 | Cart::add([$product1, $product2]); 108 | 109 | ``` 110 | 111 | ### Cart::update() 112 | 113 | To update an item in the cart, you'll first need the rowId of the item. 114 | Next you can use the `update()` method to update it. 115 | 116 | If you simply want to update the quantity, you'll pass the update method the rowId and the new quantity: 117 | 118 | ```php 119 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 120 | 121 | Cart::update($rowId, 2); // Will update the quantity 122 | ``` 123 | 124 | If you would like to update options of an item inside the cart, 125 | 126 | ```php 127 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 128 | 129 | Cart::update($rowId, ['options' => ['size' => 'small']]); // Will update the size option with new value 130 | ``` 131 | 132 | 133 | 134 | If you want to update more attributes of the item, you can either pass the update method an array or a `Buyable` as the second parameter. This way you can update all information of the item with the given rowId. 135 | 136 | ```php 137 | Cart::update($rowId, ['name' => 'Product 1']); // Will update the name 138 | 139 | Cart::update($rowId, $product); // Will update the id, name and price 140 | 141 | ``` 142 | 143 | ### Cart::remove() 144 | 145 | To remove an item for the cart, you'll again need the rowId. This rowId you simply pass to the `remove()` method and it will remove the item from the cart. 146 | 147 | ```php 148 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 149 | 150 | Cart::remove($rowId); 151 | ``` 152 | 153 | ### Cart::get() 154 | 155 | If you want to get an item from the cart using its rowId, you can simply call the `get()` method on the cart and pass it the rowId. 156 | 157 | ```php 158 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 159 | 160 | Cart::get($rowId); 161 | ``` 162 | 163 | ### Cart::content() 164 | 165 | Of course you also want to get the carts content. This is where you'll use the `content` method. This method will return a Collection of CartItems which you can iterate over and show the content to your customers. 166 | 167 | ```php 168 | Cart::content(); 169 | ``` 170 | 171 | This method will return the content of the current cart instance, if you want the content of another instance, simply chain the calls. 172 | 173 | ```php 174 | Cart::instance('wishlist')->content(); 175 | ``` 176 | 177 | ### Cart::destroy() 178 | 179 | If you want to completely remove the content of a cart, you can call the destroy method on the cart. This will remove all CartItems from the cart for the current cart instance. 180 | 181 | ```php 182 | Cart::destroy(); 183 | ``` 184 | 185 | ### Cart::weight() 186 | 187 | The `weight()` method can be used to get the weight total of all items in the cart, given their weight and quantity. 188 | 189 | ```php 190 | Cart::weight(); 191 | ``` 192 | 193 | The method will automatically format the result, which you can tweak using the three optional parameters 194 | 195 | ```php 196 | Cart::weight($decimals, $decimalSeperator, $thousandSeperator); 197 | ``` 198 | 199 | You can set the default number format in the config file. 200 | 201 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the total property `$cart->weight`** 202 | 203 | ### Cart::total() 204 | 205 | The `total()` method can be used to get the calculated total of all items in the cart, given there price and quantity. 206 | 207 | ```php 208 | Cart::total(); 209 | ``` 210 | 211 | The method will automatically format the result, which you can tweak using the three optional parameters 212 | 213 | ```php 214 | Cart::total($decimals, $decimalSeparator, $thousandSeparator); 215 | ``` 216 | 217 | You can set the default number format in the config file. 218 | 219 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the total property `$cart->total`** 220 | 221 | ### Cart::tax() 222 | 223 | The `tax()` method can be used to get the calculated amount of tax for all items in the cart, given there price and quantity. 224 | 225 | ```php 226 | Cart::tax(); 227 | ``` 228 | 229 | The method will automatically format the result, which you can tweak using the three optional parameters 230 | 231 | ```php 232 | Cart::tax($decimals, $decimalSeparator, $thousandSeparator); 233 | ``` 234 | 235 | You can set the default number format in the config file. 236 | 237 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the tax property `$cart->tax`** 238 | 239 | ### Cart::subtotal() 240 | 241 | The `subtotal()` method can be used to get the total of all items in the cart, minus the total amount of tax. 242 | 243 | ```php 244 | Cart::subtotal(); 245 | ``` 246 | 247 | The method will automatically format the result, which you can tweak using the three optional parameters 248 | 249 | ```php 250 | Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); 251 | ``` 252 | 253 | You can set the default number format in the config file. 254 | 255 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property `$cart->subtotal`** 256 | 257 | ### Cart::discount() 258 | 259 | The `discount()` method can be used to get the total discount of all items in the cart. 260 | 261 | ```php 262 | Cart::discount(); 263 | ``` 264 | 265 | The method will automatically format the result, which you can tweak using the three optional parameters 266 | 267 | ```php 268 | Cart::discount($decimals, $decimalSeparator, $thousandSeparator); 269 | ``` 270 | 271 | You can set the default number format in the config file. 272 | 273 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property `$cart->discount`** 274 | 275 | ### Cart::initial() 276 | 277 | The `initial()` method can be used to get the total price of all items in the cart before applying discount and taxes. 278 | 279 | It could be deprecated in the future. **When rounded could be affected by the rounding issue**, use it carefully or use [Cart::priceTotal()](#Cart::priceTotal()) 280 | 281 | ```php 282 | Cart::initial(); 283 | ``` 284 | 285 | The method will automatically format the result, which you can tweak using the three optional parameters. 286 | 287 | ```php 288 | Cart::initial($decimals, $decimalSeparator, $thousandSeparator); 289 | ``` 290 | 291 | You can set the default number format in the config file. 292 | 293 | ### Cart::priceTotal() 294 | 295 | The `priceTotal()` method can be used to get the total price of all items in the cart before applying discount and taxes. 296 | 297 | ```php 298 | Cart::priceTotal(); 299 | ``` 300 | 301 | The method return the result rounded based on the default number format, but you can tweak using the three optional parameters 302 | 303 | ```php 304 | Cart::priceTotal($decimals, $decimalSeparator, $thousandSeparator); 305 | ``` 306 | 307 | You can set the default number format in the config file. 308 | 309 | **If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property `$cart->initial`** 310 | 311 | ### Cart::count() 312 | 313 | If you want to know how many items there are in your cart, you can use the `count()` method. This method will return the total number of items in the cart. So if you've added 2 books and 1 shirt, it will return 3 items. 314 | 315 | ```php 316 | Cart::count(); 317 | $cart->count(); 318 | ``` 319 | 320 | ### Cart::search() 321 | 322 | To find an item in the cart, you can use the `search()` method. 323 | 324 | **This method was changed on version 2** 325 | 326 | Behind the scenes, the method simply uses the filter method of the Laravel Collection class. This means you must pass it a Closure in which you'll specify you search terms. 327 | 328 | If you for instance want to find all items with an id of 1: 329 | 330 | ```php 331 | $cart->search(function ($cartItem, $rowId) { 332 | return $cartItem->id === 1; 333 | }); 334 | ``` 335 | 336 | As you can see the Closure will receive two parameters. The first is the CartItem to perform the check against. The second parameter is the rowId of this CartItem. 337 | 338 | **The method will return a Collection containing all CartItems that where found** 339 | 340 | This way of searching gives you total control over the search process and gives you the ability to create very precise and specific searches. 341 | 342 | ### Cart::setTax($rowId, $taxRate) 343 | 344 | You can use the `setTax()` method to change the tax rate that applies to the CartItem. This will overwrite the value set in the config file. 345 | 346 | ```php 347 | Cart::setTax($rowId, 21); 348 | $cart->setTax($rowId, 21); 349 | ``` 350 | 351 | ### Cart::setGlobalTax($taxRate) 352 | 353 | You can use the `setGlobalTax()` method to change the tax rate for all items in the cart. New items will receive the setGlobalTax as well. 354 | 355 | ```php 356 | Cart::setGlobalTax(21); 357 | $cart->setGlobalTax(21); 358 | ``` 359 | 360 | ### Cart::setGlobalDiscount($discountRate) 361 | 362 | You can use the `setGlobalDiscount()` method to change the discount rate for all items in the cart. New items will receive the discount as well. 363 | 364 | ```php 365 | Cart::setGlobalDiscount(50); 366 | $cart->setGlobalDiscount(50); 367 | ``` 368 | 369 | ### Cart::setDiscount($rowId, $taxRate) 370 | 371 | You can use the `setDiscount()` method to change the discount rate that applies a CartItem. Keep in mind that this value will be changed if you set the global discount for the Cart afterwards. 372 | 373 | ```php 374 | Cart::setDiscount($rowId, 21); 375 | $cart->setDiscount($rowId, 21); 376 | ``` 377 | 378 | ### Buyable 379 | 380 | For the convenience of faster adding items to cart and their automatic association, your model has to implement the `Buyable` interface. You can use the `CanBeBought` trait to implement the required methods but keep in mind that these will use predefined fields on your model for the required values. 381 | ```php 382 | id; 398 | } 399 | public function getBuyableDescription(){ 400 | return $this->name; 401 | } 402 | public function getBuyablePrice(){ 403 | return $this->price; 404 | } 405 | public function getBuyableWeight(){ 406 | return $this->weight; 407 | } 408 | ``` 409 | 410 | Example: 411 | 412 | ```php 413 | id; 422 | } 423 | public function getBuyableDescription($options = null) { 424 | return $this->name; 425 | } 426 | public function getBuyablePrice($options = null) { 427 | return $this->price; 428 | } 429 | public function getBuyableWeight($options = null) { 430 | return $this->weight; 431 | } 432 | } 433 | ``` 434 | 435 | ## Collections 436 | 437 | On multiple instances the Cart will return to you a Collection. This is just a simple Laravel Collection, so all methods you can call on a Laravel Collection are also available on the result. 438 | 439 | As an example, you can quicky get the number of unique products in a cart: 440 | 441 | ```php 442 | Cart::content()->count(); 443 | ``` 444 | 445 | Or you can group the content by the id of the products: 446 | 447 | ```php 448 | Cart::content()->groupBy('id'); 449 | ``` 450 | 451 | ## Instances 452 | 453 | The packages supports multiple instances of the cart. The way this works is like this: 454 | 455 | You can set the current instance of the cart by calling `Cart::instance('newInstance')`. From this moment, the active instance of the cart will be `newInstance`, so when you add, remove or get the content of the cart, you're work with the `newInstance` instance of the cart. 456 | If you want to switch instances, you just call `Cart::instance('otherInstance')` again, and you're working with the `otherInstance` again. 457 | 458 | So a little example: 459 | 460 | ```php 461 | Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); 462 | 463 | // Get the content of the 'shopping' cart 464 | Cart::content(); 465 | 466 | Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); 467 | 468 | // Get the content of the 'wishlist' cart 469 | Cart::content(); 470 | 471 | // If you want to get the content of the 'shopping' cart again 472 | Cart::instance('shopping')->content(); 473 | 474 | // And the count of the 'wishlist' cart again 475 | Cart::instance('wishlist')->count(); 476 | ``` 477 | 478 | You can also use the `InstanceIdentifier` Contract to extend a desired Model to assign / create a Cart instance for it. This also allows to directly set the global discount. 479 | ``` 480 | email; 499 | } 500 | 501 | /** 502 | * Get the unique identifier to load the Cart from 503 | * 504 | * @return int|string 505 | */ 506 | public function getInstanceGlobalDiscount($options = null) 507 | { 508 | return $this->discountRate ?: 0; 509 | } 510 | } 511 | 512 | // Inside Controller 513 | $user = \Auth::user(); 514 | $cart = Cart::instance($user); 515 | 516 | 517 | 518 | ``` 519 | 520 | **N.B. Keep in mind that the cart stays in the last set instance for as long as you don't set a different one during script execution.** 521 | 522 | **N.B.2 The default cart instance is called `default`, so when you're not using instances,`Cart::content();` is the same as `Cart::instance('default')->content()`.** 523 | 524 | ## Models 525 | 526 | Because it can be very convenient to be able to directly access a model from a CartItem is it possible to associate a model with the items in the cart. Let's say you have a `Product` model in your application. With the `associate()` method, you can tell the cart that an item in the cart, is associated to the `Product` model. 527 | 528 | That way you can access your model right from the `CartItem`! 529 | 530 | The model can be accessed via the `model` property on the CartItem. 531 | 532 | **If your model implements the `Buyable` interface and you used your model to add the item to the cart, it will associate automatically.** 533 | 534 | Here is an example: 535 | 536 | ```php 537 | 538 | // First we'll add the item to the cart. 539 | $cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 540 | 541 | // Next we associate a model with the item. 542 | Cart::associate($cartItem->rowId, 'Product'); 543 | 544 | // Or even easier, call the associate method on the CartItem! 545 | $cartItem->associate('Product'); 546 | 547 | // You can even make it a one-liner 548 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); 549 | 550 | // Now, when iterating over the content of the cart, you can access the model. 551 | foreach(Cart::content() as $row) { 552 | echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; 553 | } 554 | ``` 555 | ## Database 556 | 557 | - [Config](#configuration) 558 | - [Storing the cart](#storing-the-cart) 559 | - [Restoring the cart](#restoring-the-cart) 560 | 561 | ### Configuration 562 | To save cart into the database so you can retrieve it later, the package needs to know which database connection to use and what the name of the table is. 563 | By default the package will use the default database connection and use a table named `shoppingcart`. You can change that in the configuration. 564 | 565 | To make your life easy, the package also includes a ready to use `migration` which you can publish by running: 566 | 567 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" 568 | 569 | This will place a `shoppingcart` table's migration file into `database/migrations` directory. Now all you have to do is run `php artisan migrate` to migrate your database. 570 | 571 | ### Storing the cart 572 | To store your cart instance into the database, you have to call the `store($identifier) ` method. Where `$identifier` is a random key, for instance the id or username of the user. 573 | 574 | Cart::store('username'); 575 | 576 | // To store a cart instance named 'wishlist' 577 | Cart::instance('wishlist')->store('username'); 578 | 579 | ### Restoring the cart 580 | If you want to retrieve the cart from the database and restore it, all you have to do is call the `restore($identifier)` where `$identifier` is the key you specified for the `store` method. 581 | 582 | Cart::restore('username'); 583 | 584 | // To restore a cart instance named 'wishlist' 585 | Cart::instance('wishlist')->restore('username'); 586 | 587 | ### Merge the cart 588 | If you want to merge the cart with another one from the database, all you have to do is call the `merge($identifier)` where `$identifier` is the key you specified for the `store` method. You can also define if you want to keep the discount and tax rates of the items and if you want to dispatch "cart.added" events. 589 | 590 | // Merge the contents of 'savedcart' into 'username'. 591 | Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, $dispatchAdd, 'savedcartinstance'); 592 | 593 | ### Erasing the cart 594 | If you want to erase the cart from the database, all you have to do is call the `erase($identifier)` where `$identifier` is the key you specified for the `store` method. 595 | 596 | Cart::erase('username'); 597 | 598 | // To erase a cart switching to an instance named 'wishlist' 599 | Cart::instance('wishlist')->erase('username'); 600 | 601 | ## Calculators 602 | 603 | The calculation logic for the package is implemented and defined in `Calculator` classes. These implement the `Gloudemans\Shoppingcart\Contracts\Calculator` Contract and and determine how the prices are calculated and rounded. The calculators can be configured in the confugration file. This is the default calculator: 604 | ```php 605 | price * ($cartItem->getDiscountRate() / 100); 621 | case 'tax': 622 | return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals); 623 | case 'priceTax': 624 | return round($cartItem->priceTarget + $cartItem->tax, $decimals); 625 | case 'discountTotal': 626 | return round($cartItem->discount * $cartItem->qty, $decimals); 627 | case 'priceTotal': 628 | return round($cartItem->price * $cartItem->qty, $decimals); 629 | case 'subtotal': 630 | return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0); 631 | case 'priceTarget': 632 | return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals); 633 | case 'taxTotal': 634 | return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals); 635 | case 'total': 636 | return round($cartItem->subtotal + $cartItem->taxTotal, $decimals); 637 | default: 638 | return; 639 | } 640 | } 641 | } 642 | 643 | ``` 644 | 645 | ## Exceptions 646 | 647 | The Cart package will throw exceptions if something goes wrong. This way it's easier to debug your code using the Cart package or to handle the error based on the type of exceptions. The Cart packages can throw the following exceptions: 648 | 649 | | Exception | Reason | 650 | | ---------------------------- | ---------------------------------------------------------------------------------- | 651 | | *CartAlreadyStoredException* | When trying to store a cart that was already stored using the specified identifier | 652 | | *InvalidRowIDException* | When the rowId that got passed doesn't exists in the current cart instance | 653 | | *UnknownModelException* | When you try to associate an none existing model to a CartItem. | 654 | 655 | ## Events 656 | 657 | The cart also has events build in. There are five events available for you to listen for. 658 | 659 | | Event | Fired | Parameter | 660 | | ------------- | ---------------------------------------- | ------------------------------------- | 661 | | cart.adding | When adding an item to the cart. | The `CartItem` that is being added. | 662 | | cart.updating | When updating an item to the cart. | The `CartItem` that is being updated. | 663 | | cart.removing | When removing an item to the cart. | The `CartItem` that is being removed. | 664 | | cart.added | When an item was added to the cart. | The `CartItem` that was added. | 665 | | cart.updated | When an item was updated to the cart. | The `CartItem` that was updated. | 666 | | cart.removed | When an item was removed from the cart. | The `CartItem` that was removed. | 667 | | cart.merged | When the content of a cart is merged | - | 668 | | cart.stored | When the content of a cart was stored. | - | 669 | | cart.restored | When the content of a cart was restored. | - | 670 | | cart.erased | When the content of a cart was erased. | - | 671 | 672 | ## Example 673 | 674 | Below is a little example of how to list the cart content in a table: 675 | 676 | ```php 677 | 678 | // Add some items in your Controller. 679 | Cart::add('192ao12', 'Product 1', 1, 9.99); 680 | Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); 681 | 682 | // Display the content in a View. 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 |
ProductQtyPriceSubtotal
699 |

name; ?>

700 |

options->has('size') ? $row->options->size : ''); ?>

701 |
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
729 | ``` 730 | 731 | ## Contributors 732 | 733 | 734 | 735 | 742 | 749 | 756 | 763 | 770 | 777 | 778 | 785 | 792 | 799 | 806 | 813 | 820 | 821 | 828 | 835 | 842 | 849 | 856 | 863 | 864 | 871 | 878 | 885 | 892 | 899 | 906 | 907 | 914 | 921 | 928 | 935 | 942 | 949 | 950 | 957 | 964 | 971 | 978 | 985 | 992 | 993 | 1000 | 1007 | 1014 | 1021 | 1028 | 1035 | 1036 | 1043 |
736 | 737 | bumbummen99 738 |
739 | Patrick 740 |
741 |
743 | 744 | Crinsane 745 |
746 | Rob Gloudemans 747 |
748 |
750 | 751 | Norris1z 752 |
753 | Norris Oduro 754 |
755 |
757 | 758 | anayarojo 759 |
760 | Raul Anaya Rojo 761 |
762 |
764 | 765 | olegbespalov 766 |
767 | Oleg Bespalov 768 |
769 |
771 | 772 | cwprogger 773 |
774 | Andrew Savchenko 775 |
776 |
779 | 780 | ChrisThompsonTLDR 781 |
782 | Chris Thompson 783 |
784 |
786 | 787 | Jam-Iko 788 |
789 | Jam-Iko 790 |
791 |
793 | 794 | mattusik 795 |
796 | Matus Rohal 797 |
798 |
800 | 801 | rakibabu 802 |
803 | Rakhal Imming 804 |
805 |
807 | 808 | tiotobing 809 |
810 | Tiotobing 811 |
812 |
814 | 815 | Sartoric 816 |
817 | Sartoric 818 |
819 |
822 | 823 | trippo 824 |
825 | Alberto Peripolli 826 |
827 |
829 | 830 | macbookandrew 831 |
832 | Andrew Minion 833 |
834 |
836 | 837 | dtwebuk 838 |
839 | Daniel Tomlinson 840 |
841 |
843 | 844 | tkaw220 845 |
846 | Edwin Aw 847 |
848 |
850 | 851 | manojo123 852 |
853 | Jorge Moura 854 |
855 |
857 | 858 | jorgejavierleon 859 |
860 | Jorge Javier León 861 |
862 |
865 | 866 | geisi 867 |
868 | Tim Geisendörfer 869 |
870 |
872 | 873 | adamgoose 874 |
875 | Adam Engebretson 876 |
877 |
879 | 880 | andcl 881 |
882 | Andrés 883 |
884 |
886 | 887 | ganyicz 888 |
889 | Filip Ganyicz 890 |
891 |
893 | 894 | guysolamour 895 |
896 | Guy-roland ASSALE 897 |
898 |
900 | 901 | jackmcdade 902 |
903 | Jack McDade 904 |
905 |
908 | 909 | jeremyvaught 910 |
911 | Jeremy Vaught 912 |
913 |
915 | 916 | jmarkese 917 |
918 | John Markese 919 |
920 |
922 | 923 | nexxai 924 |
925 | JT Smith 926 |
927 |
929 | 930 | mrabbani 931 |
932 | Mahbub Rabbani 933 |
934 |
936 | 937 | mauriciv 938 |
939 | Mauricio Vera 940 |
941 |
943 | 944 | xpundel 945 |
946 | Mikhail Lisnyak 947 |
948 |
951 | 952 | absemetov 953 |
954 | Nadir Absemetov 955 |
956 |
958 | 959 | nielsiano 960 |
961 | Niels Stampe 962 |
963 |
965 | 966 | 4ilo 967 |
968 | Olivier 969 |
970 |
972 | 973 | PazkaL 974 |
975 | Pascal Kousbroek 976 |
977 |
979 | 980 | quintenbuis 981 |
982 | Quinten Buis 983 |
984 |
986 | 987 | publiux 988 |
989 | Raul Ruiz 990 |
991 |
994 | 995 | royduin 996 |
997 | Roy Duineveld 998 |
999 |
1001 | 1002 | CaddyDz 1003 |
1004 | Salim Djerbouh 1005 |
1006 |
1008 | 1009 | pendalff 1010 |
1011 | Fukalov Sem 1012 |
1013 |
1015 | 1016 | sobhanatar 1017 |
1018 | Sobhan Atar 1019 |
1020 |
1022 | 1023 | mightyteja 1024 |
1025 | Teja Babu S 1026 |
1027 |
1029 | 1030 | kekenec 1031 |
1032 | Kekenec 1033 |
1034 |
1037 | 1038 | sasin91 1039 |
1040 | Sasin91 1041 |
1042 |
1044 | 1045 | -------------------------------------------------------------------------------- /README_es.md: -------------------------------------------------------------------------------- 1 | 2 | ## LaravelShoppingcart 3 | [![codecov](https://codecov.io/gh/anayarojo/laravel-shopping-cart/branch/master/graph/badge.svg)](https://codecov.io/gh/anayarojo/laravel-shopping-cart) 4 | [![Latest Stable Version](https://poser.pugx.org/anayarojo/shoppingcart/v/stable)](https://packagist.org/packages/anayarojo/shoppingcart) 5 | [![Latest Unstable Version](https://poser.pugx.org/anayarojo/shoppingcart/v/unstable)](https://packagist.org/packages/anayarojo/shoppingcart) 6 | [![License](https://poser.pugx.org/anayarojo/shoppingcart/license)](https://packagist.org/packages/anayarojo/shoppingcart) 7 | 8 | Esta es una copia de [LaravelShoppingcart de bumbummen99](https://github.com/bumbummen99/LaravelShoppingcart), extendida con características menores compatibles con Laravel 11+. Un ejemplo de integración se puede [encontrar aquí](https://github.com/bumbummen99/LaravelShoppingcartDemo). 9 | 10 | ## Instalación 11 | 12 | Instala el [paquete](https://packagist.org/packages/anayarojo/shoppingcart) a través de [Composer](http://getcomposer.org/). 13 | 14 | Ejecuta el comando Composer `require` desde la Terminal:: 15 | 16 | composer require anayarojo/shoppingcart 17 | 18 | Ahora estás listo para comenzar a usar el Laravel Shopping Cart en tu aplicación. 19 | 20 | **A partir de la versión 2 de este paquete, es posible utilizar la inyección de dependencias para inyectar una instancia de la clase Cart en tu controlador u otra clase.** 21 | 22 | Definitivamente deberías publicar el archivo de `config` y echarle un vistazo.. 23 | 24 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" 25 | 26 | Esto te proporcionará un archivo de configuración `cart.php` en el cual puedes realizar cambios en el comportamiento del paquete." 27 | 28 | ## Actualizaciones 29 | 30 | As of version **4.2.0** this package does, when being used with PostgreSQL, encode the cart content to base64 before storing into database due to an [issue with saving values including zero bytes](https://github.com/bumbummen99/LaravelShoppingcart/pull/167). Please consider clearing your cart table in case you are upgrading using PostgreSQL from a version **<4.2.0**. 31 | 32 | A partir de la versión **4.2.0**, este paquete, al ser utilizado con PostgreSQL, codifica el contenido del carrito a base64 antes de almacenarlo en la base de datos debido a un [problema al guardar valores que incluyen bytes nulos](https://github.com/bumbummen99/LaravelShoppingcart/pull/167). Por favor, considera limpiar la tabla de tu carrito en caso de que estés realizando una actualización utilizando PostgreSQL desde una versión **<4.2.0**. 33 | 34 | ## Tabla de Contenidos 35 | Consulta uno de los siguientes temas para obtener más información sobre LaravelShoppingcart 36 | 37 | * [Nota importante](#nota-importante) 38 | * [Uso](#uso) 39 | * [Colecciones](#colecciones) 40 | * [Instancias](#instancias) 41 | * [Modelos](#modelos) 42 | * [Base de Datos](#base-de-datos) 43 | * [Calculadoras](#calculadoras) 44 | * [Excepciones](#excepciones) 45 | * [Eventos](#eventos) 46 | * [Ejemplo](#ejemplo) 47 | * [Colaboradores](#colaboradores) 48 | 49 | ## Nota importante 50 | 51 | Como todos los carritos de compras que calculan precios incluyendo impuestos y descuentos, este módulo también podría verse afectado por el "problema de redondeo de totales" ([*](https://stackoverflow.com/questions/13529580/magento-tax-rounding-issue)) debido a la precisión decimal utilizada para los precios y para los resultados. Con el fin de evitar (o al menos minimizar) este problema, en el paquete Laravel shoppingcart los totales se calculan utilizando el método **"por fila"** y se devuelven ya redondeados en función del formato numérico establecido por defecto en el archivo de configuración (cart.php). Debido a esto, **DESACONSEJAMOS ESTABLECER ALTA PRECISIÓN COMO VALOR POR DEFECTO Y FORMATEAR EL RESULTADO DE SALIDA UTILIZANDO MENOS DECIMALES**. Hacer esto puede llevar al problema de redondeo. 52 | 53 | El precio base (precio del producto) se deja sin redondear. 54 | 55 | ## Uso 56 | 57 | El carrito de compras te proporciona los siguientes métodos para usar: 58 | 59 | ### Cart::add() 60 | 61 | Agregar un artículo al carrito es realmente simple, solo usas el método `add()`, el cual acepta una variedad de parámetros. 62 | 63 | En su forma más básica, puedes especificar el id, nombre, cantidad, precio y peso del producto que deseas agregar al carrito. 64 | 65 | ```php 66 | Cart::add('293ad', 'Product 1', 1, 9.99, 550); 67 | ``` 68 | 69 | Como un quinto parámetro opcional, puedes pasarle opciones, de modo que puedas agregar múltiples elementos con el mismo id, pero con (por ejemplo) un tamaño diferente. 70 | 71 | ```php 72 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 73 | ``` 74 | 75 | **El método `add()` devolverá una instancia de CartItem del elemento que acabas de agregar al carrito.** 76 | 77 | ¿Quizás prefieras agregar el artículo usando un array? Siempre y cuando el array contenga las claves requeridas, puedes pasarlo al método. La clave de opciones es opcional. 78 | 79 | ```php 80 | Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); 81 | ``` 82 | 83 | Novedad en la versión 2 del paquete es la posibilidad de trabajar con la interfaz [Buyable](#buyable). La forma en que esto funciona es que tienes un modelo que implementa la interfaz [Buyable](#buyable), lo que te obliga a implementar algunos métodos para que el paquete sepa cómo obtener el id, nombre y precio de tu modelo. De esta manera, simplemente puedes pasar al método `add()` un modelo y la cantidad, y automáticamente lo agregará al carrito. 84 | 85 | **Como bono adicional, asociará automáticamente el modelo con el CartItem.** 86 | 87 | ```php 88 | Cart::add($product, 1, ['size' => 'large']); 89 | ``` 90 | As an optional third parameter you can add options. 91 | ```php 92 | Cart::add($product, 1, ['size' => 'large']); 93 | ``` 94 | 95 | Finalmente, también puedes agregar varios elementos al carrito a la vez. 96 | Simplemente puedes pasar al método `add()` un array de arrays, o un array de Buyables y serán agregados al carrito. 97 | 98 | **Cuando agregas varios elementos al carrito, el método `add()` devolverá un array de CartItems.** 99 | 100 | ```php 101 | Cart::add([ 102 | ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], 103 | ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] 104 | ]); 105 | 106 | Cart::add([$product1, $product2]); 107 | 108 | ``` 109 | 110 | ### Cart::update() 111 | 112 | Para actualizar un elemento en el carrito, primero necesitarás el rowId del elemento. 113 | Luego puedes usar el método `update()` para actualizarlo. 114 | 115 | Si simplemente deseas actualizar la cantidad, pasarás al método de actualización el rowId y la nueva cantidad: 116 | 117 | ```php 118 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 119 | 120 | Cart::update($rowId, 2); // Se actualizará la cantidad 121 | ``` 122 | 123 | Si deseas actualizar las opciones de un artículo dentro del carrito, 124 | 125 | ```php 126 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 127 | 128 | Cart::update($rowId, ['options' => ['size' => 'small']]); // Se actualizará la opción de tamaño con un nuevo valor 129 | ``` 130 | 131 | Si deseas actualizar más atributos del artículo, puedes pasar al método de actualización un array o un `Buyable` como segundo parámetro. De esta manera, puedes actualizar toda la información del artículo con el rowId proporcionado. 132 | 133 | ```php 134 | Cart::update($rowId, ['name' => 'Product 1']); // Se actualizará el nombre 135 | 136 | Cart::update($rowId, $product); // Se actualizará el id, el nombre y el precio 137 | 138 | ``` 139 | 140 | ### Cart::remove() 141 | 142 | Para eliminar un elemento del carrito, nuevamente necesitarás el rowId. Este rowId lo pasas simplemente al método `remove()` y eliminará el elemento del carrito. 143 | 144 | ```php 145 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 146 | 147 | Cart::remove($rowId); 148 | ``` 149 | 150 | ### Cart::get() 151 | 152 | Si deseas obtener un artículo del carrito utilizando su rowId, simplemente llama al método `get()` en el carrito y pásale el rowId. 153 | 154 | ```php 155 | $rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 156 | 157 | Cart::get($rowId); 158 | ``` 159 | 160 | ### Cart::content() 161 | 162 | Por supuesto, también querrás obtener el contenido del carrito. Aquí es donde utilizarás el método `content`. Este método devolverá una Colección de CartItems que puedes iterar y mostrar el contenido a tus clientes. 163 | 164 | ```php 165 | Cart::content(); 166 | ``` 167 | 168 | Este método devolverá el contenido de la instancia actual del carrito. Si deseas el contenido de otra instancia, simplemente encadena las llamadas. 169 | 170 | ```php 171 | Cart::instance('wishlist')->content(); 172 | ``` 173 | 174 | ### Cart::destroy() 175 | 176 | Si deseas eliminar completamente el contenido de un carrito, puedes llamar al método `destroy` en el carrito. Esto eliminará todos los CartItems del carrito para la instancia actual del carrito. 177 | 178 | ```php 179 | Cart::destroy(); 180 | ``` 181 | 182 | ### Cart::weight() 183 | 184 | El método `weight()` se puede utilizar para obtener el peso total de todos los elementos en el carrito, dado su peso y cantidad. 185 | 186 | ```php 187 | Cart::weight(); 188 | ``` 189 | 190 | El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales. 191 | 192 | ```php 193 | Cart::weight($decimals, $decimalSeperator, $thousandSeperator); 194 | ``` 195 | 196 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 197 | 198 | **Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad total `$cart->weight`** 199 | 200 | ### Cart::total() 201 | 202 | El método `total()` se puede utilizar para obtener el total calculado de todos los elementos en el carrito, dado su precio y cantidad. 203 | 204 | ```php 205 | Cart::total(); 206 | ``` 207 | 208 | El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales. 209 | 210 | ```php 211 | Cart::total($decimals, $decimalSeparator, $thousandSeparator); 212 | ``` 213 | 214 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 215 | 216 | **Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad total `$cart->total`** 217 | 218 | ### Cart::tax() 219 | 220 | El método `tax()` se puede utilizar para obtener el monto calculado de impuestos para todos los elementos en el carrito, dado su precio y cantidad. 221 | 222 | ```php 223 | Cart::tax(); 224 | ``` 225 | 226 | El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales. 227 | 228 | ```php 229 | Cart::tax($decimals, $decimalSeparator, $thousandSeparator); 230 | ``` 231 | 232 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 233 | 234 | **Si no estás utilizando el Facade, sino que utilizas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad de impuestos `$cart->tax`** 235 | 236 | ### Cart::subtotal() 237 | 238 | El método `subtotal()` se puede utilizar para obtener el total de todos los elementos en el carrito, menos el monto total de impuestos. 239 | 240 | ```php 241 | Cart::subtotal(); 242 | ``` 243 | 244 | El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales. 245 | 246 | ```php 247 | Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); 248 | ``` 249 | 250 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 251 | 252 | **Si no estás utilizando el Facade, sino que usas inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad subtotal `$cart->subtotal`** 253 | 254 | ### Cart::discount() 255 | 256 | El método `discount()` se puede utilizar para obtener el descuento total de todos los elementos en el carrito. 257 | 258 | ```php 259 | Cart::discount(); 260 | ``` 261 | 262 | El método formateará automáticamente el resultado, el cual puedes ajustar usando los tres parámetros opcionales. 263 | 264 | ```php 265 | Cart::discount($decimals, $decimalSeparator, $thousandSeparator); 266 | ``` 267 | 268 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 269 | 270 | **Si no estás utilizando el Facade, sino que usas la inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad de descuento `$cart->discount`** 271 | 272 | ### Cart::initial() 273 | 274 | El método `initial()` se puede utilizar para obtener el precio total de todos los elementos en el carrito antes de aplicar descuentos e impuestos. 275 | 276 | Podría ser obsoleto en el futuro. **Cuando se redondee, podría verse afectado por el problema de redondeo**, úsalo con cuidado o utiliza [Cart::priceTotal()](#Cart::priceTotal()). 277 | 278 | ```php 279 | Cart::initial(); 280 | ``` 281 | 282 | El método formateará automáticamente el resultado, el cual puedes ajustar utilizando los tres parámetros opcionales. 283 | 284 | ```php 285 | Cart::initial($decimals, $decimalSeparator, $thousandSeparator); 286 | ``` 287 | 288 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 289 | 290 | ### Cart::priceTotal() 291 | 292 | El método `priceTotal()` se puede utilizar para obtener el precio total de todos los elementos en el carrito antes de aplicar descuentos e impuestos. 293 | 294 | ```php 295 | Cart::priceTotal(); 296 | ``` 297 | 298 | El método devuelve el resultado redondeado basado en el formato numérico predeterminado, pero puedes ajustarlo utilizando los tres parámetros opcionales. 299 | 300 | ```php 301 | Cart::priceTotal($decimals, $decimalSeparator, $thousandSeparator); 302 | ``` 303 | 304 | Puedes establecer el formato numérico predeterminado en el archivo de configuración. 305 | 306 | **Si no estás utilizando el Facade, sino que usas inyección de dependencias en tu (por ejemplo) Controlador, también puedes simplemente obtener la propiedad subtotal `$cart->initial`** 307 | 308 | ### Cart::count() 309 | 310 | Si deseas saber cuántos artículos hay en tu carrito, puedes usar el método `count()`. Este método devolverá el número total de artículos en el carrito. Entonces, si has agregado 2 libros y 1 camisa, devolverá 3 artículos. 311 | 312 | ```php 313 | Cart::count(); 314 | $cart->count(); 315 | ``` 316 | 317 | ### Cart::search() 318 | 319 | Para encontrar un elemento en el carrito, puedes usar el método `search()`. 320 | 321 | **Este método fue cambiado en la versión 2** 322 | 323 | Detrás de escena, el método simplemente utiliza el método de filtro de la clase Laravel Collection. Esto significa que debes pasarle un Closure en el cual especificarás tus términos de búsqueda. 324 | 325 | Si, por ejemplo, deseas encontrar todos los elementos con un id de 1: 326 | 327 | ```php 328 | $cart->search(function ($cartItem, $rowId) { 329 | return $cartItem->id === 1; 330 | }); 331 | ``` 332 | Como puedes ver, el Closure recibirá dos parámetros. El primero es el CartItem para realizar la verificación. El segundo parámetro es el rowId de este CartItem. 333 | 334 | **El método devolverá una Colección que contiene todos los CartItems que se encontraron** 335 | 336 | Esta forma de búsqueda te brinda un control total sobre el proceso de búsqueda y te da la capacidad de crear búsquedas muy precisas y específicas. 337 | 338 | ### Cart::setTax($rowId, $taxRate) 339 | 340 | Puedes usar el método `setTax()` para cambiar la tasa impositiva que se aplica al CartItem. Esto sobrescribirá el valor establecido en el archivo de configuración. 341 | 342 | ```php 343 | Cart::setTax($rowId, 21); 344 | $cart->setTax($rowId, 21); 345 | ``` 346 | 347 | ### Cart::setGlobalTax($taxRate) 348 | 349 | Puedes usar el método `setGlobalTax()` para cambiar la tasa impositiva para todos los elementos en el carrito. Los nuevos elementos también recibirán el impuesto global establecido. 350 | 351 | ```php 352 | Cart::setGlobalTax(21); 353 | $cart->setGlobalTax(21); 354 | ``` 355 | 356 | ### Cart::setGlobalDiscount($discountRate) 357 | 358 | Puedes usar el método `setGlobalDiscount()` para cambiar la tasa de descuento para todos los elementos en el carrito. Los nuevos elementos también recibirán el descuento establecido. 359 | 360 | ```php 361 | Cart::setGlobalDiscount(50); 362 | $cart->setGlobalDiscount(50); 363 | ``` 364 | 365 | ### Cart::setDiscount($rowId, $taxRate) 366 | 367 | Puedes usar el método `setDiscount()` para cambiar la tasa de descuento que se aplica a un CartItem. Ten en cuenta que este valor se cambiará si estableces el descuento global para el Carrito posteriormente. 368 | 369 | ```php 370 | Cart::setDiscount($rowId, 21); 371 | $cart->setDiscount($rowId, 21); 372 | ``` 373 | 374 | ### Buyable 375 | 376 | Para la conveniencia de agregar rápidamente elementos al carrito y su asociación automática, tu modelo debe implementar la interfaz `Buyable`. Puedes usar el trait `CanBeBought` para implementar los métodos requeridos, pero ten en cuenta que estos utilizarán campos predefinidos en tu modelo para los valores requeridos. 377 | 378 | ```php 379 | id; 395 | } 396 | public function getBuyableDescription(){ 397 | return $this->name; 398 | } 399 | public function getBuyablePrice(){ 400 | return $this->price; 401 | } 402 | public function getBuyableWeight(){ 403 | return $this->weight; 404 | } 405 | ``` 406 | 407 | Ejemplo: 408 | 409 | ```php 410 | id; 419 | } 420 | public function getBuyableDescription($options = null) { 421 | return $this->name; 422 | } 423 | public function getBuyablePrice($options = null) { 424 | return $this->price; 425 | } 426 | public function getBuyableWeight($options = null) { 427 | return $this->weight; 428 | } 429 | } 430 | ``` 431 | 432 | ## Collections 433 | 434 | En múltiples instancias, el Carrito te devolverá una Colección. Esta es simplemente una Colección de Laravel, por lo que todos los métodos que puedes llamar en una Colección de Laravel también están disponibles en el resultado. 435 | 436 | Como ejemplo, puedes obtener rápidamente el número de productos únicos en un carrito: 437 | 438 | ```php 439 | Cart::content()->count(); 440 | ``` 441 | 442 | O puedes agrupar el contenido por el id de los productos: 443 | 444 | ```php 445 | Cart::content()->groupBy('id'); 446 | ``` 447 | 448 | ## Instancias 449 | 450 | El paquete soporta múltiples instancias del carrito. La forma en que funciona es la siguiente: 451 | 452 | Puedes establecer la instancia actual del carrito llamando a `Cart::instance('nuevaInstancia')`. A partir de este momento, la instancia activa del carrito será nuevaInstancia, por lo que cuando agregues, elimines o obtengas el contenido del carrito, estarás trabajando con la instancia `nuevaInstancia` del carrito. 453 | Si deseas cambiar de instancia, simplemente llama nuevamente a `Cart::instance('otraInstancia')`, y estarás trabajando con la `otraInstancia` nuevamente. 454 | 455 | Así que un pequeño ejemplo: 456 | 457 | ```php 458 | Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); 459 | 460 | // Get the content of the 'shopping' cart 461 | Cart::content(); 462 | 463 | Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); 464 | 465 | // Get the content of the 'wishlist' cart 466 | Cart::content(); 467 | 468 | // If you want to get the content of the 'shopping' cart again 469 | Cart::instance('shopping')->content(); 470 | 471 | // And the count of the 'wishlist' cart again 472 | Cart::instance('wishlist')->count(); 473 | ``` 474 | 475 | También puedes usar el contrato `InstanceIdentifier` para extender un Modelo deseado y asignar/crear una instancia de Carrito para él. Esto también permite establecer directamente el descuento global. 476 | ``` 477 | email; 496 | } 497 | 498 | /** 499 | * Get the unique identifier to load the Cart from 500 | * 501 | * @return int|string 502 | */ 503 | public function getInstanceGlobalDiscount($options = null) 504 | { 505 | return $this->discountRate ?: 0; 506 | } 507 | } 508 | 509 | // Inside Controller 510 | $user = \Auth::user(); 511 | $cart = Cart::instance($user); 512 | 513 | ``` 514 | 515 | **N.B. Ten en cuenta que el carrito permanece en la última instancia establecida durante todo el tiempo que no establezcas una diferente durante la ejecución del script.** 516 | 517 | **N.B.2 La instancia de carrito predeterminada se llama `default`, por lo que cuando no estás usando instancias, `Cart::content();` es lo mismo que `Cart::instance('default')->content()`.** 518 | 519 | ## Modelos 520 | 521 | Porque puede ser muy conveniente poder acceder directamente a un modelo desde un CartItem, es posible asociar un modelo con los elementos en el carrito. Digamos que tienes un modelo `Product` en tu aplicación. Con el método `associate()`, puedes decirle al carrito que un elemento en el carrito está asociado al modelo `Product`. 522 | 523 | De esta manera, ¡puedes acceder a tu modelo directamente desde el `CartItem`! 524 | 525 | El modelo se puede acceder a través de la propiedad `model` en el CartItem. 526 | 527 | **Si tu modelo implementa la interfaz `Buyable` y utilizaste tu modelo para agregar el elemento al carrito, se asociará automáticamente.** 528 | 529 | Aquí tienes un ejemplo: 530 | 531 | ```php 532 | 533 | // Primero agregaremos el elemento al carrito. 534 | $cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); 535 | 536 | // A continuación, asociamos un modelo con el elemento. 537 | Cart::associate($cartItem->rowId, 'Product'); 538 | 539 | // ¡O aún más fácil, llama al método associate en el CartItem! 540 | $cartItem->associate('Product'); 541 | 542 | // Incluso puedes hacerlo en una sola línea 543 | Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); 544 | 545 | // Ahora, al iterar sobre el contenido del carrito, puedes acceder al modelo. 546 | foreach(Cart::content() as $row) { 547 | echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; 548 | } 549 | ``` 550 | ## Base de Datos 551 | 552 | - [Configuración](#configuración) 553 | - [Almacenamiento del carrito](#almacenamiento-del-carrito) 554 | - [Restauración del carrito](#restauración-del-carrito) 555 | 556 | ### Configuración 557 | Para guardar el carrito en la base de datos para poder recuperarlo más tarde, el paquete necesita saber qué conexión de base de datos usar y cuál es el nombre de la tabla. 558 | Por defecto, el paquete utilizará la conexión de base de datos predeterminada y usará una tabla llamada `shoppingcart`. Puedes cambiar eso en la configuración. 559 | 560 | php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" 561 | 562 | Esto colocará un archivo de migración de la tabla `shoppingcart` en el directorio `database/migrations`. Ahora todo lo que tienes que hacer es ejecutar `php artisan migrate` para migrar tu base de datos. 563 | 564 | ### Almacenamiento del carrito 565 | Para almacenar tu instancia de carrito en la base de datos, debes llamar al método `store($identifier)`. Donde `$identifier` es una clave aleatoria, por ejemplo, el id o nombre de usuario del usuario. 566 | 567 | Cart::store('username'); 568 | 569 | // Para almacenar una instancia de carrito llamada 'wishlist' 570 | Cart::instance('wishlist')->store('username'); 571 | 572 | ### Restauración del carrito 573 | Si deseas recuperar el carrito de la base de datos y restaurarlo, todo lo que tienes que hacer es llamar al método `restore($identifier)` donde `$identifier` es la clave que especificaste para el método store. 574 | 575 | Cart::restore('username'); 576 | 577 | // Para restaurar una instancia de carrito llamada 'wishlist' 578 | Cart::instance('wishlist')->restore('username'); 579 | 580 | ### Fusión del carrito 581 | Si deseas fusionar el carrito con otro de la base de datos, todo lo que tienes que hacer es llamar al método `merge($identifier)` donde `$identifier` es la clave que especificaste para el método `store`. También puedes definir si deseas mantener los descuentos y las tasas de impuestos de los artículos y si deseas enviar eventos "cart.added". 582 | 583 | // Fusionar el contenido de 'savedcart' en 'username'. 584 | Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, $dispatchAdd, 'savedcartinstance'); 585 | 586 | ### Borrado del carrito 587 | Si deseas borrar el carrito de la base de datos, todo lo que tienes que hacer es llamar al método `erase($identifier)` donde `$identifier` es la clave que especificaste para el método `store`. 588 | 589 | Cart::erase('username'); 590 | 591 | // Para borrar un carrito cambiando a una instancia llamada 'wishlist' 592 | Cart::instance('wishlist')->erase('username'); 593 | 594 | ## Calculadoras 595 | La lógica de cálculo para el paquete está implementada y definida en clases `Calculator`. Estas implementan el contrato `Gloudemans\Shoppingcart\Contracts\Calculator` y determinan cómo se calculan y redondean los precios. Las calculadoras se pueden configurar en el archivo de configuración. Esta es la calculadora por defecto: 596 | 597 | ```php 598 | price * ($cartItem->getDiscountRate() / 100); 614 | case 'tax': 615 | return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals); 616 | case 'priceTax': 617 | return round($cartItem->priceTarget + $cartItem->tax, $decimals); 618 | case 'discountTotal': 619 | return round($cartItem->discount * $cartItem->qty, $decimals); 620 | case 'priceTotal': 621 | return round($cartItem->price * $cartItem->qty, $decimals); 622 | case 'subtotal': 623 | return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0); 624 | case 'priceTarget': 625 | return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals); 626 | case 'taxTotal': 627 | return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals); 628 | case 'total': 629 | return round($cartItem->subtotal + $cartItem->taxTotal, $decimals); 630 | default: 631 | return; 632 | } 633 | } 634 | } 635 | 636 | ``` 637 | 638 | ## Excepciones 639 | El paquete Cart lanzará excepciones si algo sale mal. De esta manera, es más fácil depurar tu código utilizando el paquete Cart o manejar el error según el tipo de excepciones. El paquete Cart puede lanzar las siguientes excepciones: 640 | 641 | | Excepción | Razón | 642 | | ---------------------------- | ------------------------------------------------------------------------------------------------- | 643 | | *CartAlreadyStoredException* | Cuando se intenta almacenar un carrito que ya fue almacenado usando el identificador especificado | 644 | | *InvalidRowIDException* | Cuando el rowId que se pasó no existe en la instancia actual del carrito | 645 | | *UnknownModelException* | Cuando intentas asociar un modelo que no existe a un CartItem. | 646 | 647 | ## Eventos 648 | 649 | The cart also has events build in. There are five events available for you to listen for. 650 | 651 | | Evento | Se activa | Parámetro | 652 | | -------------- | ------------------------------------------------------ | --------------------------------------- | 653 | | cart.adding | Cuando se agrega un elemento al carrito. | El `CartItem` que se está agregando. | 654 | | cart.updating | Cuando se actualiza un elemento en el carrito. | El `CartItem` que se está actualizando. | 655 | | cart.removing | Cuando se elimina un elemento del carrito. | El `CartItem` que se está eliminando. | 656 | | cart.added | Cuando se ha agregado un elemento al carrito. | El `CartItem` que se agregó. | 657 | | cart.updated | Cuando se ha actualizado un elemento en el carrito. | El `CartItem` que se actualizó. | 658 | | cart.removed | Cuando se ha eliminado un elemento del carrito. | El `CartItem` que se eliminó. | 659 | | cart.merged | Cuando se fusiona el contenido de un carrito. | - | 660 | | cart.stored | Cuando se ha almacenado el contenido de un carrito. | - | 661 | | cart.restored | Cuando se ha restaurado el contenido de un carrito. | - | 662 | | cart.erased | Cuando se ha borrado el contenido de un carrito. | - | 663 | 664 | ## Ejemplo 665 | 666 | A continuación, te muestro un pequeño ejemplo de cómo listar el contenido del carrito en una tabla: 667 | 668 | ```php 669 | 670 | // Agrega algunos elementos en tu controlador. 671 | Cart::add('192ao12', 'Product 1', 1, 9.99); 672 | Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); 673 | 674 | // Muestra el contenido en una vista. 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 |
ProductQtyPriceSubtotal
691 |

name; ?>

692 |

options->has('size') ? $row->options->size : ''); ?>

693 |
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
721 | ``` 722 | 723 | ## Contribuidores 724 | 725 | 726 | 727 | 734 | 741 | 748 | 755 | 762 | 769 | 770 | 777 | 784 | 791 | 798 | 805 | 812 | 813 | 820 | 827 | 834 | 841 | 848 | 855 | 856 | 863 | 870 | 877 | 884 | 891 | 898 | 899 | 906 | 913 | 920 | 927 | 934 | 941 | 942 | 949 | 956 | 963 | 970 | 977 | 984 | 985 | 992 | 999 | 1006 | 1013 | 1020 | 1027 | 1028 | 1035 |
728 | 729 | bumbummen99 730 |
731 | Patrick 732 |
733 |
735 | 736 | Crinsane 737 |
738 | Rob Gloudemans 739 |
740 |
742 | 743 | Norris1z 744 |
745 | Norris Oduro 746 |
747 |
749 | 750 | anayarojo 751 |
752 | Raul Anaya Rojo 753 |
754 |
756 | 757 | olegbespalov 758 |
759 | Oleg Bespalov 760 |
761 |
763 | 764 | cwprogger 765 |
766 | Andrew Savchenko 767 |
768 |
771 | 772 | ChrisThompsonTLDR 773 |
774 | Chris Thompson 775 |
776 |
778 | 779 | Jam-Iko 780 |
781 | Jam-Iko 782 |
783 |
785 | 786 | mattusik 787 |
788 | Matus Rohal 789 |
790 |
792 | 793 | rakibabu 794 |
795 | Rakhal Imming 796 |
797 |
799 | 800 | tiotobing 801 |
802 | Tiotobing 803 |
804 |
806 | 807 | Sartoric 808 |
809 | Sartoric 810 |
811 |
814 | 815 | trippo 816 |
817 | Alberto Peripolli 818 |
819 |
821 | 822 | macbookandrew 823 |
824 | Andrew Minion 825 |
826 |
828 | 829 | dtwebuk 830 |
831 | Daniel Tomlinson 832 |
833 |
835 | 836 | tkaw220 837 |
838 | Edwin Aw 839 |
840 |
842 | 843 | manojo123 844 |
845 | Jorge Moura 846 |
847 |
849 | 850 | jorgejavierleon 851 |
852 | Jorge Javier León 853 |
854 |
857 | 858 | geisi 859 |
860 | Tim Geisendörfer 861 |
862 |
864 | 865 | adamgoose 866 |
867 | Adam Engebretson 868 |
869 |
871 | 872 | andcl 873 |
874 | Andrés 875 |
876 |
878 | 879 | ganyicz 880 |
881 | Filip Ganyicz 882 |
883 |
885 | 886 | guysolamour 887 |
888 | Guy-roland ASSALE 889 |
890 |
892 | 893 | jackmcdade 894 |
895 | Jack McDade 896 |
897 |
900 | 901 | jeremyvaught 902 |
903 | Jeremy Vaught 904 |
905 |
907 | 908 | jmarkese 909 |
910 | John Markese 911 |
912 |
914 | 915 | nexxai 916 |
917 | JT Smith 918 |
919 |
921 | 922 | mrabbani 923 |
924 | Mahbub Rabbani 925 |
926 |
928 | 929 | mauriciv 930 |
931 | Mauricio Vera 932 |
933 |
935 | 936 | xpundel 937 |
938 | Mikhail Lisnyak 939 |
940 |
943 | 944 | absemetov 945 |
946 | Nadir Absemetov 947 |
948 |
950 | 951 | nielsiano 952 |
953 | Niels Stampe 954 |
955 |
957 | 958 | 4ilo 959 |
960 | Olivier 961 |
962 |
964 | 965 | PazkaL 966 |
967 | Pascal Kousbroek 968 |
969 |
971 | 972 | quintenbuis 973 |
974 | Quinten Buis 975 |
976 |
978 | 979 | publiux 980 |
981 | Raul Ruiz 982 |
983 |
986 | 987 | royduin 988 |
989 | Roy Duineveld 990 |
991 |
993 | 994 | CaddyDz 995 |
996 | Salim Djerbouh 997 |
998 |
1000 | 1001 | pendalff 1002 |
1003 | Fukalov Sem 1004 |
1005 |
1007 | 1008 | sobhanatar 1009 |
1010 | Sobhan Atar 1011 |
1012 |
1014 | 1015 | mightyteja 1016 |
1017 | Teja Babu S 1018 |
1019 |
1021 | 1022 | kekenec 1023 |
1024 | Kekenec 1025 |
1026 |
1029 | 1030 | sasin91 1031 |
1032 | Sasin91 1033 |
1034 |
1036 | 1037 | --------------------------------------------------------------------------------