The goal of this update is to implement PSR-15 handlers into Dotkernel Light. There are several advantages to using handlers, which we will explore below.

We strive to keep our applications up-to-date with the recommended design guidelines. This ensures that we keep the applications secure, while also implementing standards widely adopted by the PHP community.

What makes handlers better than controllers?

It’s all fine and good if you have one large controller file with several actions, but handlers split the code into manageable chunks that make your life a lot easier in the long run. This follows the first of the SOLID principles. SOLID stands for:

  • S – Single-responsibility Principle
  • O – Open-closed Principle
  • L – Liskov Substitution Principle
  • I – Interface Segregation Principle
  • D – Dependency Inversion Principle

We are focusing on that first S in the SOLID acronym. Instead if having multiple actions we would normally include in controllers, with single-responsibility the handlers separate each action into its own class. This makes handlers easier to maintain, refactor and test.

Expanding your application is also helped by handlers. Rather than searching for a place to fit in that new code, simply create a handler to keep thing orderly. Your future self or the programmer that takes over from you will thank you for it.

Refactoring is always easier if you don’t have to worry about edge cases that are unexpectedly not supported because of an error on your part. Simpler code means refactoring steps are more obvious.

Writing tests for actions that have multiple branches tends to take a lot of time. Since handlers only deal with a single action, your tests only have to inject or bind mocks for that specific action.

How to implement the Page handler

When it comes to Dotkernel Light, replacing controllers with handlers means we don’t need the dot-controller package any more. Go ahead and remove it, along with any Controllers you may have.

Below we are going to detail how to set up the GetPageViewHandler. If you already have Controllers in your application, you will have to repeat the steps below for each controller. Based on your application, you may have to split your actions over multiple Handlers.

For Dotkernel Light we were able to combine the functionality of most of the old Controller’s actions under a single Handler, since the actions performed a single task – displaying static content. The only exception is IndexHandler.php which we opted to leave separate, but its setup is similar to GetPageViewHandler.php.

Handlers use the ConfigProvider in each module to map factories under getDependencies(). You should already have delegators and aliases, but make sure to add GetPageViewHandler under the factories key and remove any reference to PageController.

public function getDependencies(): array
{
    return [
        'delegators' => [
            Application::class => [
                RoutesDelegator::class,
            ],
        ],
        'factories'  => [
            GetPageViewHandler::class => GetPageViewHandlerFactory::class,
            PageService::class        => PageServiceFactory::class,
        ],
        'aliases'    => [
            PageServiceInterface::class => PageService::class,
        ],
    ];
}

GetPageViewHandlerFactory.php adds the template as a dependency, making it available in the Handler. We don’t need the PageControllerFactory.php file, so go ahead and delete it.

<?php

declare(strict_types=1);

namespace Light\Page\Factory;

use Light\Page\Handler\GetPageViewHandler;
use Mezzio\Template\TemplateRendererInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

use function assert;

class GetPageViewHandlerFactory
{
    /**
     * @param class-string $requestedName
     * @throws NotFoundExceptionInterface
     * @throws ContainerExceptionInterface
     */
    public function __invoke(ContainerInterface $container, string $requestedName): GetPageViewHandler
    {
        $template = $container->get(TemplateRendererInterface::class);
        assert($template instanceof TemplateRendererInterface);

        return new GetPageViewHandler($template);
    }
}

GetPageViewHandler.php determines the template file name from the route name and displays it. No dynamic elements are included, since we are dealing only with static pages right now. If you haven’t already, delete PageController.php.

<?php

declare(strict_types=1);

namespace Light\Page\Handler;

use Laminas\Diactoros\Response\HtmlResponse;
use Mezzio\Router\RouteResult;
use Mezzio\Template\TemplateRendererInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class GetPageViewHandler implements RequestHandlerInterface
{
    public function __construct(
        protected TemplateRendererInterface $template,
    ) {
    }

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $template = $request->getAttribute(RouteResult::class)->getMatchedRouteName();

        return new HtmlResponse(
            $this->template->render($template)
        );
    }
}

And that’s it! These are the bare essentials to get yourself started with handlers for a website that displays static pages.

Additional resources

PSR-15

Dotkernel Light

While the PR for replacing controllers with handlers might not be as focused on the task, because it implements a few other bells and whistles, it’s worth reviewing since it includes all the coding details.

Mezzio features

Single Action Handlers in PHP Frameworks


Looking for PHP, Laminas or Mezzio Support?

As part of the Laminas Commercial Vendor Program, Apidemia offers expert technical support and services for:

  • Modernising Legacy Applications
  • Migration from any version of Zend Framework to Laminas
  • Migration from legacy Laminas API Tools (formerly Apigility) to Dotkernel API
  • Mezzio and Laminas Consulting and Technical Audit
  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>