Passed
Push — main ( df9a67...447794 )
by Michael
03:30
created

Coupon::isEnabled()   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
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\Couponables\Models;
6
7
use Illuminate\Database\Eloquent\Factories\HasFactory;
8
use Illuminate\Database\Eloquent\Model;
9
use MichaelRubel\Couponables\Models\Contracts\CouponContract;
10
use MichaelRubel\Couponables\Models\Traits\DefinesColumnChecks;
11
use MichaelRubel\Couponables\Models\Traits\DefinesColumns;
12
use MichaelRubel\Couponables\Models\Traits\DefinesModelRelations;
13
use MichaelRubel\Couponables\Traits\Concerns\CalculatesCosts;
14
use MichaelRubel\EnhancedContainer\Core\CallProxy;
15
16
class Coupon extends Model implements CouponContract
17
{
18
    use HasFactory;
19
    use DefinesColumns;
20
    use DefinesColumnChecks;
21
    use DefinesModelRelations;
22
    use CalculatesCosts;
23
24
    /**
25
     * The attributes that aren't mass assignable.
26
     *
27
     * @var array<string>|bool
28
     */
29
    protected $guarded = [];
30
31
    /**
32
     * The attributes that should be cast.
33
     *
34
     * @var array<string, string>
35
     */
36
    protected $casts = [
37
        'code'       => 'string',
38
        'type'       => 'string',
39
        'value'      => 'string',
40
        'is_enabled' => 'boolean',
41
        'data'       => 'collection',
42
        'quantity'   => 'integer',
43
        'limit'      => 'integer',
44
        'expires_at' => 'datetime',
45
    ];
46
47
    /**
48
     * @var CallProxy
49
     */
50
    protected static CallProxy $bindable;
51
52
    /**
53
     * @param  array  $attributes
54
     */
55 76
    public function __construct(array $attributes = [])
56
    {
57 76
        parent::__construct($attributes);
58
59 76
        $this->table = config('couponables.table', 'coupons');
60
61 76
        static::$bindable = call($this);
62
    }
63
64
    /**
65
     * Check if code is expired.
66
     *
67
     * @return bool
68
     */
69 33
    public function isExpired(): bool
70
    {
71 33
        $expires_at = $this->{static::$bindable->getExpiresAtColumn()};
72
73 33
        return $expires_at && now()->gte($expires_at);
74
    }
75
76
    /**
77
     * Check if code is not expired.
78
     *
79
     * @return bool
80
     */
81 1
    public function isNotExpired(): bool
82
    {
83 1
        return ! static::$bindable->isExpired();
84
    }
85
86
    /**
87
     * Check if code is enabled.
88
     *
89
     * @return bool
90
     */
91 36
    public function isEnabled(): bool
92
    {
93 36
        return $this->{static::$bindable->getIsEnabledColumn()} ?? true;
94
    }
95
96
    /**
97
     * Check if code is disabled.
98
     *
99
     * @return bool
100
     */
101 34
    public function isDisabled(): bool
102
    {
103 34
        return ! static::$bindable->isEnabled();
104
    }
105
106
    /**
107
     * Check if code amount is over.
108
     *
109
     * @return bool
110
     */
111 29
    public function isOverQuantity(): bool
112
    {
113 29
        $quantity = $this->{static::$bindable->getQuantityColumn()};
114
115 29
        return ! is_null($quantity) && $quantity <= 0;
116
    }
117
118
    /**
119
     * Check if coupon is disposable.
120
     *
121
     * @return bool
122
     */
123 29
    public function isDisposable(): bool
124
    {
125 29
        $limit = $this->{static::$bindable->getLimitColumn()};
126
127 29
        return ! is_null($limit) && $limit == 1;
128
    }
129
130
    /**
131
     * Check if the code is reached its global limit.
132
     *
133
     * @param  Model  $redeemer
134
     * @param  string|null  $code
135
     *
136
     * @return bool
137
     */
138 29
    public function isOverLimit(Model $redeemer, ?string $code): bool
139
    {
140 29
        return (static::$bindable->isDisposable() && call($redeemer)->isCouponAlreadyUsed($code))
141 29
            || static::$bindable->isOverLimitFor($redeemer);
142
    }
143
144
    /**
145
     * Check if the code is reached its limit for the passed model.
146
     *
147
     * @param  Model  $redeemer
148
     *
149
     * @return bool
150
     */
151 29
    public function isOverLimitFor(Model $redeemer): bool
152
    {
153 29
        $column = static::$bindable->getCodeColumn();
154 29
        $limit  = $this->{static::$bindable->getLimitColumn()};
155
156 29
        return ! is_null($limit) && $limit <= $redeemer
157 29
            ->coupons()
158 29
            ->where($column, $this->{$column})
159 29
            ->count();
160
    }
161
162
    /**
163
     * Check if coupon is already redeemed by the model.
164
     *
165
     * @param  Model  $redeemer
166
     *
167
     * @return bool
168
     */
169 2
    public function isRedeemedBy(Model $redeemer): bool
170
    {
171 2
        $column = static::$bindable->getCodeColumn();
172
173 2
        return $redeemer->coupons()->where($column, $this->{$column})->exists();
174
    }
175
176
    /**
177
     * Check if the model is allowed to redeem.
178
     *
179
     * @param  Model  $redeemer
180
     *
181
     * @return bool
182
     */
183 30
    public function isAllowedToRedeemBy(Model $redeemer): bool
184
    {
185 30
        return with(static::$bindable, function ($coupon) use ($redeemer) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return with(static::bind...ion(...) { /* ... */ }) could return the type MichaelRubel\EnhancedContainer\Core\CallProxy which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
186 30
            if ($coupon->isMorphColumnsFilled() && ! $coupon->redeemer()->is($redeemer)) {
187 4
                return false;
188
            }
189
190 26
            if ($coupon->isOnlyRedeemerTypeFilled() && ! $coupon->isSameRedeemerModel($redeemer)) {
191 1
                return false;
192
            }
193
194 25
            return true;
195 30
        });
196
    }
197
198
    /**
199
     * Assign the model to the latest redeemed coupon.
200
     *
201
     * @param  Model  $redeemed
202
     *
203
     * @return CouponContract
204
     */
205 1
    public function for(Model $redeemed): CouponContract
206
    {
207 1
        return with($this->couponables()->first(), function ($couponable) use ($redeemed) {
208 1
            $morphValues = transform($couponable, fn ($bindable) => [
209 1
                $bindable->getRedeemedTypeColumn() => $redeemed->getMorphClass(),
210 1
                $bindable->getRedeemedIdColumn()   => $redeemed->id,
211 1
            ]);
212
213 1
            $couponable->update($morphValues);
214
215 1
            return $this;
216 1
        });
217
    }
218
}
219