LaraCart::increment()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 1
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
class LaraCart implements LaraCartContract
17
{
18
    const SERVICE = 'laracart';
19
    const HASH = 'generateCartHash';
20
    const RANHASH = 'generateRandomCartItemHash';
21
22
    protected $events;
23
    protected $session;
24
    protected $authManager;
25
26
    public $cart;
27
    public $prefix;
28
    public $itemModel;
29
    public $itemModelRelations;
30
31
    /**
32
     * LaraCart constructor.
33
     *
34
     * @param SessionManager $session
35
     * @param Dispatcher     $events
36
     * @param AuthManager    $authManager
37
     */
38
    public function __construct(SessionManager $session, Dispatcher $events, AuthManager $authManager)
39
    {
40
        $this->session = $session;
41
        $this->events = $events;
42
        $this->authManager = $authManager;
43
        $this->prefix = config('laracart.cache_prefix', 'laracart');
44
        $this->itemModel = config('laracart.item_model', null);
45
        $this->itemModelRelations = config('laracart.item_model_relations', []);
46
47
        $this->setInstance($this->session->get($this->prefix.'.instance', 'default'));
48
    }
49
50
    /**
51
     * Gets all current instances inside the session.
52
     *
53
     * @return mixed
54
     */
55
    public function getInstances()
56
    {
57
        return $this->session->get($this->prefix.'.instances', []);
58
    }
59
60
    /**
61
     * Sets and Gets the instance of the cart in the session we should be using.
62
     *
63
     * @param string $instance
64
     *
65
     * @return LaraCart
66
     */
67
    public function setInstance($instance = 'default')
68
    {
69
        $this->get($instance);
70
71
        $this->session->set($this->prefix.'.instance', $instance);
72
73
        if (!in_array($instance, $this->getInstances())) {
74
            $this->session->push($this->prefix.'.instances', $instance);
75
        }
76
        $this->events->fire('laracart.new');
77
78
        return $this;
79
    }
80
81
    /**
82
     * Gets the instance in the session.
83
     *
84
     * @param string $instance
85
     *
86
     * @return $this cart instance
87
     */
88
    public function get($instance = 'default')
89
    {
90
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
91
            if (!empty($cartSessionID = $this->authManager->user()->cart_session_id)) {
92
                $this->session->setId($cartSessionID);
93
                $this->session->start();
94
            }
95
        }
96
97
        if (empty($this->cart = $this->session->get($this->prefix.'.'.$instance))) {
98
            $this->cart = new Cart($instance);
99
        }
100
101
        return $this;
102
    }
103
104
    /**
105
     * Gets an an attribute from the cart.
106
     *
107
     * @param $attribute
108
     * @param $defaultValue
109
     *
110
     * @return mixed
111
     */
112
    public function getAttribute($attribute, $defaultValue = null)
113
    {
114
        return array_get($this->cart->attributes, $attribute, $defaultValue);
115
    }
116
117
    /**
118
     * Gets all the carts attributes.
119
     *
120
     * @return mixed
121
     */
122
    public function getAttributes()
123
    {
124
        return $this->cart->attributes;
125
    }
126
127
    /**
128
     * Adds an Attribute to the cart.
129
     *
130
     * @param $attribute
131
     * @param $value
132
     */
133
    public function setAttribute($attribute, $value)
134
    {
135
        array_set($this->cart->attributes, $attribute, $value);
136
137
        $this->update();
138
    }
139
140
    /**
141
     * Updates cart session.
142
     */
143
    public function update()
144
    {
145
        $this->session->set($this->prefix.'.'.$this->cart->instance, $this->cart);
146
147
        if (config('laracart.cross_devices', false) && $this->authManager->check()) {
148
            $this->authManager->user()->cart_session_id = $this->session->getId();
149
            $this->authManager->user()->save();
150
        }
151
152
        $this->session->save();
153
154
        $this->events->fire('laracart.update', $this->cart);
155
    }
156
157
    /**
158
     * Removes an attribute from the cart.
159
     *
160
     * @param $attribute
161
     */
162
    public function removeAttribute($attribute)
163
    {
164
        array_forget($this->cart->attributes, $attribute);
165
166
        $this->update();
167
    }
168
169
    /**
170
     * Creates a CartItem and then adds it to cart.
171
     *
172
     * @param string|int $itemID
173
     * @param null       $name
174
     * @param int        $qty
175
     * @param string     $price
176
     * @param array      $options
177
     * @param bool|true  $taxable
178
     *
179
     * @return CartItem
180
     */
181
    public function addLine($itemID, $name = null, $qty = 1, $price = '0.00', $options = [], $taxable = true)
182
    {
183
        return $this->add($itemID, $name, $qty, $price, $options, $taxable, true);
184
    }
185
186
    /**
187
     * Creates a CartItem and then adds it to cart.
188
     *
189
     * @param $itemID
190
     * @param null       $name
191
     * @param int        $qty
192
     * @param string     $price
193
     * @param array      $options
194
     * @param bool|false $taxable
195
     * @param bool|false $lineItem
196
     *
197
     * @throws ModelNotFound
198
     *
199
     * @return CartItem
200
     */
201
    public function add(
202
        $itemID,
203
        $name = null,
204
        $qty = 1,
205
        $price = '0.00',
206
        $options = [],
207
        $taxable = true,
208
        $lineItem = false
209
    ) {
210
        if (!empty(config('laracart.item_model'))) {
211
            $itemModel = $itemID;
212
213
            if (!$this->isItemModel($itemModel)) {
214
                $itemModel = (new $this->itemModel())->with($this->itemModelRelations)->find($itemID);
215
            }
216
217
            if (empty($itemModel)) {
218
                throw new ModelNotFound('Could not find the item '.$itemID);
219
            }
220
221
            $bindings = config('laracart.item_model_bindings');
222
223
            $itemID = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_ID]];
224
            $name = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_NAME]];
225
226
            if (empty($qty = $name) || !is_int($name)) {
227
                $qty = 1;
228
            }
229
230
            $price = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_PRICE]];
231
232
            $options = array_merge($options,
233
                $this->getItemModelOptions($itemModel, $bindings[\LukePOLO\LaraCart\CartItem::ITEM_OPTIONS]));
234
235
            $taxable = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_TAXABLE]] ? true : false;
236
        }
237
238
        $item = $this->addItem(new CartItem(
239
            $itemID,
240
            $name,
241
            $qty,
242
            $price,
243
            $options,
244
            $taxable,
245
            $lineItem
246
        ));
247
248
        $this->update();
249
250
        return $this->getItem($item->getHash());
251
    }
252
253
    /**
254
     * Adds the cartItem into the cart session.
255
     *
256
     * @param CartItem $cartItem
257
     *
258
     * @return CartItem
259
     */
260
    public function addItem(CartItem $cartItem)
261
    {
262
        $itemHash = $cartItem->generateHash();
263
264
        if ($this->getItem($itemHash)) {
265
            $this->getItem($itemHash)->qty += $cartItem->qty;
266
        } else {
267
            $this->cart->items[] = $cartItem;
268
        }
269
270
        $this->events->fire('laracart.addItem', $cartItem);
271
272
        return $cartItem;
273
    }
274
275
    /**
276
     * Increment the quantity of a cartItem based on the itemHash.
277
     *
278
     * @param $itemHash
279
     *
280
     * @return CartItem | null
281
     */
282
    public function increment($itemHash)
283
    {
284
        $item = $this->getItem($itemHash);
285
        $item->qty++;
286
        $this->update();
287
288
        return $item;
289
    }
290
291
    /**
292
     * Decrement the quantity of a cartItem based on the itemHash.
293
     *
294
     * @param $itemHash
295
     *
296
     * @return CartItem | null
297
     */
298
    public function decrement($itemHash)
299
    {
300
        $item = $this->getItem($itemHash);
301
        if ($item->qty > 1) {
302
            $item->qty--;
303
            $this->update();
304
305
            return $item;
306
        }
307
        $this->removeItem($itemHash);
308
        $this->update();
309
    }
310
311
    /**
312
     * Find items in the cart matching a data set.
313
     *
314
     *
315
     * param $data
316
     *
317
     * @return array
318
     */
319
    public function find($data)
320
    {
321
        $matches = [];
322
323
        foreach ($this->getItems() as $item) {
324
            if ($item->find($data)) {
325
                $matches[] = $item;
326
            }
327
        }
328
329
        return $matches;
330
    }
331
332
    /**
333
     * Finds a cartItem based on the itemHash.
334
     *
335
     * @param $itemHash
336
     *
337
     * @return CartItem | null
338
     */
339
    public function getItem($itemHash)
340
    {
341
        return array_get($this->getItems(), $itemHash);
342
    }
343
344
    /**
345
     * Gets all the items within the cart.
346
     *
347
     * @return array
348
     */
349
    public function getItems()
350
    {
351
        $items = [];
352
        if (isset($this->cart->items) === true) {
353
            foreach ($this->cart->items as $item) {
354
                $items[$item->getHash()] = $item;
355
            }
356
        }
357
358
        return $items;
359
    }
360
361
    /**
362
     * Updates an items attributes.
363
     *
364
     * @param $itemHash
365
     * @param $key
366
     * @param $value
367
     *
368
     * @throws Exceptions\InvalidPrice
369
     * @throws Exceptions\InvalidQuantity
370
     *
371
     * @return CartItem
372
     */
373
    public function updateItem($itemHash, $key, $value)
374
    {
375
        if (empty($item = $this->getItem($itemHash)) === false) {
376
            $item->$key = $value;
377
        }
378
379
        $item->generateHash();
380
381
        $this->update();
382
383
        return $item;
384
    }
385
386
    /**
387
     * Removes a CartItem based on the itemHash.
388
     *
389
     * @param $itemHash
390
     */
391
    public function removeItem($itemHash)
392
    {
393
        foreach ($this->cart->items as $itemKey => $item) {
394
            if ($item->getHash() == $itemHash) {
395
                unset($this->cart->items[$itemKey]);
396
                break;
397
            }
398
        }
399
400
        $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 393. 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...
401
402
        $this->update();
403
    }
404
405
    /**
406
     * Empties the carts items.
407
     */
408
    public function emptyCart()
409
    {
410
        unset($this->cart->items);
411
412
        $this->update();
413
414
        $this->events->fire('laracart.empty', $this->cart->instance);
415
    }
416
417
    /**
418
     * Completely destroys cart and anything associated with it.
419
     */
420
    public function destroyCart()
421
    {
422
        $instance = $this->cart->instance;
423
424
        $this->session->forget($this->prefix.'.'.$instance);
425
426
        $this->setInstance('default');
427
428
        $this->events->fire('laracart.destroy', $instance);
429
430
        $this->update();
431
    }
432
433
    /**
434
     * Gets the coupons for the current cart.
435
     *
436
     * @return array
437
     */
438
    public function getCoupons()
439
    {
440
        return $this->cart->coupons;
441
    }
442
443
    /**
444
     * Finds a specific coupon in the cart.
445
     *
446
     * @param $code
447
     *
448
     * @return mixed
449
     */
450
    public function findCoupon($code)
451
    {
452
        return array_get($this->cart->coupons, $code);
453
    }
454
455
    /**
456
     * Applies a coupon to the cart.
457
     *
458
     * @param CouponContract $coupon
459
     */
460
    public function addCoupon(CouponContract $coupon)
461
    {
462
        if (!$this->cart->multipleCoupons) {
463
            $this->cart->coupons = [];
464
        }
465
466
        $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...
467
468
        $this->update();
469
    }
470
471
    /**
472
     * Removes a coupon in the cart.
473
     *
474
     * @param $code
475
     */
476
    public function removeCoupon($code)
477
    {
478
        $this->removeCouponFromItems($code);
479
        array_forget($this->cart->coupons, $code);
480
        $this->update();
481
    }
482
483
    /**
484
     * Removes all coupons from the cart.
485
     */
486
    public function removeCoupons()
487
    {
488
        $this->removeCouponFromItems();
489
        $this->cart->coupons = [];
490
        $this->update();
491
    }
492
493
    /**
494
     * Gets a specific fee from the fees array.
495
     *
496
     * @param $name
497
     *
498
     * @return mixed
499
     */
500
    public function getFee($name)
501
    {
502
        return array_get($this->cart->fees, $name, new CartFee(null, false));
503
    }
504
505
    /**
506
     * Allows to charge for additional fees that may or may not be taxable
507
     * ex - service fee , delivery fee, tips.
508
     *
509
     * @param $name
510
     * @param $amount
511
     * @param bool|false $taxable
512
     * @param array      $options
513
     */
514
    public function addFee($name, $amount, $taxable = false, array $options = [])
515
    {
516
        array_set($this->cart->fees, $name, new CartFee($amount, $taxable, $options));
517
518
        $this->update();
519
    }
520
521
    /**
522
     * Removes a fee from the fee array.
523
     *
524
     * @param $name
525
     */
526
    public function removeFee($name)
527
    {
528
        array_forget($this->cart->fees, $name);
529
530
        $this->update();
531
    }
532
533
    /**
534
     * Removes all the fees set in the cart.
535
     */
536
    public function removeFees()
537
    {
538
        $this->cart->fees = [];
539
540
        $this->update();
541
    }
542
543
    /**
544
     * Gets the total tax for the cart.
545
     *
546
     * @param bool|true $format
547
     *
548
     * @return string
549
     */
550
    public function taxTotal($format = true)
551
    {
552
        $totalTax = 0;
553
        $discounted = 0;
554
        $totalDiscount = $this->totalDiscount(false);
555
556
        if ($this->count() != 0) {
557
            foreach ($this->getItems() as $item) {
558
                if ($discounted >= $totalDiscount) {
559
                    $totalTax += $item->tax();
560
                } else {
561
                    $itemPrice = $item->subTotal(false);
562
563
                    if (($discounted + $itemPrice) > $totalDiscount) {
564
                        $totalTax += $item->tax($totalDiscount - $discounted);
565
                    }
566
567
                    $discounted += $itemPrice;
568
                }
569
            }
570
        }
571
572
        foreach ($this->getFees() as $fee) {
573
            if ($fee->taxable) {
574
                $totalTax += $fee->amount * $fee->tax;
575
            }
576
        }
577
578
        return $this->formatMoney($totalTax, null, null, $format);
579
    }
580
581
    /**
582
     * Gets the total of the cart with or without tax.
583
     *
584
     * @param bool $format
585
     * @param bool $withDiscount
586
     *
587
     * @return string
588
     */
589
    public function total($format = true, $withDiscount = true, $withTax = true)
590
    {
591
        $total = $this->subTotal(false) + $this->feeTotals(false);
592
593
        if ($withDiscount) {
594
            $total -= $this->totalDiscount(false);
595
        }
596
597
        if ($withTax) {
598
            $total += $this->taxTotal(false);
599
        }
600
601
        return $this->formatMoney($total, null, null, $format);
602
    }
603
604
    /**
605
     * Gets the subtotal of the cart with or without tax.
606
     *
607
     * @param bool $format
608
     * @param bool $withDiscount
609
     *
610
     * @return string
611
     */
612
    public function subTotal($format = true, $withDiscount = true)
613
    {
614
        $total = 0;
615
616
        if ($this->count() != 0) {
617
            foreach ($this->getItems() as $item) {
618
                $total += $item->subTotal(false, $withDiscount);
619
            }
620
        }
621
622
        return $this->formatMoney($total, null, null, $format);
623
    }
624
625
    /**
626
     * Get the count based on qty, or number of unique items.
627
     *
628
     * @param bool $withItemQty
629
     *
630
     * @return int
631
     */
632
    public function count($withItemQty = true)
633
    {
634
        $count = 0;
635
636
        foreach ($this->getItems() as $item) {
637
            if ($withItemQty) {
638
                $count += $item->qty;
639
            } else {
640
                $count++;
641
            }
642
        }
643
644
        return $count;
645
    }
646
647
    /**
648
     * Formats the number into a money format based on the locale and international formats.
649
     *
650
     * @param $number
651
     * @param $locale
652
     * @param $internationalFormat
653
     * @param $format
654
     *
655
     * @return string
656
     */
657
    public static function formatMoney($number, $locale = null, $internationalFormat = null, $format = true)
658
    {
659
        $number = number_format($number, 2, '.', '');
660
661
        if ($format) {
662
            setlocale(LC_MONETARY, null);
663
            setlocale(LC_MONETARY, empty($locale) ? config('laracart.locale', 'en_US.UTF-8') : $locale);
664
665
            if (empty($internationalFormat) === true) {
666
                $internationalFormat = config('laracart.international_format', false);
667
            }
668
669
            $number = money_format($internationalFormat ? '%i' : '%n', $number);
670
        }
671
672
        return $number;
673
    }
674
675
    /**
676
     * Gets all the fee totals.
677
     *
678
     * @param bool $format
679
     *
680
     * @return string
681
     */
682
    public function feeTotals($format = true, $withTax = false)
683
    {
684
        $feeTotal = 0;
685
686
        foreach ($this->getFees() as $fee) {
687
            $feeTotal += $fee->amount;
688
689
            if ($withTax && $fee->taxable && $fee->tax > 0) {
690
                $feeTotal += $fee->amount * $fee->tax;
691
            }
692
        }
693
694
        return $this->formatMoney($feeTotal, null, null, $format);
695
    }
696
697
    /**
698
     * Gets all the fees on the cart object.
699
     *
700
     * @return mixed
701
     */
702
    public function getFees()
703
    {
704
        return $this->cart->fees;
705
    }
706
707
    /**
708
     * Gets the total amount discounted.
709
     *
710
     * @param bool $format
711
     *
712
     * @return string
713
     */
714
    public function totalDiscount($format = true)
715
    {
716
        $total = 0;
717
718
        foreach ($this->cart->coupons as $coupon) {
719
            if ($coupon->appliedToCart) {
720
                $total += $coupon->discount();
721
            }
722
        }
723
724
        return $this->formatMoney($total, null, null, $format);
725
    }
726
727
    /**
728
     * Checks to see if its an item model.
729
     *
730
     * @param $itemModel
731
     *
732
     * @return bool
733
     */
734
    private function isItemModel($itemModel)
735
    {
736
        if (is_object($itemModel) && get_class($itemModel) == config('laracart.item_model')) {
737
            return true;
738
        }
739
740
        return false;
741
    }
742
743
    /**
744
     * Gets the item models options based the config.
745
     *
746
     * @param Model $itemModel
747
     * @param array $options
748
     *
749
     * @return array
750
     */
751
    private function getItemModelOptions(Model $itemModel, $options = [])
752
    {
753
        $itemOptions = [];
754
        foreach ($options as $option) {
755
            $itemOptions[$option] = $this->getFromModel($itemModel, $option);
756
        }
757
758
        return array_filter($itemOptions, function ($value) {
759
            if ($value !== false && empty($value)) {
760
                return false;
761
            }
762
763
            return true;
764
        });
765
    }
766
767
    /**
768
     * Gets a option from the model.
769
     *
770
     * @param Model $itemModel
771
     * @param $attr
772
     * @param null $defaultValue
773
     *
774
     * @return Model|null
775
     */
776
    private function getFromModel(Model $itemModel, $attr, $defaultValue = null)
777
    {
778
        $variable = $itemModel;
779
780
        if (!empty($attr)) {
781
            foreach (explode('.', $attr) as $attr) {
782
                $variable = array_get($variable, $attr, $defaultValue);
783
            }
784
        }
785
786
        return $variable;
787
    }
788
789
    /**
790
     * Removes a coupon from the item.
791
     *
792
     * @param null $code
793
     */
794
    private function removeCouponFromItems($code = null)
795
    {
796
        foreach ($this->getItems() as $item) {
797
            if (isset($item->code) && (empty($code) || $item->code == $code)) {
798
                $item->code = null;
799
                $item->discount = null;
800
                $item->couponInfo = [];
801
            }
802
        }
803
    }
804
}
805