Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Cart model customizable and easy to exchange #13

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Laravel 5.5+

## Table of contents
* [Configuration Options](#configuration-options)
* [Custom models](#custom-models)
* [Drivers](#drivers)
* Using the Package
* [Cart Management](#cart-management)
Expand All @@ -103,38 +104,92 @@ Laravel 5.5+

`cart_manager.php` file contains the following config options for the package:

1. **driver :** *(default: DatabaseDriver)*
1. **cart_model :** *(default: Cart)*
The [cart model](#custom-models) that should be used. You can use the existing model or create your own.

2. **cart_item_model :** *(default: CartItem)*
The [cart item model](#custom-models) that should be used. You can use the existing model or create your own.

3. **driver :** *(default: DatabaseDriver)*
The [driver](#drivers) that should be used to store and retrieve cart details. You can use existing ones or create your own.

2. **auth_guard :** *(default: web)*
4. **auth_guard :** *(default: web)*
The authentication guard that should be used to identify the logged in customer. This package can store carts for guest users as well as logged in users.

3. **shipping_charges :** *(default: 10)*
5. **shipping_charges :** *(default: 10)*
The amount that should be applied as shipping of the order.

4. **shipping_charges_threshold :** *(default: 100)*
6. **shipping_charges_threshold :** *(default: 100)*
The minimum order amount to avoid the shipping charges. Take a note that order amount is calculated as subtotal of the cart items - discount amount.

5. **tax_percentage :** *(default: 6%)*
7. **tax_percentage :** *(default: 6%)*
Tax is applied on subtotal of the cart items - discount amount + shipping charges and rounded to 2 decimals.

6. **round_off_to :** *(default: 0.05)*
8. **round_off_to :** *(default: 0.05)*
You may wish to round of the order amount to the nearest decimal point. Options are (0 or 0.05 or 0.1 or 0.5 or 1)

7. **cookie_name :** *(default: cart_identifier)*
9. **cookie_name :** *(default: cart_identifier)*
The name of the cookie that this package stores to identify the guests of the web app and store their cart data.

8. **cookie_lifetime :** *(default: 1 week)*
10. **cookie_lifetime :** *(default: 1 week)*
Number of minutes for which the cart cookie should be valid in customer's browser.

9. **LC_MONETARY :** *(default: en_US.UTF-8)*
11. **LC_MONETARY :** *(default: en_US.UTF-8)*
This option is used to display the various totals of the cart with a currency symbol. We use php's native [money_format()](//php.net/manual/en/function.money-format.php) function to display currency with amount.

10. **cart_data_validity :** *(default: 1 week) (Database driver only)*
12. **cart_data_validity :** *(default: 1 week) (Database driver only)*
You may wish to remove old/invalid cart data from the database. You can specify the validity period and run/schedule the [ClearCartDataCommand](#commands) for the same.

**[⬆ back to top](#table-of-contents)**

## Custom models

You can use your own custom Cart and CartItem model that extends the default.

Example for custom Cart model:

```php
namespace App;

use Freshbitsweb\LaravelCartManager\Models\Cart;

class CustomCart extends Cart
{
// Set table to carts table
protected $table = 'carts';

// User relation
public function user()
{
return $this->belongsTo(User::class, 'auth_user', 'id');
}

// Coupon relation (Coupon class is not included in this package)
public function coupon()
{
return $this->belongsTo(Coupon::class, 'coupon_id', 'id');
}
}
```

Example for custom CartItem model:

```php
namespace App;

use Freshbitsweb\LaravelCartManager\Models\CartItem;

class CustomCartItem extends CartItem
{
protected $table = 'cart_items';

public function cart()
{
return $this->belongsTo(CustomCart::class, 'cart_id', 'id');
}
}
```

## Drivers

You can set the driver that should be used to store and retrieve cart details in the `cart_manager.php` config file. You can use existing ones or create your own driver.
Expand Down
6 changes: 6 additions & 0 deletions config/cart_manager.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<?php

return [
// Custom cart class
'cart_model' => Freshbitsweb\LaravelCartManager\Models\Cart::class,

// Custom cart item class
'cart_item_model' => Freshbitsweb\LaravelCartManager\Models\CartItem::class,

// The driver that should be used to manage the cart (database/session/custom)
'driver' => Freshbitsweb\LaravelCartManager\Drivers\DatabaseDriver::class,

Expand Down
7 changes: 5 additions & 2 deletions src/CartManagerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Freshbitsweb\LaravelCartManager\Core\Cart;
use Freshbitsweb\LaravelCartManager\Contracts\CartDriver;
use Freshbitsweb\LaravelCartManager\Observers\CartObserver;
use Freshbitsweb\LaravelCartManager\Models\Cart as CartModel;
use Freshbitsweb\LaravelCartManager\Contracts\Cart as CartContract;
use Freshbitsweb\LaravelCartManager\Console\Commands\ClearCartDataCommand;

class CartManagerServiceProvider extends ServiceProvider
Expand All @@ -30,7 +30,7 @@ public function boot()
$this->commands([ClearCartDataCommand::class]);
}

CartModel::observe(CartObserver::class);
resolve(config('cart_manager.cart_model'))::observe(CartObserver::class);
}

/**
Expand All @@ -48,6 +48,9 @@ public function register()
// Bind the driver with contract
$this->app->bind(CartDriver::class, $this->app['config']['cart_manager']['driver']);

// Bind the custom cart model with contract
$this->app->bind(CartContract::class, $this->app['config']['cart_manager']['cart_model']);

// Bind the cart class
$this->app->bind(Cart::class, function ($app) {
return new Cart($app->make(CartDriver::class));
Expand Down
6 changes: 2 additions & 4 deletions src/Console/Commands/ClearCartDataCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Schema;
use Freshbitsweb\LaravelCartManager\Models\Cart;
use Freshbitsweb\LaravelCartManager\Models\CartItem;

class ClearCartDataCommand extends Command
{
Expand Down Expand Up @@ -40,14 +38,14 @@ public function handle()

$validHours = config('cart_manager.cart_data_validity');

$query = Cart::where('updated_at', '<', Carbon::now()->subHours($validHours));
$query = resolve(config('cart_manager.cart_model'))::where('updated_at', '<', Carbon::now()->subHours($validHours));

$cartIds = $query->get(['id']);

if ($cartIds->isNotEmpty()) {
$cartsDeleted = $query->delete();

CartItem::whereIn('cart_id', $cartIds->pluck('id')->toArray())->delete();
resolve(config('cart_manager.cart_item_model'))::whereIn('cart_id', $cartIds->pluck('id')->toArray())->delete();

$this->info("$cartsDeleted older cart record(s) removed from the table.");
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/Contracts/Cart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Freshbitsweb\LaravelCartManager\Contracts;

interface Cart
{
/**
* Get the items of the cart.
*/
public function items();
}
11 changes: 11 additions & 0 deletions src/Contracts/CartItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Freshbitsweb\LaravelCartManager\Contracts;

interface CartItem
{
/**
* Get the cart.
*/
public function cart();
}
23 changes: 11 additions & 12 deletions src/Drivers/DatabaseDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Freshbitsweb\LaravelCartManager\Models\Cart;
use Freshbitsweb\LaravelCartManager\Models\CartItem;
use Freshbitsweb\LaravelCartManager\Contracts\Cart;
use Freshbitsweb\LaravelCartManager\Contracts\CartDriver;

class DatabaseDriver implements CartDriver
Expand All @@ -20,14 +19,14 @@ public function getCartData()
{
$selectColumns = ['id', 'subtotal', 'discount', 'discount_percentage', 'coupon_id', 'shipping_charges', 'net_total', 'tax', 'total', 'round_off', 'payable'];

$cartData = Cart::with($this->cartItemsQuery())
$cartData = resolve(config('cart_manager.cart_model'))::with($this->cartItemsQuery())
->where($this->cartIdentifier())
->first($selectColumns)
;

// If there is no cart record for the logged in customer, try with cookie identifier
if (! $cartData && Auth::guard(config('cart_manager.auth_guard'))->check()) {
$cartData = Cart::with($this->cartItemsQuery())
$cartData = resolve(config('cart_manager.cart_model'))::with($this->cartItemsQuery())
->where($this->getCookieElement())
->first($selectColumns)
;
Expand Down Expand Up @@ -68,7 +67,7 @@ protected function cartItemsQuery()
protected function assignCustomerToCartRecord()
{
// Assign the logged in customer to the cart record
Cart::where($this->getCookieElement())->update([
resolve(config('cart_manager.cart_model'))::where($this->getCookieElement())->update([
'auth_user' => Auth::guard(config('cart_manager.auth_guard'))->id(),
]);
}
Expand Down Expand Up @@ -101,7 +100,7 @@ public function updateCart($cartId, $cartData)
{
$cartData = $this->arraySnakeCase($cartData);

Cart::where('id', $cartId)->update($cartData);
resolve(config('cart_manager.cart_model'))::where('id', $cartId)->update($cartData);
}

/**
Expand All @@ -116,7 +115,7 @@ public function addCartItem($cartId, $cartItem)
$cartItem = $this->arraySnakeCase($cartItem);

$cartItem['cart_id'] = $cartId;
CartItem::create($cartItem);
resolve(config('cart_manager.cart_item_model'))::create($cartItem);
}

/**
Expand All @@ -127,7 +126,7 @@ public function addCartItem($cartId, $cartItem)
*/
public function removeCartItem($cartItemId)
{
CartItem::destroy($cartItemId);
resolve(config('cart_manager.cart_item_model'))::destroy($cartItemId);
}

/**
Expand All @@ -140,7 +139,7 @@ protected function storeCartDetails($cartData)
{
$cartData = $this->arraySnakeCase($cartData);

$cart = Cart::updateOrCreate(
$cart = resolve(config('cart_manager.cart_model'))::updateOrCreate(
$this->cartIdentifier(),
array_merge($cartData, $this->getCookieElement())
);
Expand Down Expand Up @@ -199,7 +198,7 @@ protected function getCookieElement()
*/
public function setCartItemQuantity($cartItemId, $newQuantity)
{
CartItem::where('id', $cartItemId)->update(['quantity' => $newQuantity]);
resolve(config('cart_manager.cart_item_model'))::where('id', $cartItemId)->update(['quantity' => $newQuantity]);
}

/**
Expand All @@ -209,7 +208,7 @@ public function setCartItemQuantity($cartItemId, $newQuantity)
*/
public function clearData()
{
$cart = Cart::where($this->cartIdentifier())->first();
$cart = resolve(config('cart_manager.cart_model'))::where($this->cartIdentifier())->first();

if ($cart) {
$cart->delete();
Expand Down Expand Up @@ -242,7 +241,7 @@ private function arraySnakeCase($array)
public function updateItemsData($items)
{
$items->each(function ($item) {
CartItem::where('id', $item->id)->update([
resolve(config('cart_manager.cart_item_model'))::where('id', $item->id)->update([
'name' => $item->name,
'price' => $item->price,
'image' => $item->image,
Expand Down
1 change: 1 addition & 0 deletions src/Drivers/SessionDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Freshbitsweb\LaravelCartManager\Drivers;

use Freshbitsweb\LaravelCartManager\Contracts\Cart;
use Freshbitsweb\LaravelCartManager\Contracts\CartDriver;

class SessionDriver implements CartDriver
Expand Down
5 changes: 3 additions & 2 deletions src/Models/Cart.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace Freshbitsweb\LaravelCartManager\Models;

use Illuminate\Database\Eloquent\Model;
use Freshbitsweb\LaravelCartManager\Contracts\Cart as CartContract;

class Cart extends Model
class Cart extends Model implements CartContract
{
/**
* The attributes that are mass assignable.
Expand All @@ -21,6 +22,6 @@ class Cart extends Model
*/
public function items()
{
return $this->hasMany(CartItem::class);
return $this->hasMany(config('cart_manager.cart_item_model'));
}
}
5 changes: 5 additions & 0 deletions src/Models/CartItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@ public function toArray()
'quantity' => $this->quantity,
];
}

public function cart()
{
return $this->belongsTo();
}
}
6 changes: 3 additions & 3 deletions src/Observers/CartObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

namespace Freshbitsweb\LaravelCartManager\Observers;

use Freshbitsweb\LaravelCartManager\Models\Cart;
use Freshbitsweb\LaravelCartManager\Contracts\Cart as CartContract;

class CartObserver
{
/**
* Listen to the Cart deleting event.
*
* @param \Freshbitsweb\LaravelCartManager\Models\Cart $cart
* @param \Freshbitsweb\LaravelCartManager\Contracts\Cart $cart
* @return void
*/
public function deleting(Cart $cart)
public function deleting(CartContract $cart)
{
$cart->items()->delete();
}
Expand Down
3 changes: 1 addition & 2 deletions tests/ClearCartDataCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Freshbitsweb\LaravelCartManager\Test;

use Carbon\Carbon;
use Freshbitsweb\LaravelCartManager\Models\Cart;

class ClearCartDataCommandTest extends TestCase
{
Expand All @@ -26,7 +25,7 @@ public function clear_cart_command_removes_invalid_data()

$validHours = config('cart_manager.cart_data_validity') + 1;

Cart::where('id', 1)->update(['updated_at' => Carbon::now()->subHours($validHours)]);
resolve(config('cart_manager.cart_model'))::where('id', 1)->update(['updated_at' => Carbon::now()->subHours($validHours)]);

$this->artisan('lcm_carts:clear_old');

Expand Down