Welcome to Abdul Malik Ikhsan's Blog

Create templated 404 page in Slim 4 with CallableResolver

Posted in Slim 4, Tutorial PHP by samsonasik on November 8, 2019

In Slim 4 Skeleton, the 404 response handled in App\Application\Handlers\HttpErrorHandler::respond() which check against Slim\Exception\HttpNotFoundException. We can create a templated 404 page for it with utilize Slim\CallableResolver via callableResolver property which can resolve the callable handler. I assume that we are using Twig template engine, and already has setup of Twig service like in my previous post.

We can create a not found handler like the following in src/Application/Handlers/NotFoundHandler.php

<?php
// src/Application/Handlers/NotFoundHandler.php
declare(strict_types=1);

namespace App\Application\Handlers;

use Psr\Http\Message\ResponseInterface;
use Slim\Views\Twig;

use function compact;

class NotFoundHandler
{
    private $view;

    public function __construct(Twig $view)
    {
        $this->view = $view;
    }

    public function __invoke(
        ResponseInterface $response,
        string            $message
    ): ResponseInterface {
        return $this->view->render($response, '404.html.twig', compact('message'));
    }
}

We can create a view based on it like the following at templates/404.html.twig:

{# templates/404.html.twig #}

{% extends "layout.html.twig" %}
{% block title '404 - '~parent() %}

{% block body %}

{{ message }}

{% endblock %}

Now, in App\Application\Handlers\HttpErrorHandler::respond(), we can check when $exception instanceof HttpNotFoundException to make a self called resolved NotFoundHandler.

// src/Application/Handlers/HttpErrorHandler.php
// ...
    protected function respond(): Response
    {
        // ...
        if ($exception instanceof HttpNotFoundException) {
            $response = $this->responseFactory->createResponse($statusCode);
            return ($this->callableResolver->resolve(NotFoundHandler::class)(
                $response,
                $exception->getMessage()
            ));
        }
        // ...
    }
// ...

So, when the Slim\Exception\HttpNotFoundException thrown, it will shown the 404 page with brought the message passed into it like the following:

Bonus

We can create a handling against Request as well, eg: show 404 templated page only when request doesn’t has Accept: application/json or X-Requested-With:XmlHttpRequest header, so, we can modify like the following:

// src/Application/Handlers/HttpErrorHandler.php
// ...
    protected function respond(): Response
    {
        // ...
        if ($exception instanceof HttpNotFoundException) {            
            $isAppJsonAccept  = $this->request->getHeaderLine('Accept')           === 'application/json';
            $isXmlHttpRequest = $this->request->getHeaderLine('X-Requested-With') === 'XmlHttpRequest';

            if (! $isAppJsonAccept && ! $isXmlHttpRequest) {
                $response = $this->responseFactory->createResponse($statusCode);
                return ($this->callableResolver->resolve(NotFoundHandler::class)(
                    $response,
                    $exception->getMessage()
                ));
            }

            // already return early, no need else
            $error->setType(ActionError::RESOURCE_NOT_FOUND);
        }
        // ...
    }
// ...

So, for example, when called via ajax, it will show like the following:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: