181 lines
6.7 KiB
PHP
181 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Forum;
|
|
use App\Models\Post;
|
|
use App\Models\Thread;
|
|
use App\Models\User;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class PortalController extends Controller
|
|
{
|
|
public function __invoke(Request $request): JsonResponse
|
|
{
|
|
$forums = Forum::query()
|
|
->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;
|
|
}
|
|
}
|