From 4702cf98abe7b7c73bb814bda5febb03a4009de8 Mon Sep 17 00:00:00 2001 From: Joel Male Date: Wed, 27 May 2026 20:55:01 +1000 Subject: [PATCH] feat: add DatabaseDriver Implements CartDriver for Eloquent-backed persistence using firstOrNew pattern. Includes 9 unit tests covering CRUD, flush, clearItems, and model retrieval. --- src/Drivers/DatabaseDriver.php | 82 +++++++++++++++++++++++ tests/Unit/Drivers/DatabaseDriverTest.php | 71 ++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/Drivers/DatabaseDriver.php create mode 100644 tests/Unit/Drivers/DatabaseDriverTest.php diff --git a/src/Drivers/DatabaseDriver.php b/src/Drivers/DatabaseDriver.php new file mode 100644 index 0000000..6d6bef7 --- /dev/null +++ b/src/Drivers/DatabaseDriver.php @@ -0,0 +1,82 @@ +model = (new $modelClass)->firstOrNew([$idColumn => $sessionKey]); + $this->model[$itemsColumn] ??= []; + $this->model[$conditionsColumn] ??= []; + } + + public function getSessionKey(): string + { + return $this->sessionKey; + } + + public function setSessionKey(string $sessionKey): void + { + $this->sessionKey = $sessionKey; + $this->model[$this->idColumn] = $sessionKey; + $this->model->save(); + } + + public function getDriverName(): string + { + return 'database'; + } + + public function getItems(): array + { + return $this->model[$this->itemsColumn] ?? []; + } + + public function putItems(array $items): void + { + $this->model[$this->itemsColumn] = $items; + $this->model->save(); + } + + public function getConditions(): array + { + return $this->model[$this->conditionsColumn] ?? []; + } + + public function putConditions(array $conditions): void + { + $this->model[$this->conditionsColumn] = $conditions; + $this->model->save(); + } + + public function clearItems(): void + { + $this->model[$this->itemsColumn] = []; + $this->model->save(); + } + + public function flush(): void + { + $this->model->delete(); + $this->model[$this->itemsColumn] = []; + $this->model[$this->conditionsColumn] = []; + } + + public function getSessionModel(): mixed + { + return $this->model; + } +} diff --git a/tests/Unit/Drivers/DatabaseDriverTest.php b/tests/Unit/Drivers/DatabaseDriverTest.php new file mode 100644 index 0000000..eb70351 --- /dev/null +++ b/tests/Unit/Drivers/DatabaseDriverTest.php @@ -0,0 +1,71 @@ +driver = new DatabaseDriver( + MockCartModel::class, + 'session_id', + 'items', + 'conditions', + 'test-session', + ); +}); + +describe('DatabaseDriver', function () { + test('returns its session key', function () { + expect($this->driver->getSessionKey())->toBe('test-session'); + }); + + test('returns database as driver name', function () { + expect($this->driver->getDriverName())->toBe('database'); + }); + + test('starts with empty items', function () { + expect($this->driver->getItems())->toBe([]); + }); + + test('stores and retrieves items', function () { + $items = [['id' => 1, 'name' => 'Widget']]; + $this->driver->putItems($items); + + $fresh = new DatabaseDriver(MockCartModel::class, 'session_id', 'items', 'conditions', 'test-session'); + expect($fresh->getItems())->toBe($items); + }); + + test('starts with empty conditions', function () { + expect($this->driver->getConditions())->toBe([]); + }); + + test('stores and retrieves conditions', function () { + $conditions = [['name' => 'GST', 'type' => 'tax', 'value' => '10%']]; + $this->driver->putConditions($conditions); + + $fresh = new DatabaseDriver(MockCartModel::class, 'session_id', 'items', 'conditions', 'test-session'); + expect($fresh->getConditions())->toBe($conditions); + }); + + test('flush deletes the database record', function () { + $this->driver->putItems([['id' => 1]]); + $this->driver->flush(); + + expect(MockCartModel::count())->toBe(0); + }); + + test('clearItems empties items but keeps conditions', function () { + $this->driver->putItems([['id' => 1]]); + $this->driver->putConditions([['name' => 'GST']]); + $this->driver->clearItems(); + + $fresh = new DatabaseDriver(MockCartModel::class, 'session_id', 'items', 'conditions', 'test-session'); + expect($fresh->getItems())->toBe([]); + expect($fresh->getConditions())->toBe([['name' => 'GST']]); + }); + + test('getSessionModel returns the Eloquent model', function () { + expect($this->driver->getSessionModel())->toBeInstanceOf(MockCartModel::class); + }); +});