Passed
Push — main ( 3abdd4...bfa13e )
by Michael
03:43
created

CouponService::getCoupon()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\Couponables\Services;
6
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Traits\Macroable;
9
use MichaelRubel\Couponables\Events\CouponRedeemed;
10
use MichaelRubel\Couponables\Exceptions\CouponExpiredException;
11
use MichaelRubel\Couponables\Exceptions\InvalidCouponException;
12
use MichaelRubel\Couponables\Exceptions\NotAllowedToRedeemException;
13
use MichaelRubel\Couponables\Exceptions\OverLimitException;
14
use MichaelRubel\Couponables\Exceptions\OverQuantityException;
15
use MichaelRubel\Couponables\Models\Contracts\CouponContract;
16
use MichaelRubel\Couponables\Models\Contracts\CouponPivotContract;
17
use MichaelRubel\Couponables\Services\Contracts\CouponServiceContract;
18
use MichaelRubel\EnhancedContainer\Call;
19
use MichaelRubel\EnhancedContainer\Core\CallProxy;
20
21
class CouponService implements CouponServiceContract
22
{
23
    use Macroable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Support\Traits\Macroable requires the property $name which is not provided by MichaelRubel\Couponables\Services\CouponService.
Loading history...
24
25
    /**
26
     * @var CallProxy
27
     */
28
    protected CallProxy $model;
29
30
    /**
31
     * @var CallProxy
32
     */
33
    protected CallProxy $pivot;
34
35
    /**
36
     * @param CouponContract      $model
37
     * @param CouponPivotContract $pivot
38
     */
39 26
    public function __construct(CouponContract $model, CouponPivotContract $pivot)
40
    {
41 26
        $this->model = call($model);
42 26
        $this->pivot = call($pivot);
43
    }
44
45
    /**
46
     * Get the coupon model by the code.
47
     *
48
     * @param string|null $code
49
     *
50
     * @return CouponContract|null
51
     */
52 25
    public function getCoupon(?string $code): ?CouponContract
53
    {
54 25
        return $this->model->firstWhere($this->model->getCodeColumn(), $code);
55
    }
56
57
    /**
58
     * Verify if coupon is valid otherwise throw an exception.
59
     *
60
     * @param string|null $code
61
     * @param Model       $redeemer
62
     *
63
     * @return CouponContract
64
     * @throws OverQuantityException
65
     * @throws OverLimitException
66
     * @throws NotAllowedToRedeemException
67
     * @throws CouponExpiredException
68
     * @throws InvalidCouponException
69
     */
70 25
    public function verifyCoupon(?string $code, Model $redeemer): CouponContract
71
    {
72 25
        $coupon = call($this->getCoupon($code) ?? throw new InvalidCouponException);
73
74 19
        if ($coupon->isExpired()) {
75 1
            throw new CouponExpiredException;
76
        }
77
78 18
        if ($coupon->isOverQuantity()) {
79 1
            throw new OverQuantityException;
80
        }
81
82 18
        if ($this->isOverLimit($coupon, $redeemer, $code)) {
83 2
            throw new OverLimitException;
84
        }
85
86 18
        if (! $this->isAllowedToRedeem($coupon, $redeemer)) {
87 2
            throw new NotAllowedToRedeemException;
88
        }
89
90 16
        return $coupon->getInternal(Call::INSTANCE);
91
    }
92
93
    /**
94
     * Apply the coupon.
95
     *
96
     * @param CouponContract $coupon
97
     * @param Model          $redeemer
98
     *
99
     * @return CouponContract
100
     */
101 15
    public function applyCoupon(CouponContract $coupon, Model $redeemer): CouponContract
102
    {
103 15
        $redeemer->coupons()->attach($coupon, [
104 15
            $this->pivot->getRedeemedAtColumn() => now(),
105
        ]);
106
107 15
        if (! is_null($coupon->{$this->model->getQuantityColumn()})) {
108 3
            $coupon->decrement($this->model->getQuantityColumn());
0 ignored issues
show
Bug introduced by
The method decrement() does not exist on MichaelRubel\Couponables...ontracts\CouponContract. Since it exists in all sub-types, consider adding an abstract or default implementation to MichaelRubel\Couponables...ontracts\CouponContract. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
            $coupon->/** @scrutinizer ignore-call */ 
109
                     decrement($this->model->getQuantityColumn());
Loading history...
109
        }
110
111 15
        event(new CouponRedeemed($this, $coupon));
112
113 15
        return $coupon;
114
    }
115
116
    /**
117
     * @param CallProxy   $coupon
118
     * @param Model       $redeemer
119
     * @param string|null $code
120
     *
121
     * @return bool
122
     */
123 18
    protected function isOverLimit(CallProxy $coupon, Model $redeemer, ?string $code): bool
124
    {
125 18
        return ($coupon->isDisposable() && call($redeemer)->isCouponRedeemed($code))
126 18
            || $coupon->isOverLimitFor($redeemer);
127
    }
128
129
    /**
130
     * @param CallProxy $coupon
131
     * @param Model     $redeemer
132
     *
133
     * @return bool
134
     * @throws NotAllowedToRedeemException
135
     */
136 18
    protected function isAllowedToRedeem(CallProxy $coupon, Model $redeemer): bool
137
    {
138 18
        if ($coupon->isMorphColumnsFilled() && ! $coupon->redeemer()?->is($redeemer)) {
139 1
            return false;
140
        }
141
142 17
        if ($coupon->isOnlyRedeemerTypeFilled() && ! $coupon->isSameRedeemerModel($redeemer)) {
143 1
            return false;
144
        }
145
146 16
        return true;
147
    }
148
}
149