Hey I always used to create some pretty messy endpoints and I decided that it's about time to start writing cleaner code, I'll provide an example of how would I design an endpoint and I'll be thankful for any insights why is it good or bad practice to do it like this also I'd be more than happy if you provide your own tweaks with explanation why would you do it like that.
Scenario: user post endpoint
Pathname: /api/user
Method: POST
Controller:
class UserPostEndpoint extends AbstractController
{
private UserPostHandlerInterface $handler;
private SerializerInterface $serializer;
function __construct(UserPostHandlerInterface $handler, SerializerInterface $serializer)
{
$this->handler = $handler;
$this->serializer = $serializer;
}
public function handleRequest(Request $request): Response
{
try {
$userPostRequestDTO = $this->serializer->unserialize($request->getContent(), UserPostRequestDTO::class);
$response = $this->handler->handle($userPostRequestDTO); // <- ApiSuccessResponse(statusCode = 201)
} catch(SerializationException $e) {
$response = new ApiErrorResponse('Invalid request content provided', 400);
} catch(ApiRequestContentValidationException $e) {
$response = new ApiErrorReponse($e->getPlainMessage(), 400);
} catch(\Exception $e) {
$response = new ApiErrorReponse('Internal server error', 500);
} finally {
return $response;
}
}
}
Handler would contain some basic things like: invoke createUser in UserFactory, persist the entity with repository.
ApiErrorReponse: message, statusCode
ApiSuccessReponse: UserPostResponseDTO, statusCode(201)
Would you add / change / remove something from the given example to make it more "correct"?
Thanks for the insights
EDIT
After a few comments from @slepic I refactored the code into this
class UserPostEndpoint extends AbstractController
{
private UserPostHandlerInterface $handler;
private DataTransformerInterface $dataTransformer;
function __construct(UserPostHandlerInterface $handler, DataTransformerInterface $dataTransformer)
{
$this->handler = $handler;
$this->dataTransformer = $dataTransformer;
}
public function handleRequest(Request $request): Response
{
try {
$userPostRequestDTO = $this->dataTransformer->toRequestDto($request->getContent(), $request->headers->get('Content-Type', 'application/json'));
$userPostResponseDTO = $this->handler->handle($userPostRequestDTO);
$response = new ApiSuccessResponse($userPostResponseDTO, Response::HTTP_CREATED);
} catch(SerializationException $e) {
$response = new ApiErrorResponse('Invalid request content provided', Response::HTTP_BAD_REQUEST);
} catch(ApiRequestContentValidationException $e) {
$response = new ApiErrorReponse($e->getPlainMessage(), Response::HTTP_BAD_REQUEST);
} catch(\Exception $e) {
$response = new ApiErrorReponse('Internal server error', Response::HTTP_INTERNAL_SERVER_ERROR);
} finally {
return $response;
}
}
}
I hope this is more appropriate