Completed
Push — master ( c367e1...ec6a94 )
by Carlos
02:13
created

Cart::remove()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of the overtrue/laravel-shopping-cart.
5
 *
6
 * (c) 2016 overtrue <[email protected]>
7
 */
8
9
namespace Overtrue\LaravelShoppingCart;
10
11
use Illuminate\Contracts\Events\Dispatcher;
12
use Illuminate\Session\SessionManager;
13
use Illuminate\Support\Collection;
14
15
/**
16
 * Main class of Overtrue\LaravelShoppingCart package.
17
 */
18
class Cart
19
{
20
    /**
21
     * Session manager.
22
     *
23
     * @var \Illuminate\Session\SessionManager
24
     */
25
    protected $session;
26
27
    /**
28
     * Event dispatcher.
29
     *
30
     * @var \Illuminate\Contracts\Events\Dispatcher
31
     */
32
    protected $event;
33
34
    /**
35
     * Current cart name.
36
     *
37
     * @var string
38
     */
39
    protected $name = 'cart.default';
40
41
    /**
42
     * Associated model name.
43
     *
44
     * @var string
45
     */
46
    protected $model;
47
48
    /**
49
     * Constructor.
50
     *
51
     * @param \Illuminate\Session\SessionManager      $session Session class name
52
     * @param \Illuminate\Contracts\Events\Dispatcher $event   Event class name
53
     */
54 13
    public function __construct(SessionManager $session, Dispatcher $event)
55
    {
56 13
        $this->session = $session;
57 13
        $this->event = $event;
58 13
    }
59
60
    /**
61
     * Set the current cart name.
62
     *
63
     * @param string $name Cart name name
64
     *
65
     * @return Cart
66
     */
67 1
    public function name($name)
68
    {
69 1
        $this->name = 'cart.'.$name;
70
71 1
        return $this;
72
    }
73
74
    /**
75
     * Associated model.
76
     *
77
     * @param string $model The name of the model
78
     *
79
     * @return Cart
80
     */
81 2
    public function associate($model)
82
    {
83 2
        if (!class_exists($model)) {
84 1
            throw new Exception("Invalid model name '$model'.");
85
        }
86 1
        $this->model = $model;
87
88 1
        return $this;
89
    }
90
91
    /**
92
     * Get all items.
93
     *
94
     * @return \Illuminate\Support\Collection
95
     */
96 2
    public function all()
97
    {
98 2
        return $this->getCart();
99
    }
100
101
    /**
102
     * Add a row to the cart.
103
     *
104
     * @param int|string $id         Unique ID of the item
105
     * @param string     $name       Name of the item
106
     * @param int        $qty        Item qty to add to the cart
107
     * @param float      $price      Price of one item
108
     * @param array      $attributes Array of additional attributes, such as 'size' or 'color'...
109
     *
110
     * @return string
111
     */
112 10
    public function add($id, $name = null, $qty = null, $price = null, array $attributes = [])
113
    {
114 10
        $cart = $this->getCart();
115
116 10
        $this->event->fire('cart.adding', [$attributes, $cart]);
117
118 10
        $row = $this->addRow($id, $name, $qty, $price, $attributes);
119
120 8
        $this->event->fire('cart.added', [$attributes, $cart]);
121
122 8
        return $row;
123
    }
124
125
    /**
126
     * Update the quantity of one row of the cart.
127
     *
128
     * @param string    $rawId     The __raw_id of the item you want to update
129
     * @param int|array $attribute New quantity of the item|Array of attributes to update
130
     *
131
     * @return Item|bool
132
     */
133 3
    public function update($rawId, $attribute)
134
    {
135 3
        if (!$row = $this->get($rawId)) {
136 1
            throw new Exception('Item not found.');
137
        }
138
139 2
        $cart = $this->getCart();
140
141 2
        $this->event->fire('cart.updating', [$row, $cart]);
142
143 2
        if (is_array($attribute)) {
144 1
            $raw = $this->updateAttribute($rawId, $attribute);
145 1
        } else {
146 2
            $raw = $this->updateQty($rawId, $attribute);
147
        }
148
149 2
        $this->event->fire('cart.updated', [$row, $cart]);
150
151 2
        return $raw;
152
    }
153
154
    /**
155
     * Remove a row from the cart.
156
     *
157
     * @param string $rawId The __raw_id of the item
158
     *
159
     * @return bool
160
     */
161 3
    public function remove($rawId)
162
    {
163 3
        if (!$row = $this->get($rawId)) {
164 1
            return true;
165
        }
166
167 3
        $cart = $this->getCart();
168
169 3
        $this->event->fire('cart.removing', [$row, $cart]);
170
171 3
        $cart->forget($rawId);
172
173 3
        $this->event->fire('cart.removed', [$row, $cart]);
174
175 3
        $this->save($cart);
176
177 3
        return true;
178
    }
179
180
    /**
181
     * Get a row of the cart by its ID.
182
     *
183
     * @param string $rawId The ID of the row to fetch
184
     *
185
     * @return Item
186
     */
187 5
    public function get($rawId)
188
    {
189 5
        $row = $this->getCart()->get($rawId);
190
191 5
        return is_null($row) ? null : new Item($row);
192
    }
193
194
    /**
195
     * Clean the cart.
196
     *
197
     * @return bool
198
     */
199 5
    public function destroy()
200
    {
201 5
        $cart = $this->getCart();
202
203 5
        $this->event->fire('cart.destroying', $cart);
204
205 5
        $this->save(null);
206
207 5
        $this->event->fire('cart.destroyed', $cart);
208
209 5
        return true;
210
    }
211
212
    /**
213
     * Alias of destory().
214
     *
215
     * @return bool
216
     */
217 1
    public function clean()
218
    {
219 1
        $this->destroy();
220 1
    }
221
222
    /**
223
     * Get the price total.
224
     *
225
     * @return float
226
     */
227 2
    public function total()
228
    {
229 2
        return $this->totalPrice();
230
    }
231
232
    /**
233
     * Return total price of cart.
234
     *
235
     * @return
236
     */
237 2
    public function totalPrice()
238
    {
239 2
        $total = 0;
240
241 2
        $cart = $this->getCart();
242
243 2
        if ($cart->isEmpty()) {
244 1
            return $total;
245
        }
246
247 2
        foreach ($cart as $row) {
248 2
            $total += $row->qty * $row->price;
249 2
        }
250
251 2
        return $total;
252
    }
253
254
    /**
255
     * Get the number of items in the cart.
256
     *
257
     * @param bool $totalItems Get all the items (when false, will return the number of rows)
258
     *
259
     * @return int
260
     */
261 2
    public function count($totalItems = true)
262
    {
263 2
        $items = $this->getCart();
264
265 2
        if (!$totalItems) {
266 2
            return $items->count();
267
        }
268
269 2
        $count = 0;
270
271 2
        foreach ($items as $row) {
272 2
            $count += $row->qty;
273 2
        }
274
275 2
        return $count;
276
    }
277
278
    /**
279
     * Get rows count.
280
     *
281
     * @return int
282
     */
283 2
    public function countRows()
284
    {
285 2
        return $this->count(false);
286
    }
287
288
    /**
289
     * Search if the cart has a item.
290
     *
291
     * @param array $search An array with the item ID and optional options
292
     *
293
     * @return array
294
     */
295 1
    public function search(array $search)
296
    {
297 1
        $rows = new Collection();
298
299 1
        if (empty($search)) {
300 1
            return $rows;
301
        }
302
303 1
        foreach ($this->getCart() as $item) {
304 1
            if (array_intersect_assoc($item->intersect($search)->toArray(), $search)) {
305 1
                $rows->put($item->__raw_id, $item);
306 1
            }
307 1
        }
308
309 1
        return $rows;
310
    }
311
312
    /**
313
     * Get current cart name.
314
     *
315
     * @return string
316
     */
317 1
    public function getName()
318
    {
319 1
        return $this->name;
320
    }
321
322
    /**
323
     * Get current associated model.
324
     *
325
     * @return string
326
     */
327 1
    public function getModel()
328
    {
329 1
        return $this->model;
330
    }
331
332
    /**
333
     * Return whether the shopping cart is empty.
334
     *
335
     * @return bool
336
     */
337
    public function isEmpty()
338
    {
339
        return $this->count() <= 0;
340
    }
341
342
    /**
343
     * Add row to the cart.
344
     *
345
     * @param string $id         Unique ID of the item
346
     * @param string $name       Name of the item
347
     * @param int    $qty        Item qty to add to the cart
348
     * @param float  $price      Price of one item
349
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
350
     *
351
     * @return string
352
     */
353 10
    protected function addRow($id, $name, $qty, $price, array $attributes = [])
354
    {
355 10
        if (!is_numeric($qty) || $qty < 1) {
356 1
            throw new Exception('Invalid quantity.');
357
        }
358
359 9
        if (!is_numeric($price) || $price < 0) {
360 1
            throw new Exception('Invalid price.');
361
        }
362
363 8
        $cart = $this->getCart();
364
365 8
        $rawId = $this->generateRawId($id, $attributes);
366
367 8
        if ($row = $cart->get($rawId)) {
368 1
            $row = $this->updateQty($rawId, $row->qty + $qty);
369 1
        } else {
370 8
            $row = $this->insertRow($rawId, $id, $name, $qty, $price, $attributes);
371
        }
372
373 8
        return $row;
374
    }
375
376
    /**
377
     * Generate a unique id for the new row.
378
     *
379
     * @param string $id         Unique ID of the item
380
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
381
     *
382
     * @return string
383
     */
384 8
    protected function generateRawId($id, $attributes)
385
    {
386 8
        ksort($attributes);
387
388 8
        return md5($id.serialize($attributes));
389
    }
390
391
    /**
392
     * Sync the cart to session.
393
     *
394
     * @param \Illuminate\Support\Collection|null $cart The new cart content
395
     *
396
     * @return \Illuminate\Support\Collection
397
     */
398 8
    protected function save($cart)
399
    {
400 8
        $this->session->put($this->name, $cart);
401
402 8
        return $cart;
403
    }
404
405
    /**
406
     * Get the carts content.
407
     *
408
     * @return \Illuminate\Support\Collection
409
     */
410 11
    protected function getCart()
411
    {
412 11
        $cart = $this->session->get($this->name);
413
414 11
        return $cart instanceof Collection ? $cart : new Collection();
415
    }
416
417
    /**
418
     * Update a row if the rawId already exists.
419
     *
420
     * @param string $rawId      The ID of the row to update
421
     * @param array  $attributes The quantity to add to the row
422
     *
423
     * @return Item
424
     */
425 3
    protected function updateRow($rawId, array $attributes)
426
    {
427 3
        $cart = $this->getCart();
428
429 3
        $row = $cart->get($rawId);
430
431 3
        foreach ($attributes as $key => $value) {
432 3
            $row->put($key, $value);
433 3
        }
434
435 3
        if (count(array_intersect(array_keys($attributes), ['qty', 'price']))) {
436 3
            $row->put('total', $row->qty * $row->price);
437 3
        }
438
439 3
        $cart->put($rawId, $row);
440
441 3
        return $row;
442
    }
443
444
    /**
445
     * Create a new row Object.
446
     *
447
     * @param string $rawId      The ID of the new row
448
     * @param string $id         Unique ID of the item
449
     * @param string $name       Name of the item
450
     * @param int    $qty        Item qty to add to the cart
451
     * @param float  $price      Price of one item
452
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
453
     *
454
     * @return Item
455
     */
456 8
    protected function insertRow($rawId, $id, $name, $qty, $price, $attributes = [])
457
    {
458 8
        $newRow = $this->makeRow($rawId, $id, $name, $qty, $price, $attributes);
459
460 8
        $cart = $this->getCart();
461
462 8
        $cart->put($rawId, $newRow);
463
464 8
        $this->save($cart);
465
466 8
        return $newRow;
467
    }
468
469
    /**
470
     * Make a row item.
471
     *
472
     * @param string $rawId      Raw id.
473
     * @param mixed  $id         Item id.
474
     * @param string $name       Item name.
475
     * @param int    $qty        Quantity.
476
     * @param float  $price      Price.
477
     * @param array  $attributes Other attributes.
478
     *
479
     * @return Item
480
     */
481 8
    protected function makeRow($rawId, $id, $name, $qty, $price, array $attributes = [])
482
    {
483 8
        return new Item(array_merge([
484 8
                                     '__raw_id' => $rawId,
485 8
                                     'id' => $id,
486 8
                                     'name' => $name,
487 8
                                     'qty' => $qty,
488 8
                                     'price' => $price,
489 8
                                     'total' => $qty * $price,
490 8
                                     '__model' => $this->model,
491 8
                                    ], $attributes));
492
    }
493
494
    /**
495
     * Update the quantity of a row.
496
     *
497
     * @param string $rawId The ID of the row
498
     * @param int    $qty   The qty to add
499
     *
500
     * @return Item|bool
501
     */
502 3
    protected function updateQty($rawId, $qty)
503
    {
504 3
        if ($qty <= 0) {
505 1
            return $this->remove($rawId);
506
        }
507
508 3
        return $this->updateRow($rawId, ['qty' => $qty]);
509
    }
510
511
    /**
512
     * Update an attribute of the row.
513
     *
514
     * @param string $rawId      The ID of the row
515
     * @param array  $attributes An array of attributes to update
516
     *
517
     * @return Item
518
     */
519 1
    protected function updateAttribute($rawId, $attributes)
520
    {
521 1
        return $this->updateRow($rawId, $attributes);
522
    }
523
}
524