Adds POST /admin/posts/feature, POST /admin/posts/unfeature (both auth-required, admin-checked inside controller via the use case's ForbiddenException), and public GET /posts/featured. Post serialization now includes featureSlot.
260 lines
8.1 KiB
PHP
260 lines
8.1 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Exceptions\BadRequestException;
|
|
use App\Exceptions\ForbiddenException;
|
|
use App\Post\Post;
|
|
use App\Post\UseCases\ClearFeaturedPost\ClearFeaturedPost;
|
|
use App\Post\UseCases\ClearFeaturedPost\ClearFeaturedPostRequest;
|
|
use App\Post\UseCases\CreatePost\CreatePost;
|
|
use App\Post\UseCases\CreatePost\CreatePostRequest;
|
|
use App\Post\UseCases\DeletePost\DeletePost;
|
|
use App\Post\UseCases\DeletePost\DeletePostRequest;
|
|
use App\Post\UseCases\GetPost\GetPost;
|
|
use App\Post\UseCases\ListFeaturedPosts\ListFeaturedPosts;
|
|
use App\Post\UseCases\ListRecentPosts\ListRecentPosts;
|
|
use App\Post\UseCases\ListRecentPosts\ListRecentPostsRequest;
|
|
use App\Post\UseCases\ListUserPosts\ListUserPosts;
|
|
use App\Post\UseCases\ListUserPosts\ListUserPostsRequest;
|
|
use App\Post\UseCases\SetFeaturedPost\SetFeaturedPost;
|
|
use App\Post\UseCases\SetFeaturedPost\SetFeaturedPostRequest;
|
|
use App\User\User;
|
|
use App\User\UserRepository;
|
|
use DomainException;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class PostController
|
|
{
|
|
private const RECENT_LIMIT = 20;
|
|
|
|
public function __construct(
|
|
private CreatePost $createPost,
|
|
private DeletePost $deletePost,
|
|
private GetPost $getPost,
|
|
private ListRecentPosts $listRecentPosts,
|
|
private ListUserPosts $listUserPosts,
|
|
private SetFeaturedPost $setFeaturedPost,
|
|
private ClearFeaturedPost $clearFeaturedPost,
|
|
private ListFeaturedPosts $listFeaturedPosts,
|
|
private UserRepository $userRepo,
|
|
) {}
|
|
|
|
public function recent(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$posts = $this->listRecentPosts->execute(
|
|
new ListRecentPostsRequest(limit: self::RECENT_LIMIT),
|
|
);
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'posts' => array_map(
|
|
function (Post $post) {
|
|
return $this->serialize($post);
|
|
},
|
|
$posts,
|
|
),
|
|
], 200);
|
|
}
|
|
|
|
public function show(Request $request, int $id): JsonResponse
|
|
{
|
|
try {
|
|
$post = $this->getPost->execute($id);
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
}
|
|
if ($post === null) {
|
|
return new JsonResponse(['error' => 'post not found'], 404);
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'post' => $this->serialize($post),
|
|
], 200);
|
|
}
|
|
|
|
public function listByUser(
|
|
Request $request,
|
|
string $displayName,
|
|
): JsonResponse {
|
|
$user = $this->userRepo->findByDisplayName($displayName);
|
|
if ($user === null) {
|
|
return new JsonResponse(['error' => 'user not found'], 404);
|
|
}
|
|
try {
|
|
$posts = $this->listUserPosts->execute(
|
|
new ListUserPostsRequest(userId: $user->getId()),
|
|
);
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'user' => [
|
|
'id' => $user->getId(),
|
|
'displayName' => $user->getDisplayName(),
|
|
],
|
|
'posts' => array_map(
|
|
function (Post $post) {
|
|
return $this->serialize($post);
|
|
},
|
|
$posts,
|
|
),
|
|
], 200);
|
|
}
|
|
|
|
public function create(Request $request): JsonResponse
|
|
{
|
|
/** @var User $user */
|
|
$user = $request->attributes->get('user');
|
|
try {
|
|
$post = $this->createPost->execute(new CreatePostRequest(
|
|
userId: $user->getId(),
|
|
title: $request->input('title'),
|
|
body: $request->input('body'),
|
|
));
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'post' => $this->serialize($post),
|
|
], 201);
|
|
}
|
|
|
|
public function listFeatured(Request $request): JsonResponse
|
|
{
|
|
$posts = $this->listFeaturedPosts->execute();
|
|
|
|
return new JsonResponse([
|
|
'posts' => array_map(
|
|
function (Post $post) {
|
|
return $this->serialize($post);
|
|
},
|
|
$posts,
|
|
),
|
|
], 200);
|
|
}
|
|
|
|
public function feature(Request $request): JsonResponse
|
|
{
|
|
/** @var User $user */
|
|
$user = $request->attributes->get('user');
|
|
try {
|
|
$post = $this->setFeaturedPost->execute(
|
|
new SetFeaturedPostRequest(
|
|
postId: (int) $request->input('postId'),
|
|
slot: (int) $request->input('slot'),
|
|
requesterIsAdmin: $user->isAdmin(),
|
|
),
|
|
);
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
} catch (ForbiddenException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 403,
|
|
);
|
|
} catch (DomainException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 404,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'post' => $this->serialize($post),
|
|
], 200);
|
|
}
|
|
|
|
public function unfeature(Request $request): JsonResponse
|
|
{
|
|
/** @var User $user */
|
|
$user = $request->attributes->get('user');
|
|
try {
|
|
$this->clearFeaturedPost->execute(
|
|
new ClearFeaturedPostRequest(
|
|
postId: (int) $request->input('postId'),
|
|
requesterIsAdmin: $user->isAdmin(),
|
|
),
|
|
);
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
} catch (ForbiddenException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 403,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse(null, 204);
|
|
}
|
|
|
|
public function delete(Request $request, int $id): JsonResponse
|
|
{
|
|
/** @var User $user */
|
|
$user = $request->attributes->get('user');
|
|
try {
|
|
$this->deletePost->execute(new DeletePostRequest(
|
|
postId: $id,
|
|
requesterId: $user->getId(),
|
|
requesterIsAdmin: $user->isAdmin(),
|
|
));
|
|
} catch (BadRequestException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 400,
|
|
);
|
|
} catch (ForbiddenException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 403,
|
|
);
|
|
} catch (DomainException $exception) {
|
|
return new JsonResponse(
|
|
['error' => $exception->getMessage()], 409,
|
|
);
|
|
}
|
|
|
|
return new JsonResponse(null, 204);
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* id: int,
|
|
* userId: int,
|
|
* authorDisplayName: string,
|
|
* title: string,
|
|
* body: string,
|
|
* createdAt: string,
|
|
* featureSlot: ?int
|
|
* }
|
|
*/
|
|
private function serialize(Post $post): array
|
|
{
|
|
$author = $this->userRepo->find($post->getUserId());
|
|
|
|
return [
|
|
'id' => $post->getId(),
|
|
'userId' => $post->getUserId(),
|
|
'authorDisplayName' => $author === null
|
|
? ''
|
|
: $author->getDisplayName(),
|
|
'title' => $post->getTitle(),
|
|
'body' => $post->getBody(),
|
|
'createdAt' => $post->getCreatedAt()->format(DATE_ATOM),
|
|
'featureSlot' => $post->getFeatureSlot(),
|
|
];
|
|
}
|
|
}
|