From e47c3ea7fa930fdfbe06c65117f3695e296e21be Mon Sep 17 00:00:00 2001 From: Samuel Gfeller Date: Tue, 7 Nov 2023 10:36:34 +0100 Subject: [PATCH] More use-case specific responder [SLE-192] --- .htaccess | 3 + .../Ajax/AccountUnlockProcessAction.php | 12 +- .../Authentication/Ajax/LoginSubmitAction.php | 26 +- .../Ajax/NewPasswordResetSubmitAction.php | 20 +- .../PasswordForgottenEmailSubmitAction.php | 14 +- .../Ajax/RegisterVerifyProcessAction.php | 20 +- .../Authentication/Page/LoginPageAction.php | 16 +- .../Authentication/Page/LogoutPageAction.php | 6 +- .../Page/PasswordResetPageAction.php | 8 +- .../Client/Ajax/ApiClientCreateAction.php | 8 +- .../Action/Client/Ajax/ClientCreateAction.php | 8 +- .../Action/Client/Ajax/ClientDeleteAction.php | 8 +- .../Client/Ajax/ClientFetchListAction.php | 8 +- .../Action/Client/Ajax/ClientUpdateAction.php | 8 +- ...chDropdownOptionsForClientCreateAction.php | 8 +- .../Client/Page/ClientListPageAction.php | 10 +- .../Client/Page/ClientReadPageAction.php | 8 +- .../Action/Common/TranslateAction.php | 8 +- .../Action/Dashboard/DashboardPageAction.php | 6 +- .../DashboardTogglePanelProcessAction.php | 6 +- .../Action/Dashboard/PhpDevTestAction.php | 4 +- .../Action/Note/Ajax/NoteCreateAction.php | 8 +- .../Action/Note/Ajax/NoteDeleteAction.php | 10 +- .../Action/Note/Ajax/NoteFetchListAction.php | 8 +- .../Action/Note/Ajax/NoteUpdateAction.php | 8 +- .../Action/Note/Page/NoteReadPageAction.php | 12 +- ...etchDropdownOptionsForUserCreateAction.php | 6 +- .../User/Ajax/PasswordChangeSubmitAction.php | 8 +- .../User/Ajax/UserActivityFetchListAction.php | 6 +- .../Action/User/Ajax/UserCreateAction.php | 10 +- .../Action/User/Ajax/UserDeleteAction.php | 8 +- .../Action/User/Ajax/UserFetchListAction.php | 8 +- .../Action/User/Ajax/UserUpdateAction.php | 8 +- .../Action/User/Page/UserListPageAction.php | 9 +- .../Action/User/Page/UserReadPageAction.php | 6 +- .../ForbiddenExceptionMiddleware.php | 10 +- .../InvalidOperationExceptionMiddleware.php | 10 +- .../UserAuthenticationMiddleware.php | 20 +- .../ValidationExceptionMiddleware.php | 10 +- src/Application/Responder/JsonResponder.php | 32 +++ src/Application/Responder/RedirectHandler.php | 68 +++++ src/Application/Responder/Responder.php | 236 ------------------ .../Responder/TemplateRenderer.php | 111 ++++++++ 43 files changed, 412 insertions(+), 415 deletions(-) create mode 100644 src/Application/Responder/JsonResponder.php create mode 100644 src/Application/Responder/RedirectHandler.php delete mode 100644 src/Application/Responder/Responder.php create mode 100644 src/Application/Responder/TemplateRenderer.php diff --git a/.htaccess b/.htaccess index 659993ef..16764177 100644 --- a/.htaccess +++ b/.htaccess @@ -1,3 +1,6 @@ +# Turn on the rewrite engine RewriteEngine on +# If the URL path is empty, rewrite to the 'public/' directory RewriteRule ^$ public/ [L] +# For any requested URL path, rewrite to the 'public/' directory followed by the requested path RewriteRule (.*) public/$1 [L] diff --git a/src/Application/Action/Authentication/Ajax/AccountUnlockProcessAction.php b/src/Application/Action/Authentication/Ajax/AccountUnlockProcessAction.php index aab62a82..cc767a1b 100644 --- a/src/Application/Action/Authentication/Ajax/AccountUnlockProcessAction.php +++ b/src/Application/Action/Authentication/Ajax/AccountUnlockProcessAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Authentication\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; use App\Domain\Authentication\Exception\InvalidTokenException; use App\Domain\Authentication\Exception\UserAlreadyVerifiedException; use App\Domain\Authentication\Service\AccountUnlockTokenVerifier; @@ -17,7 +17,7 @@ final class AccountUnlockProcessAction { public function __construct( private readonly LoggerInterface $logger, - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, private readonly AccountUnlockTokenVerifier $accountUnlockTokenVerifier @@ -52,10 +52,10 @@ public function __invoke(ServerRequest $request, Response $response): Response ) ); - return $this->responder->redirectToUrl($response, $queryParams['redirect']); + return $this->redirectHandler->redirectToUrl($response, $queryParams['redirect']); } - return $this->responder->redirectToRouteName($response, 'home-page'); + return $this->redirectHandler->redirectToRouteName($response, 'home-page'); } catch (InvalidTokenException $ite) { $flash->add( 'error', @@ -65,7 +65,7 @@ public function __invoke(ServerRequest $request, Response $response): Response $newQueryParam = isset($queryParams['redirect']) ? ['redirect' => $queryParams['redirect']] : []; // Redirect to login page with redirect query param if set - return $this->responder->redirectToRouteName($response, 'login-page', [], $newQueryParam); + return $this->redirectHandler->redirectToRouteName($response, 'login-page', [], $newQueryParam); } catch (UserAlreadyVerifiedException $uave) { $flash->add('info', $uave->getMessage()); $this->logger->info( @@ -73,7 +73,7 @@ public function __invoke(ServerRequest $request, Response $response): Response ); $newQueryParam = isset($queryParams['redirect']) ? ['redirect' => $queryParams['redirect']] : []; - return $this->responder->redirectToRouteName( + return $this->redirectHandler->redirectToRouteName( $response, 'login-page', [], diff --git a/src/Application/Action/Authentication/Ajax/LoginSubmitAction.php b/src/Application/Action/Authentication/Ajax/LoginSubmitAction.php index c37fb790..951339db 100644 --- a/src/Application/Action/Authentication/Ajax/LoginSubmitAction.php +++ b/src/Application/Action/Authentication/Ajax/LoginSubmitAction.php @@ -2,7 +2,8 @@ namespace App\Application\Action\Authentication\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; +use App\Application\Responder\TemplateRenderer; use App\Domain\Authentication\Exception\InvalidCredentialsException; use App\Domain\Authentication\Exception\UnableToLoginStatusNotActiveException; use App\Domain\Authentication\Service\LoginVerifier; @@ -18,7 +19,8 @@ final class LoginSubmitAction { public function __construct( - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, + private readonly TemplateRenderer $templateRenderer, private readonly LoggerInterface $logger, private readonly LoginVerifier $loginVerifier, private readonly SessionManagerInterface $sessionManager, @@ -57,17 +59,17 @@ public function __invoke(ServerRequest $request, Response $response): Response // After register and login success, check if user should be redirected if (isset($queryParams['redirect'])) { - return $this->responder->redirectToUrl( + return $this->redirectHandler->redirectToUrl( $response, $request->getQueryParams()['redirect'], $themeQueryParams ); } - return $this->responder->redirectToRouteName($response, 'home-page', [], $themeQueryParams); + return $this->redirectHandler->redirectToRouteName($response, 'home-page', [], $themeQueryParams); } // When the response is not JSON but rendered, the validation exception has to be caught in action catch (ValidationException $ve) { - return $this->responder->renderOnValidationError( + return $this->templateRenderer->renderOnValidationError( $response, 'authentication/login.html.php', $ve, @@ -79,10 +81,10 @@ public function __invoke(ServerRequest $request, Response $response): Response 'InvalidCredentialsException thrown with message: "' . $e->getMessage() . '" user "' . $e->getUserEmail() . '"' ); - $this->responder->addPhpViewAttribute('formError', true); - $this->responder->addPhpViewAttribute('formErrorMessage', __('Invalid credentials. Please try again.')); + $this->templateRenderer->addPhpViewAttribute('formError', true); + $this->templateRenderer->addPhpViewAttribute('formErrorMessage', __('Invalid credentials. Please try again.')); - return $this->responder->render( + return $this->templateRenderer->render( $response->withStatus(401), 'authentication/login.html.php', // Provide same query params passed to login page to be added to the login submit request @@ -94,7 +96,7 @@ public function __invoke(ServerRequest $request, Response $response): Response throw $securityException; } - return $this->responder->respondWithFormThrottle( + return $this->templateRenderer->respondWithFormThrottle( $response, 'authentication/login.html.php', $securityException, @@ -103,11 +105,11 @@ public function __invoke(ServerRequest $request, Response $response): Response ); } catch (UnableToLoginStatusNotActiveException $unableToLoginException) { // When user doesn't have status active - $this->responder->addPhpViewAttribute('formError', true); + $this->templateRenderer->addPhpViewAttribute('formError', true); // Add form error message - $this->responder->addPhpViewAttribute('formErrorMessage', $unableToLoginException->getMessage()); + $this->templateRenderer->addPhpViewAttribute('formErrorMessage', $unableToLoginException->getMessage()); - return $this->responder->render( + return $this->templateRenderer->render( $response->withStatus(401), 'authentication/login.html.php', // Provide same query params passed to login page to be added to the login submit request diff --git a/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php b/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php index 93a693cf..70e0bee5 100644 --- a/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php +++ b/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php @@ -2,7 +2,8 @@ namespace App\Application\Action\Authentication\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; +use App\Application\Responder\TemplateRenderer; use App\Domain\Authentication\Exception\InvalidTokenException; use App\Domain\Authentication\Service\PasswordResetterWithToken; use App\Domain\Validation\ValidationException; @@ -14,7 +15,8 @@ class NewPasswordResetSubmitAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, + private readonly RedirectHandler $redirectHandler, private readonly SessionInterface $session, private readonly PasswordResetterWithToken $passwordResetterWithToken, private readonly LoggerInterface $logger, @@ -44,9 +46,9 @@ public function __invoke(ServerRequest $request, Response $response): Response sprintf(__('Successfully changed password. %s'), __('Please log in.')) ); - return $this->responder->redirectToRouteName($response, 'login-page'); + return $this->redirectHandler->redirectToRouteName($response, 'login-page'); } catch (InvalidTokenException $ite) { - $this->responder->addPhpViewAttribute( + $this->templateRenderer->addPhpViewAttribute( 'formErrorMessage', __( 'Invalid, used or expired link.
Please request a new link below and make @@ -55,7 +57,7 @@ public function __invoke(ServerRequest $request, Response $response): Response ); // Pre-fill email input field for more user comfort. if ($ite->userData->email !== null) { - $this->responder->addPhpViewAttribute('preloadValues', ['email' => $ite->userData->email]); + $this->templateRenderer->addPhpViewAttribute('preloadValues', ['email' => $ite->userData->email]); } $this->logger->error( @@ -64,15 +66,15 @@ public function __invoke(ServerRequest $request, Response $response): Response // The login page is rendered but the url is reset-password. In login-main.js the url is replaced and // the password forgotten form is shown instead of the login form. - return $this->responder->render($response, 'authentication/login.html.php'); + return $this->templateRenderer->render($response, 'authentication/login.html.php'); } // Validation Exception has to be caught here and not middleware as we need to add token and id to php view catch (ValidationException $validationException) { $flash->add('error', $validationException->getMessage()); // Add token and id to php view attribute like PasswordResetAction does - $this->responder->addPhpViewAttribute('token', $parsedBody['token']); - $this->responder->addPhpViewAttribute('id', $parsedBody['id']); + $this->templateRenderer->addPhpViewAttribute('token', $parsedBody['token']); + $this->templateRenderer->addPhpViewAttribute('id', $parsedBody['id']); - return $this->responder->renderOnValidationError( + return $this->templateRenderer->renderOnValidationError( $response, 'authentication/reset-password.html.php', $validationException, diff --git a/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php b/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php index 82b954a5..1ede6d82 100644 --- a/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php +++ b/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php @@ -2,7 +2,8 @@ namespace App\Application\Action\Authentication\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; +use App\Application\Responder\TemplateRenderer; use App\Domain\Authentication\Service\PasswordRecoveryEmailSender; use App\Domain\Exception\DomainRecordNotFoundException; use App\Domain\Security\Exception\SecurityException; @@ -16,7 +17,8 @@ final class PasswordForgottenEmailSubmitAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, + private readonly RedirectHandler $redirectHandler, private readonly SessionInterface $session, private readonly PasswordRecoveryEmailSender $passwordRecoveryEmailSender, private readonly LoggerInterface $logger, @@ -46,14 +48,14 @@ public function __invoke(ServerRequest $request, Response $response): Response ); } catch (ValidationException $validationException) { // Form error messages set in function below - return $this->responder->renderOnValidationError( + return $this->templateRenderer->renderOnValidationError( $response, 'authentication/login.html.php', $validationException, $request->getQueryParams(), ); } catch (SecurityException $securityException) { - return $this->responder->respondWithFormThrottle( + return $this->templateRenderer->respondWithFormThrottle( $response, 'authentication/login.html.php', $securityException, @@ -63,7 +65,7 @@ public function __invoke(ServerRequest $request, Response $response): Response } catch (TransportExceptionInterface $transportException) { $flash->add('error', __('There was an error when sending the email.')); - return $this->responder->render( + return $this->templateRenderer->render( $response, 'authentication/login.html.php', $request->getQueryParams(), @@ -77,6 +79,6 @@ public function __invoke(ServerRequest $request, Response $response): Response ) ); - return $this->responder->redirectToRouteName($response, 'login-page'); + return $this->redirectHandler->redirectToRouteName($response, 'login-page'); } } diff --git a/src/Application/Action/Authentication/Ajax/RegisterVerifyProcessAction.php b/src/Application/Action/Authentication/Ajax/RegisterVerifyProcessAction.php index dcfb5019..e1cf0dbb 100644 --- a/src/Application/Action/Authentication/Ajax/RegisterVerifyProcessAction.php +++ b/src/Application/Action/Authentication/Ajax/RegisterVerifyProcessAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Authentication\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; use App\Domain\Authentication\Exception\InvalidTokenException; use App\Domain\Authentication\Exception\UserAlreadyVerifiedException; use App\Domain\Authentication\Service\RegisterTokenVerifier; @@ -12,12 +12,14 @@ use Psr\Http\Message\ServerRequestInterface as ServerRequest; use Psr\Log\LoggerInterface; use Slim\Exception\HttpBadRequestException; +use Slim\Interfaces\RouteParserInterface; final class RegisterVerifyProcessAction { public function __construct( private readonly LoggerInterface $logger, - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, + private readonly RouteParserInterface $routeParser, private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, private readonly RegisterTokenVerifier $registerTokenVerifier @@ -51,17 +53,17 @@ public function __invoke(ServerRequest $request, Response $response): Response $this->session->set('user_id', $userId); if (isset($queryParams['redirect'])) { - return $this->responder->redirectToUrl($response, $queryParams['redirect']); + return $this->redirectHandler->redirectToUrl($response, $queryParams['redirect']); } - return $this->responder->redirectToRouteName($response, 'home-page'); + return $this->redirectHandler->redirectToRouteName($response, 'home-page'); } catch (InvalidTokenException $ite) { $flash->add('error', __('Invalid or expired link. Please log in to receive a new link.')); $this->logger->error('Invalid or expired token user_verification id: ' . $queryParams['id']); $newQueryParam = isset($queryParams['redirect']) ? ['redirect' => $queryParams['redirect']] : []; // Redirect to login page with redirect query param if set - return $this->responder->redirectToRouteName($response, 'login-page', [], $newQueryParam); + return $this->redirectHandler->redirectToRouteName($response, 'login-page', [], $newQueryParam); } catch (UserAlreadyVerifiedException $uave) { // Check if already logged in if ($this->session->get('user_id') === null) { @@ -69,23 +71,23 @@ public function __invoke(ServerRequest $request, Response $response): Response $flash->add('info', __('You are already verified. Please log in.')); $newQueryParam = isset($queryParams['redirect']) ? ['redirect' => $queryParams['redirect']] : []; - return $this->responder->redirectToRouteName($response, 'login-page', [], $newQueryParam); + return $this->redirectHandler->redirectToRouteName($response, 'login-page', [], $newQueryParam); } // Already logged in $flash->add( 'info', sprintf( __('You are already logged-in.
Would you like to %slogout%s?'), - '', + '', '' ) ); if (isset($queryParams['redirect'])) { - return $this->responder->redirectToUrl($response, $queryParams['redirect']); + return $this->redirectHandler->redirectToUrl($response, $queryParams['redirect']); } - return $this->responder->redirectToRouteName($response, 'home-page'); + return $this->redirectHandler->redirectToRouteName($response, 'home-page'); } } diff --git a/src/Application/Action/Authentication/Page/LoginPageAction.php b/src/Application/Action/Authentication/Page/LoginPageAction.php index 7556e0b4..1f70aaf1 100644 --- a/src/Application/Action/Authentication/Page/LoginPageAction.php +++ b/src/Application/Action/Authentication/Page/LoginPageAction.php @@ -2,15 +2,19 @@ namespace App\Application\Action\Authentication\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; +use App\Application\Responder\TemplateRenderer; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Slim\Interfaces\RouteParserInterface; final class LoginPageAction { public function __construct( - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, + private readonly RouteParserInterface $routeParser, + private readonly TemplateRenderer $templateRenderer, private readonly SessionInterface $session, ) { } @@ -34,20 +38,20 @@ public function __invoke(ServerRequestInterface $request, ResponseInterface $res 'info', sprintf( __('You are already logged-in.
Would you like to %slogout%s?'), - '', + '', '' ) ); // If redirect param set, redirect to this url if (isset($queryParams['redirect'])) { - return $this->responder->redirectToUrl($response, $queryParams['redirect']); + return $this->redirectHandler->redirectToUrl($response, $queryParams['redirect']); } // Otherwise, go to home page - return $this->responder->redirectToRouteName($response, 'home-page'); + return $this->redirectHandler->redirectToRouteName($response, 'home-page'); } - return $this->responder->render( + return $this->templateRenderer->render( $response, 'authentication/login.html.php', // Provide same query params passed to login page to be added to the login submit request diff --git a/src/Application/Action/Authentication/Page/LogoutPageAction.php b/src/Application/Action/Authentication/Page/LogoutPageAction.php index 490d9956..274db8f6 100644 --- a/src/Application/Action/Authentication/Page/LogoutPageAction.php +++ b/src/Application/Action/Authentication/Page/LogoutPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Authentication\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; use Odan\Session\SessionInterface; use Odan\Session\SessionManagerInterface; use Psr\Http\Message\ResponseInterface as Response; @@ -13,7 +13,7 @@ final class LogoutPageAction public function __construct( private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, ) { } @@ -26,6 +26,6 @@ public function __invoke(ServerRequest $request, Response $response): Response // Add flash message to inform user of the success $this->session->getFlash()->add('success', __('Logged out successfully.')); - return $this->responder->redirectToRouteName($response, 'login-page'); + return $this->redirectHandler->redirectToRouteName($response, 'login-page'); } } diff --git a/src/Application/Action/Authentication/Page/PasswordResetPageAction.php b/src/Application/Action/Authentication/Page/PasswordResetPageAction.php index 111edf36..048ade35 100644 --- a/src/Application/Action/Authentication/Page/PasswordResetPageAction.php +++ b/src/Application/Action/Authentication/Page/PasswordResetPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Authentication\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as ServerRequest; @@ -11,7 +11,7 @@ class PasswordResetPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly SessionInterface $session, private readonly LoggerInterface $logger, ) { @@ -34,7 +34,7 @@ public function __invoke(ServerRequest $request, Response $response): Response // There may be other query params e.g. redirect if (isset($queryParams['id'], $queryParams['token'])) { - return $this->responder->render($response, 'authentication/reset-password.html.php', [ + return $this->templateRenderer->render($response, 'authentication/reset-password.html.php', [ 'token' => $queryParams['token'], 'id' => $queryParams['id'], ]); @@ -47,7 +47,7 @@ public function __invoke(ServerRequest $request, Response $response): Response // If the user clicks on the link and the token's missing, load page with 400 Bad request status $response = $response->withStatus(400); - return $this->responder->render($response, 'authentication/reset-password.html.php', [ + return $this->templateRenderer->render($response, 'authentication/reset-password.html.php', [ 'formErrorMessage' => __('Token not found. Please click on the link you received via email.'), ]); } diff --git a/src/Application/Action/Client/Ajax/ApiClientCreateAction.php b/src/Application/Action/Client/Ajax/ApiClientCreateAction.php index 0d918bbd..ad65709f 100644 --- a/src/Application/Action/Client/Ajax/ApiClientCreateAction.php +++ b/src/Application/Action/Client/Ajax/ApiClientCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Service\ClientCreatorFromApi; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -13,7 +13,7 @@ final class ApiClientCreateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientCreatorFromApi $clientCreatorFromClientSubmit, ) { } @@ -37,10 +37,10 @@ public function __invoke( $insertId = $this->clientCreatorFromClientSubmit->createClientFromClientSubmit($clientValues); if (0 !== $insertId) { - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); } - $response = $this->responder->respondWithJson($response, [ + $response = $this->jsonResponder->respondWithJson($response, [ 'status' => 'warning', 'message' => 'Client not created', ]); diff --git a/src/Application/Action/Client/Ajax/ClientCreateAction.php b/src/Application/Action/Client/Ajax/ClientCreateAction.php index 354a7778..ae9db634 100644 --- a/src/Application/Action/Client/Ajax/ClientCreateAction.php +++ b/src/Application/Action/Client/Ajax/ClientCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Service\ClientCreator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ final class ClientCreateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientCreator $clientCreator, ) { } @@ -35,9 +35,9 @@ public function __invoke( $insertId = $this->clientCreator->createClient($clientValues); if (0 !== $insertId) { - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); } - $response = $this->responder->respondWithJson($response, [ + $response = $this->jsonResponder->respondWithJson($response, [ 'status' => 'warning', 'message' => 'Client not created', ]); diff --git a/src/Application/Action/Client/Ajax/ClientDeleteAction.php b/src/Application/Action/Client/Ajax/ClientDeleteAction.php index 30c61cb6..9d32627e 100644 --- a/src/Application/Action/Client/Ajax/ClientDeleteAction.php +++ b/src/Application/Action/Client/Ajax/ClientDeleteAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Service\ClientDeleter; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface; @@ -11,7 +11,7 @@ final class ClientDeleteAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientDeleter $clientDeleter, private readonly SessionInterface $session, ) { @@ -42,10 +42,10 @@ public function __invoke( // Add flash here as user gets redirected to client list after deletion $flash->add('success', __('Successfully deleted client.')); - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null]); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null]); } - $response = $this->responder->respondWithJson( + $response = $this->jsonResponder->respondWithJson( $response, ['status' => 'warning', 'message' => 'Client not deleted.'] ); diff --git a/src/Application/Action/Client/Ajax/ClientFetchListAction.php b/src/Application/Action/Client/Ajax/ClientFetchListAction.php index c4294ce0..dfa3a68d 100644 --- a/src/Application/Action/Client/Ajax/ClientFetchListAction.php +++ b/src/Application/Action/Client/Ajax/ClientFetchListAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Exception\InvalidClientFilterException; use App\Domain\Client\Service\ClientFinderWithFilter; use App\Test\Integration\Client\ClientListActionTest; @@ -13,7 +13,7 @@ final class ClientFetchListAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientFinderWithFilter $clientFilterFinder, ) { } @@ -36,9 +36,9 @@ public function __invoke( // Retrieve posts with given filter values (or none) $clientResultCollection = $this->clientFilterFinder->findClientsWithFilter($request->getQueryParams()); - return $this->responder->respondWithJson($response, $clientResultCollection); + return $this->jsonResponder->respondWithJson($response, $clientResultCollection); } catch (InvalidClientFilterException $invalidClientFilterException) { - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, /** @see ClientListActionTest::testClientListActionInvalidFilters() */ [ diff --git a/src/Application/Action/Client/Ajax/ClientUpdateAction.php b/src/Application/Action/Client/Ajax/ClientUpdateAction.php index c78f29a8..6a1daf1e 100644 --- a/src/Application/Action/Client/Ajax/ClientUpdateAction.php +++ b/src/Application/Action/Client/Ajax/ClientUpdateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Service\ClientUpdater; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ final class ClientUpdateAction { public function __construct( - protected readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientUpdater $clientUpdater, ) { } @@ -34,12 +34,12 @@ public function __invoke( $updateData = $this->clientUpdater->updateClient($clientId, $clientValues); if ($updateData['updated']) { - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, ['status' => 'success', 'data' => $updateData['data']] ); } - $response = $this->responder->respondWithJson($response, [ + $response = $this->jsonResponder->respondWithJson($response, [ 'status' => 'warning', 'message' => 'The client was not updated.', 'data' => $updateData['data'], diff --git a/src/Application/Action/Client/Ajax/FetchDropdownOptionsForClientCreateAction.php b/src/Application/Action/Client/Ajax/FetchDropdownOptionsForClientCreateAction.php index 1672cfb2..33dce79e 100644 --- a/src/Application/Action/Client/Ajax/FetchDropdownOptionsForClientCreateAction.php +++ b/src/Application/Action/Client/Ajax/FetchDropdownOptionsForClientCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Client\Exception\InvalidClientFilterException; use App\Domain\Client\Service\ClientUtilFinder; use Fig\Http\Message\StatusCodeInterface; @@ -12,7 +12,7 @@ class FetchDropdownOptionsForClientCreateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly ClientUtilFinder $clientUtilFinder, ) { } @@ -34,7 +34,7 @@ public function __invoke( try { $dropdownOptions = $this->clientUtilFinder->findClientDropdownValues(); } catch (InvalidClientFilterException $invalidClientFilterException) { - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, // Response format tested in PostFilterProvider.php [ @@ -45,6 +45,6 @@ public function __invoke( ); } - return $this->responder->respondWithJson($response, $dropdownOptions); + return $this->jsonResponder->respondWithJson($response, $dropdownOptions); } } diff --git a/src/Application/Action/Client/Page/ClientListPageAction.php b/src/Application/Action/Client/Page/ClientListPageAction.php index d30465e1..831f2810 100644 --- a/src/Application/Action/Client/Page/ClientListPageAction.php +++ b/src/Application/Action/Client/Page/ClientListPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use App\Domain\Authorization\Privilege; use App\Domain\Client\Authorization\ClientAuthorizationChecker; use App\Domain\Client\Service\ClientListFilter\ClientListFilterChipProvider; @@ -12,7 +12,7 @@ final class ClientListPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly ClientListFilterChipProvider $clientListFilterChipGetter, private readonly ClientAuthorizationChecker $clientAuthorizationChecker ) { @@ -38,12 +38,12 @@ public function __invoke( // Retrieving available filters $clientListFilters = $this->clientListFilterChipGetter->getActiveAndInactiveClientListFilters(); - $this->responder->addPhpViewAttribute('clientListFilters', $clientListFilters); - $this->responder->addPhpViewAttribute( + $this->templateRenderer->addPhpViewAttribute('clientListFilters', $clientListFilters); + $this->templateRenderer->addPhpViewAttribute( 'clientCreatePrivilege', $this->clientAuthorizationChecker->isGrantedToCreate() ? Privilege::CREATE : Privilege::NONE ); - return $this->responder->render($response, 'client/clients-list.html.php'); + return $this->templateRenderer->render($response, 'client/clients-list.html.php'); } } diff --git a/src/Application/Action/Client/Page/ClientReadPageAction.php b/src/Application/Action/Client/Page/ClientReadPageAction.php index 6abfca3b..5bd333ef 100644 --- a/src/Application/Action/Client/Page/ClientReadPageAction.php +++ b/src/Application/Action/Client/Page/ClientReadPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Client\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use App\Domain\Client\Service\ClientFinder; use App\Domain\Client\Service\ClientUtilFinder; use Psr\Http\Message\ResponseInterface; @@ -11,9 +11,9 @@ final class ClientReadPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly ClientFinder $clientFinder, - protected readonly ClientUtilFinder $clientUtilFinder, + private readonly ClientUtilFinder $clientUtilFinder, ) { } @@ -36,7 +36,7 @@ public function __invoke( $clientAggregate = $this->clientFinder->findClientReadAggregate((int)$args['client_id'], false); $dropdownValues = $this->clientUtilFinder->findClientDropdownValues($clientAggregate->userId); - return $this->responder->render( + return $this->templateRenderer->render( $response, 'client/client-read.html.php', ['clientAggregate' => $clientAggregate, 'dropdownValues' => $dropdownValues] diff --git a/src/Application/Action/Common/TranslateAction.php b/src/Application/Action/Common/TranslateAction.php index 6aff8e81..593b4746 100644 --- a/src/Application/Action/Common/TranslateAction.php +++ b/src/Application/Action/Common/TranslateAction.php @@ -2,14 +2,14 @@ namespace App\Application\Action\Common; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; final class TranslateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, ) { } @@ -34,9 +34,9 @@ public function __invoke( $translatedStrings[$string] = __($string); } - return $this->responder->respondWithJson($response, $translatedStrings); + return $this->jsonResponder->respondWithJson($response, $translatedStrings); } - return $this->responder->respondWithJson($response, ['error' => 'Wrong request body format.'], 400); + return $this->jsonResponder->respondWithJson($response, ['error' => 'Wrong request body format.'], 400); } } diff --git a/src/Application/Action/Dashboard/DashboardPageAction.php b/src/Application/Action/Dashboard/DashboardPageAction.php index 95601c69..80736cb7 100644 --- a/src/Application/Action/Dashboard/DashboardPageAction.php +++ b/src/Application/Action/Dashboard/DashboardPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Dashboard; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use App\Domain\Dashboard\DashboardPanelProvider; use App\Domain\FilterSetting\FilterModule; use App\Domain\FilterSetting\FilterSettingFinder; @@ -13,7 +13,7 @@ final class DashboardPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly SessionInterface $session, private readonly FilterSettingFinder $filterSettingFinder, private readonly DashboardPanelProvider $dashboardGetter, @@ -38,7 +38,7 @@ public function __invoke( ): ResponseInterface { $dashboards = $this->dashboardGetter->getAuthorizedDashboards(); - return $this->responder->render( + return $this->templateRenderer->render( $response, 'dashboard/dashboard.html.php', [ diff --git a/src/Application/Action/Dashboard/DashboardTogglePanelProcessAction.php b/src/Application/Action/Dashboard/DashboardTogglePanelProcessAction.php index ce36d8fb..fad7610c 100644 --- a/src/Application/Action/Dashboard/DashboardTogglePanelProcessAction.php +++ b/src/Application/Action/Dashboard/DashboardTogglePanelProcessAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Dashboard; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Application\Validation\MalformedRequestBodyChecker; use App\Domain\FilterSetting\FilterModule; use App\Domain\FilterSetting\FilterSettingSaver; @@ -14,7 +14,7 @@ final class DashboardTogglePanelProcessAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly SessionInterface $session, private readonly FilterSettingSaver $filterSettingSaver, private readonly MalformedRequestBodyChecker $malformedRequestBodyChecker, @@ -43,7 +43,7 @@ public function __invoke( FilterModule::DASHBOARD_PANEL ); - return $this->responder->respondWithJson($response, ['success' => true]); + return $this->jsonResponder->respondWithJson($response, ['success' => true]); } $flash = $this->session->getFlash(); $flash->add('error', __('Malformed request body syntax. Please contact an administrator.')); diff --git a/src/Application/Action/Dashboard/PhpDevTestAction.php b/src/Application/Action/Dashboard/PhpDevTestAction.php index 95081768..e231acf4 100644 --- a/src/Application/Action/Dashboard/PhpDevTestAction.php +++ b/src/Application/Action/Dashboard/PhpDevTestAction.php @@ -2,7 +2,6 @@ namespace App\Application\Action\Dashboard; -use App\Application\Responder\Responder; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -12,7 +11,6 @@ class PhpDevTestAction { public function __construct( - private readonly Responder $responder, ) { } @@ -30,6 +28,6 @@ public function __invoke( ResponseInterface $response, array $args ): ResponseInterface { - return $this->responder->createResponse(); + return $response; } } diff --git a/src/Application/Action/Note/Ajax/NoteCreateAction.php b/src/Application/Action/Note/Ajax/NoteCreateAction.php index 205a8811..c30383da 100644 --- a/src/Application/Action/Note/Ajax/NoteCreateAction.php +++ b/src/Application/Action/Note/Ajax/NoteCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Note\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Note\Service\NoteCreator; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ final class NoteCreateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly NoteCreator $noteCreator, ) { } @@ -36,7 +36,7 @@ public function __invoke( if (0 !== $noteCreationData['note_id']) { // camelCase according to Google recommendation - return $this->responder->respondWithJson($response, [ + return $this->jsonResponder->respondWithJson($response, [ 'status' => 'success', 'data' => [ 'userFullName' => $noteCreationData['user_full_name'], @@ -45,7 +45,7 @@ public function __invoke( ], ], 201); } - $response = $this->responder->respondWithJson($response, [ + $response = $this->jsonResponder->respondWithJson($response, [ 'status' => 'warning', 'message' => 'Note not created', ]); diff --git a/src/Application/Action/Note/Ajax/NoteDeleteAction.php b/src/Application/Action/Note/Ajax/NoteDeleteAction.php index 9c3c5f79..3e04e14a 100644 --- a/src/Application/Action/Note/Ajax/NoteDeleteAction.php +++ b/src/Application/Action/Note/Ajax/NoteDeleteAction.php @@ -2,19 +2,17 @@ namespace App\Application\Action\Note\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Note\Service\NoteDeleter; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Psr\Log\LoggerInterface; final class NoteDeleteAction { - protected LoggerInterface $logger; public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly NoteDeleter $noteDeleter, private readonly SessionInterface $session, ) { @@ -40,10 +38,10 @@ public function __invoke( $deleted = $this->noteDeleter->deleteNote($noteId); if ($deleted) { - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null]); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null]); } - $response = $this->responder->respondWithJson( + $response = $this->jsonResponder->respondWithJson( $response, ['status' => 'warning', 'message' => 'Note not deleted.'] ); diff --git a/src/Application/Action/Note/Ajax/NoteFetchListAction.php b/src/Application/Action/Note/Ajax/NoteFetchListAction.php index 0447183e..a243d6fb 100644 --- a/src/Application/Action/Note/Ajax/NoteFetchListAction.php +++ b/src/Application/Action/Note/Ajax/NoteFetchListAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Note\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Note\Exception\InvalidNoteFilterException; use App\Domain\Note\Service\NoteFilterFinder; use Fig\Http\Message\StatusCodeInterface; @@ -12,7 +12,7 @@ final class NoteFetchListAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly NoteFilterFinder $noteFilterFinder, ) { } @@ -35,7 +35,7 @@ public function __invoke( // Retrieve notes with given filter values (or none) $filteredNotes = $this->noteFilterFinder->findNotesWithFilter($request->getQueryParams()); } catch (InvalidNoteFilterException $invalidNoteFilterException) { - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, // Response format tested in NoteFilterProvider.php [ @@ -46,6 +46,6 @@ public function __invoke( ); } - return $this->responder->respondWithJson($response, $filteredNotes); + return $this->jsonResponder->respondWithJson($response, $filteredNotes); } } diff --git a/src/Application/Action/Note/Ajax/NoteUpdateAction.php b/src/Application/Action/Note/Ajax/NoteUpdateAction.php index a1acf440..eb3fc528 100644 --- a/src/Application/Action/Note/Ajax/NoteUpdateAction.php +++ b/src/Application/Action/Note/Ajax/NoteUpdateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\Note\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Note\Service\NoteUpdater; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ final class NoteUpdateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly NoteUpdater $noteUpdater, ) { } @@ -35,9 +35,9 @@ public function __invoke( $updated = $this->noteUpdater->updateNote($noteIdToChange, $noteValues); if ($updated) { - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null]); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null]); } - $response = $this->responder->respondWithJson($response, [ + $response = $this->jsonResponder->respondWithJson($response, [ 'status' => 'warning', 'message' => 'The note was not updated.', ]); diff --git a/src/Application/Action/Note/Page/NoteReadPageAction.php b/src/Application/Action/Note/Page/NoteReadPageAction.php index abe61247..078a7d23 100644 --- a/src/Application/Action/Note/Page/NoteReadPageAction.php +++ b/src/Application/Action/Note/Page/NoteReadPageAction.php @@ -2,16 +2,18 @@ namespace App\Application\Action\Note\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\RedirectHandler; use App\Domain\Note\Service\NoteFinder; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Slim\Interfaces\RouteParserInterface; final class NoteReadPageAction { public function __construct( - private readonly Responder $responder, + private readonly RedirectHandler $redirectHandler, + private readonly RouteParserInterface $routeParser, private readonly NoteFinder $noteFinder, private readonly SessionInterface $session, ) { @@ -34,9 +36,9 @@ public function __invoke( $noteData = $this->noteFinder->findNote((int)$args['note_id']); if ($noteData->id) { // Redirect to client read page with hash anchor to the correct note container - return $this->responder->redirectToUrl( + return $this->redirectHandler->redirectToUrl( $response, - $this->responder->urlFor('client-read-page', ['client_id' => (string)$noteData->clientId]) . + $this->routeParser->urlFor('client-read-page', ['client_id' => (string)$noteData->clientId]) . "#note-$noteData->id-container" ); } @@ -45,6 +47,6 @@ public function __invoke( $flash->add('error', __('The note was not not found.')); // When note does not exist link to client list page - return $this->responder->redirectToRouteName($response, 'client-list-page'); + return $this->redirectHandler->redirectToRouteName($response, 'client-list-page'); } } diff --git a/src/Application/Action/User/Ajax/FetchDropdownOptionsForUserCreateAction.php b/src/Application/Action/User/Ajax/FetchDropdownOptionsForUserCreateAction.php index 04dd4b33..2b448c4d 100644 --- a/src/Application/Action/User/Ajax/FetchDropdownOptionsForUserCreateAction.php +++ b/src/Application/Action/User/Ajax/FetchDropdownOptionsForUserCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\User\Service\UserUtilFinder; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ class FetchDropdownOptionsForUserCreateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly UserUtilFinder $userUtilFinder, ) { } @@ -31,6 +31,6 @@ public function __invoke( ): ResponseInterface { $dropdownOptions = $this->userUtilFinder->findUserDropdownValues(); - return $this->responder->respondWithJson($response, $dropdownOptions); + return $this->jsonResponder->respondWithJson($response, $dropdownOptions); } } diff --git a/src/Application/Action/User/Ajax/PasswordChangeSubmitAction.php b/src/Application/Action/User/Ajax/PasswordChangeSubmitAction.php index 58eb34ba..db1e7127 100644 --- a/src/Application/Action/User/Ajax/PasswordChangeSubmitAction.php +++ b/src/Application/Action/User/Ajax/PasswordChangeSubmitAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Authentication\Service\PasswordChanger; use Odan\Session\SessionInterface; use Odan\Session\SessionManagerInterface; @@ -15,8 +15,8 @@ class PasswordChangeSubmitAction { public function __construct( - private readonly Responder $responder, - protected readonly SessionManagerInterface $sessionManager, + private readonly JsonResponder $jsonResponder, + private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, private readonly PasswordChanger $passwordChanger ) { @@ -43,6 +43,6 @@ public function __invoke(ServerRequest $request, Response $response, array $args $this->sessionManager->regenerateId(); } - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null]); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null]); } } diff --git a/src/Application/Action/User/Ajax/UserActivityFetchListAction.php b/src/Application/Action/User/Ajax/UserActivityFetchListAction.php index a6a1eccd..8cd6a078 100644 --- a/src/Application/Action/User/Ajax/UserActivityFetchListAction.php +++ b/src/Application/Action/User/Ajax/UserActivityFetchListAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\FilterSetting\FilterModule; use App\Domain\FilterSetting\FilterSettingSaver; use App\Domain\UserActivity\Service\UserActivityFinder; @@ -12,7 +12,7 @@ class UserActivityFetchListAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly UserActivityFinder $userActivityFinder, private readonly FilterSettingSaver $filterSettingSaver, ) { @@ -47,6 +47,6 @@ public function __invoke( ); } - return $this->responder->respondWithJson($response, $userResultDataArray); + return $this->jsonResponder->respondWithJson($response, $userResultDataArray); } } diff --git a/src/Application/Action/User/Ajax/UserCreateAction.php b/src/Application/Action/User/Ajax/UserCreateAction.php index fdc13965..c1ed9933 100644 --- a/src/Application/Action/User/Ajax/UserCreateAction.php +++ b/src/Application/Action/User/Ajax/UserCreateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\User\Service\UserCreator; use Odan\Session\SessionInterface; use Psr\Http\Message\ResponseInterface as Response; @@ -14,8 +14,8 @@ final class UserCreateAction { public function __construct( private readonly LoggerInterface $logger, - protected Responder $responder, - protected UserCreator $userCreator, + private readonly JsonResponder $jsonResponder, + private readonly UserCreator $userCreator, private readonly SessionInterface $session, ) { } @@ -45,12 +45,12 @@ public function __invoke(ServerRequest $request, Response $response): Response $response = $response->withAddedHeader('Warning', 'The post could not be created'); } - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null], 201); } catch (TransportExceptionInterface $e) { // Flash message has to be added in the frontend as form is submitted via Ajax $this->logger->error('Mailer exception: ' . $e->getMessage()); - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, ['status' => 'error', 'message' => __('Email error. Please contact an administrator.')] ); diff --git a/src/Application/Action/User/Ajax/UserDeleteAction.php b/src/Application/Action/User/Ajax/UserDeleteAction.php index cb21f2cd..2f1cd624 100644 --- a/src/Application/Action/User/Ajax/UserDeleteAction.php +++ b/src/Application/Action/User/Ajax/UserDeleteAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\User\Service\UserDeleter; use Odan\Session\SessionInterface; use Odan\Session\SessionManagerInterface; @@ -12,7 +12,7 @@ final class UserDeleteAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly UserDeleter $userDeleter, private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, @@ -58,10 +58,10 @@ public function __invoke( ); } - return $this->responder->respondWithJson($response, ['status' => 'success']); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success']); } - $response = $this->responder->respondWithJson( + $response = $this->jsonResponder->respondWithJson( $response, // response json body asserted in UserDeleteActionTest ['status' => 'warning', 'message' => 'User not deleted.'] diff --git a/src/Application/Action/User/Ajax/UserFetchListAction.php b/src/Application/Action/User/Ajax/UserFetchListAction.php index 7cfc2e4a..0b8cfca2 100644 --- a/src/Application/Action/User/Ajax/UserFetchListAction.php +++ b/src/Application/Action/User/Ajax/UserFetchListAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\User\Enum\UserStatus; use App\Domain\User\Service\UserFinder; use Psr\Http\Message\ResponseInterface; @@ -11,8 +11,8 @@ final class UserFetchListAction { public function __construct( - private readonly Responder $responder, - protected readonly UserFinder $userFinder, + private readonly JsonResponder $jsonResponder, + private readonly UserFinder $userFinder, ) { } @@ -36,7 +36,7 @@ public function __invoke( // $clientResultCollection = $this->clientFilterFinder->findClientsWithFilter($request->getQueryParams()); $userResultDataArray = $this->userFinder->findAllUsersResultDataForList(); - return $this->responder->respondWithJson($response, [ + return $this->jsonResponder->respondWithJson($response, [ 'userResultDataArray' => $userResultDataArray, 'statuses' => UserStatus::toTranslatedNamesArray(), ]); diff --git a/src/Application/Action/User/Ajax/UserUpdateAction.php b/src/Application/Action/User/Ajax/UserUpdateAction.php index 2762c92e..c3d16ee1 100644 --- a/src/Application/Action/User/Ajax/UserUpdateAction.php +++ b/src/Application/Action/User/Ajax/UserUpdateAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Ajax; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\User\Service\UserUpdater; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -10,7 +10,7 @@ final class UserUpdateAction { public function __construct( - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, private readonly UserUpdater $userUpdater, ) { } @@ -35,11 +35,11 @@ public function __invoke( $updated = $this->userUpdater->updateUser($userIdToChange, $userValuesToChange); if ($updated) { - return $this->responder->respondWithJson($response, ['status' => 'success', 'data' => null]); + return $this->jsonResponder->respondWithJson($response, ['status' => 'success', 'data' => null]); } // If for example values didn't change - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, ['status' => 'warning', 'message' => 'User wasn\'t updated'] ); diff --git a/src/Application/Action/User/Page/UserListPageAction.php b/src/Application/Action/User/Page/UserListPageAction.php index 8cc3272c..4181d197 100644 --- a/src/Application/Action/User/Page/UserListPageAction.php +++ b/src/Application/Action/User/Page/UserListPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use App\Domain\User\Authorization\UserAuthorizationChecker; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -11,7 +11,7 @@ final class UserListPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly UserAuthorizationChecker $userAuthorizationChecker, ) { } @@ -23,9 +23,6 @@ public function __construct( * @param ResponseInterface $response The response * @param array $args * - * @throws \JsonException - * @throws \Throwable - * * @return ResponseInterface The response */ public function __invoke( @@ -34,7 +31,7 @@ public function __invoke( array $args ): ResponseInterface { if ($this->userAuthorizationChecker->isGrantedToRead()) { - return $this->responder->render($response, 'user/user-list.html.php'); + return $this->templateRenderer->render($response, 'user/user-list.html.php'); } throw new HttpForbiddenException($request, 'Not allowed to see this page.'); diff --git a/src/Application/Action/User/Page/UserReadPageAction.php b/src/Application/Action/User/Page/UserReadPageAction.php index 064f5450..5b705701 100644 --- a/src/Application/Action/User/Page/UserReadPageAction.php +++ b/src/Application/Action/User/Page/UserReadPageAction.php @@ -2,7 +2,7 @@ namespace App\Application\Action\User\Page; -use App\Application\Responder\Responder; +use App\Application\Responder\TemplateRenderer; use App\Domain\Exception\DomainRecordNotFoundException; use App\Domain\User\Enum\UserStatus; use App\Domain\User\Service\UserFinder; @@ -14,7 +14,7 @@ final class UserReadPageAction { public function __construct( - private readonly Responder $responder, + private readonly TemplateRenderer $templateRenderer, private readonly UserFinder $userFinder, private readonly SessionInterface $session, ) { @@ -38,7 +38,7 @@ public function __invoke( $userId = (int)($args['user_id'] ?? $authenticatedUserId); try { // Retrieve user infos - return $this->responder->render($response, 'user/user-read.html.php', [ + return $this->templateRenderer->render($response, 'user/user-read.html.php', [ 'user' => $this->userFinder->findUserReadResult($userId), 'isOwnProfile' => $userId === $authenticatedUserId, // Get all user status cases as enums diff --git a/src/Application/Middleware/ForbiddenExceptionMiddleware.php b/src/Application/Middleware/ForbiddenExceptionMiddleware.php index d1d77419..84bfe1ed 100644 --- a/src/Application/Middleware/ForbiddenExceptionMiddleware.php +++ b/src/Application/Middleware/ForbiddenExceptionMiddleware.php @@ -2,9 +2,10 @@ namespace App\Application\Middleware; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Authentication\Exception\ForbiddenException; use Fig\Http\Message\StatusCodeInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -13,7 +14,8 @@ class ForbiddenExceptionMiddleware implements MiddlewareInterface { public function __construct( - private readonly Responder $responder, + private readonly ResponseFactoryInterface $responseFactory, + private readonly JsonResponder $jsonResponder, ) { } @@ -23,9 +25,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } catch (ForbiddenException $forbiddenException) { // Create response (status code and header are added later) - $response = $this->responder->createResponse(); + $response = $this->responseFactory->createResponse(); - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, [ 'status' => 'error', diff --git a/src/Application/Middleware/InvalidOperationExceptionMiddleware.php b/src/Application/Middleware/InvalidOperationExceptionMiddleware.php index 0d707f84..1efb955a 100644 --- a/src/Application/Middleware/InvalidOperationExceptionMiddleware.php +++ b/src/Application/Middleware/InvalidOperationExceptionMiddleware.php @@ -3,9 +3,10 @@ namespace App\Application\Middleware; use App\Application\Data\UserNetworkSessionData; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Exception\InvalidOperationException; use Fig\Http\Message\StatusCodeInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -15,7 +16,8 @@ class InvalidOperationExceptionMiddleware implements MiddlewareInterface { public function __construct( - private readonly Responder $responder, + private readonly ResponseFactoryInterface $responseFactory, + private readonly JsonResponder $jsonResponder, private readonly UserNetworkSessionData $userNetworkSessionData, private readonly LoggerInterface $logger, ) { @@ -26,14 +28,14 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface try { return $handler->handle($request); } catch (InvalidOperationException $exception) { - $response = $this->responder->createResponse(); + $response = $this->responseFactory->createResponse(); $this->logger->notice( 'Invalid operation from user ' . $this->userNetworkSessionData->userId . ' on ' . $request->getUri()->getPath() . ' with message: ' . $exception->getMessage() ); - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, [ 'status' => 'error', diff --git a/src/Application/Middleware/UserAuthenticationMiddleware.php b/src/Application/Middleware/UserAuthenticationMiddleware.php index 7c3df4c1..777adfae 100644 --- a/src/Application/Middleware/UserAuthenticationMiddleware.php +++ b/src/Application/Middleware/UserAuthenticationMiddleware.php @@ -2,22 +2,28 @@ namespace App\Application\Middleware; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; +use App\Application\Responder\RedirectHandler; use App\Domain\User\Enum\UserStatus; use App\Domain\User\Service\UserFinder; use Odan\Session\SessionInterface; use Odan\Session\SessionManagerInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Slim\Interfaces\RouteParserInterface; final class UserAuthenticationMiddleware implements MiddlewareInterface { public function __construct( private readonly SessionManagerInterface $sessionManager, private readonly SessionInterface $session, - private readonly Responder $responder, + private readonly JsonResponder $jsonResponder, + private readonly RedirectHandler $redirectHandler, + private readonly RouteParserInterface $routeParser, + private readonly ResponseFactoryInterface $responseFactory, private readonly UserFinder $userFinder, ) { } @@ -47,7 +53,7 @@ public function process( $this->sessionManager->regenerateId(); } - $response = $this->responder->createResponse(); + $response = $this->responseFactory->createResponse(); // Inform user that he/she has to login first $this->session->getFlash()->add('info', 'Please login to access this page.'); @@ -56,7 +62,7 @@ public function process( // If header Redirect-to-route-name-if-unauthorized is set, add it to the query params of the login route if (($routeName = $request->getHeaderLine('Redirect-to-route-name-if-unauthorized')) !== '') { // Redirect to after login - $queryParams['redirect'] = $this->responder->urlFor($routeName); + $queryParams['redirect'] = $this->routeParser->urlFor($routeName); } // If header Redirect-to-route-name-if-unauthorized is set, add it to the query params of the login route if (($routeName = $request->getHeaderLine('Redirect-to-url-if-unauthorized')) !== '') { @@ -66,15 +72,15 @@ public function process( // If it's a JSON request return 401 with the login url and its possible query params if ($request->getHeaderLine('Content-Type') === 'application/json') { - return $this->responder->respondWithJson( + return $this->jsonResponder->respondWithJson( $response, - ['loginUrl' => $this->responder->urlFor('login-page', [], $queryParams)], + ['loginUrl' => $this->routeParser->urlFor('login-page', [], $queryParams)], 401 ); } // If no redirect header is set, and it's not a JSON request, redirect to same url as the request after login $queryParams = ['redirect' => $request->getUri()->getPath()]; - return $this->responder->redirectToRouteName($response, 'login-page', [], $queryParams); + return $this->redirectHandler->redirectToRouteName($response, 'login-page', [], $queryParams); } } diff --git a/src/Application/Middleware/ValidationExceptionMiddleware.php b/src/Application/Middleware/ValidationExceptionMiddleware.php index 4dd4e9bb..ffc74fd2 100644 --- a/src/Application/Middleware/ValidationExceptionMiddleware.php +++ b/src/Application/Middleware/ValidationExceptionMiddleware.php @@ -2,8 +2,9 @@ namespace App\Application\Middleware; -use App\Application\Responder\Responder; +use App\Application\Responder\JsonResponder; use App\Domain\Validation\ValidationException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -12,7 +13,8 @@ class ValidationExceptionMiddleware implements MiddlewareInterface { public function __construct( - private readonly Responder $responder, + private readonly ResponseFactoryInterface $responseFactory, + private readonly JsonResponder $jsonResponder, ) { } @@ -22,7 +24,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } catch (ValidationException $validationException) { // Create response (status code and header are added later) - $response = $this->responder->createResponse(); + $response = $this->responseFactory->createResponse(); $responseData = [ 'status' => 'error', @@ -31,7 +33,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface 'data' => ['errors' => $validationException->validationErrors], ]; - return $this->responder->respondWithJson($response, $responseData, 422); + return $this->jsonResponder->respondWithJson($response, $responseData, 422); } } } diff --git a/src/Application/Responder/JsonResponder.php b/src/Application/Responder/JsonResponder.php new file mode 100644 index 00000000..59d47fad --- /dev/null +++ b/src/Application/Responder/JsonResponder.php @@ -0,0 +1,32 @@ +getBody()->write((string)json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR)); + $response = $response->withStatus($status); + + return $response->withHeader('Content-Type', 'application/json'); + } +} \ No newline at end of file diff --git a/src/Application/Responder/RedirectHandler.php b/src/Application/Responder/RedirectHandler.php new file mode 100644 index 00000000..fd5b58d7 --- /dev/null +++ b/src/Application/Responder/RedirectHandler.php @@ -0,0 +1,68 @@ +routeParser = $routeParser; + } + /** + * Creates a redirect for the given url. + * + * This method prepares the response object to return an HTTP Redirect + * response to the client. + * + * @param ResponseInterface $response The response + * @param string $destination The redirect destination (url or route name) + * @param array $queryParams Optional query string parameters + * + * @return ResponseInterface The response + */ + public function redirectToUrl( + ResponseInterface $response, + string $destination, + array $queryParams = [] + ): ResponseInterface { + if ($queryParams) { + $destination = sprintf('%s?%s', $destination, http_build_query($queryParams)); + } + + return $response->withStatus(302)->withHeader('Location', $destination); + } + + /** + * Creates a redirect for the given route name. + * + * This method prepares the response object to return an HTTP Redirect + * response to the client. + * + * @param ResponseInterface $response The response + * @param string $routeName The redirect route name + * @param array $data Named argument replacement data + * @param array $queryParams Optional query string parameters + * + * @return ResponseInterface The response + */ + public function redirectToRouteName( + ResponseInterface $response, + string $routeName, + array $data = [], + array $queryParams = [] + ): ResponseInterface { + return $this->redirectToUrl($response, $this->routeParser->urlFor($routeName, $data, $queryParams)); + } + +} diff --git a/src/Application/Responder/Responder.php b/src/Application/Responder/Responder.php deleted file mode 100644 index 400caabb..00000000 --- a/src/Application/Responder/Responder.php +++ /dev/null @@ -1,236 +0,0 @@ -responseFactory->createResponse()->withHeader('Content-Type', 'text/html; charset=utf-8'); - } - - /** - * Output rendered template. - * - * @param ResponseInterface $response The response - * @param string $template Template pathname relative to templates directory - * @param array $data Associative array of template variables - * - * @return ResponseInterface The response - */ - public function render( - ResponseInterface $response, - string $template, - array $data = [] - ): ResponseInterface { - return $this->phpRenderer->render($response, $template, $data); - } - - /** - * Add global variable accessible in templates. - * - * @param string $key - * @param mixed $value - * - * @return void - */ - public function addPhpViewAttribute(string $key, mixed $value): void - { - $this->phpRenderer->addAttribute($key, $value); - } - - /** - * Creates a redirect for the given url. - * - * This method prepares the response object to return an HTTP Redirect - * response to the client. - * - * @param ResponseInterface $response The response - * @param string $destination The redirect destination (url or route name) - * @param array $queryParams Optional query string parameters - * - * @return ResponseInterface The response - */ - public function redirectToUrl( - ResponseInterface $response, - string $destination, - array $queryParams = [] - ): ResponseInterface { - if ($queryParams) { - $destination = sprintf('%s?%s', $destination, http_build_query($queryParams)); - } - - return $response->withStatus(302)->withHeader('Location', $destination); - } - - /** - * Creates a redirect for the given route name. - * - * This method prepares the response object to return an HTTP Redirect - * response to the client. - * - * @param ResponseInterface $response The response - * @param string $routeName The redirect route name - * @param array $data Named argument replacement data - * @param array $queryParams Optional query string parameters - * - * @return ResponseInterface The response - */ - public function redirectToRouteName( - ResponseInterface $response, - string $routeName, - array $data = [], - array $queryParams = [] - ): ResponseInterface { - return $this->redirectToUrl($response, $this->routeParser->urlFor($routeName, $data, $queryParams)); - } - - /** - * Build the path for a named route including the base path. - * - * @param string $routeName Route name - * @param array $data Named argument replacement data - * @param array $queryParams Optional query string parameters - * - * @return string - */ - public function urlFor(string $routeName, array $data = [], array $queryParams = []): string - { - return $this->routeParser->urlFor($routeName, $data, $queryParams); - } - - /** - * Render template with validation errors. - * - * @param ResponseInterface $response - * @param string $template - * @param ValidationException $validationException - * @param array $queryParams same query params passed to page to be added again to form after validation error - * @param array|null $preloadValues - * - * @return ResponseInterface - */ - public function renderOnValidationError( - ResponseInterface $response, - string $template, - ValidationException $validationException, - array $queryParams = [], - ?array $preloadValues = null, - ): ResponseInterface { - $this->phpRenderer->addAttribute('preloadValues', $preloadValues); - - // Add the validation errors to phpRender attributes - $this->phpRenderer->addAttribute('validation', $validationException->validationErrors); - $this->phpRenderer->addAttribute('formError', true); - // Provide same query params passed to page to be added again after validation error (e.g. redirect) - $this->phpRenderer->addAttribute('queryParams', $queryParams); - - // Render template with status code - return $this->render($response->withStatus(422), $template); - } - - /** - * Respond with delay user has to wait or action that needs to be made before repeating the action. - * - * @param ResponseInterface $response - * @param int|string $remainingDelay - * @param string $template - * @param array|null $preloadValues - * @param array $queryParams same query params passed to page to be added again to form after validation error - * - * @throws \Throwable - * - * @return ResponseInterface - */ - public function respondWithThrottle( - ResponseInterface $response, - int|string $remainingDelay, - string $template, - ?array $preloadValues = null, - array $queryParams = [] - ): ResponseInterface { - $this->phpRenderer->addAttribute('throttleDelay', $remainingDelay); - $this->phpRenderer->addAttribute('preloadValues', $preloadValues); - $this->phpRenderer->addAttribute('formError', true); - // Provide same query params passed to page to be added again after validation error (e.g. redirect) - $this->phpRenderer->addAttribute('queryParams', $queryParams); - - return $this->render($response->withStatus(422), $template); - } - - /** - * Respond with delay user has to wait or action that needs to be made before repeating the action. - * Specifically for form errors. - * - * @param ResponseInterface $response - * @param string $template - * @param SecurityException $securityException - * @param array|null $preloadValues - * @param array $queryParams same query params passed to page to be added again to form after validation error - * - * @throws \Throwable - * - * @return ResponseInterface - */ - public function respondWithFormThrottle( - ResponseInterface $response, - string $template, - SecurityException $securityException, - array $queryParams = [], - ?array $preloadValues = null, - ): ResponseInterface { - $this->phpRenderer->addAttribute('throttleDelay', $securityException->getRemainingDelay()); - $this->phpRenderer->addAttribute('formErrorMessage', $securityException->getPublicMessage()); - $this->phpRenderer->addAttribute('preloadValues', $preloadValues); - $this->phpRenderer->addAttribute('formError', true); - - // Provide same query params passed to page to be added again after validation error (e.g. redirect) - $this->phpRenderer->addAttribute('queryParams', $queryParams); - - return $this->render($response->withStatus(422), $template); - } - - /** - * Write JSON to the response body. - * - * This method prepares the response object to return an HTTP JSON - * response to the client. - * - * @param ResponseInterface $response The response - * @param mixed $data The data - * @param int $status - * - * @return ResponseInterface The response - */ - public function respondWithJson( - ResponseInterface $response, - mixed $data = null, - int $status = 200 - ): ResponseInterface { - $response->getBody()->write((string)json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR)); - $response = $response->withStatus($status); - - return $response->withHeader('Content-Type', 'application/json'); - } -} diff --git a/src/Application/Responder/TemplateRenderer.php b/src/Application/Responder/TemplateRenderer.php new file mode 100644 index 00000000..df9fa749 --- /dev/null +++ b/src/Application/Responder/TemplateRenderer.php @@ -0,0 +1,111 @@ +phpRenderer->render($response, $template, $data); + } + + /** + * Add global variable accessible in templates. + * + * @param string $key + * @param mixed $value + * + * @return void + */ + public function addPhpViewAttribute(string $key, mixed $value): void + { + $this->phpRenderer->addAttribute($key, $value); + } + + /** + * Render template with validation errors. + * + * @param ResponseInterface $response + * @param string $template + * @param ValidationException $validationException + * @param array $queryParams same query params passed to page to be added again to form after validation error + * @param array|null $preloadValues + * + * @return ResponseInterface + */ + public function renderOnValidationError( + ResponseInterface $response, + string $template, + ValidationException $validationException, + array $queryParams = [], + ?array $preloadValues = null, + ): ResponseInterface { + $this->phpRenderer->addAttribute('preloadValues', $preloadValues); + + // Add the validation errors to phpRender attributes + $this->phpRenderer->addAttribute('validation', $validationException->validationErrors); + $this->phpRenderer->addAttribute('formError', true); + // Provide same query params passed to page to be added again after validation error (e.g. redirect) + $this->phpRenderer->addAttribute('queryParams', $queryParams); + + // Render template with status code + return $this->render($response->withStatus(422), $template); + } + + + /** + * Respond with delay user has to wait or action that needs to be made before repeating the action. + * Specifically for form errors. + * + * @param ResponseInterface $response + * @param string $template + * @param SecurityException $securityException + * @param array|null $preloadValues + * @param array $queryParams same query params passed to page to be added again to form after validation error + * + * @return ResponseInterface + * @throws \Throwable + * + */ + public function respondWithFormThrottle( + ResponseInterface $response, + string $template, + SecurityException $securityException, + array $queryParams = [], + ?array $preloadValues = null, + ): ResponseInterface { + $this->phpRenderer->addAttribute('throttleDelay', $securityException->getRemainingDelay()); + $this->phpRenderer->addAttribute('formErrorMessage', $securityException->getPublicMessage()); + $this->phpRenderer->addAttribute('preloadValues', $preloadValues); + $this->phpRenderer->addAttribute('formError', true); + + // Provide same query params passed to page to be added again after validation error (e.g. redirect) + $this->phpRenderer->addAttribute('queryParams', $queryParams); + + return $this->render($response->withStatus(422), $template); + } +} \ No newline at end of file