From ec99aa05630e53bba700aea2e9c1e6b7665edfc6 Mon Sep 17 00:00:00 2001 From: Joel Male Date: Tue, 25 Feb 2025 14:53:49 +1000 Subject: [PATCH] feat: clean up and starting documentation --- README.md | 26 ++++++++++++- config/config.php | 22 +++++------ src/Cart.php | 50 +++++++++++++++++-------- src/CartServiceProvider.php | 21 ++++------- src/CartSession.php | 17 ++++----- tests/Helpers/ConfigConditionsMock.php | 3 +- tests/Helpers/ConfigMock.php | 1 + tests/Helpers/ConfigMockOtherFormat.php | 2 +- tests/Unit/CartTestEventsTest.php | 42 ++++++++++----------- 9 files changed, 111 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 222964e..3c5fc6e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ ## Table of Contents - [Configuration](#configuration) - [Basic Usage](#basic-usage) - [Conditions](#conditions) - - [Items](#items) + - [Cart Items](#cart-items) + - [Database Support](#database-support) - [Credits](#-credits) - [License](#-license) @@ -684,6 +685,29 @@ ### Cart Items $item->getPriceSumWithConditions(); ``` +### Database Support + +Sometimes you might want to store the cart in the database so that the cart can be retrieved even after the user logs out or closes the browser. + +You may also find yourself wanting to add cart timeouts and support for multiple computers. + +To get started, you'll need to create a new model for your Cart, the package requires only 3 columns, but you're free to extend this as you wish: + +```php +$table->string('session_id'); +$table->text('items'); +$table->text('conditions'); +``` + +Then add some json casts to your model: + +```php +protected $casts = [ + 'items' => 'array', + 'conditions' => 'array', +]; +``` + ## 🫡 Credits This package was orignally created by [darryldecode](https://github.com/darryldecode) but has since seen almost no updates. I have decided to take the old package and transform it into a new package with new features and updates. diff --git a/config/config.php b/config/config.php index 56412ae..cebf9f3 100644 --- a/config/config.php +++ b/config/config.php @@ -14,18 +14,18 @@ /* * --------------------------------------------------------------- - * Persistence + * Storage * --------------------------------------------------------------- */ - 'storage' => null, - 'storage_id' => null, - 'storage_items' => null, - 'storage_conditions' => null, + 'driver' => 'session', - /* - * --------------------------------------------------------------- - * Events - * --------------------------------------------------------------- - */ - 'events' => null, + 'storage' => [ + 'session', + 'database' => [ + 'model' => '', + 'id' => '', + 'items' => '', + 'conditions' => '', + ], + ], ]; diff --git a/src/Cart.php b/src/Cart.php index 71bd7dd..24973ad 100644 --- a/src/Cart.php +++ b/src/Cart.php @@ -27,15 +27,38 @@ class Cart protected $decimals; protected $decPoint; - public function __construct($session, $events, $instanceName, $sessionKey, $config) + public function __construct($sessionStorage, $events, $instanceName, $sessionKey, $config) { + $this->validateConfig($config); + $this->events = $events; - $this->session = new CartSession($session, $sessionKey, $config); + $this->session = new CartSession($sessionStorage, $sessionKey, $config); $this->instanceName = $instanceName; $this->config = $config; $this->currentItemId = null; - $this->fireEvent('created'); + $this->fireEvent('Created'); + } + + private function validateConfig($config) + { + if ($config['driver'] == 'database') { + if (! isset($config['storage']['database']['model'])) { + throw new \Exception('You need to set a model for the database driver in the cart configuration.'); + } + + if (! isset($config['storage']['database']['id'])) { + throw new \Exception('You need to set an id (for the session) for the database driver in the cart configuration.'); + } + + if (! isset($config['storage']['database']['items'])) { + throw new \Exception('You need to set an items key for the database driver in the cart configuration.'); + } + + if (! isset($config['storage']['database']['conditions'])) { + throw new \Exception('You need to set a conditions key for the database driver in the cart configuration.'); + } + } } /** @@ -145,7 +168,7 @@ public function add( */ public function update(int|string $id, array $data): bool { - if ($this->fireEvent('updating', $data) === false) { + if ($this->fireEvent('Updating', $data) === false) { return false; } @@ -178,7 +201,7 @@ public function update(int|string $id, array $data): bool $this->save($cart); - $this->fireEvent('updated', $item); + $this->fireEvent('Updated', $item); return true; } @@ -237,7 +260,7 @@ public function remove($id) { $cart = $this->getContent(); - if ($this->fireEvent('removing', $id) === false) { + if ($this->fireEvent('Removing', $id) === false) { return false; } @@ -245,7 +268,7 @@ public function remove($id) $this->save($cart); - $this->fireEvent('removed', $id); + $this->fireEvent('Removed', $id); return true; } @@ -257,13 +280,13 @@ public function remove($id) */ public function clear() { - if ($this->fireEvent('clearing') === false) { + if ($this->fireEvent('Clearing') === false) { return false; } $this->session->putItems([]); - $this->fireEvent('cleared'); + $this->fireEvent('Cleared'); return true; } @@ -691,7 +714,7 @@ protected function validate($item) */ protected function addItem($id, $item) { - if ($this->fireEvent('adding', $item) === false) { + if ($this->fireEvent('Adding', $item) === false) { return false; } @@ -701,7 +724,7 @@ protected function addItem($id, $item) $this->save($cart); - $this->fireEvent('added', $item); + $this->fireEvent('Added', $item); return true; } @@ -792,12 +815,9 @@ public function setDecimals($decimals) $this->decimals = $decimals; } - /** - * @return mixed - */ protected function fireEvent($name, $value = []) { - return $this->events->dispatch($this->getInstanceName() . '.' . $name, array_values([$value, $this]), true); + return $this->events->dispatch('LaravelCart.' . $name, array_values([$value, $this]), true); } /** diff --git a/src/CartServiceProvider.php b/src/CartServiceProvider.php index ecd768b..4cb74db 100644 --- a/src/CartServiceProvider.php +++ b/src/CartServiceProvider.php @@ -35,24 +35,19 @@ public function register() $this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'cart'); $this->app->singleton('cart', function ($app) { - $storageClass = config('cart.storage'); - $eventsClass = config('cart.events'); + $config = config('cart'); + $events = $app['events']; - $storage = $storageClass ? new $storageClass : $app['session']; - $events = $eventsClass ? new $eventsClass : $app['events']; - $instanceName = 'cart'; - - // default session or cart identifier. This will be overridden when calling Cart::session($sessionKey)->add() etc.. - // like when adding a cart for a specific user name. Session Key can be string or maybe a unique identifier to bind a cart - // to a specific user, this can also be a user ID - $session_key = '4yTlTDKu3oJOfzD'; + $storage = $config['driver'] === 'database' + ? new $config['storage']['database']['model'] + : $app['session']; return new Cart( $storage, $events, - $instanceName, - $session_key, - config('cart') + 'cart', + '4yTlTDKu3oJOfzD', + $config ); }); } diff --git a/src/CartSession.php b/src/CartSession.php index 8946ee4..4bd4a14 100644 --- a/src/CartSession.php +++ b/src/CartSession.php @@ -14,23 +14,22 @@ class CartSession public function __construct($session, $sessionKey, $config) { - if ($this->isInternalSession($session)) { + if ($config['driver'] === 'session') { $this->customStorage = false; $this->session = $session; $this->itemsKey = $sessionKey . '_cart_items'; $this->conditionsKey = $sessionKey . '_cart_conditions'; - } else { + } elseif ($config['driver'] === 'database') { $this->customStorage = true; - // @TODO validate these 3 exist - $this->sessionId = $config['storage_id']; - $this->itemsKey = $config['storage_items']; - $this->conditionsKey = $config['storage_conditions']; + $this->sessionId = $config['storage']['database']['id']; + $this->itemsKey = $config['storage']['database']['items']; + $this->conditionsKey = $config['storage']['database']['conditions']; - // get the base class to do findOrCreate - $this->session = $config['storage']; - $this->session = (new $this->session)->firstOrNew([$this->sessionId => $sessionKey]); + // find or create the session in the database + $this->session = $session->firstOrNew([$this->sessionId => $sessionKey]); + // initialize the items and conditions $this->session[$this->itemsKey] = $this->session[$this->itemsKey] ?? []; $this->session[$this->conditionsKey] = $this->session[$this->conditionsKey] ?? []; } diff --git a/tests/Helpers/ConfigConditionsMock.php b/tests/Helpers/ConfigConditionsMock.php index bf208ed..b5cd675 100644 --- a/tests/Helpers/ConfigConditionsMock.php +++ b/tests/Helpers/ConfigConditionsMock.php @@ -4,6 +4,5 @@ 'format_numbers' => true, 'decimals' => 2, 'dec_point' => '.', - 'thousands_sep' => ',', - 'use_cents' => false, + 'driver' => 'session', ]; diff --git a/tests/Helpers/ConfigMock.php b/tests/Helpers/ConfigMock.php index a2bf615..94c3538 100644 --- a/tests/Helpers/ConfigMock.php +++ b/tests/Helpers/ConfigMock.php @@ -4,4 +4,5 @@ 'format_numbers' => true, 'decimals' => 2, 'round_mode' => 'up', + 'driver' => 'session', ]; diff --git a/tests/Helpers/ConfigMockOtherFormat.php b/tests/Helpers/ConfigMockOtherFormat.php index 01cfec5..fc2323d 100644 --- a/tests/Helpers/ConfigMockOtherFormat.php +++ b/tests/Helpers/ConfigMockOtherFormat.php @@ -4,5 +4,5 @@ 'format_numbers' => false, 'decimals' => 3, 'dec_point' => ',', - 'thousands_sep' => '.', + 'driver' => 'session', ]; diff --git a/tests/Unit/CartTestEventsTest.php b/tests/Unit/CartTestEventsTest.php index 686de6a..80c0ef1 100644 --- a/tests/Unit/CartTestEventsTest.php +++ b/tests/Unit/CartTestEventsTest.php @@ -9,9 +9,9 @@ Mockery::close(); }); -test('event cart created', function () { +test('event cart Created', function () { $events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); $cart = new Cart( new SessionMock, @@ -26,9 +26,9 @@ test('event cart adding', function () { $events = Mockery::mock('Illuminate\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->once()->with('cart.adding', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->once()->with('cart.added', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Adding', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Added', Mockery::type('array'), true); $cart = new Cart( new SessionMock, @@ -45,9 +45,9 @@ test('event cart adding multiple times', function () { $events = Mockery::mock('Illuminate\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(2)->with('cart.adding', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(2)->with('cart.added', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(2)->with('LaravelCart.Adding', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(2)->with('LaravelCart.Added', Mockery::type('array'), true); $cart = new Cart( new SessionMock, @@ -65,9 +65,9 @@ test('event cart adding multiple times scenario two', function () { $events = Mockery::mock('Illuminate\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Adding', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Added', Mockery::type('array'), true); $items = [ [ @@ -108,11 +108,11 @@ test('event cart remove item', function () { $events = Mockery::mock('Illuminate\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(1)->with('cart.removing', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(1)->with('cart.removed', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Adding', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Added', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(1)->with('LaravelCart.Removing', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(1)->with('LaravelCart.Removed', Mockery::type('array'), true); $items = [ [ @@ -155,11 +155,11 @@ test('event cart clear', function () { $events = Mockery::mock('Illuminate\Events\Dispatcher'); - $events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->once()->with('cart.clearing', Mockery::type('array'), true); - $events->shouldReceive('dispatch')->once()->with('cart.cleared', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Created', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Adding', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->times(3)->with('LaravelCart.Added', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Clearing', Mockery::type('array'), true); + $events->shouldReceive('dispatch')->once()->with('LaravelCart.Cleared', Mockery::type('array'), true); $items = [ [