diff --git a/README.md b/README.md index 8152023..baae813 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](https://packagist.org/packages/bumbummen99/shoppingcart) [![License](https://poser.pugx.org/bumbummen99/shoppingcart/license)](https://packagist.org/packages/bumbummen99/shoppingcart) -This is a fork of [Crisane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) extended with minor features compatible with Laravel 5.8. +This is a fork of [Crinsane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) extended with minor features compatible with Laravel 7. An example integration can be [found here](https://github.com/bumbummen99/LaravelShoppingcartDemo). ## Installation @@ -24,6 +24,7 @@ Now you're ready to start using the shoppingcart in your application. ## Table of Contents Look at one of the following topics to learn more about LaravelShoppingcart +* [Important note](#important-note) * [Usage](#usage) * [Collections](#collections) * [Instances](#instances) @@ -33,6 +34,14 @@ Look at one of the following topics to learn more about LaravelShoppingcart * [Events](#events) * [Example](#example) +## Important note + +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. +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). +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. + +The base price (product price) is left not rounded. + ## Usage The shoppingcart gives you the following methods to use: @@ -41,16 +50,16 @@ The shoppingcart gives you the following methods to use: Adding an item to the cart is really simple, you just use the `add()` method, which accepts a variety of parameters. -In its most basic form you can specify the id, name, quantity, price of the product you'd like to add to the cart. +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. ```php -Cart::add('293ad', 'Product 1', 1, 9.99); +Cart::add('293ad', 'Product 1', 1, 9.99, 550); ``` 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. ```php -Cart::add('293ad', 'Product 1', 1, 9.99, 'weight' => 550, ['size' => 'large']); +Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); ``` **The `add()` method will return an CartItem instance of the item you just added to the cart.** @@ -155,7 +164,7 @@ Cart::destroy(); ### Cart::weight() -The `weight()` method can be used to get the weight total of all items in the cart, given there weight and quantity. +The `weight()` method can be used to get the weight total of all items in the cart, given their weight and quantity. ```php Cart::weight(); @@ -245,13 +254,15 @@ You can set the default number format in the config file. ### Cart::initial() -The `initial()` method can be used to get the total price of all items in the cart before discount. +The `initial()` method can be used to get the total price of all items in the cart before applying discount and taxes. + +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()) ```php Cart::initial(); ``` -The method will automatically format the result, which you can tweak using the three optional parameters +The method will automatically format the result, which you can tweak using the three optional parameters. ```php Cart::initial($decimals, $decimalSeparator, $thousandSeparator); @@ -259,6 +270,22 @@ Cart::initial($decimals, $decimalSeparator, $thousandSeparator); You can set the default number format in the config file. +### Cart::priceTotal() + +The `priceTotal()` method can be used to get the total price of all items in the cart before applying discount and taxes. + +```php +Cart::priceTotal(); +``` + +The method return the result rounded based on the default number format, but you can tweak using the three optional parameters + +```php +Cart::priceTotal($decimals, $decimalSeparator, $thousandSeparator); +``` + +You can set the default number format in the config file. + **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`** ### Cart::count() @@ -306,8 +333,8 @@ $cart->setTax($rowId, 21); 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. ```php -Cart::setGlobalDiscount(21); -$cart->setGlobalDiscount(21); +Cart::setGlobalTax(21); +$cart->setGlobalTax(21); ``` ### Cart::setGlobalDiscount($discountRate) @@ -315,8 +342,8 @@ $cart->setGlobalDiscount(21); 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. ```php -Cart::setGlobalDiscount(21); -$cart->setGlobalDiscount(21); +Cart::setGlobalDiscount(50); +$cart->setGlobalDiscount(50); ``` ### Cart::setDiscount($rowId, $taxRate) @@ -339,7 +366,7 @@ use Gloudemans\Shoppingcart\Contracts\Buyable; use Illuminate\Database\Eloquent\Model; class Product extends Model implements Buyable { - use Gloudemans\Shoppingcart\CanBeNought; + use Gloudemans\Shoppingcart\CanBeBought; } ``` @@ -540,10 +567,18 @@ If you want to retrieve the cart from the database and restore it, all you have Cart::instance('wishlist')->restore('username'); ### Merge the cart -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. +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. // Merge the contents of 'savedcart' into 'username'. - Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate); + Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, $dispatchAdd); + +### Erasing the cart +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. + + Cart::erase('username'); + + // To erase a cart switching to an instance named 'wishlist' + Cart::instance('wishlist')->erase('username'); ## Exceptions @@ -564,8 +599,10 @@ The cart also has events build in. There are five events available for you to li | cart.added | When an item was added to the cart. | The `CartItem` that was added. | | cart.updated | When an item in the cart was updated. | The `CartItem` that was updated. | | cart.removed | When an item is removed from the cart. | The `CartItem` that was removed. | +| cart.merged | When the content of a cart is merged | - | | cart.stored | When the content of a cart was stored. | - | | cart.restored | When the content of a cart was restored. | - | +| cart.erased | When the content of a cart was erased. | - | ## Example diff --git a/README_Idn.md b/README_Idn.md new file mode 100644 index 0000000..3839008 --- /dev/null +++ b/README_Idn.md @@ -0,0 +1,628 @@ +## LaravelShoppingcart +[![Build Status](https://travis-ci.org/bumbummen99/LaravelShoppingcart.png?branch=master)](https://travis-ci.org/bumbummen99/LaravelShoppingcart) +[![codecov](https://codecov.io/gh/bumbummen99/LaravelShoppingcart/branch/master/graph/badge.svg)](https://codecov.io/gh/bumbummen99/LaravelShoppingcart) +[![StyleCI](https://styleci.io/repos/152610878/shield?branch=master)](https://styleci.io/repos/152610878) +[![Total Downloads](https://poser.pugx.org/bumbummen99/shoppingcart/downloads.png)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![Latest Stable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/stable)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![License](https://poser.pugx.org/bumbummen99/shoppingcart/license)](https://packagist.org/packages/bumbummen99/shoppingcart) + +Ini adalah percabangan dari [Crinsane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) dikembangkan dengan fitur-fitur minor yang kompatibel dengan Laravel 6 + +## Instalasi + +Install paket(https://packagist.org/packages/bumbummen99/shoppingcart) menggunakan [Composer](http://getcomposer.org/). + +Jalankan Composer dengan menggunakan perintah berikut: + + composer require bumbummen99/shoppingcart + +Sekarang Anda siap untuk mulai menggunakan shoppingcart di aplikasi Anda. + +**Pada versi 2 dari paket ini memungkinkan untuk menggunakan injeksi dependensi untuk memasukkan instance Class Cart ke controller Anda atau Class lain** + +## Gambaran +Lihat salah satu topik berikut untuk mempelajari lebih lanjut tentang LaravelShoppingcart + +* [Usage](#usage) +* [Collections](#collections) +* [Instances](#instances) +* [Models](#models) +* [Database](#database) +* [Exceptions](#exceptions) +* [Events](#events) +* [Example](#example) + +## Penggunaan + +Shoppingcart memberi Anda metode berikut untuk digunakan: + +### Cart::add() + +Menambahkan item ke troli sangat sederhana, Anda cukup menggunakan metode `add ()`, yang menerima berbagai parameter. + +Dalam bentuknya yang paling mendasar, Anda dapat menentukan id, nama, jumlah, harga, dan berat produk yang ingin Anda tambahkan ke troli. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 550); +``` + +Sebagai opsional parameter kelima, Anda dapat memberikan opsi, sehingga Anda dapat menambahkan beberapa item dengan id yang sama, tetapi dengan (instance) ukuran yang berbeda. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); +``` + +**Metode `add ()` akan mengembalikan instance CartItem dari item yang baru saja Anda tambahkan ke troli.** + +Mungkin Anda lebih suka menambahkan item menggunakan array? Selama array berisi kunci yang diperlukan, Anda bisa meneruskannya ke metode. Tombol opsi adalah opsional. + +```php +Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); +``` + +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. +Dengan cara ini Anda bisa meneruskan metode `add ()` model dan kuantitas dan secara otomatis akan menambahkannya ke troli. + +**Sebagai bonus tambahan, itu akan secara otomatis mengaitkan model dengan CartItem** + +```php +Cart::add($product, 1, ['size' => 'large']); +``` +Sebagai parameter ketiga opsional, Anda dapat menambahkan opsi. +```php +Cart::add($product, 1, ['size' => 'large']); +``` + +Terakhir, Anda juga dapat menambahkan banyak item ke troli sekaligus. +Anda bisa meneruskan metode `add ()` sebuah array array, atau array yang dapat dibeli dan mereka akan ditambahkan ke troli. + +**Saat menambahkan beberapa item ke troli, metode `add ()` akan mengembalikan array CartItems.** + +```php +Cart::add([ + ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], + ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] +]); + +Cart::add([$product1, $product2]); + +``` + +### Cart::update() + +Untuk memperbarui item di troli, Anda harus terlebih dahulu membutuhkan rowId item. +Selanjutnya Anda dapat menggunakan metode `update ()` untuk memperbaruinya. + +Jika Anda hanya ingin memperbarui kuantitas, Anda akan melewati metode pembaruan rowId dan kuantitas baru: + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::update($rowId, 2); // Will update the quantity +``` +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. + +```php +Cart::update($rowId, ['name' => 'Product 1']); // Will update the name + +Cart::update($rowId, $product); // Will update the id, name and price + +``` + +### Cart::remove() + +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. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::remove($rowId); +``` + +### Cart::get() + +Jika Anda ingin mendapatkan item dari troli menggunakan rowId-nya, Anda bisa memanggil metode `get ()` di troli dan meneruskannya dengan rowId. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::get($rowId); +``` + +### Cart::content() + +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. + +```php +Cart::content(); +``` +Metode ini akan mengembalikan konten instance keranjang saat ini, jika Anda ingin konten instance lain, cukup lakukan panggilan. + +```php +Cart::instance('wishlist')->content(); +``` + +### Cart::destroy() + +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. + +```php +Cart::destroy(); +``` + +### Cart::weight() + +Metode `weight ()` dapat digunakan untuk mendapatkan total berat semua item di troli, mengingat berat dan kuantitasnya. + + +```php +Cart::weight(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::weight($decimals, $decimalSeperator, $thousandSeperator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> weight`** + +### Cart::total() + +Maka `total ()` dapat digunakan untuk mendapatkan total yang dihitung dari semua item dalam troli, mengingat ada harga dan kuantitas. + +```php +Cart::total(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::total($decimals, $decimalSeparator, $thousandSeparator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> total`** + +### Cart::tax() + +Maka `tax ()` dapat digunakan untuk mendapatkan jumlah pajak yang dihitung untuk semua item di troli, mengingat ada harga dan kuantitas. + +```php +Cart::tax(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::tax($decimals, $decimalSeparator, $thousandSeparator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> tax`** + +### Cart::subtotal() + +Maka `subtotal ()` dapat digunakan untuk mendapatkan total semua item dalam troli, dikurangi jumlah total pajak. + +```php +Cart::subtotal(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> subtotal`** + +### Cart::discount() + +Maka `diskon ()` dapat digunakan untuk mendapatkan diskon total semua item di troli. + +```php +Cart::discount(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::discount($decimals, $decimalSeparator, $thousandSeparator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> discount`** + +### Cart::initial() + +maka `initial ()` dapat digunakan untuk mendapatkan harga total semua item di troli sebelum diskon. + +```php +Cart::initial(); +``` + +Metode ini akan memformat hasilnya secara otomatis, yang dapat Anda atur menggunakan tiga parameter opsional + +```php +Cart::initial($decimals, $decimalSeparator, $thousandSeparator); +``` + +Anda dapat mengatur format angka default dalam file konfigurasi. + +**Jika Anda tidak menggunakan Facade, tetapi menggunakan injeksi ketergantungan pada Pengontrol Anda (misalnya), Anda juga bisa mendapatkan total properti `$ cart-> initial`** + +### Cart::count() + +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. + +```php +Cart::count(); +$cart->count(); +``` + +### Cart::search() + +Untuk menemukan item di troli, Anda dapat menggunakan metode `search ()`. + +**Metode ini diubah pada versi 2** + +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. + +Jika Anda misalnya ingin menemukan semua item dengan id 1: + +```php +$cart->search(function ($cartItem, $rowId) { + return $cartItem->id === 1; +}); +``` + +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. + +** Metode ini akan mengembalikan Koleksi yang berisi semua CartItems yang ditemukan ** + +Cara pencarian ini memberi Anda kontrol total atas proses pencarian dan memberi Anda kemampuan untuk membuat pencarian yang sangat tepat dan spesifik. + +### Cart :: setTax ($ rowId, $ taxRate) + +Anda dapat menggunakan metode `setTax ()` untuk mengubah tarif pajak yang berlaku untuk CartItem. Ini akan menimpa nilai yang ditetapkan dalam file konfigurasi. + +```php +Cart::setTax($rowId, 21); +$cart->setTax($rowId, 21); +``` + +### Cart::setGlobalTax($taxRate) + +Anda dapat menggunakan metode `setGlobalTax ()` untuk mengubah tarif pajak untuk semua item di troli. Item baru juga akan menerima setGlobalTax. + +```php +Cart::setGlobalTax(21); +$cart->setGlobalTax(21); +``` + +### Cart::setGlobalDiscount($discountRate) + +Anda dapat menggunakan metode `setGlobalDiscount ()` untuk mengubah tingkat diskonto untuk semua item di troli. Barang baru akan menerima diskon juga. + +```php +Cart::setGlobalDiscount(50); +$cart->setGlobalDiscount(50); +``` + +### Cart::setDiscount($rowId, $taxRate) + +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. + +```php +Cart::setDiscount($rowId, 21); +$cart->setDiscount($rowId, 21); +``` + +### Buyable + +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. + +```php +id; + } + public function getBuyableDescription(){ + return $this->name; + } + public function getBuyablePrice(){ + return $this->price; + } + public function getBuyableWeight(){ + return $this->weight; + } +``` + +Contoh: + +```php +id; + } + public function getBuyableDescription($options = null) { + return $this->name; + } + public function getBuyablePrice($options = null) { + return $this->price; + } +} +``` + +## Collections + +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. + +Sebagai contoh, Anda dapat dengan cepat mendapatkan jumlah produk unik dalam keranjang: + +```php +Cart::content()->count(); +``` + +Atau Anda dapat mengelompokkan konten berdasarkan id produk: + +```php +Cart::content()->groupBy('id'); +``` + +## Instances + +Paket-paket mendukung beberapa instance dari kereta. Cara kerjanya seperti ini: + +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. +Jika Anda ingin mengganti instance, Anda cukup memanggil `Cart :: instance ('otherInstance')` lagi, dan Anda bekerja dengan `otherInstance` lagi. + +Contoh Kecil: + +```php +Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); + +// Get the content of the 'shopping' cart +Cart::content(); + +Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); + +// Get the content of the 'wishlist' cart +Cart::content(); + +// If you want to get the content of the 'shopping' cart again +Cart::instance('shopping')->content(); + +// And the count of the 'wishlist' cart again +Cart::instance('wishlist')->count(); +``` + +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. +``` +email; + } + + /** + * Get the unique identifier to load the Cart from + * + * @return int|string + */ + public function getInstanceGlobalDiscount($options = null) + { + return $this->discountRate ?: 0; + } +} + +// Inside Controller +$user = \Auth::user(); +$cart = Cart::instance($user); + + + +``` + +**N.B. Ingatlah bahwa troli tetap berada di set instance terakhir selama Anda tidak menyetel yang berbeda selama eksekusi skrip.** + +**N.B.2 Contoh cart default disebut `default`, jadi ketika Anda tidak menggunakan instance,` Cart :: konten (); `sama dengan` Cart :: instance ('default') -> konten () `.** + +## Models + +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`. + +Dengan begitu Anda dapat mengakses model Anda langsung dari `CartItem`! + +Model ini dapat diakses melalui properti `model` di CartItem. + +**Jika model Anda mengimplementasikan antarmuka `Buy Able` dan Anda menggunakan model Anda untuk menambahkan item ke troli, itu akan dikaitkan secara otomatis.** + +Berikut adalah contoh: + +```php + +// First we'll add the item to the cart. +$cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); + +// Next we associate a model with the item. +Cart::associate($cartItem->rowId, 'Product'); + +// Or even easier, call the associate method on the CartItem! +$cartItem->associate('Product'); + +// You can even make it a one-liner +Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); + +// Now, when iterating over the content of the cart, you can access the model. +foreach(Cart::content() as $row) { + echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; +} +``` +## Database + +- [Config](#configuration) +- [Storing the cart](#storing-the-cart) +- [Restoring the cart](#restoring-the-cart) + +### Konfigurasi +Untuk menyimpan keranjang ke dalam basis data sehingga Anda dapat mengambilnya nanti, paket perlu mengetahui koneksi basis data yang digunakan dan apa nama tabelnya. +Secara default paket akan menggunakan koneksi database default dan menggunakan tabel bernama `shoppingcart`. +Jika Anda ingin mengubah opsi ini, Anda harus menerbitkan file `config`. + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" + +Ini akan memberi Anda file konfigurasi `cart.php` di mana Anda dapat melakukan perubahan. + +Untuk memudahkan hidup Anda, paket ini juga menyertakan `migration` yang siap digunakan yang dapat Anda terbitkan dengan menjalankan: + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" + +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. + +### Menyimpan ke Troli +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. + + Cart::store('username'); + + // To store a cart instance named 'wishlist' + Cart::instance('wishlist')->store('username'); + +### Mengembalikan ke Troli +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`. + + Cart::restore('username'); + + // To restore a cart instance named 'wishlist' + Cart::instance('wishlist')->restore('username'); + +### Menggabungkan Troli +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. + + // Merge the contents of 'savedcart' into 'username'. + Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate); + +## Pengecualian + +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: + +| Exception | Reason | +| ---------------------------- | ---------------------------------------------------------------------------------- | +| *CartAlreadyStoredException* | Saat mencoba menyimpan keranjang yang sudah disimpan menggunakan pengenal yang ditentukan | +| *InvalidRowIDException* | Ketika rowId yang diteruskan tidak ada dalam instance troli saat ini | +| *UnknownModelException* | Saat Anda mencoba mengaitkan model yang tidak ada dengan Item Keranjang. | + +## Events + +Troli juga memiliki event. Ada lima event yang bisa Anda lakukan. + +| Event | Fired | Parameter | +| ------------- | ---------------------------------------- | -------------------------------- | +| cart.added | Saat item ditambahkan ke troli. | The `CartItem` that was added. | +| cart.updated | Ketika item dalam troli diperbarui. | The `CartItem` that was updated. | +| cart.removed | Ketika item dalam troli dihapus. | The `CartItem` that was removed. | +| cart.stored | Ketika isi trol disimpan. | - | +| cart.restored | Ketika konten keranjang Dikembalikan. | - | + +## Contoh + +Di bawah ini adalah sedikit contoh cara membuat daftar isi keranjang dalam sebuah tabel: + +```php + +// Tambahkan beberapa item di Kontroler Anda. +Cart::add('192ao12', 'Product 1', 1, 9.99); +Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); + +// Tampilkan konten dalam Tampilan. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductQtyPriceSubtotal
+

name; ?>

+

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

+
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
+``` diff --git a/README_uk-UA.md b/README_uk-UA.md new file mode 100644 index 0000000..af44477 --- /dev/null +++ b/README_uk-UA.md @@ -0,0 +1,626 @@ +## LaravelShoppingcart +[![Build Status](https://travis-ci.org/bumbummen99/LaravelShoppingcart.png?branch=master)](https://travis-ci.org/bumbummen99/LaravelShoppingcart) +[![codecov](https://codecov.io/gh/bumbummen99/LaravelShoppingcart/branch/master/graph/badge.svg)](https://codecov.io/gh/bumbummen99/LaravelShoppingcart) +[![StyleCI](https://styleci.io/repos/152610878/shield?branch=master)](https://styleci.io/repos/152610878) +[![Total Downloads](https://poser.pugx.org/bumbummen99/shoppingcart/downloads.png)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![Latest Stable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/stable)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](https://packagist.org/packages/bumbummen99/shoppingcart) +[![License](https://poser.pugx.org/bumbummen99/shoppingcart/license)](https://packagist.org/packages/bumbummen99/shoppingcart) + +Цей репозиторій є відгалуженням [Crinsane's LaravelShoppingcart](https://github.com/Crinsane/LaravelShoppingcart) та містить додаткові незначні доповнення, сумісні з Laravel 6. + +## Встановлення + +Встановіть [пакет](https://packagist.org/packages/bumbummen99/shoppingcart) скориставшись [Завантажувачем](http://getcomposer.org/). + +Для запуску Завантажувача, скористайтеся командою у Терміналі: + + composer require bumbummen99/shoppingcart + +Тепер ви готові розпочати користуватися кошиком у вашому застосунку. + +**Починаючи з версії 2 даного пакету з'явилася можливість впровадження залежності для впровадження екземпляра класу Кошик (Cart) до вашого контролера або іншого класу** + +## Огляд +Щоб детальніше ознайомитися LaravelShoppingcart, можете пройти за посиланнями + +* [Застосування](#usage) +* [Колекції](#collections) +* [Екземпляри](#instances) +* [Моделі](#models) +* [База даних](#database) +* [Винятки](#exceptions) +* [Події](#events) +* [Приклад](#example) + +## Застосування + +Кошик (Cart) дозволяє вам скористатися наступними методами: + +### Cart::add() + +Додавати покупки у кошик дуже зручно - достатньо скористатися методом `add()`, який приймає різноманітні параметри. + +У найпростішій формі метода достатньо вказати ідентифікатор товару, назву, кількість, ціну та вагу товару, який ви хочете додати у кошик. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 550); +``` + +У якості додаткового п'ятого параметра можна задати додаткові опції, наприклад, щоб додати декілька одиниць з однаковим ідентифікатором, але, наприклад, різного розміру. + +```php +Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); +``` + +**Метод `add()` повертає екземпляр CartItems того товару, який ви щойно додали у кошик.** + +Можливо, вам більше до вподоби додавати товари, використовуючи масив? Якщо масив містить усі необхідні поля, ви можете передавати масив у цей метод. Поле із додатковими опціями є необов'язковим. + +```php +Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]); +``` + +У версії 2 пакета з'явилася нова можливість для роботи з інтерфейсом [Buyable](#buyable). Такий функціонал з'являється за рахунок того, що модель запускає інтерфейс [Buyable](#buyable), який дозволить імплементувати декілька методів, з яких пакет знатиме як отримати ідентифікатор, назву та ціну з вашої моделі. +Таким чином, ви можете передати метод `add()` та кількість одиниць товару до моделі, а вона автоматично додасть їх до кошика. + +**Додатковий бонус інтерфейсу - автоматичне об'єднання моделі з CartItems** + +```php +Cart::add($product, 1, ['size' => 'large']); +``` +У якості додаткового параметра, ви можете внести опції. +```php +Cart::add($product, 1, ['size' => 'large']); +``` + +Нарешті, ви також можете додавати до кошика декілька одиниць водночас. Для цього потрібно передати у `add()` масив масивів або масив Buyables, і їх буде додано в кошик. + +**Під час додавання декількох одиниць товару в кошик, метод `add()` повертає масив CartItems.** + +```php +Cart::add([ + ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550], + ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']] +]); + +Cart::add([$product1, $product2]); + +``` + +### Cart::update() + +Щоб оновити товар у кошику, вам знадобиться ідентифікатор рядка (rowId) даного товару. +Далі ви можете скористатися методом `update()` для того, щоб оновити його. + +Якщо ви просто хочете оновити кількість товару, вам необхідно передати у метод `update()` rowId і оновлену кількість: + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::update($rowId, 2); // Will update the quantity +``` + +Якщо ви хочете оновити більше атрибутів товару, вам потрібно або передати у метод `update()` масив або `Buyable` у якості другого параметра. Таким чином, ви можете оновити всю інформацію про товар за заданим rowId. + +```php +Cart::update($rowId, ['name' => 'Product 1']); // Will update the name + +Cart::update($rowId, $product); // Will update the id, name and price + +``` + +### Cart::remove() + +Щоб вилучити товар з кошика, вам знову знадобиться rowId. Такий rowId потрібно передати у метод `remove()`, який автоматично вилучить товар із кошика. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::remove($rowId); +``` + +### Cart::get() + +Якщо ви хочете отримати товар із кошика, використовуючи його rowId, вам потрібно застосувати метод `get()` щодо кошика і передати в нього rowId. + +```php +$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + +Cart::get($rowId); +``` + +### Cart::content() + +Вам також може знадобитися можливість отримати інформацію про вміст кошика. Для цього вам потрібно скористатися методом `content`. Такий метод повертає колекцію CartItems, ви можете перебирати вміст такої колекції і відобразити вміст кошика для ваших клієнтів. + +```php +Cart::content(); +``` + +Даний метод повертає вміст поточного екземпляра кошика, якщо ви хочете вміст іншого екземпляра, вам потрібно зв'язати виклики. + +```php +Cart::instance('wishlist')->content(); +``` + +### Cart::destroy() + +Якщо ви хочете остаточно вилучити вміст кошика, ви можете застосувати метод `destroy()` щодо кошика. Даний метод вилучить всі CartItems з кошика для поточного екземпляра кошика. + +```php +Cart::destroy(); +``` + +### Cart::weight() + +Метод `weight()` можна застосувати, щоб отримати розрахунок ваги усіх товарів у кошику, за умови, що задано вагу і кількість одиниць. + +```php +Cart::weight(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::weight($decimals, $decimalSeperator, $thousandSeperator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вагу товарів через `$cart->weight`** + +### Cart::total() + +Метод `total()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів у кошику, за умови, що задані ціна і кількість одиниць. + +```php +Cart::total(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::total($decimals, $decimalSeparator, $thousandSeparator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість товарів через `$cart->total`** + +### Cart::tax() + +Метод `tax()` можна застосовувати, щоб отримати розрахунок суми податків для усіх товарів у кошику, за умови, що задані ціна і кількість одиниць. + +```php +Cart::tax(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::tax($decimals, $decimalSeparator, $thousandSeparator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про суму податку на товари через `$cart->tax`** + +### Cart::subtotal() + +Метод `subtotal()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів у кошику, без урахування суми податку. + +```php +Cart::subtotal(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів без урахування суми податків через `$cart->subtotal`** + +### Cart::discount() + +Метод `discount()` можна застосовувати, щоб отримати розрахунок знижки на усі товари у кошику. + +```php +Cart::discount(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::discount($decimals, $decimalSeparator, $thousandSeparator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів з урахуванням знижки `$cart->discount`** + +### Cart::initial() + +Метод `initial()` можна застосовувати, щоб отримати розрахунок вартості усіх товарів до застосування знижки. + +```php +Cart::initial(); +``` + +Даний метод автоматично відформатує результат, який ви можете поправити за допомогою трьох додаткових параметрів. + +```php +Cart::initial($decimals, $decimalSeparator, $thousandSeparator); +``` + +Ви можете задати формат чисел за замовчуванням у файлі з конфігураціями. + +**Якщо ви не використовуєте Фасад, але застосовуєте впровадження залежності, наприклад, у вашому Контролері, ви також можете отримати інформацію про вартість усіх товарів до застосування знижки `$cart->initial`** + +### Cart::count() + +Метод `count()` можна застосовувати, щоб дізнатися кількість одиниць товарів у кошику. Даний метод повертає загальну кількість одиниць товарів у кошику. Тобто якщо ви додали 2 книжки і 1 сорочку, цей метод поверне 3 одиниці. + +```php +Cart::count(); +$cart->count(); +``` + +### Cart::search() + +Метод `search()` можна застосовувати, щоб знайти одиницю товару у кошику. + +**Даний метод було змінено у версії 2** + +У своїй імплементації, цей метод застосовує метод фільтрування з класу Laravel Collection. Це означає, що вам потрібно передати замикання (Closure) для даного методу, де ви зазначите умови для пошуку. + +Наприклад, якщо ви хочете знайти всі одиниці товару з ідентифікатором 1: + +```php +$cart->search(function ($cartItem, $rowId) { + return $cartItem->id === 1; +}); +``` + +Як ви можете побачити, замикання отримає 2 параметра. Перший - CartItem для здійснення перевірки щодо нього. Другий параметр - rowId даного CartItem. + +**Даний метод повертає колекцію, яка вміщує всі CartItems, які було знайдено** + +Такий спосіб пошуку надає вам повний контроль над процесом пошуку та дозволяє здійснювати дуже точні та конкретні пошуки. + +### Cart::setTax($rowId, $taxRate) + +Метод `setTax()` можна застосовувати, щоб змінювати ставку оподаткування, яка застосовується до CartItem. Така операція перезапише значення встановлене у файлі з конфігураціями. + +```php +Cart::setTax($rowId, 21); +$cart->setTax($rowId, 21); +``` + +### Cart::setGlobalTax($taxRate) + +Метод `setGlobalTax()` можна застосовувати, щоб змінити ставку оподаткування для усіх найменувать у кошику. Нові найменування отримають значення setGlobalTax також. + +```php +Cart::setGlobalTax(21); +$cart->setGlobalTax(21); +``` + +### Cart::setGlobalDiscount($discountRate) + +Метод `setGlobalDiscount()` можна застосовувати для заміни ставки знижки щодо усіх найменувань у кошику. Нові найменування також отримуватимуть таку знижку. + +```php +Cart::setGlobalDiscount(50); +$cart->setGlobalDiscount(50); +``` + +### Cart::setDiscount($rowId, $taxRate) + +Застосування методу `setDiscount()` полягає у заміні ставки знижки, яка застосовується до CartItem. Зверніть увагу, що дане значення ставки знижки буде змінено, якщо ви згодом встановите глобальну знижку для Кошика (Cart). + +```php +Cart::setDiscount($rowId, 21); +$cart->setDiscount($rowId, 21); +``` + +### Buyable + +Для зручного швидкого додавання товарів до кошика та їхнього автоматичного об'єднання, ваша модель повинна запустити інтерфейс `Buyable`. Ви можете застосовувати `CanBeBought` трейт для імплементації необхідних методів, але зверніть увагу, що такі методи застосовуватимуть попередньо визначені поля у вашій моделі для необхідних значень. +```php +id; + } + public function getBuyableDescription(){ + return $this->name; + } + public function getBuyablePrice(){ + return $this->price; + } + public function getBuyableWeight(){ + return $this->weight; + } +``` + +Приклад: + +```php +id; + } + public function getBuyableDescription($options = null) { + return $this->name; + } + public function getBuyablePrice($options = null) { + return $this->price; + } +} +``` + +## Колекції + +Щодо багатьох екземплярів Кошик (Cart) повертає Колекцію, яка є простим видом Laravel Collection. Таким чином усі методи, які ви можете застосовувати щодо Laravel Collection, є також доступними у результаті операції. + +Наприклад, ви можете швидко отримати кількість унікальних товарів у кошику: + +```php +Cart::content()->count(); +``` + +Або групувати вміст за ідентифікатором товару: + +```php +Cart::content()->groupBy('id'); +``` + +## Екземпляри + +Пакет підтримує декілька екземплярів кошика. Як це працює: + +Ви можете встановити поточний екземпляр кошика через виклик `Cart::instance('newInstance')`. З цього моменту, активний екземляр кошика буде `newInstance`, тому коли ви додаєте, вилучаєте або отримуєте інформацію щодо вмісту кошика, ви працюєте з екземпляром `newInstance` кошика. +Якщо ви хочете переключитися між екзмеплярами, ви можете викликати `Cart::instance('otherInstance')` ще раз, і ви знову працюватимете з `otherInstance`. + +Короткий приклад: + +```php +Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550); + +// Get the content of the 'shopping' cart +Cart::content(); + +Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']); + +// Get the content of the 'wishlist' cart +Cart::content(); + +// If you want to get the content of the 'shopping' cart again +Cart::instance('shopping')->content(); + +// And the count of the 'wishlist' cart again +Cart::instance('wishlist')->count(); +``` + +Ви також можете застосувати Контракт `InstanceIdentifier` для розширення бажаної моделі через призначення / створення екземпляру Кошика (Cart) для неї. Така дія також дозволить напряму встановлювати глобальну знижку. +``` +email; + } + + /** + * Get the unique identifier to load the Cart from + * + * @return int|string + */ + public function getInstanceGlobalDiscount($options = null) + { + return $this->discountRate ?: 0; + } +} + +// Inside Controller +$user = \Auth::user(); +$cart = Cart::instance($user); + + + +``` + +**N.B. Зверніть увагу, що кошик залишається у стані останнього призначеного екземпляра, доки ви не встановите інший екземпляр протягом виконання скрипта.** + +**N.B.2 За замовчуванням екземпляр кошика називається `default`, тому коли ви не використовуєте екземпляри, `Cart::content();` залишається таким самим як і `Cart::instance('default')->content()`.** + +## Моделі + +Через те, що можливість прямого доступу до моделі з CartItem може бути дуже зручною, виникає питання чи можливо об'єднати модель із товарами у кошику. Скажімо, у вашому застосунку є модель `Product`. Завдяки методу `associate()` ви можете вказати кошику, що товар у кошику об'єднаний з моделлю `Product`. + +Таким чином ви можете отримати доступ до вашої моделі одразу з `CartItem`! + +Доступ до моделі також можна отримати через властивість CartItem `model`. + +**Якщо ваша модель запускає інтерфейс `Buyable` і ви використовували вашу модель для додавання товару до кошика, вони будуть об'єднані автоматично.** + +Ось приклад: + +```php + +// First we'll add the item to the cart. +$cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); + +// Next we associate a model with the item. +Cart::associate($cartItem->rowId, 'Product'); + +// Or even easier, call the associate method on the CartItem! +$cartItem->associate('Product'); + +// You can even make it a one-liner +Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product'); + +// Now, when iterating over the content of the cart, you can access the model. +foreach(Cart::content() as $row) { + echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.'; +} +``` +## База даних + +- [Конфігурації](#configuration) +- [Збереження кошика](#storing-the-cart) +- [Відновлення кошика](#restoring-the-cart) + +### Конфігурації +Для збереження кошика до бази даних, щоб ви могли отримати його пізніше, пакет повинен знати яке підключення до бази даних використовувати і яка назва окремої таблиці. +За замовчуванням, пакет використовуватиме підключення до бази даних, яке вказане за замовчуванням, та використовуватиме таблицію `shoppingcart`. +Якщо ви хочете змінити ці значення, вам потрібно буде опублікувати файл з конфігураціями `config`. + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config" + +Така дія створить вам файл з конфігураціями `cart.php`, в якому ви можете внести бажані зміни. + +Щоб спростити ваше життя, пакет також включає готову до вжитку `migration`, яку можна опублікувати через запуск наступної команди: + + php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations" + +Така дія розмістить файл з міграцією таблиці `shoppingcart` в директорію `database/migrations`. Все що вам залишається зробити, це запустити `php artisan migrate` для міграції вашої бази даних. + +### Збереження кошика +Для збереження екземпляра кошика до бази даних, вам потрібно викликати метод `store($identifier) `. Де `$identifier` є випадковим ключем, наприклад, ідентифікатор або ім'я користувача. + + Cart::store('username'); + + // To store a cart instance named 'wishlist' + Cart::instance('wishlist')->store('username'); + +### Відновлення кошика +Якщо ви хочете отримати кошик із бази даних і відновити його, вам знадобиться викликати метод `restore($identifier)`, де `$identifier` - це ключ, який ви зазначили у методі `store`. + + Cart::restore('username'); + + // To restore a cart instance named 'wishlist' + Cart::instance('wishlist')->restore('username'); + +### Злиття кошиків +Якщо ви хочете злити кошик із іншим кошиком, збереженим у базі даних, вам знадобиться викликати метод `merge($identifier)`, де `$identifier` - це ключ, який ви зазначили у методі`store`. Ви також можете визначити чи хочете ви зберегти знижку і ставку оподаткування для товарів. + + // Merge the contents of 'savedcart' into 'username'. + Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate); + +## Перехоплення + +Пакет Кошик (Cart) видаватиме винятки/перехоплення у разі, якщо щось йде не за планом. Таким чином, вам буде простіше відлагоджувати (debug) ваш код, використовуючи пакет Кошик, або обробляти помилку за типом перехоплення. Пакети Кошика можуть видавати наступні перехоплення: + +| Перехоплення | Пояснення | +| ---------------------------- | ---------------------------------------------------------------------------------- | +| *CartAlreadyStoredException* | ПерехопленняКошикВжеЗбережено Коли ви намагаєтеся зберегти кошик, який вже було збережено, застосовуючи вказаний ідентифікатор | +| *InvalidRowIDException* | ПерехопленняНеправильнийІдРядка Коли rowId, який було передано, не існує у поточному екземплярі кошика | +| *UnknownModelException* | ПерехопленняНевідомаМодель Коли ви намагаєтеся об'єднати неіснуючу модель з CartItem. | + +## Події + +Кошик також має вбудовані події. Існує п'ять подій, які можна очікувати. + +| Подія | Видано | Параметр | +| ------------- | ---------------------------------------- | -------------------------------- | +| cart.added | Коли товар додано до кошика. | `CartItem`, який було додано. | +| cart.updated | Коли товар оновлено у кошику. | `CartItem`, який було оновлено. | +| cart.removed | Коли товар вилучено з кошика. | `CartItem`, який було вилучено. | +| cart.stored | Коли вміст кошика було збережено. | - | +| cart.restored | Коли вміст кошика було відновлено. | - | + +## Приклад + +Нижче наведено приклад як відобразити вміст кошика у таблиці: + +```php + +// Add some items in your Controller. +Cart::add('192ao12', 'Product 1', 1, 9.99); +Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']); + +// Display the content in a View. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductQtyPriceSubtotal
+

name; ?>

+

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

+
$price; ?>$total; ?>
 Subtotal
 Tax
 Total
+``` diff --git a/composer.json b/composer.json index 80d0396..3825978 100644 --- a/composer.json +++ b/composer.json @@ -6,18 +6,20 @@ "authors": [ { "name": "Rob Gloudemans", - "email": "info@robgloudemans.nl" + "email": "info@robgloudemans.nl", + "homepage": "http://robgloudemans.nl/" }, { "name": "Patrick Henninger", - "email": "privat@skyraptor.eu" + "email": "privat@skyraptor.eu", + "homepage": "https://skyraptor.eu/" } ], "require": { - "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/session": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "illuminate/events": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", - "nesbot/carbon": "^1.26.3 || ^2.0" + "illuminate/support": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0", + "illuminate/session": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0", + "illuminate/events": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0", + "nesbot/carbon": "~1.20||^1.26.3||^2.0" }, "require-dev": { "phpunit/phpunit": "~5.0||~6.0||~7.0||~8.0", diff --git a/src/Cart.php b/src/Cart.php index 2856606..182ef29 100644 --- a/src/Cart.php +++ b/src/Cart.php @@ -61,7 +61,7 @@ class Cart private $discount = 0; /** - * Defines the discount percentage. + * Defines the tax rate. * * @var float */ @@ -120,11 +120,12 @@ class Cart * @param mixed $name * @param int|float $qty * @param float $price + * @param float $weight * @param array $options * * @return \Gloudemans\Shoppingcart\CartItem */ - public function add($id, $name = null, $qty = null, $price = null, array $options = []) + public function add($id, $name = null, $qty = null, $price = null, $weight = 0, array $options = []) { if ($this->isMulti($id)) { return array_map(function ($item) { @@ -132,7 +133,7 @@ class Cart }, $id); } - $cartItem = $this->createCartItem($id, $name, $qty, $price, $options); + $cartItem = $this->createCartItem($id, $name, $qty, $price, $weight, $options); return $this->addCartItem($cartItem); } @@ -140,13 +141,14 @@ class Cart /** * Add an item to the cart. * - * @param \Gloudemans\Shoppingcart\CartItem $item Item to add to the Cart - * @param bool $keepDiscount Keep the discount rate of the Item - * @param bool $keepTax Keep the Tax rate of the Item + * @param \Gloudemans\Shoppingcart\CartItem $item Item to add to the Cart + * @param bool $keepDiscount Keep the discount rate of the Item + * @param bool $keepTax Keep the Tax rate of the Item + * @param bool $dispatchEvent * * @return \Gloudemans\Shoppingcart\CartItem The CartItem */ - public function addCartItem($item, $keepDiscount = false, $keepTax = false) + public function addCartItem($item, $keepDiscount = false, $keepTax = false, $dispatchEvent = true) { if (!$keepDiscount) { $item->setDiscountRate($this->discount); @@ -164,7 +166,9 @@ class Cart $content->put($item->rowId, $item); - $this->events->dispatch('cart.added', $item); + if ($dispatchEvent) { + $this->events->dispatch('cart.added', $item); + } $this->session->put($this->instance, $content); @@ -194,6 +198,8 @@ class Cart $content = $this->getContent(); if ($rowId !== $cartItem->rowId) { + $itemOldIndex = $content->keys()->search($rowId); + $content->pull($rowId); if ($content->has($cartItem->rowId)) { @@ -207,7 +213,13 @@ class Cart return; } else { - $content->put($cartItem->rowId, $cartItem); + if (isset($itemOldIndex)) { + $content = $content->slice(0, $itemOldIndex) + ->merge([$cartItem->rowId => $cartItem]) + ->merge($content->slice($itemOldIndex)); + } else { + $content->put($cartItem->rowId, $cartItem); + } } $this->events->dispatch('cart.updated', $cartItem); @@ -378,7 +390,7 @@ class Cart } /** - * Get the subtotal (total - tax) of the items in the cart. + * Get the discount of the items in the cart. * * @return float */ @@ -390,7 +402,7 @@ class Cart } /** - * Get the subtotal (total - tax) of the items in the cart as formatted string. + * Get the discount of the items in the cart as formatted string. * * @param int $decimals * @param string $decimalPoint @@ -404,7 +416,7 @@ class Cart } /** - * Get the subtotal (total - tax) of the items in the cart. + * Get the price of the items in the cart (not rounded). * * @return float */ @@ -416,7 +428,7 @@ class Cart } /** - * Get the subtotal (total - tax) of the items in the cart as formatted string. + * Get the price of the items in the cart as formatted string. * * @param int $decimals * @param string $decimalPoint @@ -429,6 +441,32 @@ class Cart return $this->numberFormat($this->initialFloat(), $decimals, $decimalPoint, $thousandSeperator); } + /** + * Get the price of the items in the cart (previously rounded). + * + * @return float + */ + public function priceTotalFloat() + { + return $this->getContent()->reduce(function ($initial, CartItem $cartItem) { + return $initial + $cartItem->priceTotal; + }, 0); + } + + /** + * Get the price of the items in the cart as formatted string. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * + * @return string + */ + public function priceTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + return $this->numberFormat($this->priceTotalFloat(), $decimals, $decimalPoint, $thousandSeperator); + } + /** * Get the total weight of the items in the cart. * @@ -643,8 +681,29 @@ class Cart $this->createdAt = Carbon::parse(data_get($stored, 'created_at')); $this->updatedAt = Carbon::parse(data_get($stored, 'updated_at')); - $this->getConnection()->table($this->getTableName()) - ->where('identifier', $identifier)->delete(); + $this->getConnection()->table($this->getTableName())->where('identifier', $identifier)->delete(); + } + + /** + * Erase the cart with the given identifier. + * + * @param mixed $identifier + * + * @return void + */ + public function erase($identifier) + { + if ($identifier instanceof InstanceIdentifier) { + $identifier = $identifier->getInstanceIdentifier(); + } + + if (!$this->storedCartWithIdentifierExists($identifier)) { + return; + } + + $this->getConnection()->table($this->getTableName())->where('identifier', $identifier)->delete(); + + $this->events->dispatch('cart.erased'); } /** @@ -653,10 +712,11 @@ class Cart * @param mixed $identifier Identifier of the Cart to merge with. * @param bool $keepDiscount Keep the discount of the CartItems. * @param bool $keepTax Keep the tax of the CartItems. + * @param bool $dispatchAdd Flag to dispatch the add events. * * @return bool */ - public function merge($identifier, $keepDiscount = false, $keepTax = false) + public function merge($identifier, $keepDiscount = false, $keepTax = false, $dispatchAdd = true) { if (!$this->storedCartWithIdentifierExists($identifier)) { return false; @@ -668,9 +728,11 @@ class Cart $storedContent = unserialize($stored->content); foreach ($storedContent as $cartItem) { - $this->addCartItem($cartItem, $keepDiscount, $keepTax); + $this->addCartItem($cartItem, $keepDiscount, $keepTax, $dispatchAdd); } + $this->events->dispatch('cart.merged'); + return true; } @@ -716,11 +778,12 @@ class Cart * @param mixed $name * @param int|float $qty * @param float $price + * @param float $weight * @param array $options * * @return \Gloudemans\Shoppingcart\CartItem */ - private function createCartItem($id, $name, $qty, $price, array $options) + private function createCartItem($id, $name, $qty, $price, $weight, array $options) { if ($id instanceof Buyable) { $cartItem = CartItem::fromBuyable($id, $qty ?: []); @@ -730,7 +793,7 @@ class Cart $cartItem = CartItem::fromArray($id); $cartItem->setQuantity($id['qty']); } else { - $cartItem = CartItem::fromAttributes($id, $name, $price, $options); + $cartItem = CartItem::fromAttributes($id, $name, $price, $weight, $options); $cartItem->setQuantity($qty); } diff --git a/src/CartItem.php b/src/CartItem.php index 0717ed7..63589b5 100644 --- a/src/CartItem.php +++ b/src/CartItem.php @@ -5,7 +5,20 @@ namespace Gloudemans\Shoppingcart; use Gloudemans\Shoppingcart\Contracts\Buyable; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Jsonable; +use Illuminate\Support\Arr; +/** + * @property-read mixed discount + * @property-read float discountTotal + * @property-read float priceTarget + * @property-read float priceNet + * @property-read float priceTotal + * @property-read float subtotal + * @property-read float taxTotal + * @property-read float tax + * @property-read float total + * @property-read float priceTax + */ class CartItem implements Arrayable, Jsonable { /** @@ -57,6 +70,13 @@ class CartItem implements Arrayable, Jsonable */ public $options; + /** + * The tax rate for the cart item. + * + * @var int|float + */ + public $taxRate = 0; + /** * The FQN of the associated model. * @@ -64,13 +84,6 @@ class CartItem implements Arrayable, Jsonable */ private $associatedModel = null; - /** - * The tax rate for the cart item. - * - * @var int|float - */ - private $taxRate = 0; - /** * The discount rate for the cart item. * @@ -84,6 +97,7 @@ class CartItem implements Arrayable, Jsonable * @param int|string $id * @param string $name * @param float $price + * @param float $weight * @param array $options */ public function __construct($id, $name, $price, $weight = 0, array $options = []) @@ -97,6 +111,9 @@ class CartItem implements Arrayable, Jsonable if (strlen($price) < 0 || !is_numeric($price)) { throw new \InvalidArgumentException('Please supply a valid price.'); } + if (strlen($weight) < 0 || !is_numeric($weight)) { + throw new \InvalidArgumentException('Please supply a valid weight.'); + } $this->id = $id; $this->name = $name; @@ -248,6 +265,20 @@ class CartItem implements Arrayable, Jsonable return $this->numberFormat($this->discountTotal, $decimals, $decimalPoint, $thousandSeperator); } + /** + * Returns the formatted total price for this cart item. + * + * @param int $decimals + * @param string $decimalPoint + * @param string $thousandSeperator + * + * @return string + */ + public function priceTotal($decimals = null, $decimalPoint = null, $thousandSeperator = null) + { + return $this->numberFormat($this->priceTotal, $decimals, $decimalPoint, $thousandSeperator); + } + /** * Set the quantity for this cart item. * @@ -274,7 +305,6 @@ class CartItem implements Arrayable, Jsonable $this->id = $item->getBuyableIdentifier($this->options); $this->name = $item->getBuyableDescription($this->options); $this->price = $item->getBuyablePrice($this->options); - $this->priceTax = $this->price + $this->tax; } /** @@ -286,13 +316,12 @@ class CartItem implements Arrayable, Jsonable */ public function updateFromArray(array $attributes) { - $this->id = array_get($attributes, 'id', $this->id); - $this->qty = array_get($attributes, 'qty', $this->qty); - $this->name = array_get($attributes, 'name', $this->name); - $this->price = array_get($attributes, 'price', $this->price); - $this->weight = array_get($attributes, 'weight', $this->weight); - $this->priceTax = $this->price + $this->tax; - $this->options = new CartItemOptions(array_get($attributes, 'options', $this->options)); + $this->id = Arr::get($attributes, 'id', $this->id); + $this->qty = Arr::get($attributes, 'qty', $this->qty); + $this->name = Arr::get($attributes, 'name', $this->name); + $this->price = Arr::get($attributes, 'price', $this->price); + $this->weight = Arr::get($attributes, 'weight', $this->weight); + $this->options = new CartItemOptions(Arr::get($attributes, 'options', $this->options)); $this->rowId = $this->generateRowId($this->id, $this->options->all()); } @@ -351,39 +380,69 @@ class CartItem implements Arrayable, Jsonable if (property_exists($this, $attribute)) { return $this->{$attribute}; } + $decimals = config('cart.format.decimals', 2); switch ($attribute) { - case 'discount': - return $this->price * ($this->discountRate / 100); - case 'priceTarget': - return $this->price - $this->discount; - case 'subtotal': - return $this->priceTarget * $this->qty; - case 'tax': - return $this->priceTarget * ($this->taxRate / 100); - case 'priceTax': - return $this->priceTarget + $this->tax; - case 'total': - return $this->priceTax * $this->qty; - case 'taxTotal': - return $this->tax * $this->qty; - case 'discountTotal': - return $this->discount * $this->qty; - case 'weightTotal': - return $this->weight * $this->qty; - case 'model': if (isset($this->associatedModel)) { return with(new $this->associatedModel())->find($this->id); } - case 'modelFQCN': if (isset($this->associatedModel)) { return $this->associatedModel; } + case 'weightTotal': + return round($this->weight * $this->qty, $decimals); + } - default: - return; + if (config('cart.gross_price')) { + switch ($attribute) { + case 'priceNet': + return round($this->price / (1 + ($this->taxRate / 100)), $decimals); + case 'discount': + return $this->priceNet * ($this->discountRate / 100); + case 'tax': + return round($this->priceTarget * ($this->taxRate / 100), $decimals); + case 'priceTax': + return round($this->priceTarget + $this->tax, $decimals); + case 'discountTotal': + return round($this->discount * $this->qty, $decimals); + case 'priceTotal': + return round($this->priceNet * $this->qty, $decimals); + case 'subtotal': + return round($this->priceTotal - $this->discountTotal, $decimals); + case 'priceTarget': + return round(($this->priceTotal - $this->discountTotal) / $this->qty, $decimals); + case 'taxTotal': + return round($this->subtotal * ($this->taxRate / 100), $decimals); + case 'total': + return round($this->subtotal + $this->taxTotal, $decimals); + default: + return; + } + } else { + switch ($attribute) { + case 'discount': + return $this->price * ($this->discountRate / 100); + case 'tax': + return round($this->priceTarget * ($this->taxRate / 100), $decimals); + case 'priceTax': + return round($this->priceTarget + $this->tax, $decimals); + case 'discountTotal': + return round($this->discount * $this->qty, $decimals); + case 'priceTotal': + return round($this->price * $this->qty, $decimals); + case 'subtotal': + return round($this->priceTotal - $this->discountTotal, $decimals); + case 'priceTarget': + return round(($this->priceTotal - $this->discountTotal) / $this->qty, $decimals); + case 'taxTotal': + return round($this->subtotal * ($this->taxRate / 100), $decimals); + case 'total': + return round($this->subtotal + $this->taxTotal, $decimals); + default: + return; + } } } @@ -409,7 +468,7 @@ class CartItem implements Arrayable, Jsonable */ public static function fromArray(array $attributes) { - $options = array_get($attributes, 'options', []); + $options = Arr::get($attributes, 'options', []); return new self($attributes['id'], $attributes['name'], $attributes['price'], $attributes['weight'], $options); } diff --git a/src/Config/cart.php b/src/Config/cart.php index a437d69..739265e 100644 --- a/src/Config/cart.php +++ b/src/Config/cart.php @@ -2,6 +2,18 @@ return [ + /* + |-------------------------------------------------------------------------- + | Gross price as base price + |-------------------------------------------------------------------------- + | + | This default value is used to select the method to calculate prices and taxes + | If true the item price is managed as a gross price, so taxes will be calculated by separation/exclusion + | + */ + + 'gross_price' => false, + /* |-------------------------------------------------------------------------- | Default tax rate diff --git a/src/ShoppingcartServiceProvider.php b/src/ShoppingcartServiceProvider.php index a26ef89..2e6b1b5 100644 --- a/src/ShoppingcartServiceProvider.php +++ b/src/ShoppingcartServiceProvider.php @@ -30,6 +30,6 @@ class ShoppingcartServiceProvider extends ServiceProvider $this->publishes([ realpath(__DIR__.'/Database/migrations') => $this->app->databasePath().'/migrations', - ]); + ], 'migrations'); } } diff --git a/tests/CartTest.php b/tests/CartTest.php index b51863d..24da3cf 100644 --- a/tests/CartTest.php +++ b/tests/CartTest.php @@ -264,6 +264,19 @@ class CartTest extends TestCase $cart->add(1, 'Some title', 1, 'invalid'); } + /** + * @test + */ + public function it_will_validate_the_weight() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Please supply a valid weight'); + + $cart = $this->getCart(); + + $cart->add(1, 'Some title', 1, 10.00, 'invalid'); + } + /** @test */ public function it_will_update_the_cart_if_the_item_already_exists_in_the_cart() { @@ -386,6 +399,21 @@ class CartTest extends TestCase $this->assertRowsInCart(1, $cart); } + /** @test */ + public function it_will_keep_items_sequence_if_the_options_changed() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(), 1, ['color' => 'red']); + $cart->add(new BuyableProduct(), 1, ['color' => 'green']); + $cart->add(new BuyableProduct(), 1, ['color' => 'blue']); + + $cart->update($cart->content()->values()[1]->rowId, ['options' => ['color' => 'yellow']]); + + $this->assertRowsInCart(3, $cart); + $this->assertEquals('yellow', $cart->content()->values()[1]->options->color); + } + /** @test */ public function it_can_remove_an_item_from_the_cart() { @@ -743,6 +771,20 @@ class CartTest extends TestCase $this->assertEquals('1.050,00', $cart->tax(2, ',', '.')); } + /** @test */ + public function it_can_access_tax_as_percentage() + { + $cart = $this->getCart(); + + $cart->add(new BuyableProduct(1, 'Some title', 10.00), 1); + + $cart->setTax('027c91341fd5cf4d2579b49c4b6a90da', 19); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $this->assertEquals(19, $cartItem->taxRate); + } + /** @test */ public function it_can_return_the_subtotal() { @@ -917,6 +959,52 @@ class CartTest extends TestCase $this->assertEquals(11.90, $cartItem->total(2)); } + /** @test */ + public function it_can_calculate_all_values_after_updating_from_array() + { + $cart = $this->getCartDiscount(50); + $cart->add(new BuyableProduct(1, 'First item', 10.00), 1); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', ['qty'=>2]); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $cart->setTax('027c91341fd5cf4d2579b49c4b6a90da', 19); + + $this->assertEquals(10.00, $cartItem->price(2)); + $this->assertEquals(5.00, $cartItem->discount(2)); + $this->assertEquals(10.00, $cartItem->discountTotal(2)); + $this->assertEquals(5.00, $cartItem->priceTarget(2)); + $this->assertEquals(10.00, $cartItem->subtotal(2)); + $this->assertEquals(0.95, $cartItem->tax(2)); + $this->assertEquals(1.90, $cartItem->taxTotal(2)); + $this->assertEquals(5.95, $cartItem->priceTax(2)); + $this->assertEquals(11.90, $cartItem->total(2)); + } + + /** @test */ + public function it_can_calculate_all_values_after_updating_from_buyable() + { + $cart = $this->getCartDiscount(50); + $cart->add(new BuyableProduct(1, 'First item', 5.00), 2); + + $cart->update('027c91341fd5cf4d2579b49c4b6a90da', new BuyableProduct(1, 'First item', 10.00)); + + $cartItem = $cart->get('027c91341fd5cf4d2579b49c4b6a90da'); + + $cart->setTax('027c91341fd5cf4d2579b49c4b6a90da', 19); + + $this->assertEquals(10.00, $cartItem->price(2)); + $this->assertEquals(5.00, $cartItem->discount(2)); + $this->assertEquals(10.00, $cartItem->discountTotal(2)); + $this->assertEquals(5.00, $cartItem->priceTarget(2)); + $this->assertEquals(10.00, $cartItem->subtotal(2)); + $this->assertEquals(0.95, $cartItem->tax(2)); + $this->assertEquals(1.90, $cartItem->taxTotal(2)); + $this->assertEquals(5.95, $cartItem->priceTax(2)); + $this->assertEquals(11.90, $cartItem->total(2)); + } + /** @test */ public function it_will_destroy_the_cart_when_the_user_logs_out_and_the_config_setting_was_set_to_true() { @@ -961,7 +1049,7 @@ class CartTest extends TestCase } /** @test */ - public function cart_hast_no_rounding_errors() + public function cart_has_no_rounding_errors() { $cart = $this->getCart(); @@ -1149,6 +1237,135 @@ class CartTest extends TestCase $this->assertEquals(2.50, $cart->tax(2)); // tax of 5 Bucks } + /** @test */ + public function it_does_allow_adding_cart_items_with_weight_and_options() + { + // https://github.com/bumbummen99/LaravelShoppingcart/pull/5 + $cart = $this->getCart(); + + $cartItem = $cart->add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']); + + $this->assertEquals(550, $cartItem->weight); + $this->assertTrue($cartItem->options->has('size')); + $this->assertEquals('large', $cartItem->options->size); + } + + /** @test */ + public function it_can_merge_without_dispatching_add_events() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + $cart = $this->getCartDiscount(50); + $cart->add(new BuyableProduct(1, 'Item', 10.00), 1); + $cart->add(new BuyableProduct(2, 'Item 2', 10.00), 1); + $cart->store('test'); + + Event::fakeFor(function () { + $cart2 = $this->getCart(); + $cart2->instance('test2'); + $cart2->setGlobalTax(0); + $cart2->setGlobalDiscount(0); + + $this->assertEquals('0', $cart2->countInstances()); + + $cart2->merge('test', null, null, false); + + Event::assertNotDispatched('cart.added'); + Event::assertDispatched('cart.merged'); + + $this->assertEquals('2', $cart2->countInstances()); + $this->assertEquals(20, $cart2->totalFloat()); + }); + } + + /** @test */ + public function it_can_merge_dispatching_add_events() + { + $this->artisan('migrate', [ + '--database' => 'testing', + ]); + + $cart = $this->getCartDiscount(50); + $cart->add(new BuyableProduct(1, 'Item', 10.00), 1); + $cart->add(new BuyableProduct(2, 'Item 2', 10.00), 1); + $cart->store('test'); + + Event::fakeFor(function () { + $cart2 = $this->getCart(); + $cart2->instance('test2'); + $cart2->setGlobalTax(0); + $cart2->setGlobalDiscount(0); + + $this->assertEquals('0', $cart2->countInstances()); + + $cart2->merge('test'); + + Event::assertDispatched('cart.added', 2); + Event::assertDispatched('cart.merged'); + $this->assertEquals('2', $cart2->countInstances()); + $this->assertEquals(20, $cart2->totalFloat()); + }); + } + + /** @test */ + public function it_use_correctly_rounded_values_for_totals_and_cart_summary() + { + $this->setConfigFormat(2, ',', ''); + + $cart = $this->getCartDiscount(6); + + $cartItem = $cart->add(new BuyableProduct(1, 'First item', 0.18929), 1000); + $cart->add(new BuyableProduct(2, 'Second item', 4.41632), 5); + $cart->add(new BuyableProduct(3, 'Third item', 0.37995), 25); + + $cart->setGlobalTax(22); + + // check total + $this->assertEquals('253,29', $cart->total()); + + // check that the sum of cart subvalues matches the total (in order to avoid cart summary to looks wrong) + $this->assertEquals($cart->totalFloat(), $cart->subtotalFloat() + $cart->taxFloat()); + } + + /** @test */ + public function it_use_gross_price_as_base_price() + { + $cart = $this->getCartDiscount(0); + config(['cart.gross_price' => true]); + + $cartItem = $cart->add(new BuyableProduct(1, 'First item', 100), 2); + + $cart->setGlobalTax(22); + + // check net price + $this->assertEquals(81.97, round($cartItem->priceNet, 2)); + } + + /** @test */ + public function it_use_gross_price_and_it_use_correctly_rounded_values_for_totals_and_cart_summary() + { + $this->setConfigFormat(2, ',', ''); + config(['cart.gross_price' => true]); + + $cart = $this->getCartDiscount(6); + + $cartItem = $cart->add(new BuyableProduct(1, 'First item', 0.23093), 1000); + $cart->add(new BuyableProduct(2, 'Second item', 5.38791), 5); + $cart->add(new BuyableProduct(3, 'Third item', 0.46354), 25); + + $cart->setGlobalTax(22); + + // check total + $this->assertEquals('254,12', $cart->total()); + + // check item price total + $this->assertEquals(190, $cartItem->priceTotal); + // check that the sum of cart subvalues matches the total (in order to avoid cart summary to looks wrong) + $this->assertEquals($cart->totalFloat(), $cart->subtotalFloat() + $cart->taxFloat()); + } + /** * Get an instance of the cart. * @@ -1165,12 +1382,14 @@ class CartTest extends TestCase /** * Get an instance of the cart with discount. * + * @param int $discount + * * @return \Gloudemans\Shoppingcart\Cart */ - private function getCartDiscount($discount = 0) + private function getCartDiscount($discount = 50) { $cart = $this->getCart(); - $cart->setGlobalDiscount(50); + $cart->setGlobalDiscount($discount); return $cart; }