Completed
Push — master ( 4744a7...0ce470 )
by Luke
02:15
created

LaraCart::removeCoupons()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 2
eloc 7
nc 2
nop 0
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,
237
                $this->getItemModelOptions($itemModel, $bindings[\LukePOLO\LaraCart\CartItem::ITEM_OPTIONS]));
238
239
            $taxable = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_TAXABLE]] ? true : false;
240
        }
241
242
        $item = $this->addItem(new CartItem(
243
            $itemID,
244
            $name,
245
            $qty,
246
            $price,
247
            $options,
248
            $taxable,
249
            $lineItem
250
        ));
251
252
        $this->update();
253
254
        return $this->getItem($item->getHash());
255
    }
256
257
    /**
258
     * Adds the cartItem into the cart session
259
     *
260
     * @param CartItem $cartItem
261
     *
262
     * @return CartItem
263
     */
264
    public function addItem(CartItem $cartItem)
265
    {
266
        $itemHash = $cartItem->generateHash();
267
268
        if ($this->getItem($itemHash)) {
269
            $this->getItem($itemHash)->qty += $cartItem->qty;
270
        } else {
271
            $this->cart->items[] = $cartItem;
272
        }
273
274
        $this->events->fire('laracart.addItem', $cartItem);
275
276
        return $cartItem;
277
    }
278
279
    /**
280
     * Increment the quantity of a cartItem based on the itemHash
281
     *
282
     * @param $itemHash
283
     *
284
     * @return CartItem | null
285
     */
286
    public function increment($itemHash)
287
    {
288
        $item = $this->getItem($itemHash);
289
        $item->qty++;
290
        $this->update();
291
292
        return $item;
293
    }
294
295
    /**
296
     * Decrement the quantity of a cartItem based on the itemHash
297
     *
298
     * @param $itemHash
299
     *
300
     * @return CartItem | null
301
     */
302
    public function decrement($itemHash)
303
    {
304
        $item = $this->getItem($itemHash);
305
        if ($item->qty > 1) {
306
            $item->qty--;
307
            $this->update();
308
309
            return $item;
310
        }
311
        $this->removeItem($itemHash);
312
        $this->update();
313
314
        return null;
315
    }
316
317
    /**
318
     * Find items in the cart matching a data set
319
     *
320
     *
321
     * param $data
322
     * @return array
323
     */
324
    public function find($data)
325
    {
326
        $matches = [];
327
328
        foreach ($this->getItems() as $item) {
329
            if ($item->find($data)) {
330
                $matches[] = $item;
331
            }
332
        }
333
334
        return $matches;
335
    }
336
337
    /**
338
     * Finds a cartItem based on the itemHash
339
     *
340
     * @param $itemHash
341
     *
342
     * @return CartItem | null
343
     */
344
    public function getItem($itemHash)
345
    {
346
        return array_get($this->getItems(), $itemHash);
347
    }
348
349
    /**
350
     * Gets all the items within the cart
351
     *
352
     * @return array
353
     */
354
    public function getItems()
355
    {
356
        $items = [];
357
        if (isset($this->cart->items) === true) {
358
            foreach ($this->cart->items as $item) {
359
                $items[$item->getHash()] = $item;
360
            }
361
        }
362
363
        return $items;
364
    }
365
366
    /**
367
     * Updates an items attributes
368
     *
369
     * @param $itemHash
370
     * @param $key
371
     * @param $value
372
     *
373
     * @return CartItem
374
     *
375
     * @throws Exceptions\InvalidPrice
376
     * @throws Exceptions\InvalidQuantity
377
     */
378
    public function updateItem($itemHash, $key, $value)
379
    {
380
        if (empty($item = $this->getItem($itemHash)) === false) {
381
            $item->$key = $value;
382
        }
383
384
        $item->generateHash();
385
386
        $this->update();
387
388
        return $item;
389
    }
390
391
    /**
392
     * Removes a CartItem based on the itemHash
393
     *
394
     * @param $itemHash
395
     */
396
    public function removeItem($itemHash)
397
    {
398
        foreach ($this->cart->items as $itemKey => $item) {
399
            if ($item->getHash() == $itemHash) {
400
                unset($this->cart->items[$itemKey]);
401
                break;
402
            }
403
        }
404
405
        $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 398. 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...
406
407
        $this->update();
408
    }
409
410
    /**
411
     * Empties the carts items
412
     */
413
    public function emptyCart()
414
    {
415
        unset($this->cart->items);
416
417
        $this->update();
418
419
        $this->events->fire('laracart.empty', $this->cart->instance);
420
    }
421
422
    /**
423
     * Completely destroys cart and anything associated with it
424
     */
425
    public function destroyCart()
426
    {
427
        $instance = $this->cart->instance;
428
429
        $this->session->forget($this->prefix . '.' . $instance);
430
431
        $this->setInstance('default');
432
433
        $this->events->fire('laracart.destroy', $instance);
434
435
        $this->update();
436
    }
437
438
    /**
439
     * Gets the coupons for the current cart
440
     *
441
     * @return array
442
     */
443
    public function getCoupons()
444
    {
445
        return $this->cart->coupons;
446
    }
447
448
    /**
449
     * Finds a specific coupon in the cart
450
     *
451
     * @param $code
452
     * @return mixed
453
     */
454
    public function findCoupon($code)
455
    {
456
        return array_get($this->cart->coupons, $code);
457
    }
458
459
    /**
460
     * Applies a coupon to the cart
461
     *
462
     * @param CouponContract $coupon
463
     */
464
    public function addCoupon(CouponContract $coupon)
465
    {
466
        if (!$this->cart->multipleCoupons) {
467
            $this->cart->coupons = [];
468
        }
469
470
        $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...
471
472
        $this->update();
473
    }
474
475
    /**
476
     * Removes a coupon in the cart
477
     *
478
     * @param $code
479
     */
480
    public function removeCoupon($code)
481
    {
482
        foreach ($this->getItems() as $item) {
483
            if (isset($item->code) && $item->code == $code) {
484
                $item->code = null;
485
                $item->discount = null;
486
                $item->couponInfo = [];
487
            }
488
        }
489
490
        array_forget($this->cart->coupons, $code);
491
492
        $this->update();
493
    }
494
495
    public function removeCoupons()
496
    {
497
        foreach ($this->getItems() as $item) {
498
            $item->code = null;
499
            $item->discount = null;
500
            $item->couponInfo = [];
501
        }
502
503
        $this->cart->coupons = [];
504
505
        $this->update();
506
    }
507
508
    /**
509
     * Gets a specific fee from the fees array
510
     *
511
     * @param $name
512
     *
513
     * @return mixed
514
     */
515
    public function getFee($name)
516
    {
517
        return array_get($this->cart->fees, $name, new CartFee(null, false));
518
    }
519
520
    /**
521
     * Allows to charge for additional fees that may or may not be taxable
522
     * ex - service fee , delivery fee, tips
523
     *
524
     * @param $name
525
     * @param $amount
526
     * @param bool|false $taxable
527
     * @param array $options
528
     */
529
    public function addFee($name, $amount, $taxable = false, array $options = [])
530
    {
531
        array_set($this->cart->fees, $name, new CartFee($amount, $taxable, $options));
532
533
        $this->update();
534
    }
535
536
    /**
537
     * Removes a fee from the fee array
538
     *
539
     * @param $name
540
     */
541
    public function removeFee($name)
542
    {
543
        array_forget($this->cart->fees, $name);
544
545
        $this->update();
546
    }
547
548
    /**
549
     * Removes all the fees set in the cart
550
     *
551
     */
552
    public function removeFees()
553
    {
554
        $this->cart->fees = [];
555
556
        $this->update();
557
    }
558
559
    /**
560
     * Gets the total tax for the cart
561
     *
562
     * @param bool|true $format
563
     *
564
     * @return string
565
     */
566
    public function taxTotal($format = true)
567
    {
568
        $totalTax = 0;
569
        $discounted = 0;
570
        $totalDiscount = $this->totalDiscount(false);
571
572
        if ($this->count() != 0) {
573
            foreach ($this->getItems() as $item) {
574
                if ($discounted >= $totalDiscount) {
575
                    $totalTax += $item->tax();
576
                } else {
577
                    $itemPrice = $item->subTotal(false);
578
579
                    if (($discounted + $itemPrice) > $totalDiscount) {
580
                        $totalTax += $item->tax($totalDiscount - $discounted);
581
                    }
582
583
                    $discounted += $itemPrice;
584
                }
585
            }
586
        }
587
588
        foreach ($this->getFees() as $fee) {
589
            if ($fee->taxable) {
590
                $totalTax += $fee->amount * $fee->tax;
591
            }
592
        }
593
594
        return $this->formatMoney($totalTax, null, null, $format);
595
    }
596
597
    /**
598
     * Gets the total of the cart with or without tax
599
     *
600
     * @param boolean $format
601
     * @param boolean $withDiscount
602
     *
603
     * @return string
604
     */
605
    public function total($format = true, $withDiscount = true, $withTax = true)
606
    {
607
        $total = $this->subTotal(false) + $this->feeTotals(false);
608
609
        if ($withDiscount) {
610
            $total -= $this->totalDiscount(false);
611
        }
612
613
        if ($withTax) {
614
            $total += $this->taxTotal(false);
615
        }
616
617
        return $this->formatMoney($total, null, null, $format);
618
    }
619
620
    /**
621
     * Gets the subtotal of the cart with or without tax
622
     *
623
     * @param boolean $format
624
     * @param boolean $withDiscount
625
     *
626
     * @return string
627
     */
628
    public function subTotal($format = true, $withDiscount = true)
629
    {
630
        $total = 0;
631
632
        if ($this->count() != 0) {
633
            foreach ($this->getItems() as $item) {
634
                $total += $item->subTotal(false, $withDiscount);
635
            }
636
        }
637
638
        return $this->formatMoney($total, null, null, $format);
639
    }
640
641
    /**
642
     * Get the count based on qty, or number of unique items
643
     *
644
     * @param bool $withItemQty
645
     *
646
     * @return int
647
     */
648
    public function count($withItemQty = true)
649
    {
650
        $count = 0;
651
652
        foreach ($this->getItems() as $item) {
653
            if ($withItemQty) {
654
                $count += $item->qty;
655
            } else {
656
                $count++;
657
            }
658
        }
659
660
        return $count;
661
    }
662
663
    /**
664
     *
665
     * Formats the number into a money format based on the locale and international formats
666
     *
667
     * @param $number
668
     * @param $locale
669
     * @param $internationalFormat
670
     * @param $format
671
     *
672
     * @return string
673
     */
674
    public static function formatMoney($number, $locale = null, $internationalFormat = null, $format = true)
675
    {
676
        $number = number_format($number, 2, '.', '');
677
678
        if ($format) {
679
            setlocale(LC_MONETARY, null);
680
            setlocale(LC_MONETARY, empty($locale) ? config('laracart.locale', 'en_US.UTF-8') : $locale);
681
682
            if (empty($internationalFormat) === true) {
683
                $internationalFormat = config('laracart.international_format', false);
684
            }
685
686
            $number = money_format($internationalFormat ? '%i' : '%n', $number);
687
        }
688
689
        return $number;
690
    }
691
692
    /**
693
     * Gets all the fee totals
694
     *
695
     * @param boolean $format
696
     *
697
     * @return string
698
     */
699
    public function feeTotals($format = true, $withTax = false)
700
    {
701
        $feeTotal = 0;
702
703
        foreach ($this->getFees() as $fee) {
704
            $feeTotal += $fee->amount;
705
706
            if ($withTax && $fee->taxable && $fee->tax > 0) {
707
                $feeTotal += $fee->amount * $fee->tax;
708
            }
709
        }
710
711
        return $this->formatMoney($feeTotal, null, null, $format);
712
    }
713
714
    /**
715
     * Gets all the fees on the cart object
716
     *
717
     * @return mixed
718
     */
719
    public function getFees()
720
    {
721
        return $this->cart->fees;
722
    }
723
724
    /**
725
     * Gets the total amount discounted
726
     *
727
     * @param boolean $format
728
     *
729
     * @return string
730
     */
731
    public function totalDiscount($format = true)
732
    {
733
        $total = 0;
734
735
        foreach ($this->cart->coupons as $coupon) {
736
            if ($coupon->appliedToCart) {
737
                $total += $coupon->discount();
738
            }
739
        }
740
741
        return $this->formatMoney($total, null, null, $format);
742
    }
743
744
    /**
745
     * Checks to see if its an item model
746
     *
747
     * @param $itemModel
748
     *
749
     * @return bool
750
     */
751
    private function isItemModel($itemModel)
752
    {
753
        if (is_object($itemModel) && get_class($itemModel) == config('laracart.item_model')) {
754
            return true;
755
        }
756
    }
757
758
    /**
759
     * Gets the item models options based the config
760
     *
761
     * @param Model $itemModel
762
     * @param array $options
763
     *
764
     * @return array
765
     */
766
    private function getItemModelOptions(Model $itemModel, $options = [])
767
    {
768
        $itemOptions = [];
769
        foreach ($options as $option) {
770
            $itemOptions[$option] = $this->getFromModel($itemModel, $option);
771
        }
772
773
        return array_filter($itemOptions, function ($value) {
774
            if ($value !== false && empty($value)) {
775
                return false;
776
            }
777
778
            return true;
779
        });
780
    }
781
782
    /**
783
     * Gets a option from the model
784
     *
785
     * @param Model $itemModel
786
     * @param $attr
787
     * @param null $defaultValue
788
     *
789
     * @return Model|null
790
     */
791
    private function getFromModel(Model $itemModel, $attr, $defaultValue = null)
792
    {
793
        $variable = $itemModel;
794
795
        if (!empty($attr)) {
796
            foreach (explode('.', $attr) as $attr) {
797
                $variable = array_get($variable, $attr);
798
            }
799
        }
800
801
        if (empty($variable)) {
802
            $variable = $defaultValue;
803
        }
804
        return $variable;
805
    }
806
807
}
808