Completed
Pull Request — master (#87)
by Luke
02:23
created

LaraCart::decrement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 14
rs 9.4285
cc 2
eloc 9
nc 2
nop 1
1
<?php
2
3
namespace LukePOLO\LaraCart;
4
5
use Illuminate\Auth\AuthManager;
6
use Illuminate\Contracts\Events\Dispatcher;
7
use Illuminate\Session\SessionManager;
8
use LukePOLO\LaraCart\Contracts\CouponContract;
9
use LukePOLO\LaraCart\Contracts\LaraCartContract;
10
11
/**
12
 * Class LaraCart
13
 *
14
 * @package LukePOLO\LaraCart
15
 */
16
class LaraCart implements LaraCartContract
17
{
18
    const QTY = 'qty';
19
    const HASH = 'generateCartHash';
20
    const PRICE = 'price';
21
    const SERVICE = 'laracart';
22
    const RANHASH = 'generateRandomCartItemHash';
23
24
    protected $events;
25
    protected $session;
26
    protected $authManager;
27
    protected $prefix;
28
29
    public $cart;
30
31
    /**
32
     * LaraCart constructor.
33
     *
34
     * @param SessionManager $session
35
     * @param Dispatcher $events
36
     * @param AuthManager $authManager
37
     */
38
    public function __construct(SessionManager $session, Dispatcher $events, AuthManager $authManager)
39
    {
40
        $this->session = $session;
41
        $this->events = $events;
42
        $this->authManager = $authManager;
43
        $this->prefix = config('laracart.cache_prefix', 'laracart');
44
45
        $this->setInstance($this->session->get($this->prefix . '.instance', 'default'));
46
    }
47
48
    /**
49
     * @return mixed
50
     */
51
    public function getInstances()
52
    {
53
        return $this->session->get($this->prefix . '.instances', [
54
            'default'
55
        ]);
56
    }
57
58
    /**
59
     * Sets and Gets the instance of the cart in the session we should be using
60
     *
61
     * @param string $instance
62
     *
63
     * @return LaraCart
64
     */
65
    public function setInstance($instance = 'default')
66
    {
67
        $this->get($instance);
68
69
        $this->session->set($this->prefix . '.instance', $instance);
70
71
        $this->events->fire('laracart.new');
72
73
        return $this;
74
    }
75
76
    /**
77
     * Gets the instance in the session
78
     *
79
     * @param string $instance
80
     *
81
     * @return $this cart instance
82
     */
83
    public function get($instance = 'default')
84
    {
85
        if (config('laracart.cross_devices', false)) {
86
            if (!empty($cartSessionID = $this->authManager->user()->cart_session_id)) {
87
                $this->session->setId($cartSessionID);
88
                $this->session->start();
89
            }
90
        }
91
92
        if (empty($this->cart = $this->session->get($this->prefix . '.' . $instance))) {
93
            $this->cart = new Cart($instance);
94
            $this->session->push($this->prefix . '.instances', $instance);
95
        }
96
97
        return $this;
98
    }
99
100
    /**
101
     * Gets an an attribute from the cart
102
     *
103
     * @param $attribute
104
     * @param $defaultValue
105
     *
106
     * @return mixed
107
     */
108
    public function getAttribute($attribute, $defaultValue = null)
109
    {
110
        return array_get($this->cart->attributes, $attribute, $defaultValue);
111
    }
112
113
    /**
114
     * Gets all the carts attributes
115
     *
116
     * @return mixed
117
     */
118
    public function getAttributes()
119
    {
120
        return $this->cart->attributes;
121
    }
122
123
    /**
124
     * Adds an Attribute to the cart
125
     *
126
     * @param $attribute
127
     * @param $value
128
     */
129
    public function setAttribute($attribute, $value)
130
    {
131
        array_set($this->cart->attributes, $attribute, $value);
132
133
        $this->update();
134
    }
135
136
    /**
137
     * Updates cart session
138
     */
139
    public function update()
140
    {
141
        $this->session->set($this->prefix . '.' . $this->cart->instance, $this->cart);
142
143
        if (config('laracart.cross_devices', false)) {
144
            $this->authManager->user()->cart_session_id = $this->session->getId();
145
            $this->authManager->user()->save();
146
        }
147
148
        $this->events->fire('laracart.update', $this->cart);
149
    }
150
151
    /**
152
     * Removes an attribute from the cart
153
     *
154
     * @param $attribute
155
     */
156
    public function removeAttribute($attribute)
157
    {
158
        array_forget($this->cart->attributes, $attribute);
159
160
        $this->update();
161
    }
162
163
    /**
164
     * Creates a CartItem and then adds it to cart
165
     *
166
     * @param string|int $itemID
167
     * @param null $name
168
     * @param int $qty
169
     * @param string $price
170
     * @param array $options
171
     * @param bool|true $taxable
172
     *
173
     * @return CartItem
174
     */
175
    public function addLine($itemID, $name = null, $qty = 1, $price = '0.00', $options = [], $taxable = true)
176
    {
177
        return $this->add($itemID, $name, $qty, $price, $options, $taxable, true);
178
    }
179
180
    /**
181
     * Creates a CartItem and then adds it to cart
182
     *
183
     * @param $itemID
184
     * @param null $name
185
     * @param int $qty
186
     * @param string $price
187
     * @param array $options
188
     * @param bool|false $taxable
189
     * @param bool|false $lineItem
190
     *
191
     * @return CartItem
192
     */
193
    public function add(
194
        $itemID,
195
        $name = null,
196
        $qty = 1,
197
        $price = '0.00',
198
        $options = [],
199
        $taxable = true,
200
        $lineItem = false
201
    ) {
202
        $item = $this->addItem(
203
            new CartItem(
204
                $itemID,
205
                $name,
206
                $qty,
207
                $price,
208
                $options,
209
                $taxable,
210
                $lineItem
211
            )
212
        );
213
214
        $this->update();
215
216
        return $this->getItem($item->getHash());
217
    }
218
219
    /**
220
     * Adds the cartItem into the cart session
221
     *
222
     * @param CartItem $cartItem
223
     *
224
     * @return CartItem
225
     */
226
    public function addItem(CartItem $cartItem)
227
    {
228
        $itemHash = $cartItem->generateHash();
229
230
        if ($this->getItem($itemHash)) {
231
            $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...
232
        } else {
233
            $this->cart->items[] = $cartItem;
234
        }
235
236
        $this->events->fire('laracart.addItem', $cartItem);
237
238
        return $cartItem;
239
    }
240
241
    /**
242
     * Increment the quantity of a cartItem based on the itemHash
243
     *
244
     * @param $itemHash
245
     *
246
     * @return CartItem | null
247
     */
248
    public function increment($itemHash)
249
    {
250
        $item = $this->getItem($itemHash);
251
        $item->qty++;
0 ignored issues
show
Documentation introduced by
The property qty does not exist on object<LukePOLO\LaraCart\CartItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
252
        $this->update();
253
254
        return $item;
255
    }
256
257
    /**
258
     * Decrement the quantity of a cartItem based on the itemHash
259
     *
260
     * @param $itemHash
261
     *
262
     * @return CartItem | null
263
     */
264
    public function decrement($itemHash)
265
    {
266
        $item = $this->getItem($itemHash);
267
        if ($item->qty > 1) {
0 ignored issues
show
Documentation introduced by
The property qty does not exist on object<LukePOLO\LaraCart\CartItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
268
            $item->qty--;
0 ignored issues
show
Documentation introduced by
The property qty does not exist on object<LukePOLO\LaraCart\CartItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

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