Completed
Push — master ( 110c92...41e77b )
by Luke
02:16
created

LaraCart::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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