This is the cart module for Vanilo.
Supports: Laravel 5.4 - 5.8; PHP 7.1 - 7.3
(As Standalone Component)
composer require vanilo/cart
php artisan vendor:publish --provider=Konekt\Concord\ConcordServiceProvider
- Add
Vanilo\Cart\Providers\ModuleServiceProvider::class
to modules inconfig/concord.php
php artisan migrate
The Cart facade is automatically registered with Laravel 5.5+
For Laravel 5.4 you need to manually register it in config/app.php:
'aliases' => [
// ...
'Cart' => Vanilo\Cart\Facades\Cart::class
],
The Cart is available via the Cart
facade.
The facade actually returns a CartManager
object which exposes the cart API to be used by
applications. It encapsulates the Cart
eloquent model, that also has CartItem
children.
The CartManager
was introduced in order to take care of:
- Relation of carts and the session and/or the user
- Only create carts in the db if it's necessary (aka. don't pollute DB with a cart for every single visitor/hit)
- Provide a straightforward API
As written above, the cart manager only creates a cart entry (db) if it's needed. Thus you can check whether a cart exists or not.
A non-existing cart means that the current session has no cart model/db record associated.
Cart::exists()
returns whether a cart exists for the current session.
Cart::doesNotExist()
is the opposite of exists()
;)
Example:
var_dump(Cart::exists());
// false
Cart::addItem($product);
var_dump(Cart::exists());
// true
Cart::itemCount()
returns the number of items in the cart.
It also returns 0 for non-existing carts.
To have a cleaner code, there are two methods to check if cart is empty:
Cart::isEmpty()
Cart::isNotEmpty()
Their result is based on the itemCount()
method.
You can add product to the cart with the Cart::addItem()
method.
The item is a Vanilo product by default, which can be extended.
You aren't limited to using Vanilo products, you can add any Eloquent model to the cart as "product" that implements the Buyable interface from the vanilo/contracts package.
Example:
$product = Product::findBySku('B01J4919TI'); //Salmon Fish Sushi Pillow -- check out on amazon :D
Cart::addItem($product); //Adds one product to the cart
echo Cart::itemCount();
// 1
// The second parameter is the quantity
Cart::addItem($product, 2);
echo Cart::itemCount();
// 3
First, you need to add your custom fields to cart_items
(preferably using migrations).
Example:
// The Migration:
Schema::table('cart_items', function (Blueprint $table) {
$table->integer('weight')->nullable();
});
Passing fields manually:
Cart::addItem($product, 1, [ 'attributes' => [
'weight' => 3
]
]);
Permanent extra fields
It is possible to configure the cart to always copy some extra attributes from product (Buyable) to cart items:
//config/vanilo.php:
//...
'cart' => [
'extra_product_attributes' => ['weight']
]
//...
Having this configuration the value of weight
attribute gets copied automatically to cart item:
$product = Product::create([
'name' => 'Mesh Panel Toning Trainers',
'sku' => 'LS-170161',
'price' => 34.99,
'weight' => 9
]);
$item = Cart::addItem($product);
echo $item->weight;
// 9
The CartItem
defines a
polymorphic relationship
to the Buyable object named product
.
So you have a reference to the item's product:
$product = \App\Product::find(203);
$cartItem = Cart::addItem($product);
echo $cartItem->product->id;
// 203
echo get_class($cartItem->product);
// "App\Product"
$course = \App\Course::findBySku('REACT-001');
$cartItem = Cart::addItem($course);
echo $cartItem->product->sku;
// "REACT-001"
echo get_class($cartItem->product);
// "App\Course"
The
Buyable
interface is located in the Vanilo Contracts package.
You can add any Eloquent model to the cart that implements the Buyable
interface.
Buyable classes must implement these methods:
function getId(); // the id of the entry
function name(); // the name to display in the cart
function getPrice(); // the price
function morphTypeName(); // the type name to store in the db
In order to decouple the database from the application's internal structure, it is possible to not
save the Buyable's full class name in the DB. When the cart associates a product (Buyable) with a
cart item, it fetches the type name from the Buyable::morphTypeName()
method.
The morphTypeName()
method, can either return the full class name (Eloquent's default behavior),
or some shorter version like:
Full Class Name | Short Version (Saved In DB) |
---|---|
Vanilo\Product\Models\Product | product |
App\Course | course |
If your not using the FQCN, then you have to add the mapping during boot time:
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'product' => 'Vanilo\Product\Models\Product',
'course' => 'App\Course',
]);
For more information refer to the Polymorphic Relation section in the Laravel Documentation.
There are two methods for removing specific items:
Cart::removeProduct($product)
Cart::removeItem($cartItem)
removeProduct()
example:
$product = Product::find(12345);
Cart::removeProduct($product); // Finds the item based on the product, and removes it
removeItem()
example:
//Remove the first item from the cart
$item = Cart::model()->items->first();
Cart::removeItem($item);
Cart has a state field (added in v0.4) which can be by default one of these values:
- active: the cart is active;
- checkout: the cart is being checked out;
- completed: the cart has been checked out (order was created);
- abandoned: the cart hasn't been touched for a while;
If you want to modify the possible states of the cart, follow the instructions for Customizing Enums;
The state field is not auto-managed, thus you explicitly have to update it's value.
The cart can be assigned to user automatically and/or manually.
The cart's user model is not bound to any specific class (like
App\User
), but to Laravel's authentication system.See the
auth.providers.users.model
config value for more details.
use Vanilo\Cart\Facades\Cart;
// Assign the currently logged in user:
Cart::setUser(Auth::user());
// Assign an arbitrary user:
$user = \App\User::find(1);
Cart::setUser($user);
// User id can be passed as well:
Cart::setUser(1);
// Retrieve the cart's assigned user:
$user = Cart::getUser();
// Remove the user association:
Cart::removeUser();
The cart (by default) automatically handles cart+user associations in the following cases:
Event | State | Action |
---|---|---|
User login/authentication | Cart exists | Associate cart with user |
User logout & lockout | Cart exists | Dissociate cart from user |
New cart gets created | User is logged in | Associate cart with user |
To prevent this behavior, set the vanilo.cart.auto_assign_user
config value to false:
// config/vanilo.php
return [
'cart' => [
'auto_assign_user' => false
]
];
It is possible to keep the cart for users after logout and restore it after successful login.
This feature is disabled by default. To achive this behavior, set the
vanilo.cart.preserve_for_user
config value to true
Event | State | Action |
---|---|---|
User login/authentication | Cart for this session doesn't exist, user has a saved active cart | Restore the cart |
User login/authentication | Cart for this session exists | The current cart will be kept |
User logout & lockout | Cart for this session exists | Cart will be kept for the user in db |
The item total can be accessed with the total()
method or the total
property.
The cart total can be accessed with the Cart::total()
method:
use Vanilo\Cart\Facades\Cart;
use App\Product;
$productX = Product::create(['name' => 'X', 'price' => 100]);
$productY = Product::create(['name' => 'Y', 'price' => 70]);
$item1 = Cart::addItem($productX, 3);
echo $item1->total();
// 300
echo $item1->total;
// 300
$item2 = Cart::addItem($productY, 2);
echo $item2->total();
// 140
echo Cart::total();
// 440
The Cart::clear()
method removes everything from the cart, but it doesn't destroy it, unless the
vanilo.cart.auto_destroy
config option is true.
So the entry in the cart
table will remain, and it will be assigned to the current session later
on.
In case you want to get rid of the cart use the Cart::destroy()
method.
It clears the cart, removes the record from the cart
table, and unsets the association with the
current session.
Thus, using destroy, you'll have a non-existent cart.
The Cart::forget()
method disconnects the current user/session from the current cart, but the cart
will be kept intact in the database.
Future methods for v0.6:
//Cart::addCoupon(obj|int|str)
//Cart::removeCoupon(obj|int|str)