implement featured post admin endpoints

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.
This commit is contained in:
Yisroel Baum 2026-05-06 22:32:46 +03:00
parent 8983b69fa1
commit 8ac5a5b18a
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
2 changed files with 85 additions and 1 deletions

View file

@ -5,15 +5,20 @@ 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;
@ -30,6 +35,9 @@ class PostController
private GetPost $getPost,
private ListRecentPosts $listRecentPosts,
private ListUserPosts $listUserPosts,
private SetFeaturedPost $setFeaturedPost,
private ClearFeaturedPost $clearFeaturedPost,
private ListFeaturedPosts $listFeaturedPosts,
private UserRepository $userRepo,
) {}
@ -126,6 +134,75 @@ class PostController
], 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 */
@ -160,7 +237,8 @@ class PostController
* authorDisplayName: string,
* title: string,
* body: string,
* createdAt: string
* createdAt: string,
* featureSlot: ?int
* }
*/
private function serialize(Post $post): array
@ -176,6 +254,7 @@ class PostController
'title' => $post->getTitle(),
'body' => $post->getBody(),
'createdAt' => $post->getCreatedAt()->format(DATE_ATOM),
'featureSlot' => $post->getFeatureSlot(),
];
}
}