Completed
Pull Request — master (#128)
by
unknown
02:34
created

LaraCart::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 1
eloc 8
nc 1
nop 3
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 18 and the first side effect is on line 793.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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)) {
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
            $options = $this->getItemModelOptions($itemModel, $bindings[\LukePOLO\LaraCart\CartItem::ITEM_OPTIONS]);
236
            $taxable = $itemModel[$bindings[\LukePOLO\LaraCart\CartItem::ITEM_TAXABLE]] ? true : false;
237
        }
238
239
        $item = $this->addItem(new CartItem(
240
            $itemID,
241
            $name,
242
            $qty,
243
            $price,
244
            $options,
245
            $taxable,
246
            $lineItem
247
        ));
248
249
        $this->update();
250
251
        return $this->getItem($item->getHash());
252
    }
253
254
    /**
255
     * Adds the cartItem into the cart session
256
     *
257
     * @param CartItem $cartItem
258
     *
259
     * @return CartItem
260
     */
261
    public function addItem(CartItem $cartItem)
262
    {
263
        $itemHash = $cartItem->generateHash();
264
265
        if ($this->getItem($itemHash)) {
266
            $this->getItem($itemHash)->qty += $cartItem->qty;
267
        } else {
268
            $this->cart->items[] = $cartItem;
269
        }
270
271
        $this->events->fire('laracart.addItem', $cartItem);
272
273
        return $cartItem;
274
    }
275
276
    /**
277
     * Increment the quantity of a cartItem based on the itemHash
278
     *
279
     * @param $itemHash
280
     *
281
     * @return CartItem | null
282
     */
283
    public function increment($itemHash)
284
    {
285
        $item = $this->getItem($itemHash);
286
        $item->qty++;
287
        $this->update();
288
289
        return $item;
290
    }
291
292
    /**
293
     * Decrement the quantity of a cartItem based on the itemHash
294
     *
295
     * @param $itemHash
296
     *
297
     * @return CartItem | null
298
     */
299
    public function decrement($itemHash)
300
    {
301
        $item = $this->getItem($itemHash);
302
        if ($item->qty > 1) {
303
            $item->qty--;
304
            $this->update();
305
306
            return $item;
307
        }
308
        $this->removeItem($itemHash);
309
        $this->update();
310
311
        return null;
312
    }
313
314
    /**
315
     * Find items in the cart matching a data set
316
     *
317
     *
318
     * param $data
319
     * @return array
320
     */
321
    public function find($data)
322
    {
323
        $matches = [];
324
325
        foreach ($this->getItems() as $item) {
326
            if ($item->find($data)) {
327
                $matches[] = $item;
328
            }
329
        }
330
331
        return $matches;
332
    }
333
334
    /**
335
     * Finds a cartItem based on the itemHash
336
     *
337
     * @param $itemHash
338
     *
339
     * @return CartItem | null
340
     */
341
    public function getItem($itemHash)
342
    {
343
        return array_get($this->getItems(), $itemHash);
344
    }
345
346
    /**
347
     * Gets all the items within the cart
348
     *
349
     * @return array
350
     */
351
    public function getItems($collectable = false)
352
    {
353
        $items = [];
354
        if (isset($this->cart->items) === true) {
355
            foreach ($this->cart->items as $item) {
356
                $items[$item->getHash()] = $item;
357
            }
358
        }
359
        return $collectable? collect($items):$items;
360
    }
361
362
        return $items;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_RETURN, expecting T_FUNCTION
Loading history...
Coding Style introduced by
The visibility should be declared for property $items.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
363
    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 0 spaces, found 4
Loading history...
Coding Style introduced by
Closing brace indented incorrectly; expected 0 spaces, found 4
Loading history...
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)
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 0 spaces, found 4
Loading history...
378
    {
379
        if (empty($item = $this->getItem($itemHash)) === false) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 4 spaces, found 8
Loading history...
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);
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;
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
}
0 ignored issues
show
Coding Style introduced by
It seems like the identation of this line is off (expected at least 4 spaces, but found 0).
Loading history...
794