@Tray2 thank you for being willing to take a closer look.
EventRegistration model
<?php
namespace App\Models\Events;
use App\Models\Action;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class EventRegistration extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'vendor_id',
'payment_type',
'payment_amount',
'notes',
'completed_at',
'transaction_ids',
'first_paid_at',
];
protected $hidden = ['event_id', 'user_id'];
protected $casts = ['completed_at' => 'datetime', 'payment_amount' => 'float', 'transaction_ids' => 'collection'];
/**
* @return Attribute
*/
public function status(): Attribute
{
$status = 'incomplete';
if (
!is_null($this->completed_at) &&
!empty($this->payment_amount) &&
$this->payment_amount === $this->subtotal
) {
$status = 'paid';
} elseif (!is_null($this->completed_at)) {
$status = 'completed';
}
return Attribute::make(get: fn() => $status);
}
/**
* @return Attribute
*/
public function subtotal(): Attribute
{
return Attribute::make(
get: fn() => round($this->items->sum(fn($item) => $item->cost * $item->quantity), 2, PHP_ROUND_HALF_UP)
);
}
/**
* Total amount due.
*
* @return Attribute
*/
public function total(): Attribute
{
return Attribute::make(
get: fn() => round($this->subtotal - (float) $this->payment_amount, 2, PHP_ROUND_HALF_UP)
);
}
/**
* @return Attribute
*/
public function invoiceNumber(): Attribute
{
return Attribute::make(get: fn() => sprintf('INV-%s-%s', $this->vendor_id, $this->event_id));
}
/**
* @return Attribute
*/
public function deadline(): Attribute
{
if ($this->exists) {
$hasIncompleteItems = $this->items()
->whereNull('completed_at')
->exists();
$deadline = $this->updated_at->copy()->addMinutes(30);
return Attribute::make(
get: fn() => $deadline->gt(now()) && $hasIncompleteItems ? $deadline->toISOString() : null
);
}
return Attribute::make(get: fn() => null);
}
/**
* @return BelongsTo
*/
public function event(): BelongsTo
{
return $this->belongsTo(Event::class);
}
/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(\App\Models\User::class);
}
/**
* @return BelongsTo
*/
public function vendor(): BelongsTo
{
return $this->belongsTo(\App\Models\Vendor::class);
}
/**
* @return HasMany
*/
public function items(): HasMany
{
return $this->hasMany(EventRegistrationItem::class);
}
/**
* @return MorphToMany
*/
public function sponsorships(): MorphToMany
{
return $this->morphedByMany(EventSponsorship::class, 'registerable', 'event_registration_items')
->withPivot('id', 'name', 'cost', 'quantity', 'azure_file_id')
->withTimestamps();
}
/**
* @return MorphToMany
*/
public function booths(): MorphToMany
{
return $this->morphedByMany(EventBooth::class, 'registerable', 'event_registration_items')
->withPivot(['name', 'quantity'])
->withTimestamps();
}
/**
* @return MorphMany
*/
public function actions(): MorphMany
{
return $this->morphMany(Action::class, 'actionable');
}
/**
* @param Builder $query
* @return void
*/
public function scopeExpired(Builder $query): void
{
$query
->whereHas('items', fn(Builder $query) => $query->whereNull('completed_at'))
->where('updated_at', '<', now()->subMinutes(30));
}
/**
* @param Builder $query
* @return void
*/
public function scopeCompleted(Builder $query): void
{
$query
->whereNotNull('completed_at')
->whereNotNull('payment_amount')
->whereNotNull('first_paid_at');
}
}
EventSponsorship model
<?php
namespace App\Models\Events;
use App\Models\Action;
use App\Models\BlueBookCategory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class EventSponsorship extends Model
{
use HasFactory;
protected $fillable = [
'event_sponsorship_type_id',
'blue_book_category_id',
'name',
'slug',
'image_url',
'sold_image_url',
'sell_sheet_url',
'spec_sheet_url',
'media_url',
'cost',
'quantity',
];
protected $casts = [
'quantity' => 'integer',
'cost' => 'decimal:2',
];
protected $hidden = ['event_id', 'event_sponsorship_type_id', 'blue_book_category_id'];
/**
* @return Attribute
*/
public function description(): Attribute
{
return Attribute::make(get: fn() => $this->sponsorshipType->description);
}
/**
* @return Attribute
*/
public function available(): Attribute
{
$available = 0;
$purchased = $this->query()
->select('id')
->withSum('items', 'quantity')
->with('items')
->where('id', $this->id)
->first();
$available = is_null($purchased) ? $this->quantity : $this->quantity - $purchased->items_sum_quantity;
return Attribute::make(get: fn() => $available);
}
/**
* @return Attribute
*/
public function pending(): Attribute
{
$pending = 0;
$query = $this->query()
->select('id')
->withSum('items', 'quantity')
->with('items')
->whereHas('event.registrations', function ($query) {
$query->whereNull('completed_at');
})
->where('id', $this->id)
->first();
$pending = is_null($query) ? $this->quantity : $query->items_sum_quantity;
return Attribute::make(get: fn() => $pending);
}
/**
*/
public function items()
{
return $this->morphMany(EventRegistrationItem::class, 'registerable');
}
/**
* @return BelongsTo
*/
public function event(): BelongsTo
{
return $this->belongsTo(Event::class);
}
/**
* @return BelongsTo
*/
public function sponsorshipType(): BelongsTo
{
return $this->belongsTo(EventSponsorshipType::class, 'event_sponsorship_type_id');
}
/**
* @return BelongsTo
*/
public function category(): BelongsTo
{
return $this->belongsTo(BlueBookCategory::class, 'blue_book_category_id');
}
/**
* @return MorphMany
*/
public function actions(): MorphMany
{
return $this->morphMany(Action::class, 'actionable');
}
public function registrations()
{
return $this->morphToMany(EventRegistration::class, 'registerable', 'event_registration_items')
->withPivot('id', 'name', 'cost', 'quantity', 'azure_file_id')
->withTimestamps();
}
}