withoutTrashed() ->withCount(['threads', 'posts']) ->withSum('threads', 'views_count') ->orderBy('position') ->orderBy('name') ->get(); $forumIds = $forums->pluck('id')->all(); $lastPostByForum = $this->loadLastPostsByForum($forumIds); $forumPayload = $forums->map( fn (Forum $forum) => $this->serializeForum($forum, $lastPostByForum[$forum->id] ?? null) ); $threads = Thread::query() ->withoutTrashed() ->withCount('posts') ->with([ 'user' => fn ($query) => $query->withCount(['posts', 'threads'])->with(['rank', 'roles']), 'latestPost.user.rank', 'latestPost.user.roles', ]) ->latest('created_at') ->limit(12) ->get() ->map(fn (Thread $thread) => $this->serializeThread($thread)); $stats = [ 'threads' => Thread::query()->withoutTrashed()->count(), 'posts' => Post::query()->withoutTrashed()->count(), 'users' => User::query()->count(), ]; $user = auth('sanctum')->user(); return response()->json([ 'forums' => $forumPayload, 'threads' => $threads, 'stats' => $stats, 'profile' => $user ? [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'avatar_url' => $user->avatar_path ? Storage::url($user->avatar_path) : null, 'location' => $user->location, 'rank' => $user->rank ? [ 'id' => $user->rank->id, 'name' => $user->rank->name, 'color' => $user->rank->color, ] : null, 'group_color' => $this->resolveGroupColor($user), ] : null, ]); } private function serializeForum(Forum $forum, ?Post $lastPost): array { return [ 'id' => $forum->id, 'name' => $forum->name, 'description' => $forum->description, 'type' => $forum->type, 'parent' => $forum->parent_id ? "/api/forums/{$forum->parent_id}" : null, 'position' => $forum->position, 'threads_count' => $forum->threads_count ?? 0, 'posts_count' => ($forum->posts_count ?? 0) + ($forum->threads_count ?? 0), 'views_count' => (int) ($forum->threads_sum_views_count ?? 0), 'last_post_at' => $lastPost?->created_at?->toIso8601String(), 'last_post_user_id' => $lastPost?->user_id, 'last_post_user_name' => $lastPost?->user?->name, 'last_post_user_rank_color' => $lastPost?->user?->rank?->color, 'last_post_user_group_color' => $this->resolveGroupColor($lastPost?->user), 'created_at' => $forum->created_at?->toIso8601String(), 'updated_at' => $forum->updated_at?->toIso8601String(), ]; } private function serializeThread(Thread $thread): array { return [ 'id' => $thread->id, 'title' => $thread->title, 'body' => $thread->body, 'forum' => "/api/forums/{$thread->forum_id}", 'user_id' => $thread->user_id, 'posts_count' => ($thread->posts_count ?? 0) + 1, 'views_count' => $thread->views_count ?? 0, 'user_name' => $thread->user?->name, 'user_avatar_url' => $thread->user?->avatar_path ? Storage::url($thread->user->avatar_path) : null, 'user_posts_count' => ($thread->user?->posts_count ?? 0) + ($thread->user?->threads_count ?? 0), 'user_created_at' => $thread->user?->created_at?->toIso8601String(), 'user_rank_name' => $thread->user?->rank?->name, 'user_rank_badge_type' => $thread->user?->rank?->badge_type, 'user_rank_badge_text' => $thread->user?->rank?->badge_text, 'user_rank_badge_url' => $thread->user?->rank?->badge_image_path ? Storage::url($thread->user->rank->badge_image_path) : null, 'user_rank_color' => $thread->user?->rank?->color, 'user_group_color' => $this->resolveGroupColor($thread->user), 'last_post_at' => $thread->latestPost?->created_at?->toIso8601String() ?? $thread->created_at?->toIso8601String(), 'last_post_id' => $thread->latestPost?->id, 'last_post_user_id' => $thread->latestPost?->user_id ?? $thread->user_id, 'last_post_user_name' => $thread->latestPost?->user?->name ?? $thread->user?->name, 'last_post_user_rank_color' => $thread->latestPost?->user?->rank?->color ?? $thread->user?->rank?->color, 'last_post_user_group_color' => $this->resolveGroupColor($thread->latestPost?->user) ?? $this->resolveGroupColor($thread->user), 'created_at' => $thread->created_at?->toIso8601String(), 'updated_at' => $thread->updated_at?->toIso8601String(), ]; } private function loadLastPostsByForum(array $forumIds): array { if (empty($forumIds)) { return []; } $posts = Post::query() ->select('posts.*', 'threads.forum_id as forum_id') ->join('threads', 'posts.thread_id', '=', 'threads.id') ->whereIn('threads.forum_id', $forumIds) ->whereNull('posts.deleted_at') ->whereNull('threads.deleted_at') ->orderByDesc('posts.created_at') ->with(['user.rank', 'user.roles']) ->get(); $byForum = []; foreach ($posts as $post) { $forumId = (int) ($post->forum_id ?? 0); if ($forumId && !array_key_exists($forumId, $byForum)) { $byForum[$forumId] = $post; } } return $byForum; } private function resolveGroupColor(?\App\Models\User $user): ?string { if (!$user) { return null; } $roles = $user->roles; if (!$roles) { return null; } foreach ($roles->sortBy('name') as $role) { if (!empty($role->color)) { return $role->color; } } return null; } }