Using routed PSR-15 RequestHandlerInterface in Slim 4
Slim 4 already support PSR-15, and we can use class implements Psr\Http\Server\RequestHandlerInterface
that produce response and register to the routes. First, we can install Slim-Skeleton
to get started:
$ composer create-project slim/slim-skeleton
After it, for example, we have the following AboutPage
class:
<?php // src/Application/Page/AboutPage.php declare(strict_types=1); namespace App\Application\Page; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Slim\Psr7\Response; class AboutPage implements RequestHandlerInterface { public function handle(ServerRequestInterface $request) : ResponseInterface { $response = new Response(); $response->getBody()->write('about page'); return $response; } }
We can register above to routes configuration in app/routes.php
as follow:
<?php // app/routes.php declare(strict_types=1); use App\Application\Page\AboutPage; use Slim\App; return function (App $app) { // ... $app->get('/about', AboutPage::class); };
To test, we can run PHP Development Server:
$ php -S localhost:8080 -t public
Then open it in the web browser as http://localhost:8080/about :
How about injecting service(s) to it?
We can supply the service(s) into its __construct
and it will automatically injected with the service(s), eg: we need to inject service named Psr\Log\LoggerInterface
which its service definition already registered in app/dependencies.php
:
<?php // src/Application/Page/AboutPage.php // ... use Psr\Log\LoggerInterface; // ... class AboutPage implements RequestHandlerInterface { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } // ... }
How about add template?
For example, we want to use slim/twig-view
package for it, we can first require it:
$ composer require slim/twig-view
After it, we can create cache
and templates
directories for it:
# create var/cache directory $ mkdir -p var/cache # ignore cached data from git repo (in case use git as version control) $ touch var/cache/.gitignore && (echo '*' && echo '!.gitignore') > var/cache/.gitignore # create templates directories $ mkdir -p templates/page
By above directory structure, we can register Slim\Views\Twig
service in app/dependencies.php
as follow:
<?php // app/dependencies.php // ... use Slim\Views\Twig; return function (ContainerBuilder $containerBuilder) { $containerBuilder->addDefinitions([ // ... Twig::class => function (ContainerInterface $c) { return new Twig(__DIR__ . '/../templates', [ 'cache' => __DIR__ . '/../var/cache', 'auto_reload' => true ]); }, // ... ]); };
After it, we can prepare the templates with the following structure:
├───templates │ │ layout.html.twig │ └───page │ about.html.twig
Next, apply templates/layout.html.twig
:
<!-- templates/layout.html.twig --> <!DOCTYPE html> <html> <head> <title>{% block title %}Slim App{% endblock %}</title> </head> <body> {% block body %}{% endblock %} </body> </html>
Next, apply templates/page/about.html.twig
:
<!-- templates/page/about.html.twig --> {% extends "layout.html.twig" %} {% block body %} About Page. {% endblock %}
Finally, in the AboutPage
class, we can inject Slim\Views\Twig
instance and use it to render the twig view as instance of Slim\Psr7\Response
:
<?php // src/Application/Page/AboutPage.php declare(strict_types=1); namespace App\Application\Page; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Slim\Psr7\Response; use Slim\Views\Twig; class AboutPage implements RequestHandlerInterface { private $view; public function __construct(Twig $view) { $this->view = $view; } public function handle(ServerRequestInterface $request) : ResponseInterface { return $this->view->render(new Response(), 'page/about.html.twig'); } }
How about getting request attribute value?
To do this, we need to enable it in the routing by apply Slim\Handlers\Strategies\RequestHandler
as its router invocation strategy, so, for example, we need to pass optional “id” parameter, we can do in app/routes
:
<?php // app/routes.php declare(strict_types=1); use App\Application\Page\AboutPage; use Slim\App; use Slim\Handlers\Strategies\RequestHandler; return function (App $app) { // ... $app->get('/about[/{id:[0-9]+}]', AboutPage::class) ->setInvocationStrategy(new RequestHandler(true)); };
We can also set default invocation strategy by apply it before register the route(s) that the pages implements RequestHandlerInterface
:
<?php // app/routes.php declare(strict_types=1); use App\Application\Page\AboutPage; use Slim\App; use Slim\Handlers\Strategies\RequestHandler; return function (App $app) { $routeCollector = $app->getRouteCollector(); // ... before setDefaultInvocationStrategy placement, // ... it will compatible with RequestResponse strategy $routeCollector->setDefaultInvocationStrategy(new RequestHandler(true)); // ... after setDefaultInvocationStrategy placement, // ... it will compatible only with selected default invocable strategy $app->get('/about[/{id:[0-9]+}]', AboutPage::class); };
So, from now on, we can get attribute in the handle()
method and passed parameter to the view:
<?php // src/Application/Page/AboutPage.php declare(strict_types=1); namespace App\Application\Page; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Slim\Psr7\Response; use Slim\Views\Twig; use Psr\Log\LoggerInterface; class AboutPage implements RequestHandlerInterface { private $view; public function __construct(Twig $view) { $this->view = $view; } public function handle(ServerRequestInterface $request) : ResponseInterface { $id = $request->getAttribute('id'); return $this->view->render( new Response(), 'page/about.html.twig', [ 'id' => $id ] ); } }
So, in the view, we can do:
<!-- templates/page/about.html.twig --> {% extends "layout.html.twig" %} {% block body %} About Page with id {{ id }} {% endblock %}
and when open http://localhost:8080/about/1 , we can get:
That’s it!
2 comments