Completed
Push — develop ( 714b37...8cdca7 )
by Abdelrahman
04:46
created

Booking::resource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Rinvex\Bookings\Models;
6
7
use Carbon\Carbon;
8
use Illuminate\Database\Eloquent\Model;
9
use Rinvex\Cacheable\CacheableEloquent;
10
use Illuminate\Database\Eloquent\Builder;
11
use Rinvex\Support\Traits\ValidatingTrait;
12
use Rinvex\Bookings\Contracts\RateContract;
13
use Rinvex\Bookings\Contracts\PriceContract;
14
use Rinvex\Bookings\Contracts\BookingContract;
15
use Illuminate\Database\Eloquent\Relations\MorphTo;
16
17
/**
18
 * Rinvex\Bookings\Models\Booking.
19
 *
20
 * @property int                                                $id
21
 * @property int                                                $resource_id
22
 * @property string                                             $resource_type
23
 * @property string                                             $currency
24
 * @property int                                                $customer_id
25
 * @property string                                             $customer_type
26
 * @property \Carbon\Carbon                                     $starts_at
27
 * @property \Carbon\Carbon                                     $ends_at
28
 * @property float                                              $price
29
 * @property array                                              $price_equation
30
 * @property \Carbon\Carbon                                     $cancelled_at
31
 * @property string                                             $notes
32
 * @property \Carbon\Carbon|null                                $created_at
33
 * @property \Carbon\Carbon|null                                $updated_at
34
 * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $resource
35
 * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $customer
36
 *
37
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking cancelled()
38
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking cancelledAfter($date)
39
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking cancelledBefore($date)
40
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking cancelledBetween($starts, $ends)
41
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking current()
42
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking endsAfter($date)
43
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking endsBefore($date)
44
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking endsBetween($starts, $ends)
45
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking future()
46
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking ofCustomer(\Illuminate\Database\Eloquent\Model $customer)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 145 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
47
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking ofResource(\Illuminate\Database\Eloquent\Model $resource)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 145 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
48
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking past()
49
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking startsAfter($date)
50
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking startsBefore($date)
51
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking startsBetween($starts, $ends)
52
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereCancelledAt($value)
53
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereCreatedAt($value)
54
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereCurrency($value)
55
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereCustomerId($value)
56
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereCustomerType($value)
57
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereEndsAt($value)
58
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereId($value)
59
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereNotes($value)
60
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking wherePrice($value)
61
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking wherePriceEquation($value)
62
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereResourceId($value)
63
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereResourceType($value)
64
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereStartsAt($value)
65
 * @method static \Illuminate\Database\Eloquent\Builder|\Rinvex\Bookings\Models\Booking whereUpdatedAt($value)
66
 * @mixin \Eloquent
67
 */
68
class Booking extends Model implements BookingContract
69
{
70
    use ValidatingTrait;
71
    use CacheableEloquent;
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    protected $fillable = [
77
        'resource_id',
78
        'resource_type',
79
        'customer_id',
80
        'customer_type',
81
        'starts_at',
82
        'ends_at',
83
        'price',
84
        'currency',
85
        'price_equation',
86
        'cancelled_at',
87
        'notes',
88
    ];
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    protected $casts = [
94
        'resource_id' => 'integer',
95
        'resource_type' => 'string',
96
        'customer_id' => 'integer',
97
        'customer_type' => 'string',
98
        'starts_at' => 'datetime',
99
        'ends_at' => 'datetime',
100
        'price' => 'float',
101
        'currency' => 'string',
102
        'price_equation' => 'json',
103
        'cancelled_at' => 'datetime',
104
        'notes' => 'string',
105
    ];
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    protected $observables = [
111
        'validating',
112
        'validated',
113
    ];
114
115
    /**
116
     * The default rules that the model will validate against.
117
     *
118
     * @var array
119
     */
120
    protected $rules = [];
121
122
    /**
123
     * Whether the model should throw a
124
     * ValidationException if it fails validation.
125
     *
126
     * @var bool
127
     */
128
    protected $throwValidationExceptions = true;
129
130
    /**
131
     * Create a new Eloquent model instance.
132
     *
133
     * @param array $attributes
134
     */
135
    public function __construct(array $attributes = [])
136
    {
137
        parent::__construct($attributes);
138
139
        $this->setTable(config('rinvex.bookings.tables.bookings'));
140
        $this->setRules([
141
            'resource_id' => 'required|integer',
142
            'resource_type' => 'required|string',
143
            'customer_id' => 'required|integer',
144
            'customer_type' => 'required|string',
145
            'starts_at' => 'nullable|date',
146
            'ends_at' => 'nullable|date',
147
            'price' => 'required|numeric',
148
            'currency' => 'required|alpha|size:3',
149
            'price_equation' => 'nullable|array',
150
            'cancelled_at' => 'nullable|date',
151
            'notes' => 'nullable|string|max:10000',
152
        ]);
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    protected static function boot()
159
    {
160
        parent::boot();
161
162
        static::validating(function (self $booking) {
163
            list($price, $priceEquation, $currency) = is_null($booking->price)
164
                ? $booking->calculatePrice() : [$booking->price, $booking->price_equation, $booking->currency];
165
166
            $booking->price_equation = $priceEquation;
167
            $booking->currency = $currency;
168
            $booking->price = $price;
169
        });
170
    }
171
172
    /**
173
     * Calculate the booking price.
174
     *
175
     * @return array
176
     */
177
    protected function calculatePrice(): array
178
    {
179
        switch ($this->resource->unit) {
180
            case 'd':
181
                $method = 'addDay';
182
                break;
183
            case 'm':
184
                $method = 'addMinute';
185
                break;
186
            case 'h':
187
            default:
188
                $method = 'addHour';
189
                break;
190
        }
191
192
        $prices = $this->resource->prices->map(function (PriceContract $price) {
193
            return [
194
                'weekday' => $price->weekday,
0 ignored issues
show
Bug introduced by
Accessing weekday on the interface Rinvex\Bookings\Contracts\PriceContract 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...
195
                'starts_at' => $price->starts_at,
0 ignored issues
show
Bug introduced by
Accessing starts_at on the interface Rinvex\Bookings\Contracts\PriceContract 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...
196
                'ends_at' => $price->ends_at,
0 ignored issues
show
Bug introduced by
Accessing ends_at on the interface Rinvex\Bookings\Contracts\PriceContract 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...
197
                'percentage' => $price->percentage,
0 ignored issues
show
Bug introduced by
Accessing percentage on the interface Rinvex\Bookings\Contracts\PriceContract 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...
198
            ];
199
        });
200
201
        $totalUnits = 0;
202
        $totalPrice = 0;
203
204
        for($date = $this->starts_at; $date->lte($this->ends_at); $date->$method()) {
205
            // Count units
206
            $totalUnits++;
207
208
            // Get applicable custom prices. Use first custom price matched, and ignore
209
            // others. We should not have multiple custom prices for same time range anyway!
210
            $customPrice = $prices->search(function ($price) use ($date, $resource) {
0 ignored issues
show
Bug introduced by
The variable $resource does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
211
                $dayMatched = $price['weekday'] === mb_strtolower($date->format('D'));
212
213
                return $this->resource->unit === 'd' ? $dayMatched : $dayMatched && (new Carbon($date->format('H:i:s')))->between(new Carbon($price['starts_at']), new Carbon($price['ends_at']));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 194 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
214
            });
215
216
            // Use custom price if exists (custom price is a +/- percentage of original resource price)
217
            $totalPrice += $customPrice !== false ? $this->resource->price + (($this->resource->price * $prices[$customPrice]['percentage']) / 100) : $this->resource->price;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 173 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
218
        }
219
220
        $rates = $this->resource->rates->map(function (RateContract $rate) {
221
            return [
222
                'percentage' => $rate->percentage,
0 ignored issues
show
Bug introduced by
Accessing percentage on the interface Rinvex\Bookings\Contracts\RateContract 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...
223
                'operator' => $rate->operator,
0 ignored issues
show
Bug introduced by
Accessing operator on the interface Rinvex\Bookings\Contracts\RateContract 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...
224
                'amount' => $rate->amount,
0 ignored issues
show
Bug introduced by
Accessing amount on the interface Rinvex\Bookings\Contracts\RateContract 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...
225
            ];
226
        })->toArray();
227
228
        foreach ($rates as $rate) {
229
            switch ($rate['operator']) {
230
                case '^':
231
                    $units = $totalUnits <= $rate['amount'] ? $totalUnits : $rate['amount'];
232
                    $totalPrice += (($rate['percentage'] * $this->resource->price) / 100) * $units;
233
                    break;
234
                case '>':
235
                    $totalPrice += $totalUnits > $rate['amount'] ? ((($rate['percentage'] * $this->resource->price) / 100) * $totalUnits) : 0;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
236
                    break;
237
                case '<':
238
                    $totalPrice += $totalUnits < $rate['amount'] ? ((($rate['percentage'] * $this->resource->price) / 100) * $totalUnits) : 0;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
239
                    break;
240
                case '=':
241
                default:
242
                    $totalPrice += $totalUnits == $rate['amount'] ? ((($rate['percentage'] * $this->resource->price) / 100) * $totalUnits) : 0;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 143 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
243
                    break;
244
            }
245
        }
246
247
        $priceEquation = [
248
            'price' => $this->resource->price,
249
            'unit' => $this->resource->unit,
250
            'currency' => $this->resource->currency,
251
            'total_units' => $totalUnits,
252
            'total_price' => $totalPrice,
253
            'prices' => $prices,
254
            'rates' => $rates,
255
        ];
256
257
        return [$totalPrice, $priceEquation, $this->resource->currency];
258
    }
259
260
    /**
261
     * Get the owning resource model.
262
     *
263
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
264
     */
265
    public function resource(): MorphTo
266
    {
267
        return $this->morphTo();
268
    }
269
270
    /**
271
     * Get the owning customer.
272
     *
273
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
274
     */
275
    public function customer(): MorphTo
276
    {
277
        return $this->morphTo();
278
    }
279
280
    /**
281
     * Get bookings of the given resource.
282
     *
283
     * @param \Illuminate\Database\Eloquent\Builder $builder
284
     * @param \Illuminate\Database\Eloquent\Model   $resource
285
     *
286
     * @return \Illuminate\Database\Eloquent\Builder
287
     */
288
    public function scopeOfResource(Builder $builder, Model $resource): Builder
289
    {
290
        return $builder->where('resource_type', $resource->getMorphClass())->where('resource_id', $resource->getKey());
291
    }
292
293
    /**
294
     * Get bookings of the given customer.
295
     *
296
     * @param \Illuminate\Database\Eloquent\Builder $builder
297
     * @param \Illuminate\Database\Eloquent\Model   $customer
298
     *
299
     * @return \Illuminate\Database\Eloquent\Builder
300
     */
301
    public function scopeOfCustomer(Builder $builder, Model $customer): Builder
302
    {
303
        return $builder->where('customer_type', $customer->getMorphClass())->where('customer_id', $customer->getKey());
304
    }
305
306
    /**
307
     * Get the past bookings.
308
     *
309
     * @param \Illuminate\Database\Eloquent\Builder $builder
310
     *
311
     * @return \Illuminate\Database\Eloquent\Builder
312
     */
313
    public function scopePast(Builder $builder): Builder
314
    {
315
        return $builder->whereNull('cancelled_at')
316
                       ->whereNotNull('ends_at')
317
                       ->where('ends_at', '<', Carbon::now());
318
    }
319
320
    /**
321
     * Get the future bookings.
322
     *
323
     * @param \Illuminate\Database\Eloquent\Builder $builder
324
     *
325
     * @return \Illuminate\Database\Eloquent\Builder
326
     */
327
    public function scopeFuture(Builder $builder): Builder
328
    {
329
        return $builder->whereNull('cancelled_at')
330
                       ->whereNotNull('starts_at')
331
                       ->where('starts_at', '>', Carbon::now());
332
    }
333
334
    /**
335
     * Get the current bookings.
336
     *
337
     * @param \Illuminate\Database\Eloquent\Builder $builder
338
     *
339
     * @return \Illuminate\Database\Eloquent\Builder
340
     */
341
    public function scopeCurrent(Builder $builder): Builder
342
    {
343
        return $builder->whereNull('cancelled_at')
344
                       ->whereNotNull('starts_at')
345
                       ->whereNotNull('ends_at')
346
                       ->where('starts_at', '<', Carbon::now())
347
                       ->where('ends_at', '>', Carbon::now());
348
    }
349
350
    /**
351
     * Get the cancelled bookings.
352
     *
353
     * @param \Illuminate\Database\Eloquent\Builder $builder
354
     *
355
     * @return \Illuminate\Database\Eloquent\Builder
356
     */
357
    public function scopeCancelled(Builder $builder): Builder
358
    {
359
        return $builder->whereNotNull('cancelled_at');
360
    }
361
362
    /**
363
     * Get bookings starts before the given date.
364
     *
365
     * @param \Illuminate\Database\Eloquent\Builder $builder
366
     * @param string                                $date
367
     *
368
     * @return \Illuminate\Database\Eloquent\Builder
369
     */
370
    public function scopeStartsBefore(Builder $builder, string $date): Builder
371
    {
372
        return $builder->whereNull('cancelled_at')
373
                       ->whereNotNull('starts_at')
374
                       ->where('starts_at', '<', new Carbon($date));
375
    }
376
377
    /**
378
     * Get bookings starts after the given date.
379
     *
380
     * @param \Illuminate\Database\Eloquent\Builder $builder
381
     * @param string                                $date
382
     *
383
     * @return \Illuminate\Database\Eloquent\Builder
384
     */
385
    public function scopeStartsAfter(Builder $builder, string $date): Builder
386
    {
387
        return $builder->whereNull('cancelled_at')
388
                       ->whereNotNull('starts_at')
389
                       ->where('starts_at', '>', new Carbon($date));
390
    }
391
392
    /**
393
     * Get bookings starts between the given dates.
394
     *
395
     * @param \Illuminate\Database\Eloquent\Builder $builder
396
     * @param string                                $starts
397
     * @param string                                $ends
398
     *
399
     * @return \Illuminate\Database\Eloquent\Builder
400
     */
401
    public function scopeStartsBetween(Builder $builder, string $starts, string $ends): Builder
402
    {
403
        return $builder->whereNull('cancelled_at')
404
                       ->whereNotNull('starts_at')
405
                       ->where('starts_at', '>', new Carbon($starts))
406
                       ->where('starts_at', '<', new Carbon($ends));
407
    }
408
409
    /**
410
     * Get bookings ends before the given date.
411
     *
412
     * @param \Illuminate\Database\Eloquent\Builder $builder
413
     * @param string                                $date
414
     *
415
     * @return \Illuminate\Database\Eloquent\Builder
416
     */
417
    public function scopeEndsBefore(Builder $builder, string $date): Builder
418
    {
419
        return $builder->whereNull('cancelled_at')
420
                       ->whereNotNull('ends_at')
421
                       ->where('ends_at', '<', new Carbon($date));
422
    }
423
424
    /**
425
     * Get bookings ends after the given date.
426
     *
427
     * @param \Illuminate\Database\Eloquent\Builder $builder
428
     * @param string                                $date
429
     *
430
     * @return \Illuminate\Database\Eloquent\Builder
431
     */
432
    public function scopeEndsAfter(Builder $builder, string $date): Builder
433
    {
434
        return $builder->whereNull('cancelled_at')
435
                       ->whereNotNull('ends_at')
436
                       ->where('ends_at', '>', new Carbon($date));
437
    }
438
439
    /**
440
     * Get bookings ends between the given date.
441
     *
442
     * @param \Illuminate\Database\Eloquent\Builder $builder
443
     * @param string                                $starts
444
     * @param string                                $ends
445
     *
446
     * @return \Illuminate\Database\Eloquent\Builder
447
     */
448
    public function scopeEndsBetween(Builder $builder, string $starts, string $ends): Builder
449
    {
450
        return $builder->whereNull('cancelled_at')
451
                       ->whereNotNull('ends_at')
452
                       ->where('ends_at', '>', new Carbon($starts))
453
                       ->where('ends_at', '<', new Carbon($ends));
454
    }
455
456
    /**
457
     * Get bookings cancelled before the given date.
458
     *
459
     * @param \Illuminate\Database\Eloquent\Builder $builder
460
     * @param string                                $date
461
     *
462
     * @return \Illuminate\Database\Eloquent\Builder
463
     */
464
    public function scopeCancelledBefore(Builder $builder, string $date): Builder
465
    {
466
        return $builder->whereNotNull('cancelled_at')
467
                       ->where('cancelled_at', '<', new Carbon($date));
468
    }
469
470
    /**
471
     * Get bookings cancelled after the given date.
472
     *
473
     * @param \Illuminate\Database\Eloquent\Builder $builder
474
     * @param string                                $date
475
     *
476
     * @return \Illuminate\Database\Eloquent\Builder
477
     */
478
    public function scopeCancelledAfter(Builder $builder, string $date): Builder
479
    {
480
        return $builder->whereNotNull('cancelled_at')
481
                       ->where('cancelled_at', '>', new Carbon($date));
482
    }
483
484
    /**
485
     * Get bookings cancelled between the given dates.
486
     *
487
     * @param \Illuminate\Database\Eloquent\Builder $builder
488
     * @param string                                $starts
489
     * @param string                                $ends
490
     *
491
     * @return \Illuminate\Database\Eloquent\Builder
492
     */
493
    public function scopeCancelledBetween(Builder $builder, string $starts, string $ends): Builder
494
    {
495
        return $builder->whereNotNull('cancelled_at')
496
                       ->where('cancelled_at', '>', new Carbon($starts))
497
                       ->where('cancelled_at', '<', new Carbon($ends));
498
    }
499
500
    /**
501
     * Check if the booking is cancelled.
502
     *
503
     * @return bool
504
     */
505
    public function isCancelled(): bool
506
    {
507
        return (bool) $this->cancelled_at;
508
    }
509
510
    /**
511
     * Check if the booking is past.
512
     *
513
     * @return bool
514
     */
515
    public function isPast(): bool
516
    {
517
        return ! $this->isCancelled() && $this->ends_at->isPast();
518
    }
519
520
    /**
521
     * Check if the booking is future.
522
     *
523
     * @return bool
524
     */
525
    public function isFuture(): bool
526
    {
527
        return ! $this->isCancelled() && $this->starts_at->isFuture();
528
    }
529
530
    /**
531
     * Check if the booking is current.
532
     *
533
     * @return bool
534
     */
535
    public function isCurrent(): bool
536
    {
537
        return ! $this->isCancelled() && Carbon::now()->between($this->starts_at, $this->ends_at);
538
    }
539
}
540