'datetime', 'end_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 linkedCollection(): BelongsTo { return $this->belongsTo(Collection::class, 'linked_collection_id'); } public function linkedProject(): BelongsTo { return $this->belongsTo(GroupProject::class, 'linked_project_id'); } public function featuredArtwork(): BelongsTo { return $this->belongsTo(Artwork::class, 'featured_artwork_id'); } public function artworkLinks(): HasMany { return $this->hasMany(GroupChallengeArtwork::class); } public function outcomes(): HasMany { return $this->hasMany(GroupChallengeOutcome::class) ->orderBy('sort_order') ->orderBy('position') ->orderBy('id'); } public function artworks(): BelongsToMany { return $this->belongsToMany(Artwork::class, 'group_challenge_artworks') ->withPivot(['submitted_by_user_id', 'sort_order']) ->withTimestamps() ->orderBy('group_challenge_artworks.sort_order'); } public function canBeViewedBy(?User $viewer): bool { if ($this->visibility !== self::VISIBILITY_PRIVATE) { return $this->group->canBeViewedBy($viewer); } return $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, '/'); } }