Completed
Pull Request — master (#83)
by Julien
16:18 queued 03:29
created

LaraCart   C

Complexity

Total Complexity 71

Size/Duplication

Total Lines 628
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 34
Bugs 5 Features 2
Metric Value
c 34
b 5
f 2
dl 0
loc 628
wmc 71
lcom 1
cbo 4
rs 5.4397

35 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A get() 0 8 2
A addLine() 0 4 1
A getAttributes() 0 4 1
A setAttribute() 0 6 1
A removeAttribute() 0 6 1
A getAttribute() 0 4 1
A update() 0 6 1
A setInstance() 0 10 1
A addItem() 0 16 2
A add() 0 23 1
A increment() 0 7 1
A decrement() 0 13 2
A find() 0 12 3
A getItem() 0 4 1
A getItems() 0 11 3
A updateItem() 0 10 2
A removeItem() 0 11 3
A emptyCart() 0 8 1
A destroyCart() 0 10 1
A getCoupons() 0 4 1
A findCoupon() 0 4 1
A addCoupon() 0 10 2
A removeCoupon() 0 14 4
A getFee() 0 4 1
A addFee() 0 6 1
A removeFee() 0 6 1
C taxTotal() 0 30 7
A total() 0 14 3
A subTotal() 0 12 3
A count() 0 14 3
B formatMoney() 0 17 5
B feeTotals() 0 14 5
A getFees() 0 4 1
A totalDiscount() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like LaraCart often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LaraCart, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace LukePOLO\LaraCart;
4
5
use Illuminate\Contracts\Events\Dispatcher;
6
use Illuminate\Session\SessionManager;
7
use LukePOLO\LaraCart\Contracts\CouponContract;
8
use LukePOLO\LaraCart\Contracts\LaraCartContract;
9
10
/**
11
 * Class LaraCart
12
 *
13
 * @package LukePOLO\LaraCart
14
 */
15
class LaraCart implements LaraCartContract
16
{
17
    const QTY = 'qty';
18
    const HASH = 'generateCartHash';
19
    const PRICE = 'price';
20
    const SERVICE = 'laracart';
21
    const RANHASH = 'generateRandomCartItemHash';
22
23
    protected $events;
24
    protected $session;
25
26
    public $cart;
27
28
    /**
29
     * LaraCart constructor.
30
     *
31
     * @param SessionManager $session
32
     * @param Dispatcher $events
33
     */
34
    public function __construct(SessionManager $session, Dispatcher $events)
35
    {
36
        $this->session = $session;
37
        $this->events = $events;
38
39
        $this->setInstance($this->session->get('laracart.instance', 'default'));
40
    }
41
42
    /**
43
     * Sets and Gets the instance of the cart in the session we should be using
44
     *
45
     * @param string $instance
46
     *
47
     * @return LaraCart
48
     */
49
    public function setInstance($instance = 'default')
50
    {
51
        $this->get($instance);
52
53
        $this->session->set('laracart.instance', $instance);
54
55
        $this->events->fire('laracart.new');
56
57
        return $this;
58
    }
59
60
    /**
61
     * Gets the instance in the session
62
     *
63
     * @param string $instance
64
     *
65
     * @return $this cart instance
66
     */
67
    public function get($instance = 'default')
68
    {
69
        if (empty($this->cart = $this->session->get(config('laracart.cache_prefix', 'laracart') . '.' . $instance))) {
70
            $this->cart = new Cart($instance);
71
        }
72
73
        return $this;
74
    }
75
76
    /**
77
     * Gets an an attribute from the cart
78
     *
79
     * @param $attribute
80
     * @param $defaultValue
81
     *
82
     * @return mixed
83
     */
84
    public function getAttribute($attribute, $defaultValue = null)
85
    {
86
        return array_get($this->cart->attributes, $attribute, $defaultValue);
87
    }
88
89
    /**
90
     * Gets all the carts attributes
91
     *
92
     * @return mixed
93
     */
94
    public function getAttributes()
95
    {
96
        return $this->cart->attributes;
97
    }
98
99
    /**
100
     * Adds an Attribute to the cart
101
     *
102
     * @param $attribute
103
     * @param $value
104
     */
105
    public function setAttribute($attribute, $value)
106
    {
107
        array_set($this->cart->attributes, $attribute, $value);
108
109
        $this->update();
110
    }
111
112
    /**
113
     * Updates cart session
114
     */
115
    public function update()
116
    {
117
        $this->session->set(config('laracart.cache_prefix', 'laracart') . '.' . $this->cart->instance, $this->cart);
118
119
        $this->events->fire('laracart.update', $this->cart);
120
    }
121
122
    /**
123
     * Removes an attribute from the cart
124
     *
125
     * @param $attribute
126
     */
127
    public function removeAttribute($attribute)
128
    {
129
        array_forget($this->cart->attributes, $attribute);
130
131
        $this->update();
132
    }
133
134
    /**
135
     * Creates a CartItem and then adds it to cart
136
     *
137
     * @param string|int $itemID
138
     * @param null $name
139
     * @param int $qty
140
     * @param string $price
141
     * @param array $options
142
     * @param bool|true $taxable
143
     *
144
     * @return CartItem
145
     */
146
    public function addLine($itemID, $name = null, $qty = 1, $price = '0.00', $options = [], $taxable = true)
147
    {
148
        return $this->add($itemID, $name, $qty, $price, $options, $taxable, true);
149
    }
150
151
    /**
152
     * Creates a CartItem and then adds it to cart
153
     *
154
     * @param $itemID
155
     * @param null $name
156
     * @param int $qty
157
     * @param string $price
158
     * @param array $options
159
     * @param bool|false $taxable
160
     * @param bool|false $lineItem
161
     *
162
     * @return CartItem
163
     */
164
    public function add(
165
        $itemID,
166
        $name = null,
167
        $qty = 1,
168
        $price = '0.00',
169
        $options = [],
170
        $taxable = true,
171
        $lineItem = false
172
    ) {
173
        $item = $this->addItem(
174
            new CartItem(
175
                $itemID,
176
                $name,
177
                $qty,
178
                $price,
179
                $options,
180
                $taxable,
181
                $lineItem
182
            )
183
        );
184
185
        return $this->getItem($item->getHash());
186
    }
187
188
    /**
189
     * Adds the cartItem into the cart session
190
     *
191
     * @param CartItem $cartItem
192
     *
193
     * @return CartItem
194
     */
195
    public function addItem(CartItem $cartItem)
196
    {
197
        $itemHash = $cartItem->generateHash();
198
199
        if ($this->getItem($itemHash)) {
200
            $this->getItem($itemHash)->qty += $cartItem->qty;
0 ignored issues
show
Documentation introduced by
The property qty does not exist on object<LukePOLO\LaraCart\CartItem>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
201
        } else {
202
            $this->cart->items[] = $cartItem;
203
        }
204
205
        $this->events->fire('laracart.addItem', $cartItem);
206
207
        $this->update();
208
209
        return $cartItem;
210
    }
211
212
    /**
213
     * Increment the quantity of a cartItem based on the itemHash
214
     *
215
     * @param $itemHash
216
     *
217
     * @return CartItem | null
218
     */
219
    public function increment($itemHash)
220
    {
221
        $item = array_get($this->getItems(), $itemHash);
222
        $item->qty++;
223
224
        return $item;
225
    }
226
227
    /**
228
     * Decrement the quantity of a cartItem based on the itemHash
229
     *
230
     * @param $itemHash
231
     *
232
     * @return CartItem | null
233
     */
234
    public function decrement($itemHash)
235
    {
236
        $item = array_get($this->getItems(), $itemHash);
237
        if ($item->qty > 1) {
238
            $item->qty--;
239
240
            return $item;
241
        }
242
243
        $this->removeItem($itemHash);
244
245
        return null;
246
    }
247
248
    /*
249
     * Find items in the cart matching a data set
250
     *
251
     * @return array
252
     */
253
    public function find($data)
254
    {
255
        $matches = [];
256
257
        foreach ($this->getItems() as $item) {
258
            if ($item->find($data)) {
259
                $matches[] = $item;
260
            }
261
        }
262
263
        return $matches;
264
    }
265
266
    /**
267
     * Finds a cartItem based on the itemHash
268
     *
269
     * @param $itemHash
270
     *
271
     * @return CartItem | null
272
     */
273
    public function getItem($itemHash)
274
    {
275
        return array_get($this->getItems(), $itemHash);
276
    }
277
278
    /**
279
     * Gets all the items within the cart
280
     *
281
     * @return array
282
     */
283
    public function getItems()
284
    {
285
        $items = [];
286
        if (isset($this->cart->items) === true) {
287
            foreach ($this->cart->items as $item) {
288
                $items[$item->getHash()] = $item;
289
            }
290
        }
291
292
        return $items;
293
    }
294
295
    /**
296
     * Updates an items attributes
297
     *
298
     * @param $itemHash
299
     * @param $key
300
     * @param $value
301
     *
302
     * @return CartItem
303
     *
304
     * @throws Exceptions\InvalidPrice
305
     * @throws Exceptions\InvalidQuantity
306
     */
307
    public function updateItem($itemHash, $key, $value)
308
    {
309
        if (empty($item = $this->getItem($itemHash)) === false) {
310
            $item->$key = $value;
311
        }
312
313
        $item->generateHash();
314
315
        return $item;
316
    }
317
318
    /**
319
     * Removes a CartItem based on the itemHash
320
     *
321
     * @param $itemHash
322
     */
323
    public function removeItem($itemHash)
324
    {
325
        foreach ($this->cart->items as $itemKey => $item) {
326
            if ($item->getHash() == $itemHash) {
327
                unset($this->cart->items[$itemKey]);
328
                break;
329
            }
330
        }
331
332
        $this->events->fire('laracart.removeItem', $itemHash);
333
    }
334
335
    /**
336
     * Empties the carts items
337
     */
338
    public function emptyCart()
339
    {
340
        unset($this->cart->items);
341
342
        $this->update();
343
344
        $this->events->fire('laracart.empty', $this->cart->instance);
345
    }
346
347
    /**
348
     * Completely destroys cart and anything associated with it
349
     */
350
    public function destroyCart()
351
    {
352
        $instance = $this->cart->instance;
353
354
        $this->session->forget(config('laracart.cache_prefix', 'laracart') . '.' . $instance);
355
356
        $this->setInstance('default');
357
358
        $this->events->fire('laracart.destroy', $instance);
359
    }
360
361
    /**
362
     * Gets the coupons for the current cart
363
     *
364
     * @return array
365
     */
366
    public function getCoupons()
367
    {
368
        return $this->cart->coupons;
369
    }
370
371
    /**
372
     * Finds a specific coupon in the cart
373
     *
374
     * @param $code
375
     * @return mixed
376
     */
377
    public function findCoupon($code)
378
    {
379
        return array_get($this->cart->coupons, $code);
380
    }
381
382
    /**
383
     * Applies a coupon to the cart
384
     *
385
     * @param CouponContract $coupon
386
     */
387
    public function addCoupon(CouponContract $coupon)
388
    {
389
        if (!$this->cart->multipleCoupons) {
390
            $this->cart->coupons = [];
391
        }
392
393
        $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...
394
395
        $this->update();
396
    }
397
398
    /**
399
     * Removes a coupon in the cart
400
     *
401
     * @param $code
402
     */
403
    public function removeCoupon($code)
404
    {
405
        foreach ($this->getItems() as $item) {
406
            if (isset($item->code) && $item->code == $code) {
407
                $item->code = null;
408
                $item->discount = null;
409
                $item->couponInfo = [];
410
            }
411
        }
412
413
        array_forget($this->cart->coupons, $code);
414
415
        $this->update();
416
    }
417
418
    /**
419
     * Gets a speific fee from the fees array
420
     *
421
     * @param $name
422
     *
423
     * @return mixed
424
     */
425
    public function getFee($name)
426
    {
427
        return array_get($this->cart->fees, $name, new CartFee(null, false));
428
    }
429
430
    /**
431
     * Allows to charge for additional fees that may or may not be taxable
432
     * ex - service fee , delivery fee, tips
433
     *
434
     * @param $name
435
     * @param $amount
436
     * @param bool|false $taxable
437
     * @param array $options
438
     */
439
    public function addFee($name, $amount, $taxable = false, array $options = [])
440
    {
441
        array_set($this->cart->fees, $name, new CartFee($amount, $taxable, $options));
442
443
        $this->update();
444
    }
445
446
    /**
447
     * Reemoves a fee from the fee array
448
     *
449
     * @param $name
450
     */
451
    public function removeFee($name)
452
    {
453
        array_forget($this->cart->fees, $name);
454
455
        $this->update();
456
    }
457
458
    /**
459
     * Gets the total tax for the cart
460
     *
461
     * @param bool|true $format
462
     *
463
     * @return string
464
     */
465
    public function taxTotal($format = true)
466
    {
467
        $totalTax = 0;
468
        $discounted = 0;
469
        $totalDiscount = $this->totalDiscount(false);
470
471
        if ($this->count() != 0) {
472
            foreach ($this->getItems() as $item) {
473
                if ($discounted >= $totalDiscount) {
474
                    $totalTax += $item->tax();
475
                } else {
476
                    $itemPrice = $item->subTotal(false);
477
478
                    if (($discounted + $itemPrice) > $totalDiscount) {
479
                        $totalTax += $item->tax($totalDiscount - $discounted);
480
                    }
481
482
                    $discounted += $itemPrice;
483
                }
484
            }
485
        }
486
487
        foreach ($this->getFees() as $fee) {
488
            if ($fee->taxable) {
489
                $totalTax += $fee->amount * $fee->tax;
490
            }
491
        }
492
493
        return $this->formatMoney($totalTax, null, null, $format);
494
    }
495
496
    /**
497
     * Gets the total of the cart with or without tax
498
     *
499
     * @param boolean $format
500
     * @param boolean $withDiscount
501
     *
502
     * @return string
503
     */
504
    public function total($format = true, $withDiscount = true, $withTax = true)
505
    {
506
        $total = $this->subTotal(false) + $this->feeTotals(false);
507
508
        if ($withDiscount) {
509
            $total -= $this->totalDiscount(false);
510
        }
511
512
        if ($withTax) {
513
            $total += $this->taxTotal(false);
514
        }
515
516
        return $this->formatMoney($total, null, null, $format);
517
    }
518
519
    /**
520
     * Gets the subtotal of the cart with or without tax
521
     *
522
     * @param boolean $format
523
     * @param boolean $withDiscount
524
     *
525
     * @return string
526
     */
527
    public function subTotal($format = true, $withDiscount = true)
528
    {
529
        $total = 0;
530
531
        if ($this->count() != 0) {
532
            foreach ($this->getItems() as $item) {
533
                $total += $item->subTotal(false, $withDiscount);
534
            }
535
        }
536
537
        return $this->formatMoney($total, null, null, $format);
538
    }
539
540
    /**
541
     * Get the count based on qty, or number of unique items
542
     *
543
     * @param bool $withItemQty
544
     *
545
     * @return int
546
     */
547
    public function count($withItemQty = true)
548
    {
549
        $count = 0;
550
551
        foreach ($this->getItems() as $item) {
552
            if ($withItemQty) {
553
                $count += $item->qty;
554
            } else {
555
                $count++;
556
            }
557
        }
558
559
        return $count;
560
    }
561
562
    /**
563
     *
564
     * Formats the number into a money format based on the locale and international formats
565
     *
566
     * @param $number
567
     * @param $locale
568
     * @param $internationalFormat
569
     * @param $format
570
     *
571
     * @return string
572
     */
573
    public static function formatMoney($number, $locale = null, $internationalFormat = null, $format = true)
574
    {
575
        $number = number_format($number, 2, '.', '');
576
577
        if ($format) {
578
            setlocale(LC_MONETARY, null);
579
            setlocale(LC_MONETARY, empty($locale) ? config('laracart.locale', 'en_US.UTF-8') : $locale);
580
581
            if (empty($internationalFormat) === true) {
582
                $internationalFormat = config('laracart.international_format', false);
583
            }
584
585
            $number = money_format($internationalFormat ? '%i' : '%n', $number);
586
        }
587
588
        return $number;
589
    }
590
591
    /**
592
     * Gets all the fee totals
593
     *
594
     * @param boolean $format
595
     *
596
     * @return string
597
     */
598
    public function feeTotals($format = true, $withTax = false)
599
    {
600
        $feeTotal = 0;
601
602
        foreach ($this->getFees() as $fee) {
603
            $feeTotal += $fee->amount;
604
605
            if ($withTax && $fee->taxable && $fee->tax > 0) {
606
                $feeTotal += $fee->amount * $fee->tax;
607
            }
608
        }
609
610
        return $this->formatMoney($feeTotal, null, null, $format);
611
    }
612
613
    /**
614
     * Gets all the fees on the cart object
615
     *
616
     * @return mixed
617
     */
618
    public function getFees()
619
    {
620
        return $this->cart->fees;
621
    }
622
623
    /**
624
     * Gets the total amount discounted
625
     *
626
     * @param boolean $format
627
     *
628
     * @return string
629
     */
630
    public function totalDiscount($format = true)
631
    {
632
        $total = 0;
633
634
        foreach ($this->cart->coupons as $coupon) {
635
            if ($coupon->appliedToCart) {
636
                $total += $coupon->discount();
637
            }
638
        }
639
640
        return $this->formatMoney($total, null, null, $format);
641
    }
642
}
643