Merge branch 'master' into fix_cart_database_timestamps

This commit is contained in:
Patrick Henninger
2020-05-08 20:39:53 +02:00
9 changed files with 1727 additions and 81 deletions

View File

@@ -7,7 +7,7 @@
[![Latest Unstable Version](https://poser.pugx.org/bumbummen99/shoppingcart/v/unstable)](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) [![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 ## Installation
@@ -24,6 +24,7 @@ Now you're ready to start using the shoppingcart in your application.
## Table of Contents ## Table of Contents
Look at one of the following topics to learn more about LaravelShoppingcart Look at one of the following topics to learn more about LaravelShoppingcart
* [Important note](#important-note)
* [Usage](#usage) * [Usage](#usage)
* [Collections](#collections) * [Collections](#collections)
* [Instances](#instances) * [Instances](#instances)
@@ -33,6 +34,14 @@ Look at one of the following topics to learn more about LaravelShoppingcart
* [Events](#events) * [Events](#events)
* [Example](#example) * [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 ## Usage
The shoppingcart gives you the following methods to use: 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. 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 ```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. 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 ```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.** **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() ### 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 ```php
Cart::weight(); Cart::weight();
@@ -245,13 +254,15 @@ You can set the default number format in the config file.
### Cart::initial() ### 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 ```php
Cart::initial(); 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 ```php
Cart::initial($decimals, $decimalSeparator, $thousandSeparator); 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. 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`** **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() ### 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. 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 ```php
Cart::setGlobalDiscount(21); Cart::setGlobalTax(21);
$cart->setGlobalDiscount(21); $cart->setGlobalTax(21);
``` ```
### Cart::setGlobalDiscount($discountRate) ### 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. 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 ```php
Cart::setGlobalDiscount(21); Cart::setGlobalDiscount(50);
$cart->setGlobalDiscount(21); $cart->setGlobalDiscount(50);
``` ```
### Cart::setDiscount($rowId, $taxRate) ### Cart::setDiscount($rowId, $taxRate)
@@ -339,7 +366,7 @@ use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable { 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'); Cart::instance('wishlist')->restore('username');
### Merge the cart ### 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'. // 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 ## 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.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.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.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.stored | When the content of a cart was stored. | - |
| cart.restored | When the content of a cart was restored. | - | | cart.restored | When the content of a cart was restored. | - |
| cart.erased | When the content of a cart was erased. | - |
## Example ## Example

628
README_Idn.md Normal file
View File

@@ -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
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
use Gloudemans\Shoppingcart\CanBeBought;
}
```
Jika sifat tidak berfungsi pada model atau Anda tidak dapat memetakan bidang secara manual model harus menerapkan metode antarmuka `Buy Able`. Untuk melakukannya, ia harus mengimplementasikan fungsi-fungsi tersebut:
```php
public function getBuyableIdentifier(){
return $this->id;
}
public function getBuyableDescription(){
return $this->name;
}
public function getBuyablePrice(){
return $this->price;
}
public function getBuyableWeight(){
return $this->weight;
}
```
Contoh:
```php
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
public function getBuyableIdentifier($options = null) {
return $this->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.
```
<?php
namespace App;
...
use Illuminate\Foundation\Auth\User as Authenticatable;
use Gloudemans\Shoppingcart\Contracts\InstanceIdentifier;
class User extends Authenticatable implements InstanceIdentifier
{
...
/**
* Get the unique identifier to load the Cart from
*
* @return int|string
*/
public function getInstanceIdentifier($options = null)
{
return $this->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.
<table>
<thead>
<tr>
<th>Product</th>
<th>Qty</th>
<th>Price</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach(Cart::content() as $row) :?>
<tr>
<td>
<p><strong><?php echo $row->name; ?></strong></p>
<p><?php echo ($row->options->has('size') ? $row->options->size : ''); ?></p>
</td>
<td><input type="text" value="<?php echo $row->qty; ?>"></td>
<td>$<?php echo $row->price; ?></td>
<td>$<?php echo $row->total; ?></td>
</tr>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan="2">&nbsp;</td>
<td>Subtotal</td>
<td><?php echo Cart::subtotal(); ?></td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>Tax</td>
<td><?php echo Cart::tax(); ?></td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>Total</td>
<td><?php echo Cart::total(); ?></td>
</tr>
</tfoot>
</table>
```

626
README_uk-UA.md Normal file
View File

@@ -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
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
use Gloudemans\Shoppingcart\CanBeBought;
}
```
Якщо трейт не працює на вашій моделі або ви хочете вручну перенести (мапувати) поля, модель повинна запустити методи інтерфейсу `Buyable`. Для цього, модель повинна імплементувати наступні функції:
```php
public function getBuyableIdentifier(){
return $this->id;
}
public function getBuyableDescription(){
return $this->name;
}
public function getBuyablePrice(){
return $this->price;
}
public function getBuyableWeight(){
return $this->weight;
}
```
Приклад:
```php
<?php
namespace App\Models;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Buyable {
public function getBuyableIdentifier($options = null) {
return $this->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) для неї. Така дія також дозволить напряму встановлювати глобальну знижку.
```
<?php
namespace App;
...
use Illuminate\Foundation\Auth\User as Authenticatable;
use Gloudemans\Shoppingcart\Contracts\InstanceIdentifier;
class User extends Authenticatable implements InstanceIdentifier
{
...
/**
* Get the unique identifier to load the Cart from
*
* @return int|string
*/
public function getInstanceIdentifier($options = null)
{
return $this->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.
<table>
<thead>
<tr>
<th>Product</th>
<th>Qty</th>
<th>Price</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach(Cart::content() as $row) :?>
<tr>
<td>
<p><strong><?php echo $row->name; ?></strong></p>
<p><?php echo ($row->options->has('size') ? $row->options->size : ''); ?></p>
</td>
<td><input type="text" value="<?php echo $row->qty; ?>"></td>
<td>$<?php echo $row->price; ?></td>
<td>$<?php echo $row->total; ?></td>
</tr>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan="2">&nbsp;</td>
<td>Subtotal</td>
<td><?php echo Cart::subtotal(); ?></td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>Tax</td>
<td><?php echo Cart::tax(); ?></td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>Total</td>
<td><?php echo Cart::total(); ?></td>
</tr>
</tfoot>
</table>
```

View File

@@ -6,18 +6,20 @@
"authors": [ "authors": [
{ {
"name": "Rob Gloudemans", "name": "Rob Gloudemans",
"email": "info@robgloudemans.nl" "email": "info@robgloudemans.nl",
"homepage": "http://robgloudemans.nl/"
}, },
{ {
"name": "Patrick Henninger", "name": "Patrick Henninger",
"email": "privat@skyraptor.eu" "email": "privat@skyraptor.eu",
"homepage": "https://skyraptor.eu/"
} }
], ],
"require": { "require": {
"illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", "illuminate/support": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0",
"illuminate/session": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", "illuminate/session": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0",
"illuminate/events": "5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*|5.7.*", "illuminate/events": "5.4.*||5.5.*||5.6.*||5.7.*||5.8.*||^6.0||^7.0",
"nesbot/carbon": "^1.26.3 || ^2.0" "nesbot/carbon": "~1.20||^1.26.3||^2.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~5.0||~6.0||~7.0||~8.0", "phpunit/phpunit": "~5.0||~6.0||~7.0||~8.0",

View File

@@ -61,7 +61,7 @@ class Cart
private $discount = 0; private $discount = 0;
/** /**
* Defines the discount percentage. * Defines the tax rate.
* *
* @var float * @var float
*/ */
@@ -120,11 +120,12 @@ class Cart
* @param mixed $name * @param mixed $name
* @param int|float $qty * @param int|float $qty
* @param float $price * @param float $price
* @param float $weight
* @param array $options * @param array $options
* *
* @return \Gloudemans\Shoppingcart\CartItem * @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)) { if ($this->isMulti($id)) {
return array_map(function ($item) { return array_map(function ($item) {
@@ -132,7 +133,7 @@ class Cart
}, $id); }, $id);
} }
$cartItem = $this->createCartItem($id, $name, $qty, $price, $options); $cartItem = $this->createCartItem($id, $name, $qty, $price, $weight, $options);
return $this->addCartItem($cartItem); return $this->addCartItem($cartItem);
} }
@@ -140,13 +141,14 @@ class Cart
/** /**
* Add an item to the cart. * Add an item to the cart.
* *
* @param \Gloudemans\Shoppingcart\CartItem $item Item to add 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 $keepDiscount Keep the discount rate of the Item
* @param bool $keepTax Keep the Tax rate of the Item * @param bool $keepTax Keep the Tax rate of the Item
* @param bool $dispatchEvent
* *
* @return \Gloudemans\Shoppingcart\CartItem The CartItem * @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) { if (!$keepDiscount) {
$item->setDiscountRate($this->discount); $item->setDiscountRate($this->discount);
@@ -164,7 +166,9 @@ class Cart
$content->put($item->rowId, $item); $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); $this->session->put($this->instance, $content);
@@ -194,6 +198,8 @@ class Cart
$content = $this->getContent(); $content = $this->getContent();
if ($rowId !== $cartItem->rowId) { if ($rowId !== $cartItem->rowId) {
$itemOldIndex = $content->keys()->search($rowId);
$content->pull($rowId); $content->pull($rowId);
if ($content->has($cartItem->rowId)) { if ($content->has($cartItem->rowId)) {
@@ -207,7 +213,13 @@ class Cart
return; return;
} else { } 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); $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 * @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 int $decimals
* @param string $decimalPoint * @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 * @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 int $decimals
* @param string $decimalPoint * @param string $decimalPoint
@@ -429,6 +441,32 @@ class Cart
return $this->numberFormat($this->initialFloat(), $decimals, $decimalPoint, $thousandSeperator); 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. * 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->createdAt = Carbon::parse(data_get($stored, 'created_at'));
$this->updatedAt = Carbon::parse(data_get($stored, 'updated_at')); $this->updatedAt = Carbon::parse(data_get($stored, 'updated_at'));
$this->getConnection()->table($this->getTableName()) $this->getConnection()->table($this->getTableName())->where('identifier', $identifier)->delete();
->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 mixed $identifier Identifier of the Cart to merge with.
* @param bool $keepDiscount Keep the discount of the CartItems. * @param bool $keepDiscount Keep the discount of the CartItems.
* @param bool $keepTax Keep the tax of the CartItems. * @param bool $keepTax Keep the tax of the CartItems.
* @param bool $dispatchAdd Flag to dispatch the add events.
* *
* @return bool * @return bool
*/ */
public function merge($identifier, $keepDiscount = false, $keepTax = false) public function merge($identifier, $keepDiscount = false, $keepTax = false, $dispatchAdd = true)
{ {
if (!$this->storedCartWithIdentifierExists($identifier)) { if (!$this->storedCartWithIdentifierExists($identifier)) {
return false; return false;
@@ -668,9 +728,11 @@ class Cart
$storedContent = unserialize($stored->content); $storedContent = unserialize($stored->content);
foreach ($storedContent as $cartItem) { foreach ($storedContent as $cartItem) {
$this->addCartItem($cartItem, $keepDiscount, $keepTax); $this->addCartItem($cartItem, $keepDiscount, $keepTax, $dispatchAdd);
} }
$this->events->dispatch('cart.merged');
return true; return true;
} }
@@ -716,11 +778,12 @@ class Cart
* @param mixed $name * @param mixed $name
* @param int|float $qty * @param int|float $qty
* @param float $price * @param float $price
* @param float $weight
* @param array $options * @param array $options
* *
* @return \Gloudemans\Shoppingcart\CartItem * @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) { if ($id instanceof Buyable) {
$cartItem = CartItem::fromBuyable($id, $qty ?: []); $cartItem = CartItem::fromBuyable($id, $qty ?: []);
@@ -730,7 +793,7 @@ class Cart
$cartItem = CartItem::fromArray($id); $cartItem = CartItem::fromArray($id);
$cartItem->setQuantity($id['qty']); $cartItem->setQuantity($id['qty']);
} else { } else {
$cartItem = CartItem::fromAttributes($id, $name, $price, $options); $cartItem = CartItem::fromAttributes($id, $name, $price, $weight, $options);
$cartItem->setQuantity($qty); $cartItem->setQuantity($qty);
} }

View File

@@ -5,7 +5,20 @@ namespace Gloudemans\Shoppingcart;
use Gloudemans\Shoppingcart\Contracts\Buyable; use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable; 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 class CartItem implements Arrayable, Jsonable
{ {
/** /**
@@ -57,6 +70,13 @@ class CartItem implements Arrayable, Jsonable
*/ */
public $options; public $options;
/**
* The tax rate for the cart item.
*
* @var int|float
*/
public $taxRate = 0;
/** /**
* The FQN of the associated model. * The FQN of the associated model.
* *
@@ -64,13 +84,6 @@ class CartItem implements Arrayable, Jsonable
*/ */
private $associatedModel = null; private $associatedModel = null;
/**
* The tax rate for the cart item.
*
* @var int|float
*/
private $taxRate = 0;
/** /**
* The discount rate for the cart item. * The discount rate for the cart item.
* *
@@ -84,6 +97,7 @@ class CartItem implements Arrayable, Jsonable
* @param int|string $id * @param int|string $id
* @param string $name * @param string $name
* @param float $price * @param float $price
* @param float $weight
* @param array $options * @param array $options
*/ */
public function __construct($id, $name, $price, $weight = 0, 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)) { if (strlen($price) < 0 || !is_numeric($price)) {
throw new \InvalidArgumentException('Please supply a valid 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->id = $id;
$this->name = $name; $this->name = $name;
@@ -248,6 +265,20 @@ class CartItem implements Arrayable, Jsonable
return $this->numberFormat($this->discountTotal, $decimals, $decimalPoint, $thousandSeperator); 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. * Set the quantity for this cart item.
* *
@@ -274,7 +305,6 @@ class CartItem implements Arrayable, Jsonable
$this->id = $item->getBuyableIdentifier($this->options); $this->id = $item->getBuyableIdentifier($this->options);
$this->name = $item->getBuyableDescription($this->options); $this->name = $item->getBuyableDescription($this->options);
$this->price = $item->getBuyablePrice($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) public function updateFromArray(array $attributes)
{ {
$this->id = array_get($attributes, 'id', $this->id); $this->id = Arr::get($attributes, 'id', $this->id);
$this->qty = array_get($attributes, 'qty', $this->qty); $this->qty = Arr::get($attributes, 'qty', $this->qty);
$this->name = array_get($attributes, 'name', $this->name); $this->name = Arr::get($attributes, 'name', $this->name);
$this->price = array_get($attributes, 'price', $this->price); $this->price = Arr::get($attributes, 'price', $this->price);
$this->weight = array_get($attributes, 'weight', $this->weight); $this->weight = Arr::get($attributes, 'weight', $this->weight);
$this->priceTax = $this->price + $this->tax; $this->options = new CartItemOptions(Arr::get($attributes, 'options', $this->options));
$this->options = new CartItemOptions(array_get($attributes, 'options', $this->options));
$this->rowId = $this->generateRowId($this->id, $this->options->all()); $this->rowId = $this->generateRowId($this->id, $this->options->all());
} }
@@ -351,39 +380,69 @@ class CartItem implements Arrayable, Jsonable
if (property_exists($this, $attribute)) { if (property_exists($this, $attribute)) {
return $this->{$attribute}; return $this->{$attribute};
} }
$decimals = config('cart.format.decimals', 2);
switch ($attribute) { 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': case 'model':
if (isset($this->associatedModel)) { if (isset($this->associatedModel)) {
return with(new $this->associatedModel())->find($this->id); return with(new $this->associatedModel())->find($this->id);
} }
case 'modelFQCN': case 'modelFQCN':
if (isset($this->associatedModel)) { if (isset($this->associatedModel)) {
return $this->associatedModel; return $this->associatedModel;
} }
case 'weightTotal':
return round($this->weight * $this->qty, $decimals);
}
default: if (config('cart.gross_price')) {
return; 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) 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); return new self($attributes['id'], $attributes['name'], $attributes['price'], $attributes['weight'], $options);
} }

View File

@@ -2,6 +2,18 @@
return [ 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 | Default tax rate

View File

@@ -30,6 +30,6 @@ class ShoppingcartServiceProvider extends ServiceProvider
$this->publishes([ $this->publishes([
realpath(__DIR__.'/Database/migrations') => $this->app->databasePath().'/migrations', realpath(__DIR__.'/Database/migrations') => $this->app->databasePath().'/migrations',
]); ], 'migrations');
} }
} }

View File

@@ -264,6 +264,19 @@ class CartTest extends TestCase
$cart->add(1, 'Some title', 1, 'invalid'); $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 */ /** @test */
public function it_will_update_the_cart_if_the_item_already_exists_in_the_cart() 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); $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 */ /** @test */
public function it_can_remove_an_item_from_the_cart() 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, ',', '.')); $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 */ /** @test */
public function it_can_return_the_subtotal() public function it_can_return_the_subtotal()
{ {
@@ -917,6 +959,52 @@ class CartTest extends TestCase
$this->assertEquals(11.90, $cartItem->total(2)); $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 */ /** @test */
public function it_will_destroy_the_cart_when_the_user_logs_out_and_the_config_setting_was_set_to_true() 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 */ /** @test */
public function cart_hast_no_rounding_errors() public function cart_has_no_rounding_errors()
{ {
$cart = $this->getCart(); $cart = $this->getCart();
@@ -1149,6 +1237,135 @@ class CartTest extends TestCase
$this->assertEquals(2.50, $cart->tax(2)); // tax of 5 Bucks $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. * Get an instance of the cart.
* *
@@ -1165,12 +1382,14 @@ class CartTest extends TestCase
/** /**
* Get an instance of the cart with discount. * Get an instance of the cart with discount.
* *
* @param int $discount
*
* @return \Gloudemans\Shoppingcart\Cart * @return \Gloudemans\Shoppingcart\Cart
*/ */
private function getCartDiscount($discount = 0) private function getCartDiscount($discount = 50)
{ {
$cart = $this->getCart(); $cart = $this->getCart();
$cart->setGlobalDiscount(50); $cart->setGlobalDiscount($discount);
return $cart; return $cart;
} }