'datetime', 'is_active' => 'boolean', 'name' => 'array', 'description' => 'array', 'live_show_token_rotated_at' => 'datetime', ]; protected static function booted(): void { static::created(function (self $event): void { if ($event->joinTokens()->exists()) { return; } app(EventJoinTokenService::class)->createToken($event, [ 'label' => 'Standard-Link', 'metadata' => [ 'auto_generated' => true, ], ]); }); } public function storageAssignments(): HasMany { return $this->hasMany(EventStorageAssignment::class); } public function mediaAssets(): HasMany { return $this->hasMany(EventMediaAsset::class); } public function currentStorageTarget(?string $role = 'hot'): ?MediaStorageTarget { $assignment = $this->storageAssignments() ->where('role', $role) ->where('status', 'active') ->latest('assigned_at') ->first(); return $assignment?->storageTarget; } public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } public function eventType(): BelongsTo { return $this->belongsTo(EventType::class); } public function photos(): HasMany { return $this->hasMany(Photo::class); } public function taskCollections(): BelongsToMany { return $this->belongsToMany( TaskCollection::class, 'event_task_collection', 'event_id', 'task_collection_id' ); } public function tasks(): BelongsToMany { return $this->belongsToMany(Task::class, 'event_task', 'event_id', 'task_id') ->withPivot(['sort_order']) ->withTimestamps(); } public function eventPackage(): HasOne { return $this->hasOne(EventPackage::class)->latestOfMany(); } public function eventPackages(): HasMany { return $this->hasMany(EventPackage::class); } public function retentionOverrides(): HasMany { return $this->hasMany(RetentionOverride::class); } public function joinTokens(): HasMany { return $this->hasMany(EventJoinToken::class); } public function members(): HasMany { return $this->hasMany(EventMember::class); } public function photoboothSetting(): HasOne { return $this->hasOne(EventPhotoboothSetting::class); } public function guestNotifications(): HasMany { return $this->hasMany(GuestNotification::class); } public function hasActivePackage(): bool { return $this->eventPackage && $this->eventPackage->isActive(); } public function getPackageLimits(): array { if (! $this->hasActivePackage()) { return []; } return $this->eventPackage->package->limits; } public function canUploadPhoto(): bool { if (! $this->hasActivePackage()) { return false; } return $this->eventPackage->canUploadPhoto(); } public function ensureLiveShowToken(): string { if (is_string($this->live_show_token) && $this->live_show_token !== '') { return $this->live_show_token; } return $this->rotateLiveShowToken(); } public function rotateLiveShowToken(): string { do { $token = bin2hex(random_bytes(32)); } while (self::query()->where('live_show_token', $token)->exists()); $this->forceFill([ 'live_show_token' => $token, 'live_show_token_rotated_at' => now(), ])->save(); return $token; } public function getSettingsAttribute($value): array { if (is_array($value)) { return $value; } if (is_string($value) && $value !== '') { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : []; } return []; } public function setSettingsAttribute($value): void { if (is_string($value)) { $decoded = json_decode($value, true); $value = is_array($decoded) ? $decoded : []; } $this->attributes['settings'] = json_encode($value ?? []); } }