RitoLabo

Laravelで独自の動的エラーページ(404/503 etc)を手早く作成する(多言語対応も)

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,5.5,5.4,5.3,ErrorPage,Localization,5.6

Laravelのエラーページは美しいと評価も高いです。例えば404ページはこんな感じで

Laravelデフォルト404ページ

「Sorry, the page you are looking for could not be found.」
「申し訳ありませんが、あなたが探しているページが見つかりませんでした。」
というテキストと共に、白背景にスタイリッシュなフォント「Raleway」が洗練された雰囲気を演出してくれています。

しかし、エラーページをデフォルトのまま使用するというのは通常の開発案件ではなかなか難しいものがあります。

そこで今回は、Laravelのエラーページをカスタマイズし、オリジナルのものを出力するまでを行います。そしてついでに、エラーページを多言語化対応もさせてみます。

アジェンダ
  1. 開発環境
  2. 独自エラーページの表示ロジックについて
  3. 独自エラーページの作成
  4. エラーを発生させエラーページを表示する
  5. エラーページの多言語対応
    1. 言語ファイルの作成
    2. 言語設定
    3. 多言語対応エラーページの作成
    4. 動作確認
    5. エラーページを英語に切り替える

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.2/7.1
  • Laravel

Laravelのバージョンについては、5.6/5.5/5.4/5.3にて動作確認済みです。

Laravelのルートディレクトリを「laravel/」とします。

独自エラーページの表示ロジックについて

実はLaravelでの独自エラーページの適用は非常に簡単です。

ビューを格納している laravel/resources/views 配下に errors ディレクトリを作成し、その中にHTTPステータスコードでファイル名を作成し配置すれば、自動的に独自エラーページが表示されます。

例えば、404ページであれば「404.blade.php」というファイル名で作成すれば、404発生時には自動的に独自で作成した404エラーページを表示してくれる。という事になります。

独自エラーページの作成

では実際にエラーページを作成します。今回は、こんな構成で作成しました。

laravel
├─ resources
│   ├─ views
│   │   ├─ errors
│   │   │   ├─ 400.blade.php
│   │   │   ├─ 401.blade.php
│   │   │   ├─ 403.blade.php
│   │   │   ├─ 404.blade.php
│   │   │   ├─ 500.blade.php
│   │   │   └─ 503.blade.php
│   │   │   └─ layouts
│   │   │   └─ base.blade.php

レイアウトとしてlayoutsディレクトリにエラーページのベースとなるHTML(base.blade.php)を作成し、それを各エラーページファイルに適用させていくという構成です。

laravel/resources/views/errors/layouts/base.blade.php
<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<meta charset="UTF-8">
<title>@yield('title')</title>
<style>
.error-wrap {
margin: auto;
padding: 5px 20px;
width: 300px;
display: inline-block;
border: 1px solid #dcdcdc;
box-shadow: 0px 0px 8px #dcdcdc;
}
h1 { font-size: 18px; }
p { margin-left: 10px; font-size: 12px; }
</style>
</head>
<body>
<div class="error-wrap">
<section>
<h1>@yield('title')</h1>
<p class="error-message">@yield('message')</p>
<p class="error-detail">@yield('detail')</p>
@yield('link')
</section>
</div>
</body>
</html>
400.blade.php
@extends('errors.layouts.base')

@section('title', '400 Bad Request')

@section('message', 'There is an error in the request.')
{{-- リクエストにエラーがあります。 --}}

@section('detail', 'This response indicates that the server can not understand the request because the syntax is invalid.')
{{-- このレスポンスは、構文が無効であるためサーバーがリクエストを理解できないことを示します。 --}}
401.blade.php
@extends('errors.layouts.base')

@section('title', '401 Unauthorized')

@section('message', 'certification failed.')
{{-- 認証に失敗しました。 --}}

@section('detail', 'Authentication is required to obtain the requested resource.')
{{-- リクエストされたリソースを得るために認証が必要です。 --}}
403.blade.php
@extends('errors.layouts.base')

@section('title', '403 Forbidden')

@section('message', 'You do not have access.')
{{-- あなたにはアクセス権がありません。 --}}

@section('detail', 'It indicates that the client does not have access to the content and the server is refusing to reply the appropriate response.')
{{-- クライアントがコンテンツへのアクセス権を持たず、サーバーが適切な応答への返信を拒否していることを示します。 --}}
404.blade.php
@extends('errors.layouts.base')

@section('title', '404 Not Found')

@section('message', 'The page of the corresponding address could not be found.')
{{-- 該当アドレスのページを見つける事ができませんでした。 --}}

@section('detail', 'The server indicates that it could not find the requested resource. A typo in the URL, or the page may have been moved or deleted. Please go back to the top page or search again.')
{{-- サーバーは要求されたリソースを見つけることができなかったことを示します。 URLのタイプミス、もしくはページが移動または削除された可能性があります。 トップページに戻るか、もう一度検索してください。 --}}

@section('link')
<p><a href="{{env('APP_URL')}}">to TOP&gt;&gt;</a></p>
@endsection
500.blade.php
@extends('errors.layouts.base')

@section('title', '500 Internal Server Error')
{{-- サーバ内部エラー --}}

@section('message', 'An error occurred inside the server.')
{{-- サーバー内部でエラーが発生しました。 --}}

@section('detail', 'It will be returned when there is a syntax error in the program, or there is an error in the setting. Please contact the administrator.')
{{-- プログラムに文法エラーがあったり、設定に誤りがあった場合などに返されます。管理者へ連絡してください。 --}}
503.blade.php
@extends('errors.layouts.base')

@section('title', '503 Service Unavailable')
{{-- サービス利用不可 --}}

@section('message', 'You can not access this page due to circumstances.')
{{-- このページへは事情によりアクセスできません。 --}}

@section('detail', 'Service is temporarily unusable due to overload or maintenance.')
{{-- サービスが一時的に過負荷やメンテナンスで使用不可能な状態です。 --}}

エラーを発生させエラーページを表示する

エラーページの作成が完了したので作業的にはこれで終わりですが、実際にエラーを発生させ、実際に独自エラーページが表示されるかを確認します。

具体的な方法としては、ルーティングでエラーを発生させ、エラーページを表示させます。

laravel/routes/web.php
// HTTPステータスコードを引数に、該当するエラーページを表示させる
Route::get('error/{code}', function ($code) {
abort($code);
});

上記は、HTTPステータスコードを引数に、該当するエラーページを表示させるルーティングです。

URLにHTTPステータスコード({code}の部分)を含め、それを引き数にabort()メソッドでエラーを発生させている。という流れになっています。

こうする事で、例えば
http://YOUR-DOMAIN/error/404
というURLでブラウザからアクセスすれば、404ページが表示される。というわけです。

http://YOUR-DOMAIN/error/400
HTTPステータスコード400エラーページ_英語
http://YOUR-DOMAIN/error/401
HTTPステータスコード401エラーページ_英語
http://YOUR-DOMAIN/error/403
HTTPステータスコード403エラーページ_英語
http://YOUR-DOMAIN/error/404
HTTPステータスコード404エラーページ_英語
http://YOUR-DOMAIN/error/500
HTTPステータスコード500エラーページ_英語
http://YOUR-DOMAIN/error/503
HTTPステータスコード503エラーページ_英語

作成した全ての独自エラーページが問題なく表示されました。

エラーページの多言語対応

先ほど作成したエラーページは全て英語での表記でした。これを、英語と日本語の2言語に対応させてみます。

言語ファイルの作成

英語と日本語にて多言語対応させる場合には、それぞれの言語ファイルが必要になるので作成します。

laravel/resources/lang/ 配下の enディレクトリとjaディレクトリ内にそれぞれ http_status_code.php というPHPファイルを作成します。(jaディレクトリが無い場合は作成してください)

laravel
├─ resources
│   ├─ lang
│   │   ├─ en
│   │   │   ├─ auth.php
│   │   │   ├─
http_status_code.php
│   │   │   ├─ pagination.php
│   │   │   ├─ passwords.php
│   │   │   └─ validation.php
│   │   └─ ja
│   │   ├─ auth.php
│   │   ├─
http_status_code.php
│   │   ├─ pagination.php
│   │   ├─ passwords.php
│   │   └─ validation.php

ベースとなるその他日本語言語ファイルは以下から入手できます。
[Github]Laravel 日本語言語ファイル

ファイルが作成できたら、それぞれを以下のように記述します。

laravel/resources/lang/en/http_status_code.php
<?php

return [
'400' => [
'title' => '400 Bad Request',
'message' => 'There is an error in the request.',
'detail' => 'This response indicates that the server can not understand the request because the syntax is invalid.',
],
'401' => [
'title' => '401 Unauthorized',
'message' => 'certification failed.',
'detail' => 'Authentication is required to obtain the requested resource.',
],
'403' => [
'title' => '403 Forbidden',
'message' => 'You do not have access.',
'detail' => 'It indicates that the client does not have access to the content and the server is refusing to reply the appropriate response.',
],
'404' => [
'title' => '404 Not Found',
'message' => 'The page of the corresponding address could not be found.',
'detail' => 'The server indicates that it could not find the requested resource. A typo in the URL, or the page may have been moved or deleted. Please go back to the top page or search again.',
],
'500' => [
'title' => '500 Internal Server Error',
'message' => 'An error occurred inside the server.',
'detail' => 'It will be returned when there is a syntax error in the program, or there is an error in the setting. Please contact the administrator.',
],
'503' => [
'title' => '503 Service Unavailable',
'message' => 'You can not access this page due to circumstances.',
'detail' => 'Service is temporarily unusable due to overload or maintenance.',
],
];
laravel/resources/lang/ja/http_status_code.php
<?php

return [
'400' => [
'title' => '400 Bad Request',
'message' => 'リクエストにエラーがあります',
'detail' => 'このレスポンスは、構文が無効であるためサーバーがリクエストを理解できないことを示します。',
],
'401' => [
'title' => '401 Unauthorized',
'message' => '認証に失敗しました',
'detail' => 'リクエストされたリソースを得るために認証が必要です。',
],
'403' => [
'title' => '403 Forbidden',
'message' => 'あなたにはアクセス権がありません',
'detail' => 'クライアントがコンテンツへのアクセス権を持たず、サーバーが適切な応答への返信を拒否していることを示します。',
],
'404' => [
'title' => '404 Not Found',
'message' => '該当アドレスのページを見つける事ができませんでした',
'detail' => 'サーバーは要求されたリソースを見つけることができなかったことを示します。 URLのタイプミス、もしくはページが移動または削除された可能性があります。 トップページに戻るか、もう一度検索してください。',
],
'500' => [
'title' => '500 Internal Server Error',
'message' => 'サーバー内部でエラーが発生しました',
'detail' => 'プログラムに文法エラーがあったり、設定に誤りがあった場合などに返されます。管理者へ連絡してください。',
],
'503' => [
'title' => '503 Service Unavailable',
'message' => 'このページへは事情によりアクセスできません',
'detail' => 'サービスが一時的に過負荷やメンテナンスで使用不可能な状態です。',
],
];

これで、エラーページ用の言語ファイルは完成です。

言語設定

続いては、エラーページの言語設定を行います。

基本的に言語の切り替えというのは

  • 設定情報に基づき言語設定を固定する
  • 閲覧者の言語設定などから動的に設定する

の2パターンありますが、今回は前者の固定設定で進めていきます。

また、その際にベースの言語設定を用いても良いのですが、エラーページのみの言語切り替えが行えるように設定していきます。(その理由は後ほど)

それではエラーページ用の言語設定を行っていきます。言語に関する設定ファイルを開いて、以下を追記します。

laravel/config/app.php
// 基本言語設定
'locale' => 'ja',
// エラーページ用の言語設定
'http_status_code_locale' => 'ja',

前者はもともとある言語の設定ですが、その下に新たに「http_status_code_locale」という項目を追記しています。

上記の設定では、エラーページは「ja」つまり日本語に設定しました。

多言語対応エラーページの作成

最後に、エラーページを多言語化に対応した形で作成します。
多言語化前の形で作成したエラーページがある場合には、そこを更新する形でもOKです。

400.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.400.title"))

@section('message', __("http_status_code.400.message"))

@section('detail', __("http_status_code.400.detail"))
401.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.401.title"))

@section('message', __("http_status_code.401.message"))

@section('detail', __("http_status_code.401.detail"))
403.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.403.title"))

@section('message', __("http_status_code.403.message"))

@section('detail', __("http_status_code.403.detail"))
404.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.404.title"))

@section('message', __("http_status_code.404.message"))

@section('detail', __("http_status_code.404.detail"))

@section('link')
<p><a href="{{env('APP_URL')}}">to TOP&gt;&gt;</a></p>
@endsection
500.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.500.title"))

@section('message', __("http_status_code.500.message"))

@section('detail', __("http_status_code.500.detail"))
503.blade.php
<?php App::setLocale(config('app.http_status_code_locale')); ?>

@extends('errors.layouts.base')

@section('title', __("http_status_code.503.title"))

@section('message', __("http_status_code.503.message"))

@section('detail', __("http_status_code.503.detail"))

主要部分を解説します。

<?php App::setLocale(config('app.http_status_code_locale')); ?>

ここで、HTTPステータスコード用の言語設定を読み込み、用いる言語を指定しています。

laravel/config/app.php では、日本語を指定しましたので、日本語での表示を指定したという事になります。

@section('title', __("http_status_code.400.title"))

ここでどの言語ファイルの、どの文面を使うかを指定しています。上記では、「http_status_code」ファイルの、$配列['400']['title']を指定しています。

他のmessageやdetailに関しても、この方式で文面を割り当てています。

動作確認

これで全ての実装が完了したので、実際に表示を確認します。

http://YOUR-DOMAIN/error/400
HTTPステータスコード400エラーページ_日本語
http://YOUR-DOMAIN/error/401
HTTPステータスコード401エラーページ_日本語
http://YOUR-DOMAIN/error/403
HTTPステータスコード403エラーページ_日本語
http://YOUR-DOMAIN/error/404
HTTPステータスコード404エラーページ_日本語
http://YOUR-DOMAIN/error/500
HTTPステータスコード500エラーページ_日本語
http://YOUR-DOMAIN/error/503
HTTPステータスコード503エラーページ_日本語

問題なく日本語でエラーページが表示されました。

エラーページを英語に切り替える

ここで、「日本語だとなんだかしっくりこない」「英語の方がかっこいい」のような事を思った方もいるはずです。

その場合には、laravel/config/app.php の「http_status_code_locale」を英語「en」に変更すれば、表示は英語に切り替わります。

「フォームなどのエラーメッセージに関しては日本語で表示させたいけれど、エラーページに関してはやっぱり英語の方が良い」
そういった場合も想定して、言語設定に関しては、ベースのものとエラーページ用は分けておくと良いと思います。わざわざ設定変数を分けたのには、こういう理由がありました。

まとめ

以上で作業は完了となります。確認が終わったら、エラーページ確認用のルーティングは削除しておきましょう。

構築するWEBアプリケーションによってはここまで作りこむ必要もやはり出てくると思いますが、エラーページというのはエラーが起きない限りは現れないものでもあるので、できるだけ最少工数で実現したいですよね。

Laravelでは独自エラーページの作成も簡単に行えるので是非試してみてください。

尚、今回のサンプルソースは以下よりダウンロードできます。