Simplify setup of rendering methods by putting them in a wrapper class
This commit is contained in:
parent
8e7b2a04fd
commit
fe1caffc0f
@ -18,9 +18,8 @@ use const Aviat\AnimeClient\{
|
||||
ALPHA_SLUG_PATTERN,
|
||||
DEFAULT_CONTROLLER,
|
||||
DEFAULT_CONTROLLER_METHOD,
|
||||
KITSU_SLUG_PATTERN,
|
||||
NUM_PATTERN,
|
||||
SLUG_PATTERN,
|
||||
NUM_PATTERN,
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -60,7 +59,7 @@ $base_routes = [
|
||||
'path' => '/anime/details/{id}',
|
||||
'action' => 'details',
|
||||
'tokens' => [
|
||||
'id' => KITSU_SLUG_PATTERN,
|
||||
'id' => SLUG_PATTERN,
|
||||
],
|
||||
],
|
||||
'anime.delete' => [
|
||||
@ -97,7 +96,7 @@ $base_routes = [
|
||||
'path' => '/manga/details/{id}',
|
||||
'action' => 'details',
|
||||
'tokens' => [
|
||||
'id' => KITSU_SLUG_PATTERN,
|
||||
'id' => SLUG_PATTERN,
|
||||
],
|
||||
],
|
||||
// ---------------------------------------------------------------------
|
||||
@ -191,13 +190,13 @@ $base_routes = [
|
||||
'character' => [
|
||||
'path' => '/character/{slug}',
|
||||
'tokens' => [
|
||||
'slug' => KITSU_SLUG_PATTERN,
|
||||
'slug' => SLUG_PATTERN,
|
||||
],
|
||||
],
|
||||
'person' => [
|
||||
'path' => '/people/{slug}',
|
||||
'tokens' => [
|
||||
'slug' => KITSU_SLUG_PATTERN,
|
||||
'slug' => SLUG_PATTERN,
|
||||
],
|
||||
],
|
||||
'default_user_info' => [
|
||||
@ -291,7 +290,7 @@ $base_routes = [
|
||||
'path' => '/{controller}/edit/{id}/{status}',
|
||||
'action' => 'edit',
|
||||
'tokens' => [
|
||||
'id' => KITSU_SLUG_PATTERN,
|
||||
'id' => SLUG_PATTERN,
|
||||
'status' => SLUG_PATTERN,
|
||||
],
|
||||
],
|
||||
|
@ -139,9 +139,6 @@ return static function (array $configArray = []): Container {
|
||||
// Create session Object
|
||||
$container->set('session', static fn () => (new SessionFactory())->newInstance($_COOKIE));
|
||||
|
||||
// Miscellaneous helper methods
|
||||
$container->set('util', static fn ($container) => new Util($container));
|
||||
|
||||
// Models
|
||||
$container->set('kitsu-model', static function (ContainerInterface $container): Kitsu\Model {
|
||||
$requestBuilder = new Kitsu\RequestBuilder($container);
|
||||
@ -174,10 +171,6 @@ return static function (array $configArray = []): Container {
|
||||
|
||||
return $model;
|
||||
});
|
||||
$container->set('anime-model', static fn ($container) => new Model\Anime($container));
|
||||
$container->set('manga-model', static fn ($container) => new Model\Manga($container));
|
||||
$container->set('anime-collection-model', static fn ($container) => new Model\AnimeCollection($container));
|
||||
$container->set('manga-collection-model', static fn ($container) => new Model\MangaCollection($container));
|
||||
$container->set('settings-model', static function ($container) {
|
||||
$model = new Model\Settings($container->get('config'));
|
||||
$model->setContainer($container);
|
||||
@ -185,14 +178,20 @@ return static function (array $configArray = []): Container {
|
||||
return $model;
|
||||
});
|
||||
|
||||
$container->setSimple('anime-model', Model\Anime::class);
|
||||
$container->setSimple('manga-model', Model\Manga::class);
|
||||
$container->setSimple('anime-collection-model', Model\AnimeCollection::class);
|
||||
|
||||
// Miscellaneous Classes
|
||||
$container->set('auth', static fn ($container) => new Kitsu\Auth($container));
|
||||
$container->set('url-generator', static fn ($container) => new UrlGenerator($container));
|
||||
$container->setSimple('util', Util::class);
|
||||
$container->setSimple('auth', Kitsu\Auth::class);
|
||||
$container->setSimple('url-generator', UrlGenerator::class);
|
||||
$container->setSimple('render-helper', RenderHelper::class);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Dispatcher
|
||||
// -------------------------------------------------------------------------
|
||||
$container->set('dispatcher', static fn ($container) => new Dispatcher($container));
|
||||
$container->setSimple('dispatcher', Dispatcher::class);
|
||||
|
||||
return $container;
|
||||
};
|
||||
|
@ -19,7 +19,8 @@ use Aura\Router\RouterContainer;
|
||||
use Aura\Session\SessionFactory;
|
||||
use Aviat\AnimeClient\API\{Anilist, CacheTrait, Kitsu};
|
||||
|
||||
use Aviat\AnimeClient\{Model, UrlGenerator, Util};
|
||||
use Aviat\AnimeClient\
|
||||
{Model, RenderHelper, UrlGenerator, Util};
|
||||
use Aviat\Banker\Teller;
|
||||
use Aviat\Ion\Config;
|
||||
use Aviat\Ion\Di\{Container, ContainerAware, ContainerInterface};
|
||||
@ -239,11 +240,11 @@ abstract class BaseCommand extends Command
|
||||
return $model;
|
||||
});
|
||||
|
||||
$container->set('auth', static fn ($container) => new Kitsu\Auth($container));
|
||||
|
||||
$container->set('url-generator', static fn ($container) => new UrlGenerator($container));
|
||||
|
||||
$container->set('util', static fn ($container) => new Util($container));
|
||||
// Miscellaneous Classes
|
||||
$container->setSimple('util', Util::class);
|
||||
$container->setSimple('auth', Kitsu\Auth::class);
|
||||
$container->setSimple('url-generator', UrlGenerator::class);
|
||||
$container->setSimple('render-helper', RenderHelper::class);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ trait ComponentTrait
|
||||
$helper = $container->get('html-helper');
|
||||
|
||||
$baseData = [
|
||||
'_' => $container->get('render-helper'),
|
||||
'auth' => $container->get('auth'),
|
||||
'escape' => $helper->escape(),
|
||||
'helper' => $helper,
|
||||
|
@ -82,6 +82,11 @@ class Controller
|
||||
*/
|
||||
protected array $baseData = [];
|
||||
|
||||
/**
|
||||
* The data bag for rendering
|
||||
*/
|
||||
protected RenderHelper $renderHelper;
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
*
|
||||
@ -99,14 +104,22 @@ class Controller
|
||||
$this->auth = $container->get('auth');
|
||||
$this->cache = $container->get('cache');
|
||||
$this->config = $container->get('config');
|
||||
$this->renderHelper = $container->get('render-helper');
|
||||
$this->request = $container->get('request');
|
||||
$this->session = $session->getSegment(SESSION_SEGMENT);
|
||||
$this->url = $auraUrlGenerator;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
|
||||
$helper = $container->get('html-helper');
|
||||
|
||||
$this->baseData = [
|
||||
'_' => $this->renderHelper,
|
||||
'auth' => $container->get('auth'),
|
||||
'component' => $container->get('component-helper'),
|
||||
'container' => $container,
|
||||
'config' => $this->config,
|
||||
'escape' => $helper->escape(),
|
||||
'helper' => $helper,
|
||||
'menu_name' => '',
|
||||
'message' => $this->session->getFlash('message'), // Get message box data if it exists
|
||||
'other_type' => 'manga',
|
||||
@ -193,11 +206,7 @@ class Controller
|
||||
protected function loadPartial(HtmlView $view, string $template, array $data = []): string
|
||||
{
|
||||
$router = $this->container->get('dispatcher');
|
||||
|
||||
if (isset($this->baseData))
|
||||
{
|
||||
$data = array_merge($this->baseData, $data);
|
||||
}
|
||||
$data = array_merge($this->baseData ?? [], $data);
|
||||
|
||||
$route = $router->getRoute();
|
||||
$data['route_path'] = $route !== FALSE ? $route->path : '';
|
||||
@ -223,6 +232,8 @@ class Controller
|
||||
"child-src 'self' *.youtube.com polyfill.io",
|
||||
];
|
||||
|
||||
$data = array_merge($this->baseData ?? [], $data);
|
||||
|
||||
$view->addHeader('Content-Security-Policy', implode('; ', $csp));
|
||||
$view->appendOutput($this->loadPartial($view, 'header', $data));
|
||||
|
||||
|
@ -119,7 +119,7 @@ final class AnimeCollection extends BaseController
|
||||
*/
|
||||
#[Route('anime.collection.add.get', '/anime-collection/add')]
|
||||
#[Route('anime.collection.edit.get', '/anime-collection/edit/{id}')]
|
||||
public function form(?int $id = NULL): void
|
||||
public function form(?string $id = NULL): void
|
||||
{
|
||||
$this->checkAuth();
|
||||
|
||||
|
170
src/AnimeClient/RenderHelper.php
Normal file
170
src/AnimeClient/RenderHelper.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* Hummingbird Anime List Client
|
||||
*
|
||||
* An API client for Kitsu to manage anime and manga watch lists
|
||||
*
|
||||
* PHP version 8.1
|
||||
*
|
||||
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @version 5.2
|
||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||
*/
|
||||
|
||||
namespace Aviat\AnimeClient;
|
||||
|
||||
use Aura\Html;
|
||||
use Aviat\AnimeClient\API\Kitsu\Auth;
|
||||
use Aviat\Ion\ConfigInterface;
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
use Aviat\Ion\Di\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* A container for helper functions and data for rendering HTML output
|
||||
*/
|
||||
class RenderHelper {
|
||||
use ContainerAware;
|
||||
|
||||
/**
|
||||
* The authentication object
|
||||
*/
|
||||
public Auth $auth;
|
||||
|
||||
/**
|
||||
* The global configuration object
|
||||
*/
|
||||
public ConfigInterface $config;
|
||||
|
||||
/**
|
||||
* HTML component helper
|
||||
*/
|
||||
public Html\HelperLocator $component;
|
||||
|
||||
/**
|
||||
* HTML escaper
|
||||
*/
|
||||
public Html\Escaper $escape;
|
||||
|
||||
/**
|
||||
* HTML render helper
|
||||
*/
|
||||
public Html\HelperLocator $h;
|
||||
|
||||
/**
|
||||
* Request object
|
||||
*/
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
/**
|
||||
* Aura url generator
|
||||
*/
|
||||
protected \Aura\Router\Generator $url;
|
||||
|
||||
/**
|
||||
* Url generation class
|
||||
*/
|
||||
private UrlGenerator $urlGenerator;
|
||||
|
||||
/**
|
||||
* Routes that don't require a second navigation level
|
||||
*/
|
||||
private static array $formPages = [
|
||||
'edit',
|
||||
'add',
|
||||
'update',
|
||||
'update_form',
|
||||
'login',
|
||||
'logout',
|
||||
'details',
|
||||
'character',
|
||||
'me',
|
||||
];
|
||||
|
||||
public function __construct(ContainerInterface $container) {
|
||||
$this->setContainer($container);
|
||||
|
||||
$this->auth = $container->get('auth');
|
||||
$this->component = $container->get('component-helper');
|
||||
$this->config = $container->get('config');
|
||||
$this->h = $container->get('html-helper');
|
||||
$this->escape = $this->h->escape();
|
||||
$this->request = $this->container->get('request');
|
||||
$this->url = $container->get('aura-router')->getGenerator();
|
||||
$this->urlGenerator = $container->get('url-generator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base url for css/js/images
|
||||
*/
|
||||
public function assetUrl(string ...$args): string
|
||||
{
|
||||
return $this->urlGenerator->assetUrl(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full default path for the list pages
|
||||
*/
|
||||
public function defaultUrl(string $type): string
|
||||
{
|
||||
return $this->urlGenerator->defaultUrl($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last url segment
|
||||
*/
|
||||
public function lastSegment(): string
|
||||
{
|
||||
return $this->urlGenerator->lastSegment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a full url from a path
|
||||
*/
|
||||
public function urlFromPath(string $path): string
|
||||
{
|
||||
return $this->urlGenerator->url($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a url from its name and parameters
|
||||
*/
|
||||
public function urlFromRoute(string $name, array $data = []): string
|
||||
{
|
||||
return $this->url->generate($name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current user authenticated?
|
||||
*/
|
||||
public function isAuthenticated(): bool
|
||||
{
|
||||
return $this->auth->isAuthenticated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to show the sub-menu
|
||||
*/
|
||||
public function isViewPage(): bool
|
||||
{
|
||||
$url = $this->request->getUri();
|
||||
$pageSegments = explode('/', (string) $url);
|
||||
|
||||
$intersect = array_intersect($pageSegments, self::$formPages);
|
||||
|
||||
return empty($intersect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the page is a page with a form, and
|
||||
* not suitable for redirection
|
||||
*
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function isFormPage(): bool
|
||||
{
|
||||
return ! $this->isViewPage();
|
||||
}
|
||||
}
|
@ -31,8 +31,7 @@ const NUM_PATTERN = '[0-9]+';
|
||||
* Eugh...url slugs can have weird characters
|
||||
* So...if it's not a forward slash, sure it's valid 😅
|
||||
*/
|
||||
const KITSU_SLUG_PATTERN = '[^\/]+';
|
||||
const SLUG_PATTERN = '[a-zA-Z0-9\- ]+';
|
||||
const SLUG_PATTERN = '[^\/]+';
|
||||
|
||||
// Why doesn't this already exist?
|
||||
const MILLI_FROM_NANO = 1000 * 1000;
|
||||
|
@ -22,16 +22,6 @@ use Psr\Log\LoggerInterface;
|
||||
*/
|
||||
class Container implements ContainerInterface
|
||||
{
|
||||
/**
|
||||
* Array of object instances
|
||||
*/
|
||||
protected array $instances = [];
|
||||
|
||||
/**
|
||||
* Map of logger instances
|
||||
*/
|
||||
protected array $loggers = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -41,9 +31,24 @@ class Container implements ContainerInterface
|
||||
/**
|
||||
* Array of container Generator functions
|
||||
*/
|
||||
protected array $container = []
|
||||
protected array $container = [],
|
||||
|
||||
/**
|
||||
* Array of object instances
|
||||
*/
|
||||
protected array $instances = [],
|
||||
|
||||
/**
|
||||
* Map of logger instances
|
||||
*/
|
||||
protected array $loggers = [],
|
||||
|
||||
/**
|
||||
* Map classes back to container ids, to make automatic
|
||||
* sub-dependency setup possible
|
||||
*/
|
||||
private array $classIdMap = [],
|
||||
) {
|
||||
$this->loggers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +84,7 @@ class Container implements ContainerInterface
|
||||
/**
|
||||
* Get a new instance of the specified item
|
||||
*
|
||||
* @param string $id - Identifier of the entry to look for.
|
||||
* @param string $id - Identifier or className of the entry to look for.
|
||||
* @param array|null $args - Optional arguments for the factory callable
|
||||
* @throws ContainerException - Error while retrieving the entry.
|
||||
* @throws NotFoundException - No entry was found for this identifier.
|
||||
@ -88,6 +93,11 @@ class Container implements ContainerInterface
|
||||
{
|
||||
if ($this->has($id))
|
||||
{
|
||||
if (array_key_exists($id, $this->classIdMap))
|
||||
{
|
||||
$id = $this->classIdMap[$id];
|
||||
}
|
||||
|
||||
// By default, call a factory with the Container
|
||||
$args = \is_array($args) ? $args : [$this];
|
||||
$obj = ($this->container[$id])(...$args);
|
||||
@ -112,6 +122,20 @@ class Container implements ContainerInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a common simple factory to the container
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $className
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function setSimple(string $id, string $className): ContainerInterface
|
||||
{
|
||||
$this->classIdMap[$className] = $id;
|
||||
|
||||
return $this->set($id, static fn (ContainerInterface $container) => new $className($container));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific instance in the container for an existing factory
|
||||
*
|
||||
@ -124,6 +148,12 @@ class Container implements ContainerInterface
|
||||
throw new NotFoundException("Factory '{$id}' does not exist in container. Set that first.");
|
||||
}
|
||||
|
||||
$className = get_class($value);
|
||||
if ( ! array_key_exists((string)$className, $this->classIdMap))
|
||||
{
|
||||
$this->classIdMap[get_class($value)] = $id;
|
||||
}
|
||||
|
||||
$this->instances[$id] = $value;
|
||||
|
||||
return $this;
|
||||
@ -137,7 +167,7 @@ class Container implements ContainerInterface
|
||||
*/
|
||||
public function has(string $id): bool
|
||||
{
|
||||
return array_key_exists($id, $this->container);
|
||||
return array_key_exists($id, $this->container) || array_key_exists($id, $this->classIdMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,14 @@ interface ContainerInterface
|
||||
*/
|
||||
public function set(string $id, callable $value): ContainerInterface;
|
||||
|
||||
/**
|
||||
* Add a common simple factory to the container
|
||||
*
|
||||
* @param string $id - The identifier for the factory
|
||||
* @param string $className - The class name of the factory
|
||||
*/
|
||||
public function setSimple(string $id, string $className): ContainerInterface;
|
||||
|
||||
/**
|
||||
* Set a specific instance in the container for an existing factory
|
||||
*/
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace Aviat\Ion\View;
|
||||
|
||||
use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
|
||||
use Aviat\Ion\Di\ContainerAware;
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use Throwable;
|
||||
use const EXTR_OVERWRITE;
|
||||
@ -26,11 +26,21 @@ class HtmlView extends HttpView
|
||||
{
|
||||
use ContainerAware;
|
||||
|
||||
/**
|
||||
* Data to send to every template
|
||||
*/
|
||||
protected array $baseData = [];
|
||||
|
||||
/**
|
||||
* Response mime type
|
||||
*/
|
||||
protected string $contentType = 'text/html';
|
||||
|
||||
/**
|
||||
* Whether to 'minify' the html output
|
||||
*/
|
||||
protected bool $shouldMinify = false;
|
||||
|
||||
/**
|
||||
* Create the Html View
|
||||
*/
|
||||
@ -42,19 +52,38 @@ class HtmlView extends HttpView
|
||||
$this->response = new HtmlResponse('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data to pass to every template
|
||||
*
|
||||
* @param array $data - Keys are variable names
|
||||
*/
|
||||
public function setBaseData(array $data): self
|
||||
{
|
||||
$this->baseData = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the html be 'minified'?
|
||||
*/
|
||||
public function setMinify(bool $shouldMinify): self
|
||||
{
|
||||
$this->shouldMinify = $shouldMinify;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a basic html Template
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function renderTemplate(string $path, array $data): string
|
||||
public function renderTemplate(string $path, array $data = []): string
|
||||
{
|
||||
$helper = $this->container->get('html-helper');
|
||||
$data['component'] = $this->container->get('component-helper');
|
||||
$data['helper'] = $helper;
|
||||
$data['escape'] = $helper->escape();
|
||||
$data['container'] = $this->container;
|
||||
$data = array_merge($this->baseData, $data);
|
||||
|
||||
return (function () use ($data, $path) {
|
||||
ob_start();
|
||||
extract($data, EXTR_OVERWRITE);
|
||||
include_once $path;
|
||||
@ -62,7 +91,12 @@ class HtmlView extends HttpView
|
||||
$buffer = ($rawBuffer === FALSE) ? '' : $rawBuffer;
|
||||
|
||||
// Very basic html minify, that won't affect content between html tags
|
||||
return preg_replace('/>\s+</', '> <', $buffer) ?? $buffer;
|
||||
if ($this->shouldMinify)
|
||||
{
|
||||
$buffer = preg_replace('/>\s+</', '> <', $buffer) ?? $buffer;
|
||||
}
|
||||
return $buffer;
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,8 @@ use InvalidArgumentException;
|
||||
|
||||
use Laminas\Diactoros\Response;
|
||||
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||
use PHPUnit\Framework\Attributes\CodeCoverageIgnore;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Stringable;
|
||||
use \Stringable;
|
||||
|
||||
/**
|
||||
* Base view class for Http output
|
||||
@ -172,7 +171,6 @@ class HttpView implements HttpViewInterface, Stringable
|
||||
* @throws DoubleRenderException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
#[CodeCoverageIgnore]
|
||||
protected function output(): void
|
||||
{
|
||||
if ($this->hasRendered)
|
||||
|
Loading…
Reference in New Issue
Block a user