belongsTo(ForumCategory::class, 'parent_id'); } public function children(): HasMany { return $this->hasMany(ForumCategory::class, 'parent_id'); } public function threads(): HasMany { return $this->hasMany(ForumThread::class, 'category_id'); } public function postsThroughThreads(): HasManyThrough { return $this->hasManyThrough( ForumPost::class, ForumThread::class, 'category_id', 'thread_id', 'id', 'id' ); } public function lastThread(): HasOne { return $this->hasOne(ForumThread::class, 'category_id')->latestOfMany('last_post_at'); } public function scopeOrdered(Builder $query): Builder { return $query->orderBy('position')->orderBy('id'); } public function scopeRoots(Builder $query): Builder { return $query->whereNull('parent_id'); } public function scopeWithForumStats(Builder $query): Builder { return $query ->withCount(['threads as thread_count']) ->withCount(['postsThroughThreads as post_count']) ->with(['lastThread' => function ($relationQuery) { $relationQuery->select([ 'forum_threads.id', 'forum_threads.category_id', 'forum_threads.last_post_at', 'forum_threads.updated_at', ]); }]); } public function getPreviewImageAttribute(): string { $slug = (string) ($this->slug ?? ''); $map = (array) config('forum.preview_images.map', []); $default = (string) config('forum.preview_images.default', '/images/forum/default.jpg'); if ($slug !== '' && !empty($map[$slug])) { return (string) $map[$slug]; } if ($slug !== '') { return '/images/forum/defaults/' . $slug . '.jpg'; } return $default; } }