Created
February 1, 2022 17:50
-
-
Save thepsion5/334313ebefd336b073495220f10792a6 to your computer and use it in GitHub Desktop.
Example of Using Composition via Traits to Remove Duplicate Controller Logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types=1); | |
namespace Personly\Music\Presentation; | |
use Personly\Framework\Csrf\StoredTokenValidator; | |
use Personly\Framework\Csrf\Token; | |
use Personly\Framework\Rendering\TemplateRenderer; | |
use Personly\FrontPage\Presentation\RootPageController; | |
use Personly\Music\Domain\MusicRepository; | |
use Symfony\Component\HttpFoundation\RedirectResponse; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
final class MusicController | |
{ | |
private TemplateRenderer $templateRenderer; | |
private MusicRepository $musicRepository; | |
private StoredTokenValidator $storedTokenValidator; | |
public function __construct | |
( | |
TemplateRenderer $templateRenderer, | |
MusicRepository $musicRepository, | |
StoredTokenValidator $storedTokenValidator | |
) { | |
$this->templateRenderer = $templateRenderer; | |
$this->musicRepository = $musicRepository; | |
$this->storedTokenValidator = $storedTokenValidator; | |
} | |
public function listAlbums(Request $request, array $vars): Response | |
{ | |
$layoutToken = $request->headers->get('X-Layout'); | |
if(!$this->storedTokenValidator->validate('layout', new Token($layoutToken ?? ''))) { | |
return new RedirectResponse('/?destination=music', 302); | |
} | |
$albums = $this->musicRepository->getAlbums(); | |
$content = $this->templateRenderer->render('music/AlbumsList.html.twig', [ | |
'albums' => $albums | |
]); | |
return new Response($content); | |
} | |
public function getAlbumInfo(Request $request, array $vars = []): Response | |
{ | |
$layoutToken = $request->headers->get('X-Layout'); | |
if(!$this->storedTokenValidator->validate('layout', new Token($layoutToken ?? ''))) { | |
return new RedirectResponse('/?destination=music', 302); | |
} | |
$albumInfo = $this->musicRepository->getTracks((int) $vars['albumId']); | |
$image = $albumInfo[0] ? $albumInfo[0]->image : ''; | |
$content = $this->templateRenderer->render('music/AlbumInfo.html.twig', [ | |
'album' => $albumInfo, | |
'image' => $image | |
]); | |
return new Response($content); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
use Personly\Framework\{Csrf\StoredTokenValidator, Rendering\TemplateRenderer}; | |
use Personly\Http\Traits\{ValidatesCsrfTokens, RendersTemplates}; | |
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response}; | |
final class MusicController | |
{ | |
use ValidatesCsrfTokens, RendersTemplates; | |
/** | |
* @var StoredTokenValidator | |
*/ | |
private StoredTokenValidator $validator; | |
/** | |
* @var TemplateRenderer | |
*/ | |
private TemplateRenderer $renderer; | |
/** | |
* @var MusicRepository | |
*/ | |
private MusicRepository $musicRepository; | |
public function __construct( | |
StoredTokenValidator $storedTokenValidator, | |
TemplateRenderer $templateRenderer, | |
MusicRepository $musicRepository | |
) { | |
$this->validator = $storedTokenValidator; | |
$this->renderer = $templateRenderer; | |
$this->musicRepository = $musicRepository; | |
} | |
/** | |
* @return TemplateRenderer Retrieves the current Template Renderer instance | |
*/ | |
protected function getTemplateRenderer(): TemplateRenderer | |
{ | |
return $this->renderer; | |
} | |
/** | |
* @param Request $request | |
* @param array $vars | |
* @return Response | |
*/ | |
public function listAlbums(Request $request, array $vars) | |
{ | |
$validOrRedirect = $this->validateRequest($request, $this->validator); | |
if(!$validOrRedirect === true) { | |
return $validOrRedirect; | |
} | |
$template = 'music/AlbumsList.html.twig'; | |
$data = [ | |
'albums' => $this->musicRepository->getAlbums() | |
]; | |
$responseType = $request->header('Accepts'); | |
return $this->sendResponse($responseType, $template, $data); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Personly\Http\Traits; | |
use Personly\Framework\Rendering\TemplateRenderer; | |
use Symfony\Component\HttpFoundation\{JsonResponse, Response}; | |
trait RendersTemplates | |
{ | |
/** | |
* Returns a Template Renderer instance | |
* | |
* I debated whether to keep this as an abstract function or have it passed | |
* as a parameter for sendResponse() similar to the ValidatesCsrfTokens | |
* trait, but eventually settled on an abstract function to keep the number | |
* of arguments to sendResponse() down | |
* | |
* @return TemplateRenderer | |
*/ | |
protected abstract function getTemplateRenderer(): TemplateRenderer; | |
/** | |
* Sends the appropriate HTTP response based on the template and response type | |
* | |
* @param string $responseType The HTTP response type | |
* @param string $template The name of the template to use for non JSON responses | |
* @param array $responseData Any response data to be included | |
* @return Response|JsonResponse The correct HTTP Response | |
*/ | |
protected function sendResponse(string $responseType, string $template, array $responseData): Response | |
{ | |
if($responseType === 'application/json') { | |
return new JsonResponse($responseData); | |
} | |
$content = $this->getTemplateRenderer()->render($template, $responseData); | |
return new Response($content); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Personly\Http\Traits; | |
use Personly\Framework\Csrf\{StoredTokenValidator, Token}; | |
use Symfony\Component\HttpFoundation\{RedirectResponse, Request}; | |
trait ValidatesCsrfTokens | |
{ | |
/** | |
* Returns a redirect response to the previous page | |
* | |
* @return RedirectResponse | |
*/ | |
protected abstract function redirect(): RedirectResponse; | |
/** | |
* Validates a non-JSON request using a supplied CSRF token | |
* | |
* @param Request $request The incoming request to validate | |
* @param StoredTokenValidator $validator The validator instance used to validate the token | |
* @return bool|RedirectResponse True if validation is successful, or a Redirect response otherwise | |
*/ | |
protected function validateRequest(Request $request, StoredTokenValidator $validator) | |
{ | |
$this->responseType = $request->headers->get('Accept'); | |
if($this->responseType === 'application/json') { | |
return true; | |
} | |
$layoutToken = $request->headers->get('X-Layout'); | |
if(!$validator->validate('layout', new Token($layoutToken ?? ''))) { | |
return $this->redirect(); | |
} | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment