Files
fotospiel-app/app/Models/EventPackage.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;
}
}