エラー画面のカスタマイズ
エラーミドルウェアを登録することで、全てのエラーや例外処理をSlimが担うことになり、その結果Slimオリジナルのエラー画面が表示されるようになりました。ここから、この画面をカスタマイズする方法を紹介します。
エラーレンダラクラスによるエラー画面のカスタマイズ
Slimで独自のエラー画面を表示させるには、まず、エラー画面を表示させるクラスであるエラーレンダラクラスを作成する必要があります。それは例えば、リスト2のコードです。
<?php
namespace CodeZineSlim\FirstSlim\exceptions;
use Throwable;
use Psr\Container\ContainerInterface;
use Slim\Interfaces\ErrorRendererInterface;
class CustomErrorRenderer implements ErrorRendererInterface
{
private $container; // (1)
// (2)
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
// (3)
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
{
$twig = $this->container->get("view"); // (4)
$assign["errorMsg"] = "もう一度初めから操作してください。"; // (5)
$returnHtml = $twig->fetch("error.html", $assign); // (6)
return $returnHtml; // (7)
}
}
アプリケーションの中で、例外処理関係のクラスは1つのフォルダにまとめておいた方が良いでしょう。そこで、ここではclasses/exceptionsフォルダを作成し、その中にリスト2のエラーレンダラクラスを格納することにしています。
そのエラーレンダラクラスは、\Slim\Interfaces\ErrorRendererInterfaceインターフェースを実装する形で作成します。記述する必要があるメソッドは__invoke()です。メソッドシグネチャはリスト2の(3)にあるように、第1引数はThrowable型の$exceptionで、この第1引数に現在発生した例外インスタンスが渡されます。第2引数はbool型の$displayErrorDetailsで、引数名から分かるように、リスト1の(2)でエラーミドルウェアを登録する際に第1引数として渡した値がそのまま渡ってきます。
戻り値は、stringと文字列になっています。これは、エラー画面に表示させるHTML文字列を表します。注意しなければならないのは、これまでリクエスト処理の戻り値としてリターンしていたのはレスポンスオブジェクトですが、エラーレンダラクラスでは文字列です。したがって、Twigクラスのrender()メソッドは利用できません。代わりに、テンプレートを元に、HTML文字列を生成してくれるfetch()メソッドを利用します。それがリスト2の(6)です。その際、第1引数としてテンプレートファイルパス、第2引数としてテンプレート変数が格納された連想配列を渡します。
リスト2では、エラー画面テンプレートとして後で用意するerror.htmlを第1引数として、エラーメッセージのテンプレート変数としてリスト2の(5)で用意した連想配列$assignを、第2引数として渡しています。そのようにして生成されたHTML文字列を(7)のようにリターンすれば、独自のエラー画面として用意されたerror.htmlが表示されるようになります。
ただし、そのためにはコンテナからTwigインスタンスを取得する必要があります。さらに、コンテナインスタンスをこのエラーレンダラ内で用意しておくことも必要です。そこで、コンストラクタの引数としてコンテナインスタンスを受け取るようにしておき、それをプロパティとして格納しておきます。それが、リスト2の(1)と(2)です。
こうすることで、Slimはこのエラーレンダラクラスを利用する時に、自動的にコンテナインスタンスを渡してくれます。この仕組みのおかげで、__invoke()メソッド内では、(4)の通りいつでもコンテナ内のインスタンスを取得できるようになります。この考え方は、第6回で紹介した、コントローラクラスでのコンテナの扱いと同じです。
なお、リスト2の(6)で読み込むerror.htmlとして、例えば、リスト3のようなエラー画面を用意しておくことにします。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
<h1>Error</h1>
<section>
<h2>申し訳ございません。障害が発生しました。</h2>
<p>{{errorMsg}}</p>
</section>
</body>
</html>
エラーレンダラクラスの登録
前項で完成したエラーレンダラクラスを使って、実際に独自エラー画面を表示させるようにしましょう。そのためには、エラーミドルウェアにエラーレンダラクラスを登録する必要があります。それは、リスト1の(2)の続きとしてリスト4の(3)と(4)の2行を追記することです。
〜省略〜
$app = AppFactory::create();
$app->addRoutingMiddleware(); // (1)
$errorMiddleware = $app->addErrorMiddleware(true, true, true); // (2)
$errorHandler = $errorMiddleware->getDefaultErrorHandler(); // (3)
$errorHandler->registerErrorRenderer("text/html", CustomErrorRenderer::class); // (4)
$app->setBasePath("/firstslim/src/public");
〜省略〜
エラーレンダラクラスの登録は、エラーハンドラインスタンスに対して行います。そのため、(2)のエラーミドルウェアインスタンスである$errorMiddlewareから、getDefaultErrorHandler()メソッドを使ってエラーハンドラインスタンスを取得します。それが、リスト4の(3)です。
その戻り値であるエラーハンドラインスタンス$errorHandlerのメソッドregisterErrorRenderer()を使ってリスト2のエラーレンダラクラスを登録します。その際、第1引数としてエラーが発生したリクエストのコンテントタイプを、第2引数としてエラーレンダラクラスそのものを指定します。それがリスト4の(4)です。
ここでは画面表示を前提としているので、コンテントタイプとしてtext/htmlを指定し、エラーレンダラクラスとしてリスト2で作成したCustomErrorRendererを指定しています。ただし、クラスそのものの指定なので、CustomErrorRenderer::classという記述になります。
この状態で、これまでと同じエラーを発生させた場合、図4の画面になります。
無事、エラー画面のカスタマイズが完了したことになります。
