205 lines
6.1 KiB
PHP
205 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
class EventPackage extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $table = 'event_packages';
|
|
|
|
protected $fillable = [
|
|
'event_id',
|
|
'package_id',
|
|
'purchased_price',
|
|
'purchased_at',
|
|
'used_photos',
|
|
'used_guests',
|
|
'gallery_expires_at',
|
|
'limits_snapshot',
|
|
'extra_photos',
|
|
'extra_guests',
|
|
'extra_gallery_days',
|
|
];
|
|
|
|
protected $casts = [
|
|
'purchased_price' => 'decimal:2',
|
|
'purchased_at' => 'datetime',
|
|
'gallery_expires_at' => 'datetime',
|
|
'gallery_warning_sent_at' => 'datetime',
|
|
'gallery_expired_notified_at' => 'datetime',
|
|
'used_photos' => 'integer',
|
|
'used_guests' => 'integer',
|
|
'extra_photos' => 'integer',
|
|
'extra_guests' => 'integer',
|
|
'extra_gallery_days' => 'integer',
|
|
'limits_snapshot' => 'array',
|
|
];
|
|
|
|
public function event(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Event::class);
|
|
}
|
|
|
|
public function package(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Package::class)->withTrashed();
|
|
}
|
|
|
|
public function addons(): HasMany
|
|
{
|
|
return $this->hasMany(EventPackageAddon::class);
|
|
}
|
|
|
|
public function isActive(): bool
|
|
{
|
|
return $this->gallery_expires_at && $this->gallery_expires_at->isFuture();
|
|
}
|
|
|
|
public function canUploadPhoto(): bool
|
|
{
|
|
if (! $this->isActive()) {
|
|
return false;
|
|
}
|
|
|
|
$maxPhotos = $this->effectiveLimits()['max_photos'];
|
|
|
|
if ($maxPhotos === null) {
|
|
return true;
|
|
}
|
|
|
|
return $this->used_photos < $maxPhotos;
|
|
}
|
|
|
|
public function canAddGuest(): bool
|
|
{
|
|
if (! $this->isActive()) {
|
|
return false;
|
|
}
|
|
|
|
$maxGuests = $this->effectiveLimits()['max_guests'];
|
|
|
|
if ($maxGuests === null) {
|
|
return true;
|
|
}
|
|
|
|
return $this->used_guests < $maxGuests;
|
|
}
|
|
|
|
public function getRemainingPhotosAttribute(): int
|
|
{
|
|
$limit = $this->effectiveLimits()['max_photos'] ?? 0;
|
|
|
|
return max(0, (int) $limit - $this->used_photos);
|
|
}
|
|
|
|
public function getRemainingGuestsAttribute(): int
|
|
{
|
|
$limit = $this->effectiveLimits()['max_guests'] ?? 0;
|
|
|
|
return max(0, (int) $limit - $this->used_guests);
|
|
}
|
|
|
|
/**
|
|
* @return array{max_photos: ?int, max_guests: ?int, gallery_days: ?int, max_tasks: ?int, max_events_per_year: ?int}
|
|
*/
|
|
public function effectiveLimits(): array
|
|
{
|
|
$snapshot = is_array($this->limits_snapshot) ? $this->limits_snapshot : [];
|
|
|
|
$base = [
|
|
'max_photos' => array_key_exists('max_photos', $snapshot)
|
|
? $snapshot['max_photos']
|
|
: ($this->package->max_photos ?? null),
|
|
'max_guests' => array_key_exists('max_guests', $snapshot)
|
|
? $snapshot['max_guests']
|
|
: ($this->package->max_guests ?? null),
|
|
'gallery_days' => array_key_exists('gallery_days', $snapshot)
|
|
? $snapshot['gallery_days']
|
|
: ($this->package->gallery_days ?? null),
|
|
'max_tasks' => array_key_exists('max_tasks', $snapshot)
|
|
? $snapshot['max_tasks']
|
|
: ($this->package->max_tasks ?? null),
|
|
'max_events_per_year' => array_key_exists('max_events_per_year', $snapshot)
|
|
? $snapshot['max_events_per_year']
|
|
: ($this->package->max_events_per_year ?? null),
|
|
];
|
|
|
|
$applyExtra = static function (?int $limit, int $extra): ?int {
|
|
if ($limit === null) {
|
|
return null;
|
|
}
|
|
|
|
$safeExtra = max(0, $extra);
|
|
|
|
return max(0, $limit + $safeExtra);
|
|
};
|
|
|
|
$maxPhotos = $applyExtra($this->normalizeLimit($base['max_photos']), (int) ($this->extra_photos ?? 0));
|
|
$maxGuests = $applyExtra($this->normalizeLimit($base['max_guests']), (int) ($this->extra_guests ?? 0));
|
|
|
|
return [
|
|
'max_photos' => $maxPhotos,
|
|
'max_guests' => $maxGuests,
|
|
'gallery_days' => $this->normalizeLimit($base['gallery_days']),
|
|
'max_tasks' => $this->normalizeLimit($base['max_tasks']),
|
|
'max_events_per_year' => $this->normalizeLimit($base['max_events_per_year']),
|
|
];
|
|
}
|
|
|
|
public function effectivePhotoLimit(): ?int
|
|
{
|
|
return $this->effectiveLimits()['max_photos'];
|
|
}
|
|
|
|
public function effectiveGuestLimit(): ?int
|
|
{
|
|
return $this->effectiveLimits()['max_guests'];
|
|
}
|
|
|
|
protected static function boot()
|
|
{
|
|
parent::boot();
|
|
|
|
static::creating(function ($eventPackage) {
|
|
if (! $eventPackage->purchased_at) {
|
|
$eventPackage->purchased_at = now();
|
|
}
|
|
if (! $eventPackage->gallery_expires_at && $eventPackage->package) {
|
|
$days = $eventPackage->package->gallery_days ?? 30;
|
|
$eventPackage->gallery_expires_at = now()->addDays($days);
|
|
}
|
|
|
|
if (! $eventPackage->limits_snapshot) {
|
|
$package = $eventPackage->relationLoaded('package')
|
|
? $eventPackage->package
|
|
: Package::query()->find($eventPackage->package_id);
|
|
|
|
if ($package) {
|
|
$eventPackage->limits_snapshot = array_filter([
|
|
'max_photos' => $package->max_photos,
|
|
'max_guests' => $package->max_guests,
|
|
'gallery_days' => $package->gallery_days,
|
|
'max_tasks' => $package->max_tasks,
|
|
'max_events_per_year' => $package->max_events_per_year,
|
|
], static fn ($value) => $value !== null);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private function normalizeLimit($value): ?int
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
return is_numeric($value) ? (int) $value : null;
|
|
}
|
|
}
|