├── .gitignore
├── LICENSE.md
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml
├── src
├── CartServiceProvider.php
├── Console
│ └── Commands
│ │ └── CartCleanup.php
├── Facades
│ └── Cart.php
├── Models
│ ├── Cart.php
│ └── CartLine.php
├── config
│ └── cart.php
└── database
│ └── migrations
│ ├── 2019_12_31_151631_create_cart_table.php
│ └── 2019_12_31_151654_create_cart_lines_table.php
└── tests
├── CartTest.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) Nabeel Al-almai
4 |
5 | > Permission is hereby granted, free of charge, to any person obtaining a copy
6 | > of this software and associated documentation files (the "Software"), to deal
7 | > in the Software without restriction, including without limitation the rights
8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | > copies of the Software, and to permit persons to whom the Software is
10 | > furnished to do so, subject to the following conditions:
11 | >
12 | > The above copyright notice and this permission notice shall be included in
13 | > all copies or substantial portions of the Software.
14 | >
15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | > THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DBCart
2 |
3 |
4 | Shopping Cart library for Laravel > 5.5 and Laravel 6 that uses database instead of sessions to store carts.
5 |
6 |
7 |
8 | ## Features
9 |
10 | * Cart for guest users
11 | * Cart for logged in users
12 | * Guest Cart is merged with User Cart when logged in
13 | * Singleton Cart instance to avoid unnecessary database queries. But also possible to avoid signleton cart if needed.
14 | * Built on top of Eloquent Model, so easily extendable and all eloquent methods can be used.
15 | * Multiple instances of cart
16 | * Schedule expired carts for deletion
17 |
18 | ## Before installation
19 | if you already use `dbcart by Hassansin`, please skip DB migrations
20 | and publishing configuration. This package is fully compatible.
21 | ## Installation
22 | 1. Install the package via composer:
23 | ``` bash
24 | composer require nabeelalalmai/dbcart-laravel
25 | ```
26 |
27 | The package will automatically register itself.
28 |
29 | 2. Publish database migrations(IF YOU USE dbcart:
30 |
31 | ```bash
32 | php artisan vendor:publish --provider="NabeelAlalmai\DBcart\CartServiceProvider" --tag="migrations"
33 | ```
34 |
35 | After the migration has been published you can create the `cart` and `cart_lines` tables by running the migrations:
36 |
37 | ```bash
38 | php artisan migrate
39 | ```
40 |
41 |
42 |
43 | ## Configuration
44 |
45 | Optionally, you can publish package config file:
46 |
47 | ```sh
48 | php artisan vendor:publish --provider="NabeelAlalmai\DBcart\CartServiceProvider" --tag=config
49 | ```
50 | Now, update `config/cart.php` if required
51 |
52 | ## Usage
53 |
54 | #### Get Cart Instance:
55 | Get the current cart instance. It returns a singleton cart instance:
56 | ```php
57 | $cart = app('cart'); //using app() helper
58 | ```
59 | or,
60 |
61 | ```php
62 | $cart = App::make('cart');
63 | ```
64 | alternatively, you can avoid singleton instance and use the model class to load the cart instance from database everytime:
65 |
66 | ```php
67 | use NabeelAlalmai\DBcart\Models\Cart;
68 | //...
69 | $cart = Cart::current();
70 | ```
71 | The idea of using singleton cart is that the cart object will be available globally throughout your app (e.g. controllers/models/views/view composers etc) for a single request. Also as you manipulate cart items, `$cart->item_count` and `$cart->total_price` would get updated.
72 |
73 | #### Add an Item: `$cart->addItem($attributes)`
74 |
75 | ```php
76 | $cart->addItem([
77 | 'product_id' => 1,
78 | 'unit_price' => 10.5,
79 | 'quantity' => 1
80 | ]);
81 | ```
82 |
83 | which is equivalent to `$cart->items()->create($attributes)`
84 |
85 | #### Get Items: `$cart->items`
86 |
87 | Since `$cart` is eloquent model instance, you can use any of the eloquent methods to get items
88 |
89 | ```php
90 | $items = $cart->items // by dynamic property access
91 | $items = $cart->items()->get()
92 | $items = $cart->items()->where('quantity', '>=', 2)->get()
93 | ```
94 |
95 | #### Update an Item: `$cart->updateItem($where, $attributes)`
96 |
97 | ```php
98 | $cart->updateItem([
99 | 'id' => 2
100 | ], [
101 | 'product_id' => 1,
102 | 'unit_price' => 10.5,
103 | 'quantity' => 1
104 | ]);
105 | ```
106 |
107 | which is equivalent to `$cart->items()->where($where)->first()->update($attributes)`
108 |
109 | #### Remove an Item: `$cart->removeItem($where)`
110 |
111 | ```php
112 | $cart->removeItem([
113 | 'id' => 2
114 | ]);
115 | ```
116 |
117 | which is equivalent to `$cart->items()->where($where)->first()->delete()`
118 |
119 | #### Clear Cart Items: `$cart->clear()`
120 |
121 | Remove all items from the cart
122 |
123 | ```php
124 | $cart->clear();
125 | ```
126 | #### Checkout cart: `$cart->checkout()`
127 |
128 | This method only updates `status` and `placed_at` column values. `status` is set to `pending`
129 |
130 | ```php
131 | $cart->checkout();
132 |
133 | ```
134 |
135 |
136 | #### Move item(s) between carts:
137 |
138 | To move all items from one cart to another cart instance:
139 |
140 | ```php
141 | $cart = app('cart');
142 | $wishlist = app('cart', ['name' => 'wishlist']);
143 |
144 | //move all wishlist items to cart
145 | $wishlist->moveItemsTo($cart);
146 | ```
147 |
148 | To move a single item between carts:
149 |
150 | ```php
151 | $cart = app('cart');
152 | $wishlist = app('cart', ['name' => 'wishlist']);
153 |
154 | //move an wishlist item to cart
155 | $item = $wishlist->items()->where(['product_id' => 1])->first();
156 | $item->moveTo($cart);
157 |
158 | ```
159 |
160 |
161 | #### Get Cart Attributes
162 |
163 | ```php
164 | $total_price = $cart->total_price; // cart total price
165 | $item_count = $cart->item_count; // cart items count
166 | $date_placed = $cart->placed_at; // returns Carbon instance
167 | ```
168 | #### Cart Statuses
169 |
170 | Supports several cart statuses:
171 | * `active`: currently adding items to the cart
172 | * `expired`: cart is expired, meaningful for session carts
173 | * `pending`: checked out carts
174 | * `completed`: completed carts
175 |
176 | ```php
177 | use NabeelAlalmai\DBcart\Models\Cart;
178 |
179 | // get carts based on their status: active/expired/pending/complete
180 | $active_carts = Cart::active()->get();
181 | $expired_carts = Cart::expired()->get();
182 | $pending_carts = Cart::pending()->get();
183 | $completed_carts = Cart::completed()->get();
184 | ```
185 | #### Working with Multiple Cart Instances
186 |
187 | By default, cart instances are named as `default`. You can load other instances by providing a name:
188 |
189 | ```php
190 | $cart = app('cart'); // default cart, same as: app('cart', [ 'name' => 'default'];
191 | $sales_cart = app('cart', [ 'name' => 'sales']);
192 | $wishlist = app('cart', [ 'name' => 'wishlist']);
193 | ```
194 | or, without singleton carts:
195 |
196 | ```php
197 | use NabeelAlalmai\DBcart\Models\Cart;
198 | //...
199 | $cart = Cart::current();
200 | $sales_cart = Cart::current('sales');
201 | $wishlist = Cart::current('wishlist');
202 | ```
203 |
204 | To get carts other than `default`:
205 |
206 | ```php
207 | $pending_sales_carts = Cart::instance('sales')->pending()->get();
208 | ```
209 |
210 | #### Delete Expired Carts:
211 |
212 | The guest carts depend on the session lifetime. They are valid as long as the session is not expired. You can increase session lifetime in `config/session.php` to increase cart lifetime. When a session expires, a new cart instance will be created in database and the old one will no longer be used. Over time, these expired carts could pile-up in database.
213 |
214 | Laravel Task Scheduler comes to the rescue. To enable scheduler just add following to the crontab in your server:
215 |
216 | * * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
217 |
218 | That's it. The module will now check for expired carts in every hour and delete them. This won't affect the carts for loggedin users.
219 |
220 | #### Other features:
221 |
222 | Get Cart User: `$cart->user`
223 |
224 | Get Item Product: `$item->product`
225 |
226 | Is Cart Empty: `$cart->isEmpty()`
227 |
228 | If an item exists in cart: `$cart->hasItem(['id' => 10])`
229 |
230 | Expire the cart: `cart->expire();`
231 |
232 | Set to `completed` status: `$cart->complete();`
233 |
234 |
235 | ## Extending Cart Model
236 | It's easy to extend DBCart. You can extend base DBCart model and add your own methods or columns. Follow these steps to extend the cart model:
237 |
238 | 1. Create a model by extending `NabeelAlalmai\DBcart\Models\Cart`:
239 | ```php
240 | namespace App;
241 |
242 | use NabeelAlalmai\DBcart\Models\Cart as BaseCart;
243 |
244 | class Cart extends BaseCart
245 | {
246 | //override or add your methods here ...
247 |
248 | public function getSubTotalAttribute(){
249 | return $this->attributes['total_price'];
250 | }
251 | public function getGrandTotalAttribute(){
252 | //taking discount, tax etc. into account
253 | return $this->sub_total - $this->discount;
254 | }
255 |
256 | }
257 | ```
258 | 2. Update `cart_model` in `config/cart.php` with the fully qualified class name of the extended model.
259 |
260 | ```php
261 | 'cart_model' => App\Cart::class,
262 | ```
263 | 3. That's it, you can now load the cart as usual:
264 |
265 | ```php
266 | $cart = App::make('cart');
267 | ```
268 |
269 | You can also follow the above steps and create your own `CartLine` model by extending `NabeelAlalmai\DBcart\Models\CartLine`. Be sure to update `config/cart.php` to reflect your changes.
270 |
271 | ## Disclaimer
272 | I was using `dbcart by Hassansin` but it doesn't support Laravel 6. So, I re-wrote the package to support Laravel > 5.5. If you use dbcart by Hassansin you can use this package and it should work with no conflicts.
273 |
274 | ## Support
275 |
276 | Reach out to me at one of the following places!
277 |
278 | - Twitter at `Nabeel Al Almai - نبيل الالمعي`
279 |
280 | ---
281 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nabeelalalmai/dbcart-laravel",
3 | "description": "Shopping Cart for Laravel > 5.5 , Laravel 6 and Laravel 7 that uses Database instead of Sessions.",
4 | "keywords": [
5 | "Nabeel Al-Almai",
6 | "Shopping Cart",
7 | "dbcart-laravel",
8 | "database",
9 | "e-commerce",
10 | "laravel"
11 | ],
12 | "homepage": "https://github.com/nabeel-alalmai/dbcart-laravel",
13 | "license": "MIT",
14 | "authors": [
15 | {
16 | "name": "Nabeel Al-Almai",
17 | "email": "nabeel.alalmai@gmail.com"
18 | }
19 | ],
20 |
21 | "require": {
22 | "php": "^7.2|^8.0",
23 | "illuminate/support": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0"
24 | },
25 | "require-dev": {
26 | "fzaninotto/faker": "~1.4",
27 | "mockery/mockery": "^1.2.3",
28 | "orchestra/testbench": "^4.0"
29 | },
30 | "autoload": {
31 | "psr-4": {
32 | "NabeelAlalmai\\DBcart\\": "src/"
33 | }
34 | },
35 | "extra": {
36 | "laravel": {
37 | "providers": [
38 | "NabeelAlalmai\\DBcart\\CartServiceProvider"
39 | ]
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests
14 |
15 |
16 |
17 |
18 | ./src
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/CartServiceProvider.php:
--------------------------------------------------------------------------------
1 | publishes([
25 | __DIR__ . '/config/cart.php' => config_path('cart.php'),
26 | ], 'config');
27 |
28 | $this->publishes([
29 | __DIR__ . '/database/migrations/' => database_path('migrations')
30 | ], 'migrations');
31 |
32 | $this->app->booted(function () {
33 | $schedule = $this->app->make(Schedule::class);
34 | $schedule_frequency = config('cart.schedule_frequency', 'hourly');
35 | $schedule->command('cart:cleanup')->$schedule_frequency();
36 |
37 | });
38 | if ($this->app->runningInConsole()) {
39 | $this->commands([
40 | Console\Commands\CartCleanup::class
41 | ]);
42 | }
43 | }
44 |
45 | /**
46 | * Register the service provider.
47 | *
48 | * @return void
49 | */
50 | public function register()
51 | {
52 | $this->mergeConfigFrom(
53 | __DIR__ . '/config/cart.php', 'cart'
54 | );
55 |
56 | $this->app['cart_instances'] = [];
57 |
58 | $this->app->bind('cart', function ($app, $params) {
59 | $instance_name = !empty($params['name']) ? $params['name'] : 'default';
60 | $cart_instances = $app['cart_instances'];
61 |
62 | //implement singleton carts
63 | if (empty($cart_instances[$instance_name])) {
64 | $model = config('cart.cart_model');
65 | $cart_instances[$instance_name] = $model::current($instance_name);
66 | $app['cart_instances'] = $cart_instances;
67 | }
68 | return $app['cart_instances'][$instance_name];
69 | });
70 |
71 | }
72 |
73 |
74 | /**
75 | * Get the services provided by the provider.
76 | *
77 | * @codeCoverageIgnore
78 | * @return array
79 | */
80 | public function provides()
81 | {
82 | return ['cart', 'cart_instances'];
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Console/Commands/CartCleanup.php:
--------------------------------------------------------------------------------
1 | subMinutes($session_lifetime);
47 | $cart_model::where('updated_at', '<', $date)
48 | ->where('session', '!=', '')
49 | ->where('status','active')
50 | ->update(['status' => 'expired']);
51 | }
52 |
53 | //delete expired carts
54 | if(config('cart.delete_expired', false)){
55 | $cart_model = config('cart.cart_model');
56 | $cart_line_model = config('cart.cart_line_model');
57 | $cart_ids = $cart_model::where('status','expired')->pluck('id');
58 |
59 | $cart_line_model::whereIn('cart_id', $cart_ids->toArray())->delete();
60 | $cart_model::where('status','expired')->delete();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Facades/Cart.php:
--------------------------------------------------------------------------------
1 | 'float',
46 | 'item_count' => 'int',
47 | ];
48 |
49 | /**
50 | * Get the current cart instance
51 | *
52 | * @param string $instance_name
53 | * @param null $save_on_demand
54 | * @return mixed
55 | */
56 | public static function current($instance_name = 'default', $save_on_demand = null)
57 | {
58 | $save_on_demand = is_null($save_on_demand) ? config('cart.save_on_demand', false) : $save_on_demand;
59 | return static::init($instance_name, $save_on_demand);
60 | }
61 |
62 | /**
63 | * Initialize the cart
64 | *
65 | * @param string $instance_name
66 | * @param $save_on_demand
67 | * @return mixed
68 | */
69 | public static function init($instance_name, $save_on_demand)
70 | {
71 | $request = app('request');
72 | $session_id = $request->session()->getId();
73 | $user_id = $request->user()->id ?? false;
74 | $app = Application::getInstance();
75 | $carts = $request->offsetGet("cart_instances");
76 |
77 | //if user logged in
78 | if ($user_id) {
79 | $user_cart = static::active()->user($user_id)->where(['name' => $instance_name])->first();
80 |
81 | $session_cart_id = $request->session()->get('cart_' . $instance_name);
82 | $session_cart = is_null($session_cart_id) ? null : static::active()->session($session_cart_id)->where('name', $instance_name)->first();
83 |
84 | switch (true) {
85 |
86 | case is_null($user_cart) && is_null($session_cart): //no user cart or session cart
87 | $attributes = array(
88 | 'user_id' => $user_id,
89 | 'name' => $instance_name,
90 | 'status' => STATUS_ACTIVE
91 | );
92 | if ($save_on_demand)
93 | $cart = new static($attributes);
94 | else
95 | $cart = static::create($attributes);
96 |
97 | break;
98 |
99 | case !is_null($user_cart) && is_null($session_cart): //only user cart
100 | $cart = $user_cart;
101 | break;
102 |
103 | case is_null($user_cart) && !is_null($session_cart): //only session cart
104 | $cart = $session_cart;
105 | $cart->user_id = $user_id;
106 | $cart->session = null;
107 | $cart->save();
108 | break;
109 |
110 | case !is_null($user_cart) && !is_null($session_cart): //both user cart and session cart exists
111 | $session_cart->moveItemsTo($user_cart); //move items from session cart to user cart
112 | $session_cart->delete(); //delete session cart
113 | $cart = $user_cart;
114 | break;
115 | }
116 |
117 | $request->session()->forget('cart_' . $instance_name); //no longer need it.
118 | $carts[$instance_name] = $cart;
119 | } //guest user, create cart with session id
120 | else {
121 | $attributes = array(
122 | 'session' => $session_id,
123 | 'name' => $instance_name,
124 | 'status' => STATUS_ACTIVE
125 | );
126 | $cart = static::firstOrNew($attributes);
127 |
128 | if (!$save_on_demand)
129 | $cart->save();
130 |
131 | //save current session id, since upon login session id will be regenerated
132 | //we will use this id to get back the cart before login
133 | $request->session()->put('cart_' . $instance_name, $session_id);
134 | $carts[$instance_name] = $cart;
135 | }
136 |
137 |
138 | $app->offsetSet("cart_instances", $carts);
139 |
140 | return $carts[$instance_name];
141 | }
142 |
143 | /**
144 | * The "booting" method of the model.
145 | *
146 | * @return void
147 | */
148 | protected static function boot()
149 | {
150 | parent::boot();
151 |
152 | //delete line items
153 | static::deleting(function (Cart $cart) {
154 | $cart->items()->delete();
155 | });
156 | }
157 |
158 | /**
159 | * Get the items for the cart.
160 | */
161 | public function items()
162 | {
163 | return $this->hasMany(config('cart.cart_line_model'));
164 | }
165 |
166 | /**
167 | * Get the user that owns the cart.
168 | * @codeCoverageIgnore
169 | */
170 | public function user()
171 | {
172 | return $this->belongsTo(config('cart.user_model'));
173 | }
174 |
175 | public function scopePending($query)
176 | {
177 | return $query->where('status', STATUS_PENDING);
178 | }
179 |
180 | public function scopeCompleted($query)
181 | {
182 | return $query->where('status', STATUS_COMPLETE);
183 | }
184 |
185 | public function scopeExpired($query)
186 | {
187 | return $query->where('status', STATUS_EXPIRED);
188 | }
189 |
190 | public function scopeActive($query)
191 | {
192 | return $query->where('status', STATUS_ACTIVE);
193 | }
194 |
195 | public function scopeInstance($query, $instance_name = 'default')
196 | {
197 | return $query->where('name', $instance_name);
198 | }
199 |
200 | public function scopeUser($query, $user_id = null)
201 | {
202 | return $query->where('user_id', $user_id);
203 | }
204 |
205 | public function scopeSession($query, $session_id = null)
206 | {
207 | $session_id = $session_id ?: app('request')->session()->getId();
208 | return $query->where('session', $session_id);
209 | }
210 |
211 | public function setTotalPriceAttribute($value)
212 | {
213 | $this->attributes['total_price'] = $value;
214 | }
215 |
216 | /**
217 | * Add item to a cart. Increases quantity if the item already exists.
218 | *
219 | * @param array $attributes
220 | * @return Model|\Illuminate\Database\Eloquent\Relations\HasMany|object|null
221 | */
222 | public function addItem(array $attributes = [])
223 | {
224 | if ($item = $this->getItem(collect($attributes)->except(['quantity']))) {
225 | $item->quantity += $attributes['quantity'];
226 | $item->save();
227 | return $item;
228 | }
229 | return $this->items()->create($attributes);
230 | }
231 |
232 | public function getItem($where)
233 | {
234 | if ($where instanceof Collection) {
235 | $where = $where->toArray();
236 | }
237 | return $this->items()->where($where)->first();
238 | }
239 |
240 | /**
241 | * remove item from a cart
242 | *
243 | * @param array $attributes
244 | * @return bool|mixed|null
245 | */
246 | public function removeItem(array $attributes = [])
247 | {
248 | return $this->items()->where($attributes)->first()->delete();
249 | }
250 |
251 | /**
252 | * update item in a cart
253 | *
254 | * @param array $where
255 | * @param array $values
256 | * @return bool|int
257 | */
258 | public function updateItem(array $where, array $values)
259 | {
260 | return $this->items()->where($where)->first()->update($values);
261 | }
262 |
263 | /**
264 | * Cart checkout.
265 | *
266 | */
267 | public function checkout()
268 | {
269 | return $this->update(['status' => STATUS_PENDING, 'placed_at' => Carbon::now()]);
270 | }
271 |
272 | /**
273 | * Expires a cart
274 | *
275 | */
276 | public function expire()
277 | {
278 | return $this->update(['status' => STATUS_EXPIRED]);
279 | }
280 |
281 | /**
282 | * Set a cart as complete
283 | *
284 | */
285 | public function complete()
286 | {
287 | return $this->update(['status' => STATUS_COMPLETE, 'completed_at' => Carbon::now()]);
288 | }
289 |
290 | /**
291 | * Check if cart is empty
292 | *
293 | */
294 | public function isEmpty()
295 | {
296 | return empty($this->item_count);
297 | }
298 |
299 | public function hasItem($where)
300 | {
301 | return !is_null($this->getItem($where));
302 | }
303 |
304 | /**
305 | * Empties a cart
306 | *
307 | */
308 | public function clear()
309 | {
310 | $this->items()->delete();
311 | $this->updateTimestamps();
312 | $this->total_price = 0;
313 | $this->item_count = 0;
314 | $this->relations = [];
315 | return $this->save();
316 | }
317 |
318 | /**
319 | * Move Items to another cart instance
320 | *
321 | * @param Cart $cart
322 | * @return Cart
323 | */
324 | public function moveItemsTo(Cart $cart)
325 | {
326 |
327 | DB::transaction(function () use (&$cart) {
328 | $current_items = $cart->items()->pluck('product_id');
329 | $items_to_move = $this->items()->whereNotIn('product_id', $current_items->toArray())->get();
330 | if ($items_to_move->count() === 0) {
331 | return;
332 | }
333 | $this->items()->whereNotIn('product_id', $current_items->toArray())->update([
334 | 'cart_id' => $cart->id
335 | ]);
336 | foreach ($items_to_move as $item) {
337 | $this->item_count -= $item->quantity;
338 | $this->total_price -= $item->getPrice();
339 | $cart->item_count += $item->quantity;
340 | $cart->total_price += $item->getPrice();
341 | }
342 | $this->relations = [];
343 | $cart->relations = [];
344 | $this->save();
345 | $cart->save();
346 | });
347 | return $cart;
348 | }
349 | }
350 |
--------------------------------------------------------------------------------
/src/Models/CartLine.php:
--------------------------------------------------------------------------------
1 | 'int',
38 | 'product_id' => 'int',
39 | 'quantity' => 'int',
40 | 'unit_price' => 'float',
41 | ];
42 |
43 | /**
44 | * Get the product record associated with the item.
45 | * @codeCoverageIgnore
46 | */
47 | public function product()
48 | {
49 | return $this->belongsTo(config('cart.product_model'), 'product_id');
50 | }
51 |
52 | /**
53 | * Get the cart that owns the item.
54 | */
55 | public function cart()
56 | {
57 | return $this->belongsTo(config('cart.cart_model'), 'cart_id');
58 | }
59 |
60 | /*
61 | * Get Item orginal quantity before update
62 | *
63 | * @return integer
64 | */
65 |
66 | public function getOriginalQuantity(){
67 | return $this->original['quantity'];
68 | }
69 |
70 | /*
71 | * Get Item original price before update
72 | *
73 | * @return float
74 | */
75 | public function getOriginalUnitPrice(){
76 | return $this->original['unit_price'];
77 | }
78 |
79 | /*
80 | * Get Item price
81 | *
82 | * @return float
83 | */
84 | public function getPrice(){
85 | return $this->quantity * $this->unit_price;
86 | }
87 |
88 | /*
89 | * Get Item original price before update
90 | *
91 | * @return integer
92 | */
93 | public function getOriginalPrice(){
94 | return $this->getOriginalQuantity() * $this->getOriginalUnitPrice();
95 | }
96 |
97 | /*
98 | * Get the singleton cart of this line item.
99 | *
100 | */
101 | public function getCartInstance(){
102 | $carts = app('cart_instances');
103 | foreach ($carts as $name => $cart) {
104 | if($cart->id === $this->cart_id){
105 | return $cart;
106 | }
107 | }
108 | return null;
109 | }
110 |
111 | /*
112 | * Move this item to another cart
113 | *
114 | * @param Cart $cart
115 | */
116 | public function moveTo(Cart $cart){
117 | $model = null;
118 | \DB::transaction(function () use($cart, &$model) {
119 | $this->delete(); // delete from own cart
120 | $attr = $this->attributes;
121 | unset($attr['cart_id']);
122 | $model = $cart->items()->create($attr);
123 | });
124 | return $model;
125 | }
126 |
127 | /**
128 | * The "booting" method of the model.
129 | *
130 | * @return void
131 | */
132 | protected static function boot() {
133 | parent::boot();
134 |
135 | //when an item is created
136 | static::created(function(CartLine $line){
137 | $cart = $line->getCartInstance() ?: $line->cart;
138 | $cart->total_price = $cart->total_price + $line->getPrice();
139 | $cart->item_count = $cart->item_count + $line->quantity;
140 | $cart->relations = [];
141 | $cart->save();
142 | });
143 |
144 | //when an item is updated
145 | static::updated(function(CartLine $line){
146 | $cart = $line->getCartInstance() ?: $line->cart;
147 | $cart->total_price = $cart->total_price - $line->getOriginalPrice() + $line->getPrice();
148 | $cart->item_count = $cart->item_count - $line->getOriginalQuantity() + $line->quantity;
149 | $cart->relations = [];
150 | $cart->save();
151 | });
152 |
153 | static::deleted(function(CartLine $line){
154 | $cart = $line->getCartInstance() ?: $line->cart;
155 | $cart->total_price = $cart->total_price - $line->getPrice();
156 | $cart->item_count = $cart->item_count - $line->quantity;
157 | $cart->relations = [];
158 | $cart->save();
159 | });
160 | }
161 | }
162 |
163 |
--------------------------------------------------------------------------------
/src/config/cart.php:
--------------------------------------------------------------------------------
1 | NabeelAlalmai\DBcart\Models\Cart::class,
17 |
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Cart Line Model
22 | |--------------------------------------------------------------------------
23 | |
24 | | Cart Line Model to associate with Cart orders
25 | |
26 | */
27 |
28 | 'cart_line_model' => NabeelAlalmai\DBcart\Models\CartLine::class,
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | User Model
33 | |--------------------------------------------------------------------------
34 | |
35 | | User Model to associate with Cart orders
36 | |
37 | */
38 |
39 | 'user_model' => App\User::class,
40 |
41 | /*
42 | |--------------------------------------------------------------------------
43 | | Product Model
44 | |--------------------------------------------------------------------------
45 | |
46 | | Product Model to associate with Cart Lines
47 | |
48 | */
49 |
50 | 'product_model' => App\Product::class,
51 |
52 |
53 | /*
54 | |--------------------------------------------------------------------------
55 | | Save on demand
56 | |--------------------------------------------------------------------------
57 | |
58 | | When set to true, if a cart doesn't exist on database you'll be given a
59 | | new instance of cart object. You have to manually save the cart to DB by
60 | | $cart->save() . Useful if you want to avoid creating unnecessary empty carts
61 | | in database. But be sure to save cart before adding items
62 | |
63 | */
64 |
65 | 'save_on_demand' => false,
66 |
67 | /*
68 | |--------------------------------------------------------------------------
69 | | Auto Expire a cart
70 | |--------------------------------------------------------------------------
71 | |
72 | | Enable or disable auto expire a cart. Only applicable for session carts.
73 | | When set to true, cart status will be set to 'expired' after session 'lifetime'
74 | |
75 | | Needs Laravel task scheduler to be started: http://laravel.com/docs/master/scheduling
76 | */
77 |
78 | 'expire_cart' => true,
79 |
80 | /*
81 | |--------------------------------------------------------------------------
82 | | Delete Expired Cart
83 | |--------------------------------------------------------------------------
84 | |
85 | | Delete expired carts. If set to true, all expired carts will be deleted.
86 | |
87 | | Needs Laravel task scheduler to be started: http://laravel.com/docs/master/scheduling
88 | */
89 | 'delete_expired' => true,
90 |
91 | /*
92 | |--------------------------------------------------------------------------
93 | | Schedule Frequency
94 | |--------------------------------------------------------------------------
95 | |
96 | | How often the scheduled command would run
97 | |
98 | */
99 | 'schedule_frequency' => 'hourly',
100 |
101 | ];
102 |
--------------------------------------------------------------------------------
/src/database/migrations/2019_12_31_151631_create_cart_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
18 | $table->unsignedBigInteger('user_id')->nullable();
19 | $table->string('session')->nullable();
20 | $table->string('name')->default('default');
21 | $table->string('status', 20)->default('active');
22 | $table->decimal('total_price')->default(0.00);
23 | $table->integer('item_count')->default(0);
24 | $table->timestamp('placed_at')->nullable();
25 | $table->timestamp('completed_at')->nullable();
26 | $table->timestamps();
27 |
28 | $table->index('name');
29 | $table->index(['user_id', 'status']);
30 | $table->index(['session','status']);
31 | });
32 | }
33 |
34 | /**
35 | * Reverse the migrations.
36 | *
37 | * @return void
38 | */
39 | public function down()
40 | {
41 | Schema::dropIfExists('cart');
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/database/migrations/2019_12_31_151654_create_cart_lines_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
18 | $table->unsignedBigInteger('cart_id');
19 | $table->integer('product_id')->unsigned();
20 | $table->integer('quantity')->unsigned();
21 | $table->decimal('unit_price');
22 |
23 | $table->timestamps();
24 | $table->foreign('cart_id')->references('id')
25 | ->on('cart')
26 | ->onDelete('cascade');
27 | });
28 | }
29 |
30 | /**
31 | * Reverse the migrations.
32 | *
33 | * @return void
34 | */
35 | public function down()
36 | {
37 | Schema::dropIfExists('cart_lines');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/CartTest.php:
--------------------------------------------------------------------------------
1 | loadLaravelMigrations(['--database' => 'testbench']);
18 | $this->loadMigrationsFrom([
19 | '--database' => 'testbench',
20 | '--path' => realpath(__DIR__ . '/../src/database/migrations'),
21 | ]);
22 | }
23 |
24 | public function testCreateItem()
25 | {
26 | $cart = Cart::current();
27 | DB::enableQueryLog();
28 | $cart->addItem([
29 | 'product_id' => 1,
30 | 'unit_price' => 10.5,
31 | 'quantity' => 2
32 | ]);
33 | $this->assertCount(3, DB::getQueryLog());
34 | $this->assertEquals(2, $cart->item_count);
35 | $this->assertCount(1, $cart->items);
36 | $this->assertCount(1, $cart->items()->get());
37 | $this->assertCount(1, $cart->items()->where('quantity', '=', 2)->get());
38 | $this->assertCount(1, $cart->items()->where('unit_price', '>=', 1)->get());
39 | }
40 |
41 | public function testCreateDuplicateItem()
42 | {
43 | $cart = Cart::current();
44 | $cart->addItem([
45 | 'product_id' => 1,
46 | 'unit_price' => 10.5,
47 | 'quantity' => 2
48 | ]);
49 | DB::enableQueryLog();
50 | $cart->addItem([
51 | 'product_id' => 1,
52 | 'unit_price' => 10.5,
53 | 'quantity' => 3
54 | ]);
55 | $this->assertCount(3, DB::getQueryLog());
56 | $this->assertEquals(5, $cart->item_count);
57 | $this->assertEquals(52.5, $cart->total_price);
58 | $this->assertCount(1, $cart->items);
59 | $this->assertCount(1, $cart->items()->get());
60 | $this->assertCount(1, $cart->items()->where('quantity', '=', 5)->get());
61 | $this->assertCount(1, $cart->items()->where('unit_price', '>=', 1)->get());
62 | }
63 |
64 | public function testRemoveItem()
65 | {
66 | $cart = Cart::current();
67 | $cart->addItem([
68 | 'product_id' => 1,
69 | 'unit_price' => 10.5,
70 | 'quantity' => 2
71 | ]);
72 | $this->assertEquals(2, $cart->item_count);
73 | $this->assertCount(1, $cart->items);
74 | $this->assertCount(1, $cart->items()->get());
75 | $this->assertCount(1, $cart->items()->where('quantity', '=', 2)->get());
76 | $this->assertCount(1, $cart->items()->where('unit_price', '>=', 1)->get());
77 | $this->assertEquals(21, $cart->total_price);
78 | $this->assertFalse($cart->isEmpty());
79 |
80 | DB::enableQueryLog();
81 | $cart->removeItem(['product_id' => 1]);
82 | $this->assertCount(3, DB::getQueryLog());
83 | $this->assertEquals(0, $cart->item_count);
84 | $this->assertCount(0, $cart->items()->get());
85 | $this->assertCount(0, $cart->items);
86 | $this->assertTrue($cart->isEmpty());
87 | }
88 |
89 | public function testUpdateItem()
90 | {
91 | $cart = Cart::current();
92 | $cart->addItem([
93 | 'product_id' => 1,
94 | 'unit_price' => 10.5,
95 | 'quantity' => 2
96 | ]);
97 | $this->assertEquals(2, $cart->item_count);
98 | $this->assertCount(1, $cart->items()->get());
99 | $this->assertCount(1, $cart->items);
100 | $this->assertEquals(10.5, $cart->items->first()->unit_price);
101 | $this->assertCount(1, $cart->items()->where('quantity', '=', 2)->get());
102 | $this->assertCount(1, $cart->items()->where('unit_price', '>=', 1)->get());
103 | $this->assertEquals(21, $cart->total_price);
104 |
105 | DB::enableQueryLog();
106 | $cart->updateItem(['product_id' => 1], ['unit_price' => 15.5, 'quantity' => 3]);
107 | $this->assertCount(3, DB::getQueryLog());
108 |
109 | $this->assertCount(1, $cart->items()->get());
110 | $this->assertCount(1, $cart->items);
111 | $this->assertEquals(15.5, $cart->items->first()->unit_price);
112 | $this->assertEquals(3, $cart->item_count);
113 | $this->assertEquals(46.5, $cart->total_price);
114 | }
115 |
116 | public function testMoveItemsToEmptyCart()
117 | {
118 | $cart1 = app('cart', ['name' => 'cart1']);
119 | $cart2 = app('cart', ['name' => 'cart2']);
120 | $cart1->addItem([
121 | 'product_id' => 1,
122 | 'unit_price' => 10.5,
123 | 'quantity' => 2
124 | ]);
125 | $cart1->addItem([
126 | 'product_id' => 2,
127 | 'unit_price' => 11.5,
128 | 'quantity' => 1
129 | ]);
130 | $this->assertFalse($cart1->isEmpty());
131 | $this->assertTrue($cart2->isEmpty());
132 | $this->assertCount(2, $cart1->items()->get());
133 | $this->assertCount(0, $cart2->items()->get());
134 | $this->assertEquals(3, $cart1->item_count);
135 | $this->assertEquals(32.5, $cart1->total_price);
136 | $this->assertEquals(0, $cart2->item_count);
137 | $this->assertEquals(0, $cart2->total_price);
138 |
139 | DB::enableQueryLog();
140 | $cart1->moveItemsTo($cart2);
141 | $this->assertCount(5, DB::getQueryLog());
142 | $this->assertTrue($cart1->isEmpty());
143 | $this->assertFalse($cart2->isEmpty());
144 | $this->assertCount(0, $cart1->items()->get());
145 | $this->assertCount(2, $cart2->items()->get());
146 | $this->assertEquals(0, $cart1->item_count);
147 | $this->assertEquals(0, $cart1->total_price);
148 | $this->assertEquals(3, $cart2->item_count);
149 | $this->assertEquals(32.5, $cart2->total_price);
150 | }
151 |
152 | public function testMoveItemsToNonEmptyCart()
153 | {
154 | $cart1 = app('cart', ['name' => 'cart1']);
155 | $cart2 = app('cart', ['name' => 'cart2']);
156 | $cart1->addItem([
157 | 'product_id' => 1,
158 | 'unit_price' => 10.5,
159 | 'quantity' => 2
160 | ]);
161 | $cart1->addItem([
162 | 'product_id' => 2,
163 | 'unit_price' => 11.5,
164 | 'quantity' => 1
165 | ]);
166 |
167 | $cart2->addItem([
168 | 'product_id' => 3,
169 | 'unit_price' => 10,
170 | 'quantity' => 1
171 | ]);
172 |
173 | $this->assertFalse($cart1->isEmpty());
174 | $this->assertFalse($cart2->isEmpty());
175 | $this->assertCount(2, $cart1->items()->get());
176 | $this->assertCount(1, $cart2->items()->get());
177 | $this->assertEquals(3, $cart1->item_count);
178 | $this->assertEquals(32.5, $cart1->total_price);
179 | $this->assertEquals(1, $cart2->item_count);
180 | $this->assertEquals(10, $cart2->total_price);
181 |
182 | DB::enableQueryLog();
183 | $cart1->moveItemsTo($cart2);
184 | $this->assertCount(5, DB::getQueryLog());
185 | $this->assertTrue($cart1->isEmpty());
186 | $this->assertTrue($cart1->isEmpty());
187 | $this->assertFalse($cart2->isEmpty());
188 | $this->assertCount(0, $cart1->items()->get());
189 | $this->assertCount(3, $cart2->items()->get());
190 | $this->assertEquals(0, $cart1->item_count);
191 | $this->assertEquals(0, $cart1->total_price);
192 | $this->assertEquals(4, $cart2->item_count);
193 | $this->assertEquals(42.5, $cart2->total_price);
194 | }
195 |
196 | public function testMoveItemsToNonEmptyCartWithDuplicateProduct()
197 | {
198 | $cart1 = app('cart', ['name' => 'cart1']);
199 | $cart2 = app('cart', ['name' => 'cart2']);
200 | $cart1->addItem([
201 | 'product_id' => 1,
202 | 'unit_price' => 10.5,
203 | 'quantity' => 2
204 | ]);
205 | $cart1->addItem([
206 | 'product_id' => 2,
207 | 'unit_price' => 11.5,
208 | 'quantity' => 1
209 | ]);
210 |
211 | $cart2->addItem([
212 | 'product_id' => 1,
213 | 'unit_price' => 11.5,
214 | 'quantity' => 1
215 | ]);
216 |
217 | $this->assertFalse($cart1->isEmpty());
218 | $this->assertFalse($cart2->isEmpty());
219 | $this->assertCount(2, $cart1->items()->get());
220 | $this->assertCount(1, $cart2->items()->get());
221 | $this->assertEquals(3, $cart1->item_count);
222 | $this->assertEquals(32.5, $cart1->total_price);
223 | $this->assertEquals(1, $cart2->item_count);
224 | $this->assertEquals(11.5, $cart2->total_price);
225 |
226 | $cart1->moveItemsTo($cart2);
227 | $this->assertFalse($cart1->isEmpty());
228 | $this->assertFalse($cart2->isEmpty());
229 | $this->assertCount(1, $cart1->items()->get());
230 | $this->assertCount(2, $cart2->items()->get());
231 | $this->assertEquals(2, $cart1->item_count);
232 | $this->assertEquals(21, $cart1->total_price);
233 | $this->assertEquals(2, $cart2->item_count);
234 | $this->assertEquals(23, $cart2->total_price);
235 | }
236 |
237 | public function testMoveEmptyCart()
238 | {
239 | $cart1 = app('cart', ['name' => 'cart1']);
240 | $cart2 = app('cart', ['name' => 'cart2']);
241 |
242 | DB::enableQueryLog();
243 | $cart1->moveItemsTo($cart2);
244 | $this->assertCount(2, DB::getQueryLog());
245 | $this->assertTrue($cart1->isEmpty());
246 | $this->assertTrue($cart2->isEmpty());
247 | }
248 |
249 | public function testMoveSingleItemToEmptyCart()
250 | {
251 | $cart1 = app('cart', ['name' => 'cart1']);
252 | $cart2 = app('cart', ['name' => 'cart2']);
253 | $item = $cart1->addItem([
254 | 'product_id' => 1,
255 | 'unit_price' => 10.5,
256 | 'quantity' => 2
257 | ]);
258 | $cart1->addItem([
259 | 'product_id' => 2,
260 | 'unit_price' => 11.5,
261 | 'quantity' => 1
262 | ]);
263 | $this->assertFalse($cart1->isEmpty());
264 | $this->assertTrue($cart2->isEmpty());
265 | $this->assertCount(2, $cart1->items()->get());
266 | $this->assertCount(0, $cart2->items()->get());
267 | $this->assertEquals(3, $cart1->item_count);
268 | $this->assertEquals(32.5, $cart1->total_price);
269 | $this->assertEquals(0, $cart2->item_count);
270 | $this->assertEquals(0, $cart2->total_price);
271 | $this->assertEquals($item->cart->id, $cart1->id);
272 |
273 | $item = $item->moveTo($cart2);
274 | $this->assertFalse($cart1->isEmpty());
275 | $this->assertFalse($cart2->isEmpty());
276 | $this->assertCount(1, $cart1->items()->get());
277 | $this->assertCount(1, $cart2->items()->get());
278 | $this->assertEquals(1, $cart1->item_count);
279 | $this->assertEquals(11.5, $cart1->total_price);
280 | $this->assertEquals(2, $cart2->item_count);
281 | $this->assertEquals(21, $cart2->total_price);
282 | $this->assertEquals($item->cart->id, $cart2->id);
283 | }
284 |
285 | public function testIsEmpty()
286 | {
287 | $cart = Cart::current();
288 | DB::enableQueryLog();
289 | $this->assertTrue($cart->isEmpty());
290 | $this->assertCount(0, DB::getQueryLog());
291 | $cart->addItem([
292 | 'product_id' => 1,
293 | 'unit_price' => 10.5,
294 | 'quantity' => 2
295 | ]);
296 |
297 | $this->assertFalse($cart->isEmpty());
298 | }
299 |
300 | public function testHasItem()
301 | {
302 | $cart = Cart::current();
303 | DB::enableQueryLog();
304 | $this->assertFalse($cart->hasItem(['product_id' => 1]));
305 | $this->assertCount(1, DB::getQueryLog());
306 | $cart->addItem([
307 | 'product_id' => 1,
308 | 'unit_price' => 10.5,
309 | 'quantity' => 2
310 | ]);
311 |
312 | $this->assertTrue($cart->hasItem(['product_id' => 1]));
313 | }
314 |
315 | public function testUnknownAttribute()
316 | {
317 | $cart = Cart::current();
318 | $cart->addItem([
319 | 'foo' => 1,
320 | 'product_id' => 2,
321 | 'quantity' => 1,
322 | 'unit_price' => 0,
323 | ]);
324 | $this->assertFalse($cart->hasItem(['foo' => 1]));
325 | }
326 |
327 | public function testClear()
328 | {
329 | $cart = Cart::current();
330 | $cart->addItem([
331 | 'product_id' => 1,
332 | 'unit_price' => 10.5,
333 | 'quantity' => 2
334 | ]);
335 |
336 | DB::enableQueryLog();
337 | $cart->clear();
338 | $this->assertCount(2, DB::getQueryLog());
339 | }
340 |
341 | public function testCartStatuses()
342 | {
343 | $cart = Cart::current();
344 | $this->assertCount(1, Cart::active()->get());
345 | $this->assertCount(0, Cart::expired()->get());
346 | $this->assertCount(0, Cart::pending()->get());
347 | $this->assertCount(0, Cart::completed()->get());
348 |
349 | DB::enableQueryLog();
350 | $cart->checkout();
351 | $this->assertCount(1, DB::getQueryLog());
352 | $this->assertCount(0, Cart::active()->get());
353 | $this->assertCount(0, Cart::expired()->get());
354 | $this->assertCount(1, Cart::pending()->get());
355 | $this->assertCount(0, Cart::completed()->get());
356 |
357 | $cart->complete();
358 | $this->assertCount(0, Cart::active()->get());
359 | $this->assertCount(0, Cart::expired()->get());
360 | $this->assertCount(0, Cart::pending()->get());
361 | $this->assertCount(1, Cart::completed()->get());
362 |
363 | $cart->expire();
364 | $this->assertCount(0, Cart::active()->get());
365 | $this->assertCount(1, Cart::expired()->get());
366 | $this->assertCount(0, Cart::pending()->get());
367 | $this->assertCount(0, Cart::completed()->get());
368 | }
369 |
370 | public function testInstance()
371 | {
372 | $cart = Cart::current("cart1");
373 | $this->assertEquals($cart->id, Cart::instance("cart1")->first()->id);
374 |
375 | $cart = Cart::current("cart2");
376 | $this->assertEquals($cart->id, Cart::instance("cart2")->first()->id);
377 | }
378 |
379 | public function testCartDelete()
380 | {
381 | $cart = Cart::current();
382 | $cart->addItem([
383 | 'product_id' => 1,
384 | 'unit_price' => 10.5,
385 | 'quantity' => 2
386 | ]);
387 | DB::enableQueryLog();
388 | $cart->delete();
389 | $this->assertCount(2, DB::getQueryLog());
390 | }
391 |
392 | public function testFacade()
393 | {
394 | $cart = DBCart::current();
395 | $this->assertEquals(1, $cart->id);
396 | }
397 |
398 | public function testCleanup()
399 | {
400 | $cart = DBCart::current();
401 | $cart->addItem([
402 | 'product_id' => 1,
403 | 'unit_price' => 10.5,
404 | 'quantity' => 2
405 | ]);
406 | $cart->expire();
407 |
408 | $cmd = new CartCleanup();
409 | DB::enableQueryLog();
410 | $cmd->handle();
411 | $this->assertCount(4, DB::getQueryLog());
412 | }
413 |
414 | public function testScopeUser()
415 | {
416 | $cart = DBCart::current();
417 | $cart->user_id = 100;
418 | $cart->save();
419 | $this->assertEquals(null, Cart::active()->user()->first());
420 | $this->assertEquals(null, Cart::active()->user(200)->first());
421 | $this->assertEquals($cart->id, Cart::active()->user(100)->first()->id);
422 | }
423 |
424 | public function testSession()
425 | {
426 | $cart = DBCart::current();
427 | $cart2 = DBCart::current('another');
428 | $this->assertEquals($cart->id, Cart::session()->first()->id);
429 | $this->assertCount(2, Cart::session()->get());
430 | }
431 |
432 | protected function getPackageProviders($app)
433 | {
434 | return [CartServiceProvider::class];
435 | }
436 |
437 | protected function getEnvironmentSetUp($app)
438 | {
439 | // Setup default database to use sqlite :memory:
440 | $app['config']->set('cart.expire_cart', true);
441 | $app['config']->set('cart.delete_expired', true);
442 | $app['config']->set('database.default', 'testbench');
443 | $app['config']->set('database.connections.testbench', [
444 | 'driver' => 'sqlite',
445 | 'database' => ':memory:',
446 | 'prefix' => '',
447 | ]);
448 | $app['request']->setLaravelSession($app['session']->driver('array'));
449 | }
450 |
451 | protected function getPackageAliases($app)
452 | {
453 | return [
454 | 'DBCart' => \NabeelAlalmai\DBcart\Facades\Cart::class
455 | ];
456 | }
457 | }
458 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |