Completed
Pull Request — master (#132)
by Luke
02:36 queued 12s
created

LaraCart::add()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 52
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 1
Metric Value
c 7
b 0
f 1
dl 0
loc 52
rs 7.2396
cc 7
eloc 32
nc 11
nop 7

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace LukePOLO\LaraCart;
4
5
use Illuminate\Auth\AuthManager;
6
use Illuminate\Contracts\Events\Dispatcher;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Session\SessionManager;
9
use LukePOLO\LaraCart\Contracts\CouponContract;
10
use LukePOLO\LaraCart\Contracts\LaraCartContract;
11
use LukePOLO\LaraCart\Exceptions\ModelNotFound;
12
13
/**
14
 * Class LaraCart
15
 *
16
 * @package LukePOLO\LaraCart
17
 */
18
class LaraCart implements LaraCartContract
19
{
20
    const SERVICE = 'laracart';
21
    const HASH = 'generateCartHash';
22
    const RANHASH = 'generateRandomCartItemHash';
23
24
    protected $events;
25
    protected $session;
26
    protected $authManager;
27
28
    public $cart;
29
    public $prefix;
30
    public $itemModel;
31
    public $itemModelRelations;
32
33
    /**
34
     * LaraCart constructor.
35
     *
36
     * @param SessionManager $session
37
     * @param Dispatcher $events
38
     * @param AuthManager $authManager
39
     */
40
    public function __construct(SessionManager $session, Dispatcher $events, AuthManager $authManager)
41
    {
42
        $this->session = $session;
43
        $this->events = $events;
44
        $this->authManager = $authManager;
45
        $this->prefix = config('laracart.cache_prefix', 'laracart');
46
        $this->itemModel = config('laracart.item_model', null);
47
        $this->itemModelRelations = config('laracart.item_model_relations', []);
48
49
        $this->setInstance($this->session->get($this->prefix . '.instance', 'default'));
50
    }
51
52
    /**
53
     * Gets all current instances inside the session
54
     *
55
     * @return mixed
56
     */
57
    public function getInstances()
58
    {
59
        return $this->session->get($this->prefix . '.instances', []);
60
    }
61
62
    /**
63
     * Sets and Gets the instance of the cart in the session we should be using
64
     *
65
     * @param string $instance
66
     *
67
     * @return LaraCart
68
     */
69
    public function setInstance($instance = 'default')
70
    {
71
        $this->get($instance);
72
73
        $this->session->set($this->prefix . '.instance', $instance);
74
75
        if (!in_array($instance, $this->getInstances())) {
76
            $this->session->push($this->prefix . '.instances', $instance);
77
        }
78
        $this->events->fire('laracart.new');
79
80
        return $this;
81
    }
82
83
    /**
84
     * Gets the instance in the session
85
     *
86
     * @param string $instance
87
     *
88
     * @return $this cart instance
89
     */
90
    public function get($instance = 'default')
91
    {
92
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
93
            if (!empty($cartSessionID = $this->authManager->user()->cart_session_id)) {
94
                $this->session->setId($cartSessionID);
95
                $this->session->start();
96
            }
97
        }
98
99
        if (empty($this->cart = $this->session->get($this->prefix . '.' . $instance))) {
100
            $this->cart = new Cart($instance);
101
        }
102
103
        return $this;
104
    }
105
106
    /**
107
     * Gets an an attribute from the cart
108
     *
109
     * @param $attribute
110
     * @param $defaultValue
111
     *
112
     * @return mixed
113
     */
114
    public function getAttribute($attribute, $defaultValue = null)
115
    {
116
        return array_get($this->cart->attributes, $attribute, $defaultValue);
117
    }
118
119
    /**
120
     * Gets all the carts attributes
121
     *
122
     * @return mixed
123
     */
124
    public function getAttributes()
125
    {
126
        return $this->cart->attributes;
127
    }
128
129
    /**
130
     * Adds an Attribute to the cart
131
     *
132
     * @param $attribute
133
     * @param $value
134
     */
135
    public function setAttribute($attribute, $value)
136
    {
137
        array_set($this->cart->attributes, $attribute, $value);
138
139
        $this->update();
140
    }
141
142
    /**
143
     * Updates cart session
144
     */
145
    public function update()
146
    {
147
        $this->session->set($this->prefix . '.' . $this->cart->instance, $this->cart);
148
149
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
150
            $this->authManager->user()->cart_session_id = $this->session->getId();
151
            $this->authManager->user()->save();
152
        }
153
154
        $this->session->save();
155
156
        $this->events->fire('laracart.update', $this->cart);
157
    }
158
159
    /**
160
     * Removes an attribute from the cart
161
     *
162
     * @param $attribute
163
     */
164
    public function removeAttribute($attribute)
165
    {
166
        array_forget($this->cart->attributes, $attribute);
167
168
        $this->update();
169
    }
170
171
    /**
172
     * Creates a CartItem and then adds it to cart
173
     *
174
     * @param string|int $itemID
175
     * @param null $name
176
     * @param int $qty
177
     * @param string $price
178
     * @param array $options
179
     * @param bool|true $taxable
180
     *
181
     * @return CartItem
182
     */
183
    public function addLine($itemID, $name = null, $qty = 1, $price = '0.00', $options = [], $taxable = true)
184
    {
185
        return $this->add($itemID, $name, $qty, $price, $options, $taxable, true);
186
    }
187
188
    /**
189
     * Creates a CartItem and then adds it to cart
190
     *
191
     * @param $itemID
192
     * @param null $name
193
     * @param int $qty
194
     * @param string $price
195
     * @param array $options
196
     * @param bool|false $taxable
197
     * @param bool|false $lineItem
198
     *
199
     * @return CartItem
200
     *
201
     * @throws ModelNotFound
202
     */
203
    public function add(
204
        $itemID,
205
        $name = null,
206
        $qty = 1,
207
        $price = '0.00',
208
        $options = [],
209
        $taxable = true,
210
        $lineItem = false
211
    ) {
212
213
        if (!empty(config('laracart.item_model'))) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
214
215
            $itemModel = $itemID;
216
217
            if (!$this->isItemModel($itemModel)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isItemModel($itemModel) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
218
                $itemModel = (new $this->itemModel)->with($this->itemModelRelations)->find($itemID);
219
            }
220
221
            if (empty($itemModel)) {
222
                throw new ModelNotFound('Could not find the item ' . $itemID);
223
            }
224
225
            $bindings = config('laracart.item_model_bindings');
226
227
            $itemID = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_ID]];
228
            $name = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_NAME]];
229
230
            if (empty($qty = $name) || !is_int($name)) {
231
                $qty = 1;
232
            }
233
234
            $price = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_PRICE]];
235
236
            $options = array_merge($options, $this->getItemModelOptions($itemModel, $bindings[\LukePOLO\LaraCart\CartItem::ITEM_OPTIONS]));
237
238
            $taxable = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_TAXABLE]] ? true : false;
239
        }
240
241
        $item = $this->addItem(new CartItem(
242
            $itemID,
243
            $name,
244
            $qty,
245
            $price,
246
            $options,
247
            $taxable,
248
            $lineItem
249
        ));
250
251
        $this->update();
252
253
        return $this->getItem($item->getHash());
254
    }
255
256
    /**
257
     * Adds the cartItem into the cart session
258
     *
259
     * @param CartItem $cartItem
260
     *
261
     * @return CartItem
262
     */
263
    public function addItem(CartItem $cartItem)
264
    {
265
        $itemHash = $cartItem->generateHash();
266
267
        if ($this->getItem($itemHash)) {
268
            $this->getItem($itemHash)->qty += $cartItem->qty;
269
        } else {
270
            $this->cart->items[] = $cartItem;
271
        }
272
273
        $this->events->fire('laracart.addItem', $cartItem);
274
275
        return $cartItem;
276
    }
277
278
    /**
279
     * Increment the quantity of a cartItem based on the itemHash
280
     *
281
     * @param $itemHash
282
     *
283
     * @return CartItem | null
284
     */
285
    public function increment($itemHash)
286
    {
287
        $item = $this->getItem($itemHash);
288
        $item->qty++;
289
        $this->update();
290
291
        return $item;
292
    }
293
294
    /**
295
     * Decrement the quantity of a cartItem based on the itemHash
296
     *
297
     * @param $itemHash
298
     *
299
     * @return CartItem | null
300
     */
301
    public function decrement($itemHash)
302
    {
303
        $item = $this->getItem($itemHash);
304
        if ($item->qty > 1) {
305
            $item->qty--;
306
            $this->update();
307
308
            return $item;
309
        }
310
        $this->removeItem($itemHash);
311
        $this->update();
312
313
        return null;
314
    }
315
316
    /**
317
     * Find items in the cart matching a data set
318
     *
319
     *
320
     * param $data
321
     * @return array
322
     */
323
    public function find($data)
324
    {
325
        $matches = [];
326
327
        foreach ($this->getItems() as $item) {
328
            if ($item->find($data)) {
329
                $matches[] = $item;
330
            }
331
        }
332
333
        return $matches;
334
    }
335
336
    /**
337
     * Finds a cartItem based on the itemHash
338
     *
339
     * @param $itemHash
340
     *
341
     * @return CartItem | null
342
     */
343
    public function getItem($itemHash)
344
    {
345
        return array_get($this->getItems(), $itemHash);
346
    }
347
348
    /**
349
     * Gets all the items within the cart
350
     *
351
     * @return array
352
     */
353
    public function getItems()
354
    {
355
        $items = [];
356
        if (isset($this->cart->items) === true) {
357
            foreach ($this->cart->items as $item) {
358
                $items[$item->getHash()] = $item;
359
            }
360
        }
361
362
        return $items;
363
    }
364
365
    /**
366
     * Updates an items attributes
367
     *
368
     * @param $itemHash
369
     * @param $key
370
     * @param $value
371
     *
372
     * @return CartItem
373
     *
374
     * @throws Exceptions\InvalidPrice
375
     * @throws Exceptions\InvalidQuantity
376
     */
377
    public function updateItem($itemHash, $key, $value)
378
    {
379
        if (empty($item = $this->getItem($itemHash)) === false) {
380
            $item->$key = $value;
381
        }
382
383
        $item->generateHash();
384
385
        $this->update();
386
387
        return $item;
388
    }
389
390
    /**
391
     * Removes a CartItem based on the itemHash
392
     *
393
     * @param $itemHash
394
     */
395
    public function removeItem($itemHash)
396
    {
397
        foreach ($this->cart->items as $itemKey => $item) {
398
            if ($item->getHash() == $itemHash) {
399
                unset($this->cart->items[$itemKey]);
400
                break;
401
            }
402
        }
403
404
        $this->events->fire('laracart.removeItem', $item);
0 ignored issues
show
Bug introduced by
The variable $item seems to be defined by a foreach iteration on line 397. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
405
406
        $this->update();
407
    }
408
409
    /**
410
     * Empties the carts items
411
     */
412
    public function emptyCart()
413
    {
414
        unset($this->cart->items);
415
416
        $this->update();
417
418
        $this->events->fire('laracart.empty', $this->cart->instance);
419
    }
420
421
    /**
422
     * Completely destroys cart and anything associated with it
423
     */
424
    public function destroyCart()
425
    {
426
        $instance = $this->cart->instance;
427
428
        $this->session->forget($this->prefix . '.' . $instance);
429
430
        $this->setInstance('default');
431
432
        $this->events->fire('laracart.destroy', $instance);
433
434
        $this->update();
435
    }
436
437
    /**
438
     * Gets the coupons for the current cart
439
     *
440
     * @return array
441
     */
442
    public function getCoupons()
443
    {
444
        return $this->cart->coupons;
445
    }
446
447
    /**
448
     * Finds a specific coupon in the cart
449
     *
450
     * @param $code
451
     * @return mixed
452
     */
453
    public function findCoupon($code)
454
    {
455
        return array_get($this->cart->coupons, $code);
456
    }
457
458
    /**
459
     * Applies a coupon to the cart
460
     *
461
     * @param CouponContract $coupon
462
     */
463
    public function addCoupon(CouponContract $coupon)
464
    {
465
        if (!$this->cart->multipleCoupons) {
466
            $this->cart->coupons = [];
467
        }
468
469
        $this->cart->coupons[$coupon->code] = $coupon;
0 ignored issues
show
Bug introduced by
Accessing code on the interface LukePOLO\LaraCart\Contracts\CouponContract suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
470
471
        $this->update();
472
    }
473
474
    /**
475
     * Removes a coupon in the cart
476
     *
477
     * @param $code
478
     */
479
    public function removeCoupon($code)
480
    {
481
        foreach ($this->getItems() as $item) {
482
            if (isset($item->code) && $item->code == $code) {
483
                $item->code = null;
484
                $item->discount = null;
485
                $item->couponInfo = [];
486
            }
487
        }
488
489
        array_forget($this->cart->coupons, $code);
490
491
        $this->update();
492
    }
493
494
    /**
495
     * Gets a specific fee from the fees array
496
     *
497
     * @param $name
498
     *
499
     * @return mixed
500
     */
501
    public function getFee($name)
502
    {
503
        return array_get($this->cart->fees, $name, new CartFee(null, false));
504
    }
505
506
    /**
507
     * Allows to charge for additional fees that may or may not be taxable
508
     * ex - service fee , delivery fee, tips
509
     *
510
     * @param $name
511
     * @param $amount
512
     * @param bool|false $taxable
513
     * @param array $options
514
     */
515
    public function addFee($name, $amount, $taxable = false, array $options = [])
516
    {
517
        array_set($this->cart->fees, $name, new CartFee($amount, $taxable, $options));
518
519
        $this->update();
520
    }
521
522
    /**
523
     * Removes a fee from the fee array
524
     *
525
     * @param $name
526
     */
527
    public function removeFee($name)
528
    {
529
        array_forget($this->cart->fees, $name);
530
531
        $this->update();
532
    }
533
534
    /**
535
     * Removes all the fees set in the cart
536
     *
537
     */
538
    public function removeFees()
539
    {
540
        $this->cart->fees = [];
541
542
        $this->update();
543
    }
544
545
    /**
546
     * Gets the total tax for the cart
547
     *
548
     * @param bool|true $format
549
     *
550
     * @return string
551
     */
552
    public function taxTotal($format = true)
553
    {
554
        $totalTax = 0;
555
        $discounted = 0;
556
        $totalDiscount = $this->totalDiscount(false);
557
558
        if ($this->count() != 0) {
559
            foreach ($this->getItems() as $item) {
560
                if ($discounted >= $totalDiscount) {
561
                    $totalTax += $item->tax();
562
                } else {
563
                    $itemPrice = $item->subTotal(false);
564
565
                    if (($discounted + $itemPrice) > $totalDiscount) {
566
                        $totalTax += $item->tax($totalDiscount - $discounted);
567
                    }
568
569
                    $discounted += $itemPrice;
570
                }
571
            }
572
        }
573
574
        foreach ($this->getFees() as $fee) {
575
            if ($fee->taxable) {
576
                $totalTax += $fee->amount * $fee->tax;
577
            }
578
        }
579
580
        return $this->formatMoney($totalTax, null, null, $format);
581
    }
582
583
    /**
584
     * Gets the total of the cart with or without tax
585
     *
586
     * @param boolean $format
587
     * @param boolean $withDiscount
588
     *
589
     * @return string
590
     */
591
    public function total($format = true, $withDiscount = true, $withTax = true)
592
    {
593
        $total = $this->subTotal(false) + $this->feeTotals(false);
594
595
        if ($withDiscount) {
596
            $total -= $this->totalDiscount(false);
597
        }
598
599
        if ($withTax) {
600
            $total += $this->taxTotal(false);
601
        }
602
603
        return $this->formatMoney($total, null, null, $format);
604
    }
605
606
    /**
607
     * Gets the subtotal of the cart with or without tax
608
     *
609
     * @param boolean $format
610
     * @param boolean $withDiscount
611
     *
612
     * @return string
613
     */
614
    public function subTotal($format = true, $withDiscount = true)
615
    {
616
        $total = 0;
617
618
        if ($this->count() != 0) {
619
            foreach ($this->getItems() as $item) {
620
                $total += $item->subTotal(false, $withDiscount);
621
            }
622
        }
623
624
        return $this->formatMoney($total, null, null, $format);
625
    }
626
627
    /**
628
     * Get the count based on qty, or number of unique items
629
     *
630
     * @param bool $withItemQty
631
     *
632
     * @return int
633
     */
634
    public function count($withItemQty = true)
635
    {
636
        $count = 0;
637
638
        foreach ($this->getItems() as $item) {
639
            if ($withItemQty) {
640
                $count += $item->qty;
641
            } else {
642
                $count++;
643
            }
644
        }
645
646
        return $count;
647
    }
648
649
    /**
650
     *
651
     * Formats the number into a money format based on the locale and international formats
652
     *
653
     * @param $number
654
     * @param $locale
655
     * @param $internationalFormat
656
     * @param $format
657
     *
658
     * @return string
659
     */
660
    public static function formatMoney($number, $locale = null, $internationalFormat = null, $format = true)
661
    {
662
        $number = number_format($number, 2, '.', '');
663
664
        if ($format) {
665
            setlocale(LC_MONETARY, null);
666
            setlocale(LC_MONETARY, empty($locale) ? config('laracart.locale', 'en_US.UTF-8') : $locale);
667
668
            if (empty($internationalFormat) === true) {
669
                $internationalFormat = config('laracart.international_format', false);
670
            }
671
672
            $number = money_format($internationalFormat ? '%i' : '%n', $number);
673
        }
674
675
        return $number;
676
    }
677
678
    /**
679
     * Gets all the fee totals
680
     *
681
     * @param boolean $format
682
     *
683
     * @return string
684
     */
685
    public function feeTotals($format = true, $withTax = false)
686
    {
687
        $feeTotal = 0;
688
689
        foreach ($this->getFees() as $fee) {
690
            $feeTotal += $fee->amount;
691
692
            if ($withTax && $fee->taxable && $fee->tax > 0) {
693
                $feeTotal += $fee->amount * $fee->tax;
694
            }
695
        }
696
697
        return $this->formatMoney($feeTotal, null, null, $format);
698
    }
699
700
    /**
701
     * Gets all the fees on the cart object
702
     *
703
     * @return mixed
704
     */
705
    public function getFees()
706
    {
707
        return $this->cart->fees;
708
    }
709
710
    /**
711
     * Gets the total amount discounted
712
     *
713
     * @param boolean $format
714
     *
715
     * @return string
716
     */
717
    public function totalDiscount($format = true)
718
    {
719
        $total = 0;
720
721
        foreach ($this->cart->coupons as $coupon) {
722
            if ($coupon->appliedToCart) {
723
                $total += $coupon->discount();
724
            }
725
        }
726
727
        return $this->formatMoney($total, null, null, $format);
728
    }
729
730
    /**
731
     * Checks to see if its an item model
732
     *
733
     * @param $itemModel
734
     *
735
     * @return bool
736
     */
737
    private function isItemModel($itemModel)
738
    {
739
        if (is_object($itemModel) && get_class($itemModel) == config('laracart.item_model')) {
740
            return true;
741
        }
742
    }
743
744
    /**
745
     * Gets the item models options based the config
746
     *
747
     * @param Model $itemModel
748
     * @param array $options
749
     *
750
     * @return array
751
     */
752
    private function getItemModelOptions(Model $itemModel, $options = [])
753
    {
754
        $itemOptions = [];
755
        foreach ($options as $option) {
756
            $itemOptions[$option] = $this->getFromModel($itemModel, $option);
757
        }
758
759
        return array_filter($itemOptions, function ($value) {
760
            if ($value !== false && empty($value)) {
761
                return false;
762
            }
763
764
            return true;
765
        });
766
    }
767
768
    /**
769
     * Gets a option from the model
770
     *
771
     * @param Model $itemModel
772
     * @param $attr
773
     * @param null $defaultValue
774
     *
775
     * @return Model|null
776
     */
777
    private function getFromModel(Model $itemModel, $attr, $defaultValue = null)
778
    {
779
        $variable = $itemModel;
780
781
        if (!empty($attr)) {
782
            foreach (explode('.', $attr) as $attr) {
783
                $variable = array_get($variable, $attr);
784
            }
785
        }
786
787
        if (empty($variable)) {
788
            $variable = $defaultValue;
789
        }
790
        return $variable;
791
    }
792
793
}
794