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')->with('rank'), 'latestPost.user', ]) ->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, ] : null, ] : 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, '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, '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, '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, '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, '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, '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') ->get(); $byForum = []; foreach ($posts as $post) { $forumId = (int) ($post->forum_id ?? 0); if ($forumId && !array_key_exists($forumId, $byForum)) { $byForum[$forumId] = $post; } } return $byForum; } }