125 lines
5.5 KiB
PHP
125 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Posts;
|
|
|
|
use App\Events\Posts\PostCommented;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Posts\CreateCommentRequest;
|
|
use App\Models\Post;
|
|
use App\Models\PostComment;
|
|
use App\Services\ContentSanitizer;
|
|
use App\Services\Posts\PostCountersService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
|
|
class PostCommentController extends Controller
|
|
{
|
|
public function __construct(private PostCountersService $counters) {}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// List
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
public function index(Request $request, int $postId): JsonResponse
|
|
{
|
|
$post = Post::findOrFail($postId);
|
|
$page = max(1, (int) $request->query('page', 1));
|
|
|
|
$comments = PostComment::with(['user', 'user.profile'])
|
|
->where('post_id', $post->id)
|
|
->orderByDesc('is_highlighted') // highlighted first
|
|
->orderBy('created_at')
|
|
->paginate(20, ['*'], 'page', $page);
|
|
|
|
$formatted = $comments->getCollection()->map(fn ($c) => $this->formatComment($c));
|
|
|
|
return response()->json([
|
|
'data' => $formatted,
|
|
'meta' => [
|
|
'total' => $comments->total(),
|
|
'current_page' => $comments->currentPage(),
|
|
'last_page' => $comments->lastPage(),
|
|
'per_page' => $comments->perPage(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Store
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
public function store(CreateCommentRequest $request, int $postId): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
// Rate limit: 30 comments per hour
|
|
$key = 'comment_post:' . $user->id;
|
|
if (RateLimiter::tooManyAttempts($key, 30)) {
|
|
$seconds = RateLimiter::availableIn($key);
|
|
return response()->json([
|
|
'message' => "You're commenting too quickly. Please wait {$seconds} seconds.",
|
|
], 429);
|
|
}
|
|
RateLimiter::hit($key, 3600);
|
|
|
|
$post = Post::findOrFail($postId);
|
|
$body = ContentSanitizer::render($request->input('body'));
|
|
|
|
$comment = PostComment::create([
|
|
'post_id' => $post->id,
|
|
'user_id' => $user->id,
|
|
'body' => $body,
|
|
]);
|
|
|
|
$this->counters->incrementComments($post);
|
|
|
|
// Fire event for notification
|
|
if ($post->user_id !== $user->id) {
|
|
event(new PostCommented($post, $comment, $user));
|
|
}
|
|
|
|
$comment->load(['user', 'user.profile']);
|
|
|
|
return response()->json(['comment' => $this->formatComment($comment)], 201);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Destroy
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
public function destroy(Request $request, int $postId, int $commentId): JsonResponse
|
|
{
|
|
$comment = PostComment::where('post_id', $postId)->findOrFail($commentId);
|
|
Gate::authorize('delete', $comment);
|
|
|
|
$comment->delete();
|
|
$this->counters->decrementComments(Post::findOrFail($postId));
|
|
|
|
return response()->json(['message' => 'Comment deleted.']);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Format
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
private function formatComment(PostComment $comment): array
|
|
{
|
|
return [
|
|
'id' => $comment->id,
|
|
'body' => $comment->body,
|
|
'is_highlighted' => (bool) $comment->is_highlighted,
|
|
'created_at' => $comment->created_at->toISOString(),
|
|
'author' => [
|
|
'id' => $comment->user->id,
|
|
'username' => $comment->user->username,
|
|
'name' => $comment->user->name,
|
|
'avatar' => $comment->user->profile?->avatar_url ?? null,
|
|
'level' => (int) ($comment->user->level ?? 1),
|
|
'rank' => (string) ($comment->user->rank ?? 'Newbie'),
|
|
],
|
|
];
|
|
}
|
|
}
|