Completed
Push — master ( 73da4d...6c7af5 )
by Carlos
05:40 queued 01:38
created

Cart::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 1
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
     * Add row to the cart.
334
     *
335
     * @param string $id         Unique ID of the item
336
     * @param string $name       Name of the item
337
     * @param int    $qty        Item qty to add to the cart
338
     * @param float  $price      Price of one item
339
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
340
     *
341
     * @return string
342
     */
343 10
    protected function addRow($id, $name, $qty, $price, array $attributes = [])
344
    {
345 10
        if (!is_numeric($qty) || $qty < 1) {
346 1
            throw new Exception('Invalid quantity.');
347
        }
348
349 9
        if (!is_numeric($price) || $price < 0) {
350 1
            throw new Exception('Invalid price.');
351
        }
352
353 8
        $cart = $this->getCart();
354
355 8
        $rawId = $this->generateRawId($id, $attributes);
356
357 8
        if ($row = $cart->get($rawId)) {
358 1
            $row = $this->updateQty($rawId, $row->qty + $qty);
359 1
        } else {
360 8
            $row = $this->insertRow($rawId, $id, $name, $qty, $price, $attributes);
361
        }
362
363 8
        return $row;
364
    }
365
366
    /**
367
     * Generate a unique id for the new row.
368
     *
369
     * @param string $id         Unique ID of the item
370
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
371
     *
372
     * @return string
373
     */
374 8
    protected function generateRawId($id, $attributes)
375
    {
376 8
        ksort($attributes);
377
378 8
        return md5($id.serialize($attributes));
379
    }
380
381
    /**
382
     * Sync the cart to session.
383
     *
384
     * @param \Illuminate\Support\Collection|null $cart The new cart content
385
     *
386
     * @return \Illuminate\Support\Collection
387
     */
388 8
    protected function save($cart)
389
    {
390 8
        $this->session->put($this->name, $cart);
391
392 8
        return $cart;
393
    }
394
395
    /**
396
     * Get the carts content.
397
     *
398
     * @return \Illuminate\Support\Collection
399
     */
400 11
    protected function getCart()
401
    {
402 11
        $cart = $this->session->get($this->name);
403
404 11
        return $cart instanceof Collection ? $cart : new Collection();
405
    }
406
407
    /**
408
     * Update a row if the rawId already exists.
409
     *
410
     * @param string $rawId      The ID of the row to update
411
     * @param array  $attributes The quantity to add to the row
412
     *
413
     * @return Item
414
     */
415 3
    protected function updateRow($rawId, array $attributes)
416
    {
417 3
        $cart = $this->getCart();
418
419 3
        $row = $cart->get($rawId);
420
421 3
        foreach ($attributes as $key => $value) {
422 3
            $row->put($key, $value);
423 3
        }
424
425 3
        if (count(array_intersect(array_keys($attributes), ['qty', 'price']))) {
426 3
            $row->put('total', $row->qty * $row->price);
427 3
        }
428
429 3
        $cart->put($rawId, $row);
430
431 3
        return $row;
432
    }
433
434
    /**
435
     * Create a new row Object.
436
     *
437
     * @param string $rawId      The ID of the new row
438
     * @param string $id         Unique ID of the item
439
     * @param string $name       Name of the item
440
     * @param int    $qty        Item qty to add to the cart
441
     * @param float  $price      Price of one item
442
     * @param array  $attributes Array of additional options, such as 'size' or 'color'
443
     *
444
     * @return Item
445
     */
446 8
    protected function insertRow($rawId, $id, $name, $qty, $price, $attributes = [])
447
    {
448 8
        $newRow = $this->makeRow($rawId, $id, $name, $qty, $price, $attributes);
449
450 8
        $cart = $this->getCart();
451
452 8
        $cart->put($rawId, $newRow);
453
454 8
        $this->save($cart);
455
456 8
        return $newRow;
457
    }
458
459
    /**
460
     * Make a row item.
461
     *
462
     * @param string $rawId      Raw id.
463
     * @param mixed  $id         Item id.
464
     * @param string $name       Item name.
465
     * @param int    $qty        Quantity.
466
     * @param float  $price      Price.
467
     * @param array  $attributes Other attributes.
468
     *
469
     * @return Item
470
     */
471 8
    protected function makeRow($rawId, $id, $name, $qty, $price, array $attributes = [])
472
    {
473 8
        return new Item(array_merge([
474 8
                                     '__raw_id' => $rawId,
475 8
                                     'id' => $id,
476 8
                                     'name' => $name,
477 8
                                     'qty' => $qty,
478 8
                                     'price' => $price,
479 8
                                     'total' => $qty * $price,
480 8
                                     '__model' => $this->model,
481 8
                                    ], $attributes));
482
    }
483
484
    /**
485
     * Update the quantity of a row.
486
     *
487
     * @param string $rawId The ID of the row
488
     * @param int    $qty   The qty to add
489
     *
490
     * @return Item|bool
491
     */
492 3
    protected function updateQty($rawId, $qty)
493
    {
494 3
        if ($qty <= 0) {
495 1
            return $this->remove($rawId);
496
        }
497
498 3
        return $this->updateRow($rawId, ['qty' => $qty]);
499
    }
500
501
    /**
502
     * Update an attribute of the row.
503
     *
504
     * @param string $rawId      The ID of the row
505
     * @param array  $attributes An array of attributes to update
506
     *
507
     * @return Item
508
     */
509 1
    protected function updateAttribute($rawId, $attributes)
510
    {
511 1
        return $this->updateRow($rawId, $attributes);
512
    }
513
}
514