HasCoupons   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 15
eloc 29
c 6
b 1
f 1
dl 0
loc 141
ccs 36
cts 36
cp 1
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A initializeHasCoupons() 0 4 2
A redeemCoupon() 0 5 1
A redeemCouponOr() 0 6 3
A isCouponOverLimit() 0 5 2
A verifyCouponOr() 0 6 3
A verifyCoupon() 0 3 1
A redeemBy() 0 5 1
A coupons() 0 11 1
A isCouponAlreadyUsed() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\Couponables\Traits;
6
7
use Closure;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\MorphToMany;
10
use Illuminate\Support\Str;
11
use MichaelRubel\Couponables\Models\Contracts\CouponContract;
12
use MichaelRubel\Couponables\Services\Contracts\CouponServiceContract;
13
use Throwable;
14
15
trait HasCoupons
16
{
17
    /**
18
     * @var CouponServiceContract
19
     */
20
    protected CouponServiceContract $couponService;
21
22
    /**
23
     * Initialize the method binding objects.
24
     *
25
     * @return void
26
     */
27 61
    protected function initializeHasCoupons(): void
28
    {
29 61
        if (app()->bound(CouponServiceContract::class)) {
30 61
            $this->couponService = app(CouponServiceContract::class);
31
        }
32
    }
33
34
    /**
35
     * Polymorphic relation to the coupons.
36
     *
37
     * @return MorphToMany
38
     */
39 21
    public function coupons(): MorphToMany
40
    {
41 21
        return with($this->couponService, function (CouponServiceContract $service) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return with($this->coupo...ion(...) { /* ... */ }) could return the type MichaelRubel\Couponables...s\CouponServiceContract which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Relations\MorphToMany. Consider adding an additional type-check to rule them out.
Loading history...
42 21
            $morphName = Str::singular(config('couponables.pivot_table', 'couponables'));
43
44 21
            return $this->morphToMany($service->model, $morphName)->withPivot([
0 ignored issues
show
Bug introduced by
It seems like morphToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

44
            return $this->/** @scrutinizer ignore-call */ morphToMany($service->model, $morphName)->withPivot([
Loading history...
Bug introduced by
Accessing model on the interface MichaelRubel\Couponables...s\CouponServiceContract suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
45 21
                $service->pivot->getRedeemedTypeColumn(),
0 ignored issues
show
Bug introduced by
Accessing pivot on the interface MichaelRubel\Couponables...s\CouponServiceContract suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
46 21
                $service->pivot->getRedeemedIdColumn(),
47 21
                $service->pivot->getRedeemedAtColumn(),
48 21
                $service->pivot->getCreatedAtColumn(),
49 21
                $service->pivot->getUpdatedAtColumn(),
50 21
            ]);
51 21
        });
52
    }
53
54
    /**
55
     * Check if coupon with this code is already used.
56
     *
57
     * @param  string|null  $code
58
     *
59
     * @return bool
60
     */
61 5
    public function isCouponAlreadyUsed(?string $code): bool
62
    {
63 5
        $column = $this->couponService->model->getCodeColumn();
0 ignored issues
show
Bug introduced by
Accessing model on the interface MichaelRubel\Couponables...s\CouponServiceContract suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
64
65 5
        return $this->coupons()->where($column, $code)->exists();
66
    }
67
68
    /**
69
     * Check if the coupon is over limit for the model.
70
     *
71
     * @param  string|null  $code
72
     *
73
     * @return bool
74
     */
75 2
    public function isCouponOverLimit(?string $code): bool
76
    {
77 2
        $coupon = $this->couponService->getCoupon($code);
78
79 2
        return ! is_null($coupon) && $coupon->isOverLimitFor($this);
0 ignored issues
show
Bug introduced by
The method isOverLimitFor() 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

79
        return ! is_null($coupon) && $coupon->/** @scrutinizer ignore-call */ isOverLimitFor($this);
Loading history...
80
    }
81
82
    /**
83
     * Verify if the coupon is valid.
84
     *
85
     * @param  string|null  $code
86
     *
87
     * @return CouponContract
88
     */
89 8
    public function verifyCoupon(?string $code): CouponContract
90
    {
91 8
        return $this->couponService->verifyCoupon($code, $this);
0 ignored issues
show
Bug introduced by
$this of type MichaelRubel\Couponables\Traits\HasCoupons is incompatible with the type Illuminate\Database\Eloquent\Model|null expected by parameter $redeemer of MichaelRubel\Couponables...ontract::verifyCoupon(). ( Ignorable by Annotation )

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

91
        return $this->couponService->verifyCoupon($code, /** @scrutinizer ignore-type */ $this);
Loading history...
92
    }
93
94
    /**
95
     * Verify and use the coupon.
96
     *
97
     * @param  string|null  $code
98
     * @param  Model|null  $for
99
     *
100
     * @return CouponContract
101
     */
102 30
    public function redeemCoupon(?string $code, ?Model $for = null): CouponContract
103
    {
104 30
        $coupon = $this->couponService->verifyCoupon($code, $this);
0 ignored issues
show
Bug introduced by
$this of type MichaelRubel\Couponables\Traits\HasCoupons is incompatible with the type Illuminate\Database\Eloquent\Model|null expected by parameter $redeemer of MichaelRubel\Couponables...ontract::verifyCoupon(). ( Ignorable by Annotation )

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

104
        $coupon = $this->couponService->verifyCoupon($code, /** @scrutinizer ignore-type */ $this);
Loading history...
105
106 18
        return $this->couponService->applyCoupon($coupon, $this, $for);
0 ignored issues
show
Bug introduced by
$this of type MichaelRubel\Couponables\Traits\HasCoupons is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $redeemer of MichaelRubel\Couponables...Contract::applyCoupon(). ( Ignorable by Annotation )

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

106
        return $this->couponService->applyCoupon($coupon, /** @scrutinizer ignore-type */ $this, $for);
Loading history...
107
    }
108
109
    /**
110
     * Verify the coupon or do something else on fail.
111
     *
112
     * @param  string|null  $code
113
     * @param  Closure|null  $callback
114
     *
115
     * @return mixed
116
     */
117 4
    public function verifyCouponOr(?string $code, ?Closure $callback = null): mixed
118
    {
119
        try {
120 4
            return $this->verifyCoupon($code);
121 3
        } catch (Throwable $e) {
122 3
            return $callback instanceof Closure ? $callback($code, $e) : throw $e;
123
        }
124
    }
125
126
    /**
127
     * Redeem the coupon or do something else on fail.
128
     *
129
     * @param  string|null  $code
130
     * @param  Closure|null  $callback
131
     *
132
     * @return mixed
133
     */
134 4
    public function redeemCouponOr(?string $code, ?Closure $callback = null): mixed
135
    {
136
        try {
137 4
            return $this->redeemCoupon($code);
138 3
        } catch (Throwable $e) {
139 3
            return $callback instanceof Closure ? $callback($code, $e) : throw $e;
140
        }
141
    }
142
143
    /**
144
     * Redeem the code using model.
145
     *
146
     * @param  Model  $model
147
     * @param  string|null  $couponCode
148
     *
149
     * @return CouponContract
150
     */
151 1
    public function redeemBy(Model $model, ?string $couponCode): CouponContract
152
    {
153 1
        $coupon = $this->couponService->verifyCoupon($couponCode, $model);
154
155 1
        return $this->couponService->applyCoupon($coupon, $model, $this);
0 ignored issues
show
Bug introduced by
$this of type MichaelRubel\Couponables\Traits\HasCoupons is incompatible with the type Illuminate\Database\Eloquent\Model|null expected by parameter $for of MichaelRubel\Couponables...Contract::applyCoupon(). ( Ignorable by Annotation )

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

155
        return $this->couponService->applyCoupon($coupon, $model, /** @scrutinizer ignore-type */ $this);
Loading history...
156
    }
157
}
158