'datetime', 'end_at' => 'datetime', 'is_featured' => 'boolean', 'published_at' => 'datetime', ]; public function getRouteKeyName(): string { return 'slug'; } public function group(): BelongsTo { return $this->belongsTo(Group::class); } public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by_user_id'); } public function linkedProject(): BelongsTo { return $this->belongsTo(GroupProject::class, 'linked_project_id'); } public function linkedCollection(): BelongsTo { return $this->belongsTo(Collection::class, 'linked_collection_id'); } public function linkedChallenge(): BelongsTo { return $this->belongsTo(GroupChallenge::class, 'linked_challenge_id'); } public function canBeViewedBy(?User $viewer): bool { return match ($this->visibility) { self::VISIBILITY_PUBLIC => $this->group->canBeViewedBy($viewer), self::VISIBILITY_MEMBERS_ONLY => $viewer !== null && $this->group->hasActiveMember($viewer), default => $viewer !== null && $this->group->canViewStudio($viewer), }; } public function coverUrl(): ?string { $path = trim((string) $this->cover_path); if ($path === '') { return null; } if (str_starts_with($path, 'http://') || str_starts_with($path, 'https://')) { return $path; } return rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/') . '/' . ltrim($path, '/'); } }