Test Failed
Pull Request — main (#21)
by
unknown
03:21
created

Coupon::isDisabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 1
cts 1
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
        'data'       => 'collection',
41
        'quantity'   => 'integer',
42
        'limit'      => 'integer',
43
        'expires_at' => 'datetime',
44
        'is_enabled' => 'boolean',
45
    ];
46
47
    /**
48
     * @var CallProxy
49
     */
50
    protected static CallProxy $bindable;
51
52
    /**
53
     * @param  array  $attributes
54 71
     */
55
    public function __construct(array $attributes = [])
56 71
    {
57
        parent::__construct($attributes);
58 71
59
        $this->table = config('couponables.table', 'coupons');
60 71
61
        static::$bindable = call($this);
62
    }
63
64
    /**
65
     * Check if code is expired.
66
     *
67
     * @return bool
68 33
     */
69
    public function isExpired(): bool
70 33
    {
71
        $expires_at = $this->{static::$bindable->getExpiresAtColumn()};
72 33
73
        return $expires_at && now()->gte($expires_at);
74
    }
75
76
    /**
77
     * Check if code is not expired.
78
     *
79
     * @return bool
80 1
     */
81
    public function isNotExpired(): bool
82 1
    {
83
        return ! static::$bindable->isExpired();
84
    }
85
86
    /**
87
     * Check if code is enabled.
88
     *
89
     * @return bool
90 29
     */
91
    public function isEnabled(): bool
92 29
    {
93
        return $this->{static::$bindable->getIsEnabledColumn()} ?? true;
94 29
    }
95
96
    /**
97
     * Check if code is disabled.
98
     *
99
     * @return bool
100
     */
101
    public function isDisabled(): bool
102 29
    {
103
        return ! static::$bindable->isEnabled();
104 29
    }
105
106 29
    /**
107
     * Check if code amount is over.
108
     *
109
     * @return bool
110
     */
111
    public function isOverQuantity(): bool
112
    {
113
        $quantity = $this->{static::$bindable->getQuantityColumn()};
114
115
        return ! is_null($quantity) && $quantity <= 0;
116
    }
117 29
118
    /**
119 29
     * Check if coupon is disposable.
120 29
     *
121
     * @return bool
122
     */
123
    public function isDisposable(): bool
124
    {
125
        $limit = $this->{static::$bindable->getLimitColumn()};
126
127
        return ! is_null($limit) && $limit == 1;
128
    }
129
130 29
    /**
131
     * Check if the code is reached its global limit.
132 29
     *
133 29
     * @param  Model  $redeemer
134
     * @param  string|null  $code
135 29
     *
136 29
     * @return bool
137 29
     */
138 29
    public function isOverLimit(Model $redeemer, ?string $code): bool
139
    {
140
        return (static::$bindable->isDisposable() && call($redeemer)->isCouponAlreadyUsed($code))
141
            || 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 2
     *
149
     * @return bool
150 2
     */
151
    public function isOverLimitFor(Model $redeemer): bool
152 2
    {
153
        $column = static::$bindable->getCodeColumn();
154
        $limit  = $this->{static::$bindable->getLimitColumn()};
155
156
        return ! is_null($limit) && $limit <= $redeemer
157
            ->coupons()
158
            ->where($column, $this->{$column})
159
            ->count();
160
    }
161
162 30
    /**
163
     * Check if coupon is already redeemed by the model.
164 30
     *
165 30
     * @param  Model  $redeemer
166 4
     *
167
     * @return bool
168
     */
169 26
    public function isRedeemedBy(Model $redeemer): bool
170 1
    {
171
        $column = static::$bindable->getCodeColumn();
172
173 25
        return $redeemer->coupons()->where($column, $this->{$column})->exists();
174 30
    }
175
176
    /**
177
     * Check if the model is allowed to redeem.
178
     *
179
     * @param  Model  $redeemer
180
     *
181
     * @return bool
182
     */
183
    public function isAllowedToRedeemBy(Model $redeemer): bool
184 1
    {
185
        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 1
            if ($coupon->isMorphColumnsFilled() && ! $coupon->redeemer()->is($redeemer)) {
187 1
                return false;
188 1
            }
189 1
190 1
            if ($coupon->isOnlyRedeemerTypeFilled() && ! $coupon->isSameRedeemerModel($redeemer)) {
191
                return false;
192 1
            }
193
194 1
            return true;
195 1
        });
196
    }
197
198
    /**
199
     * Assign the model to the latest redeemed coupon.
200
     *
201
     * @param  Model  $redeemed
202
     *
203
     * @return CouponContract
204
     */
205
    public function for(Model $redeemed): CouponContract
206
    {
207
        return with($this->couponables()->first(), function ($couponable) use ($redeemed) {
208
            $morphValues = transform($couponable, fn ($bindable) => [
209
                $bindable->getRedeemedTypeColumn() => $redeemed->getMorphClass(),
210
                $bindable->getRedeemedIdColumn()   => $redeemed->id,
211
            ]);
212
213
            $couponable->update($morphValues);
214
215
            return $this;
216
        });
217
    }
218
}
219