From 48b031e19002da441101bcecf820db8b80d2e724 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 31 Jul 2020 16:22:32 -0400 Subject: [PATCH] Refactor/streamline View layer --- app/bootstrap.php | 8 +- src/AnimeClient/Controller.php | 17 +-- src/AnimeClient/Controller/Anime.php | 2 +- .../Controller/AnimeCollection.php | 2 +- src/AnimeClient/Controller/Manga.php | 9 +- src/Ion/View.php | 144 ------------------ src/Ion/View/HtmlView.php | 8 +- src/Ion/View/HttpView.php | 107 ++++++++++++- src/Ion/View/JsonView.php | 10 +- tests/AnimeClient/mocks.php | 5 +- tests/Ion/View/HttpViewTest.php | 2 +- tests/Ion/View/JsonViewTest.php | 8 +- tests/Ion/mocks.php | 14 -- 13 files changed, 132 insertions(+), 204 deletions(-) delete mode 100644 src/Ion/View.php diff --git a/app/bootstrap.php b/app/bootstrap.php index 206ed872..196457ed 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -30,7 +30,7 @@ use Aviat\Ion\Config; use Aviat\Ion\Di\Container; use Aviat\Ion\Di\ContainerInterface; use Psr\SimpleCache\CacheInterface; -use Laminas\Diactoros\{Response, ServerRequestFactory}; +use Laminas\Diactoros\ServerRequestFactory; use Monolog\Handler\RotatingFileHandler; use Monolog\Logger; @@ -96,7 +96,7 @@ return static function (array $configArray = []): Container { return $htmlHelper; }); - // Create Request/Response Objects + // Create Request Object $container->set('request', fn () => ServerRequestFactory::fromGlobals( $_SERVER, $_GET, @@ -104,7 +104,6 @@ return static function (array $configArray = []): Container { $_COOKIE, $_FILES )); - $container->set('response', fn () => new Response); // Create session Object $container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE)); @@ -122,7 +121,8 @@ return static function (array $configArray = []): Container { $listItem = new Kitsu\ListItem(); $listItem->setContainer($container); - $listItem->setJsonApiRequestBuilder($jsonApiRequestBuilder); + $listItem->setJsonApiRequestBuilder($jsonApiRequestBuilder) + ->setRequestBuilder($requestBuilder); $model = new Kitsu\Model($listItem); $model->setContainer($container); diff --git a/src/AnimeClient/Controller.php b/src/AnimeClient/Controller.php index b15ed34f..e012875d 100644 --- a/src/AnimeClient/Controller.php +++ b/src/AnimeClient/Controller.php @@ -69,12 +69,6 @@ class Controller { */ protected ServerRequestInterface $request; - /** - * Response object - * @var ResponseInterface $response - */ - public ResponseInterface $response; - /** * Url generation class * @var UrlGenerator @@ -118,7 +112,6 @@ class Controller { $this->cache = $container->get('cache'); $this->config = $container->get('config'); $this->request = $container->get('request'); - $this->response = $container->get('response'); $this->session = $session->getSegment(SESSION_SEGMENT); $this->url = $auraUrlGenerator; $this->urlGenerator = $urlGenerator; @@ -416,12 +409,12 @@ class Controller { * @throws DoubleRenderException * @return void */ - protected function outputJSON($data = 'Empty response', int $code = 200): void + protected function outputJSON($data, int $code): void { - (new JsonView($this->container)) + (new JsonView()) + ->setOutput($data) ->setStatusCode($code) - ->setOutput($data); - exit(); + ->send(); } /** @@ -433,7 +426,7 @@ class Controller { */ protected function redirect(string $url, int $code): void { - (new HttpView($this->container))->redirect($url, $code); + (new HttpView())->redirect($url, $code); exit(); } } diff --git a/src/AnimeClient/Controller/Anime.php b/src/AnimeClient/Controller/Anime.php index 6000fd3b..69bf9987 100644 --- a/src/AnimeClient/Controller/Anime.php +++ b/src/AnimeClient/Controller/Anime.php @@ -209,7 +209,7 @@ final class Anime extends BaseController { { $queryParams = $this->request->getQueryParams(); $query = $queryParams['query']; - $this->outputJSON($this->model->search($query)); + $this->outputJSON($this->model->search($query), 200); } /** diff --git a/src/AnimeClient/Controller/AnimeCollection.php b/src/AnimeClient/Controller/AnimeCollection.php index 96f4bafe..f737b2f6 100644 --- a/src/AnimeClient/Controller/AnimeCollection.php +++ b/src/AnimeClient/Controller/AnimeCollection.php @@ -82,7 +82,7 @@ final class AnimeCollection extends BaseController { { $queryParams = $this->request->getQueryParams(); $query = $queryParams['query']; - $this->outputJSON($this->animeModel->search($query)); + $this->outputJSON($this->animeModel->search($query), 200); } /** diff --git a/src/AnimeClient/Controller/Manga.php b/src/AnimeClient/Controller/Manga.php index 7e3c88e8..49ce7d53 100644 --- a/src/AnimeClient/Controller/Manga.php +++ b/src/AnimeClient/Controller/Manga.php @@ -210,7 +210,7 @@ final class Manga extends Controller { { $queryParams = $this->request->getQueryParams(); $query = $queryParams['query']; - $this->outputJSON($this->model->search($query)); + $this->outputJSON($this->model->search($query), 200); } /** @@ -336,12 +336,5 @@ final class Manga extends Controller { 'staff' => $staff, ]); } - - public function history(): void - { - $data = $this->model->getHistory(); - - $this->outputJSON($data); - } } // End of MangaController.php diff --git a/src/Ion/View.php b/src/Ion/View.php deleted file mode 100644 index a2c1cfb1..00000000 --- a/src/Ion/View.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @copyright 2015 - 2020 Timothy J. Warren - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version 5 - * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient - */ - -namespace Aviat\Ion; - -use Psr\Http\Message\ResponseInterface; - -use Aviat\Ion\Di\ContainerInterface; -use Aviat\Ion\Exception\DoubleRenderException; -use InvalidArgumentException; -use RuntimeException; - -/** - * Base view response class - */ -abstract class View - // partially - implements ViewInterface { - - use Di\ContainerAware; - - /** - * HTTP response Object - * - * @var ResponseInterface - */ - public ResponseInterface $response; - - /** - * If the view has sent output via - * __toString or send method - * - * @var boolean - */ - protected bool $hasRendered = FALSE; - - /** - * Constructor - * - * @param ContainerInterface $container - * @throws Di\Exception\ContainerException - * @throws Di\Exception\NotFoundException - */ - public function __construct(ContainerInterface $container) - { - $this->setContainer($container); - $this->response = $container->get('response'); - } - - /** - * Send output to client - */ - public function __destruct() - { - if ( ! $this->hasRendered) - { - $this->send(); - } - } - - /** - * Return rendered output as string. Renders the view, - * and any attempts to call again will result in a DoubleRenderException - * - * @throws DoubleRenderException - * @return string - */ - public function __toString(): string - { - if ($this->hasRendered) - { - throw new DoubleRenderException(); - } - $this->hasRendered = TRUE; - return $this->getOutput(); - } - - /** - * Add an http header - * - * @param string $name - * @param string|string[] $value - * @throws InvalidArgumentException - * @return ViewInterface - */ - public function addHeader(string $name, $value): ViewInterface - { - $this->response = $this->response->withHeader($name, $value); - return $this; - } - - /** - * Set the output string - * - * @param mixed $string - * @throws InvalidArgumentException - * @throws RuntimeException - * @return ViewInterface - */ - public function setOutput($string): ViewInterface - { - $this->response->getBody()->write($string); - - return $this; - } - - /** - * Append additional output. - * - * @param string $string - * @throws InvalidArgumentException - * @throws RuntimeException - * @return ViewInterface - */ - public function appendOutput(string $string): ViewInterface - { - return $this->setOutput($string); - } - - /** - * Get the current output as a string. Does not - * render view or send headers. - * - * @return string - */ - public function getOutput(): string - { - return (string)$this->response->getBody(); - } -} -// End of View.php \ No newline at end of file diff --git a/src/Ion/View/HtmlView.php b/src/Ion/View/HtmlView.php index 658c7c94..c798edf4 100644 --- a/src/Ion/View/HtmlView.php +++ b/src/Ion/View/HtmlView.php @@ -17,15 +17,18 @@ namespace Aviat\Ion\View; use Aura\Html\HelperLocator; +use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\Exception\ContainerException; use Aviat\Ion\Di\Exception\NotFoundException; +use Laminas\Diactoros\Response\HtmlResponse; use const EXTR_OVERWRITE; /** * View class for outputting HTML */ class HtmlView extends HttpView { + use ContainerAware; /** * HTML generator/escaper helper @@ -50,8 +53,11 @@ class HtmlView extends HttpView { */ public function __construct(ContainerInterface $container) { - parent::__construct($container); + parent::__construct(); + + $this->setContainer($container); $this->helper = $container->get('html-helper'); + $this->response = new HtmlResponse(''); } /** diff --git a/src/Ion/View/HttpView.php b/src/Ion/View/HttpView.php index 53397aae..808ddf8f 100644 --- a/src/Ion/View/HttpView.php +++ b/src/Ion/View/HttpView.php @@ -16,16 +16,32 @@ namespace Aviat\Ion\View; +use Aviat\Ion\ViewInterface; use Laminas\Diactoros\Response; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Aviat\Ion\Exception\DoubleRenderException; -use Aviat\Ion\View as BaseView; +use Psr\Http\Message\ResponseInterface; /** * Base view class for Http output */ -class HttpView extends BaseView { +class HttpView implements ViewInterface{ + + /** + * HTTP response Object + * + * @var ResponseInterface + */ + public ResponseInterface $response; + + /** + * If the view has sent output via + * __toString or send method + * + * @var boolean + */ + protected bool $hasRendered = FALSE; /** * Response mime type @@ -34,6 +50,91 @@ class HttpView extends BaseView { */ protected string $contentType = ''; + /** + * Constructor. + */ + public function __construct() + { + $this->response = new Response(); + } + + /** + * Send output to client + */ + public function __destruct() + { + if ( ! $this->hasRendered) + { + $this->send(); + } + } + + /** + * Return rendered output as string. Renders the view, + * and any attempts to call again will result in a DoubleRenderException + * + * @throws DoubleRenderException + * @return string + */ + public function __toString(): string + { + if ($this->hasRendered) + { + throw new DoubleRenderException(); + } + $this->hasRendered = TRUE; + return $this->getOutput(); + } + + /** + * Add an http header + * + * @param string $name + * @param string|string[] $value + * @return HttpView + */ + public function addHeader(string $name, $value): self + { + $this->response = $this->response->withHeader($name, $value); + return $this; + } + + /** + * Set the output string + * + * @param mixed $string + * @return HttpView + */ + public function setOutput($string): self + { + $this->response->getBody()->write($string); + + + return $this; + } + + /** + * Append additional output. + * + * @param string $string + * @return HttpView + */ + public function appendOutput(string $string): self + { + return $this->setOutput($string); + } + + /** + * Get the current output as a string. Does not + * render view or send headers. + * + * @return string + */ + public function getOutput(): string + { + return (string)$this->response->getBody(); + } + /** * Do a redirect * @@ -55,7 +156,7 @@ class HttpView extends BaseView { * @throws \InvalidArgumentException * @return HttpView */ - public function setStatusCode(int $code): HttpView + public function setStatusCode(int $code): self { $this->response = $this->response->withStatus($code) ->withProtocolVersion('1.1'); diff --git a/src/Ion/View/JsonView.php b/src/Ion/View/JsonView.php index 560ed8cb..c6b1525f 100644 --- a/src/Ion/View/JsonView.php +++ b/src/Ion/View/JsonView.php @@ -17,9 +17,6 @@ namespace Aviat\Ion\View; use Aviat\Ion\Json; -use Aviat\Ion\JsonException; -use Aviat\Ion\ViewInterface; -use function is_string; /** * View class to serialize Json @@ -37,12 +34,9 @@ class JsonView extends HttpView { * Set the output string * * @param mixed $string - * @throws \InvalidArgumentException - * @throws \RuntimeException - * @throws JsonException - * @return ViewInterface + * @return JsonView */ - public function setOutput($string): ViewInterface + public function setOutput($string): self { if ( ! is_string($string)) { diff --git a/tests/AnimeClient/mocks.php b/tests/AnimeClient/mocks.php index 270d49d2..fe7faa4b 100644 --- a/tests/AnimeClient/mocks.php +++ b/tests/AnimeClient/mocks.php @@ -10,7 +10,6 @@ use Aviat\AnimeClient\Model\{ }; use Aviat\Ion\{Enum, Friend, Json}; use Aviat\Ion\Transformer\AbstractTransformer; -use Aviat\Ion\View; use Aviat\Ion\View\{HtmlView, HttpView, JsonView}; // ----------------------------------------------------------------------------- @@ -83,7 +82,7 @@ trait MockViewOutputTrait { $props[$reflectProp->getName()] = $reflectProp->getValue($this); } - $view = new TestView($this->container); + $view = new TestView(); $friend = new Friend($view); foreach($props as $name => $val) { @@ -101,7 +100,7 @@ class MockUtil { } } -class TestView extends View { +class TestView extends HttpView { public function send(): void {} protected function output(): void { diff --git a/tests/Ion/View/HttpViewTest.php b/tests/Ion/View/HttpViewTest.php index ffa821e5..fbb6dc53 100644 --- a/tests/Ion/View/HttpViewTest.php +++ b/tests/Ion/View/HttpViewTest.php @@ -28,7 +28,7 @@ class HttpViewTest extends IonTestCase { public function setUp(): void { parent::setUp(); - $this->view = new TestHttpView($this->container); + $this->view = new TestHttpView(); $this->friend = new Friend($this->view); } diff --git a/tests/Ion/View/JsonViewTest.php b/tests/Ion/View/JsonViewTest.php index d6ba4bbe..d5c35130 100644 --- a/tests/Ion/View/JsonViewTest.php +++ b/tests/Ion/View/JsonViewTest.php @@ -24,26 +24,26 @@ class JsonViewTest extends HttpViewTest { public function setUp(): void { parent::setUp(); - $this->view = new TestJsonView($this->container); + $this->view = new TestJsonView(); $this->friend = new Friend($this->view); } public function testSetOutputJSON() { // Extend view class to remove destructor which does output - $view = new TestJsonView($this->container); + $view = new TestJsonView(); // Json encode non-string $content = ['foo' => 'bar']; $expected = json_encode($content); $view->setOutput($content); - $this->assertEquals($expected, $this->view->getOutput()); + $this->assertEquals($expected, $view->getOutput()); } public function testSetOutput() { // Directly set string - $view = new TestJsonView($this->container); + $view = new TestJsonView(); $content = '{}'; $expected = '{}'; $view->setOutput($content); diff --git a/tests/Ion/mocks.php b/tests/Ion/mocks.php index b5d8b05e..b464b79e 100644 --- a/tests/Ion/mocks.php +++ b/tests/Ion/mocks.php @@ -20,7 +20,6 @@ use Aviat\Ion\Enum; use Aviat\Ion\Exception\DoubleRenderException; use Aviat\Ion\Friend; use Aviat\Ion\Transformer\AbstractTransformer; -use Aviat\Ion\View; use Aviat\Ion\View\{HtmlView, HttpView, JsonView}; // ----------------------------------------------------------------------------- @@ -121,19 +120,6 @@ trait MockViewOutputTrait { } } -class TestView extends View { - public function send(): void - { - if ($this->hasRendered) - { - throw new DoubleRenderException(); - } - - $this->hasRendered = TRUE; - } - public function output() {} -} - class TestHtmlView extends HtmlView { protected function output(): void {