[0013]プロジェクト作成後初期設定(マルチ認証含む)

■.env

この例のLalavelフレームワークのバージョンは5.5です。

DB設定、メール設定、その他環境設定等を環境に合わせ変更するため、プロジェクト直下にある.env・config/database.php・config/app.phpの修正を行います。

本番の場合はAPP_DEBUGをfalseへ

.env

              
APP_NAME=SYSTEMフェイリベル
APP_URL=https://www.failibere.com/

DB_CONNECTION=mysql
DB_HOST=write.XXX.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=mgmt_db
DB_USERNAME=sysfbsql
DB_PASSWORD=xxxxxx

MAIL_DRIVER=smtp
MAIL_HOST=failibere.sakura.ne.jp
MAIL_PORT=587
MAIL_USERNAME=info@failibere.jp
MAIL_PASSWORD=XXXXXXX
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=info@failibere.jp
MAIL_FROM_NAME=SYSTEMフェイリベル
              
            

app.php

              
'name' => env('APP_NAME', 'SYSTEMフェイリベル'),
'debug' => env('APP_DEBUG', true),
'url' => env('APP_URL', 'https://www.failibere.com'),
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'faker_locale' => 'ja_JP',
'log' => env('APP_LOG', 'daily'),
'log_level' => env('APP_LOG_LEVEL', 'debug'),
'log_max_files' => 7,
              
            

ご自身の環境に合わせて変更してください。

データベースを読み取りと書き込みでそれぞれ用意する場合、プログラムで切り替えを行ってきたが、lalavel5.5では読み取りと書き込みのDB変更が自動化されました!!

readとwriteを利用します。以下記述例です。

database.php

              
'mysql' => [
            'read' => [
                'host' => 'read.XXX.rds.amazonaws.com',
            ],
            'write' => [
                'host' => 'write.XXX.rds.amazonaws.com'
            ],
            'driver' => 'mysql',
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'mgmt_db'),
            'username' => env('DB_USERNAME', 'sysfbsql'),
            'password' => env('DB_PASSWORD', 'xxxxxx'),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_general_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
              
            

データベースが1つの場合は、以下記述例です。

database.php

              
'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', 'write.XXX.rds.amazonaws.com'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'mgmt_db'),
            'username' => env('DB_USERNAME', 'sysfbsql'),
            'password' => env('DB_PASSWORD', 'xxxxxx'),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
              
            

データベースが1つか2つかでhost部分だけ違います。

.envとconfigファイルを修正したのでキャッシュのクリアを実行します。ssh接続し、Lalavelのプロジェクト配下でartisanコマンドを実行します。

この例ではプロジェクト名が[sample]でプロジェクトがあるディレクトリは/home/failibere/www/sampleです。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#キャッシュクリア
% php artisan cache:clear
% php artisan config:cache
              
            

■LOG出力Dirに権限付与

LOG出力ディレクトリに権限がない場合は付与します。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#権限付与
% chmod -R 777 /home/failibere/www/sample/storage/logs
              
            

ご自身の環境に合わせて変更してください。

■マルチ認証(userとadmin)

userはMypage画面でお客様が利用する用、adminは管理画面で社員が利用する用でそれぞれログインする仕様です。userはartisanコマンドを実行し自動で作成を行います。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#auth自動作成
% php artisan make:auth
#adminsテーブルのマイグレーションファイル作成
% php artisan make:migration create_admins_table
              
            

make:authにより以下が自動で行われる。

・resources/viewsにlayoutsフォルダ、authフォルダ、home.blade.phpが追加

・routesのweb.phpが上書き

・app/Http/ControllersにHomeController.phpが追加

make:migrationにより以下が自動で行われる。

・database/migrationsにcreate_admins_table.phpが追加

※usersテーブルのカスタマイズが必要な場合は、\database\migrationsのマイグレーションを任意に修正してmigrateを実行します。

adminsテーブルは枠だけできているのでup関数内を任意で修正してmigrateを実行します。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#migrateを実行
% php artisan migrate
              
            

DB設定で設定したデータベースにadmins・migrations・password_resets・usersテーブルが作成されました。

usersテーブルをカスタマイズしている場合はUser.phpモデルの修正、adminsテーブルは新規でAdmin.phpモデルを作成します。

次にconfig/auth.phpを修正します。defaultsのguardをwebからuserに変更し、guardsにuserとadminを追加し、providersにadminsを追加し、passwordsにadminsを追加します。

auth.php

              
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'user',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        'admins' => [
            'provider' => 'admins',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];

              
            

configファイルを修正したのでキャッシュのクリアを実行します。キャッシュのクリアは当ページの内に掲載がありますので参照してください。

ログイン認証していない場合にuserかadminかどちらのログインページへリダイレクトするか設定するため、app/Exceptions/Handler.phpのunauthenticatedメソッド内に追加します。

Handler.php

              
 /**
 * 認証していない場合にガードを見てそれぞれのログインページへ飛ばず
 *
 * @param  \Illuminate\Http\Request $request
 * @param  AuthenticationException $exception
 * @return  \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
 */
public function unauthenticated($request, AuthenticationException $exception)
{
    if($request->expectsJson()){
        return response()->json(['message' => $exception->getMessage()], 401);
    }

    if (in_array('admin', $exception->guards(), true)) {
        return redirect()->guest(route('admin.login'));
    }

    return redirect()->guest(route('login'));
}
              
            

次にルーティングの設定をroutes/web.phpに追加します。

userは「Auth::routes();」この1行でOKだが、userもサイト直下ではなく『mypage』とフォルダ階層を入れたいのでそれぞれ指定します。

× https://XXX/login  → ○ https://XXX/mypage/login

web.php

              
//■認証なし S
Route::get('/', function () {
    //return view('welcome');
    return view('top');
});
//■認証なし E

//■UserのAuth系 S
//Auth::routes();ではなく個々に入れフォルダ階層を実現
Route::get('mypage/login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('mypage/login', 'Auth\LoginController@login');
Route::post('mypage/logout', 'Auth\LoginController@logout')->name('logout');

Route::get('mypage/register', 'Auth\RegisterController@showRegistrationForm')->name('register');
Route::post('mypage/register', 'Auth\RegisterController@register');

Route::get('mypage/password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('mypage/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('mypage/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('mypage/password/reset', 'Auth\ResetPasswordController@reset');
//■UserのAuth系 E

//■Userログイン後 S
Route::group(['prefix' => 'mypage','middleware' => 'auth:user'], function() {
    Route::get('/', 'HomeController@index')->name('home');
    Route::get('home', 'HomeController@index')->name('home');
});
//■Userログイン後 E

//■Admin 認証不要 S
Route::group(['prefix' => 'admin'], function() {
    Route::get('/',         function () { return redirect('/admin/home'); });
    Route::get('login',     'Admin\LoginController@showLoginForm')->name('admin.login');
    Route::post('login',    'Admin\LoginController@login');
});
//■Admin 認証不要 E

//■Adminログイン後 S
Route::group(['prefix' => 'admin', 'middleware' => 'auth:admin'], function() {
    Route::post('logout',   'Admin\LoginController@logout')->name('admin.logout');
    Route::get('/',      'Admin\HomeController@index')->name('admin.home');
    Route::get('home',      'Admin\HomeController@index')->name('admin.home');
});
//■Adminログイン後 E
              
            

admin用のコントローラ(Controller)を新規作成します。既にあるuser用を利用するので以下をコピーして新規作成を行います。

・/Controllers/HomeController.php copy→create /Controllers/Admin/HomeController.php

・/Controllers/Auth/LoginController.php copy→create /Controllers/Admin/LoginController.php

それぞれのadmin用のコントローラを修正します。

LoginController.php(Admin)

              
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var  string
     */
    protected $redirectTo = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return  void
     */
    public function __construct()
    {
        $this->middleware('guest:admin')->except('logout');
    }
    public function showLoginForm()
    {
        return view('admin.login');
    }

    protected function guard()
    {
        return Auth::guard('admin');
    }

    public function logout(Request $request)
    {
        Auth::guard('admin')->logout();
        $request->session()->flush();
        $request->session()->regenerate();

        return redirect('/admin/login');
    }
}
				
            

HomeController.php(Admin)

              
<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return  void
     */
    public function __construct()
    {
        $this->middleware('auth:admin');
    }

    /**
     * Show the application dashboard.
     *
     * @return  \Illuminate\Http\Response
     */
    public function index()
    {
        return view('admin.home');
    }
}
			  
            

user用のコントローラは既にありますが、サイト直下ではなく『mypage』とフォルダ階層を入れたため、それぞれのuser用も修正します。変更のある関数のみ掲載します。

LoginController.php(Auth)

              
protected $redirectTo = '/mypage/home';

public function logout(Request $request)
    {
        Auth::guard()->logout();
        $request->session()->invalidate();

        return redirect('/mypage');
    }
			 
            

RegisterController.php(Auth)

              
protected $redirectTo = '/mypage/home';
			 
            

ResetPasswordController.php(Auth)

              
protected $redirectTo = '/mypage/home';
			 
            

admin用のビュー(View)を新規作成します。既にあるuser用を利用するので以下をコピーして新規作成を行います。

・/resources/views/layouts/app.blade.php copy→create /resources/views/layouts/admin/app.blade.php

admin用のapp.blade.phpを修正します。

*route('register')は不要なのでliタグごと削除

*route(‘xxxxx’)をすべてroute(‘admin.xxxxx’)に変更

・/resources/views/auth/login.blade.php copy→create /resources/views/admin/login.blade.php

admin用のlogin.blade.phpを修正します。

*route('password.request')は不要なのでaタグごと削除

*1行目の @extends('layouts.app') を @extends('layouts.admin.app') に変更

*route(‘xxxxx’)をすべてroute(‘admin.xxxxx’)に変更

・/resources/views/home.blade.php copy→create /resources/views/admin/home.blade.php

*1行目の @extends('layouts.app') を @extends('layouts.admin.app') に変更

user用のビュー(View)もuserフォルダ配下するように移動します。

・/resources/views/layouts/app.blade.php 移動→ /resources/views/layouts/user/app.blade.php

移動したのでuser用のapp.blade.php参照元を修正します。以下対象ファイルです。

/resources/views/home.blade.php、/resources/auth/register.blade.php、

/resources/auth/login.blade.php、/resources/auth/passwords/reset.blade.php、

/resources/auth/passwords/email.blade.php

*1行目の @extends('layouts.app') を @extends('layouts.user.app') に変更

一通り完成です!!

■多言語対応

元々は英語のみの対応になっているので日本語対応のため、/resources/langにあるenフォルダをコピーし、同階層にjaを作成します。

enをコピーして作成した/resources/lang/ja/auth.phpを開き、日本語に変更します。

auth.php(ja)

              
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines are used during authentication for various
    | messages that we need to display to the user. You are free to modify
    | these language lines according to your application's requirements.
    |
    */

    'failed' => 'メールアドレスかパスワードが違います。再度入力してログインしてください。また初回ログインの方やパスワードをお忘れの方は、【※初回ログインの方・パスワードをお忘れの方はこちら】より設定画面へ進んでいただきますようお願いいたします。',
    'throttle' => 'ログイン試行回数が多いため一時的にロックしました。 :seconds 秒後に再度お試しください。',

];

			 
            

日本語や英語、その他言語の切り替えがある場合は多言語化しておくととても便利です。

Authで利用するログインページの「E-Mail Address」などベタ書きの項目名を多言語対応しようと思います。

enとjaのauth.phpに以下追加します。

auth.php(ja)

              
'login' => 'ログイン',
    'email' => 'メールアドレス',
    'pw' => 'パスワード',
    'con_pw' => '確認用パスワード※再度パスワードを入力',
    'remember_me' => '継続ログイン',
    'forgot_pw' => 'パスワードをお忘れですか?',
    'send_pw' => 'パスワードリセット用のリンクを送信します',
    'reset_pw' => 'パスワードリセット',
    'register' => '新規登録',
    'name' => '名前',
    		  
            

auth.php(en)

              
'login' => 'Login',
    'email' => 'E-Mail Address',
    'pw' => 'Password',
    'con_pw' => 'Confirm Password',
    'remember_me' => 'Remember Me',
    'forgot_pw' => 'Forgot Your Password?',
    'send_pw' => 'Send Password Reset Link',
    'reset_pw' => 'Reset Password',
    'register' => 'Register',
    'name' => 'Name',
    		  
            

auth.phpに追加した名前をViewにセットします。「E-Mail Address」ベタ書き部分を{{__('auth.email')}}に変更します。

「言語ファイル名.名前」になるので、auth.phpの'email'で日本語の場合、「メールアドレス」が表示されます。新しくファイルを用意する場合、名前は何でもよいので/resources/lang/en とja配下に新規ファイルを作成し、それぞれに追加します。

利用するときに切り替えたい場合は、App::setLocale("en");でロケールをセットします。ロケールの取得は$locale = App::getLocale();です。

コントローラーやViewのPHP内で$message = 'auth.email';で多言語表示のKeyを渡してView側で{{__($message)}}も取得可能です。

■認証条件追加

デフォルトでAuthの機能にある項目だけではなく、新しくテーブルに項目を追加し、更に追加した項目を認証の条件に追加しようと思います。今回は、user,admin共に使用可否フラグ(use_flg)を追加し、use_flgが1の場合のみログインできるようにします。

まずはuserとadminテーブルにuse_flgを追加します。カラムを追加したのでモデルも必要に応じて修正します。adminは現時点で登録画面がないのでLoginControllerに認証条件を追加するだけとなります。

認証条件の追加は「credentials」関数を利用します。追加関数のみ以下記載します。

LoginController.php(Admin)(User)

              
     /**
     * 認証時に認証に他の条件も追加
     *
     * @return  Response
     */
    public function credentials(Request $request)
    {
        $temporary = $request->only($this->username(), 'password');
        $temporary['use_flg'] = 1;

        return $temporary;
    }
			  
            

userは登録画面があるので登録画面でデフォルトが利用可になるよう項目を追加します。入力項目の場合はViewにも追加が必要です。

RegisterController.php(Auth)

              
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
            'use_flg' =>1,
        ]);
    }
              
            

この例ではuse_flgが1の場合のみ認証ですが、use_flgが1か2の場合に認証のように認証条件が複数ある場合は、$temporary['use_flg'] =[1,2];と指定できます。

■DBバックアップバッチ

データベースのバックアップを毎日とっておき、ある一定期間保持しておきたいのでバッチを作成します。

デイリーで7日分保持しておくようにしたいと思います。私の環境の場合、/var/www/vhost/にbatchフォルダを作成し、シェルを配置します。

流れは、ssh接続→batchフォルダ作成&権限付与→シェル作成→バックアップ配置用フォルダ作成→cron設定→cron実行となります。

              
#vhostへ移動
% cd /var/www/vhost
#batchフォルダ作成
% mkdir batch
#batchフォルダ権限付与
% chmod 777 batch
#バックアップ配置用フォルダ作成 ※[mgmt_db]はdb_nameから取得するのでデータベース名にします
% cd batch
% mkdir mgmt_db
% chmod 777 mgmt_db
#cron開く
% crontab -e
#cron編集モードで記述
% 0 3 * * * /bin/sh /var/www/vhost/batch/sql_dump.sh >> /dev/null 2>&1
			  
			

シェルは/var/www/vhost/batch/に「sql_dump」のファイル名(任意)にしてFTPで配置します。

sql_dump.sh

              
#!/bin/sh
workdir=`dirname ${0}`

db_ip=write.XXX.rds.amazonaws.com
db_name=mgmt_db
db_user=sysfbsql
db_pass=xxxxxx

filename=${workdir}/${db_name}/bk_`date +%Y%m%d`_${db_name}.sql
oldfile=${workdir}/${db_name}/bk_`date --date "7 days ago" +%Y%m%d`_${db_name}.sql

MYSQL_PWD=$db_pass mysqldump --host=$db_ip --user=$db_user $db_name > $filename
MYSQL_PWD=
chmod 700 $filename
rm -f $oldfile*
gzip $filename
			  
			

毎日AM3時にcronが実行され、/var/www/vhost/batch/mgmt_dbに「bk_[yyyymmdd]_mgmt_db.sql.gz」が作成され、7日分保持します。

注意事項としては、mysqlのバージョン次第ではmysqldumpの前に「--skip-column-statistics」の記述がないと動かないです。

■エラー画面共通化

エラー画面は管理しやすいため、それぞれ作成せずに共通化しようと思います。

/app/Exceptions/Handler.phpに、renderHttpException関数とHttpExceptionのuseを追加し、Exceptionを拾います。

Handler.php

              
              use Symfony\Component\HttpKernel\Exception\HttpException;

     /**
     * オリジナルデザインの共通エラー画面
     *
     *use Symfony\Component\HttpKernel\Exception\HttpException;を追加!!忘れるとエラーに
     * @param    \Symfony\Component\HttpKernel\Exception\HttpException $e
     * @return  \Illuminate\Http\Response
     */
    protected function renderHttpException(HttpException $e)
    {
        $sts = $e->getStatusCode();echo $sts;exit;
        // URLのディレクトリが /admin で始まる場合
        if( strpos(\Request::path(),'/admin/')!== false){
         // テストの間のみ、常にデバッグモードでLaravel標準エラー画面を表示
         config(['app.debug' => true]);
         return parent::convertExceptionToResponse($e);
        }

        //英語でテストしたいとき
        /*App::setLocale("en");*/

        $msg = $e->getMessage();
        if (! $msg) {
            switch ($sts) {
                case 400:
                    //Bad Request
                    $msg = 'err_msg.400';
                    break;
                case 401:
                    //認証に失敗しました
                    $msg = 'err_msg.401';
                    break;
                case 403:
                    //アクセス権がありません
                    $msg = 'err_msg.403';
                    break;
                case 404:
                    //該当アドレスのページはありません
                    $msg = 'err_msg.404';
                    break;
                case 408:
                    //タイムアウトです
                    $msg = 'err_msg.408';
                    break;
                case 414:
                    //リクエストURIが長すぎます
                    $msg = 'err_msg.414';
                    break;
                case 419:
                    //不正なリクエストです(csrfエラー)
                    $msg = 'err_msg.419';
                    break;
                case 500:
                    //Internal Server Error
                    $msg = 'err_msg.500';
                    break;
                case 503:
                    //メンテナンス中
                    $msg = 'err_msg.503';
                    break;
                default:
                    $msg = 'err_msg.other';
                    break;
            }
        }

        //ロードバランサーのIPが入るので考慮
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ipArray = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = $ipArray[0];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        //エラーログ保持
        Log::error(@$ip." ".@$_SERVER["REQUEST_URI"]." ".@$_SERVER["SCRIPT_NAME"]." ".@$_SERVER["HTTP_USER_AGENT"]." {{".$msg."}}");

        return response()->view("errors.common", ['msg' => $msg,'sts' => $sts]);
    }
			  
			

ロードバランサーのIPが入るので考慮とエラーログ保持については後程の「ログ」で説明します。

「use Symfony\Component\HttpKernel\Exception\HttpException;」を追加しないとエラーになるのでUseし、renderHttpException関数を追加しました。

renderHttpException関数内で呼び出しているViewを作成します。実際にさまざまなエラー時に表示される画面になります。この例では/resources/views/errors/common.blade.phpです。

common.blade.php

              
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <meta name="csrf-token" content="Q1HtX7YjEgF430TAdOq5Yx7gVwefmOQCWkVYlDjH">

    <title>Laravel error</title>

    <link href="https://www.failibere.com/css/app.css" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">

                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse" aria-expanded="false">
                        <span class="sr-only">Toggle Navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>

                    <a class="navbar-brand" href="https://www.failibere.com">
                        {{ config('app.name', 'Laravel') }}
                    </a>
                </div>

                <div class="collapse navbar-collapse" id="app-navbar-collapse">

                    <ul class="nav navbar-nav">
                         
                    </ul>

                    <ul class="nav navbar-nav navbar-right">

                                                    <li><a href="{{ route('login') }}">Login</a></li>
                            <li><a href="{{ route('register') }}">Register</a></li>
                                            </ul>
                </div>
            </div>
        </nav>
        <div class="container">
        	<h1 class="display-3">error</h1>
            <h2>{{__($msg)}}</h2>
            <hr class="my-4">
            <p>{!! nl2br(__('err_msg.fixed_msg')) !!}</p>
            <p class="lead"><a class="btn btn-primary" href="/" role="button">トップページへ戻る</a></p>
    	</div>
    </div>

    <script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
              
			

多言語化を利用するため、Handler.phpとcommon.blade.phpで「err_msg.XXX」のように呼び出すため、エラーメッセージ用の任意ファイル「err_msg」をenとjaそれぞれ作成します。

enは「/resources/lang/en」jaは「/resources/lang/ja」に作成します。

err_msg.php(en)

              
<?php

return [
    '400'    => 'Bad Request',
    '401'    => 'Unauthorixed',
    '403'    => 'Forbidden',
    '404'    => 'Not Found',
    '408'    => 'Request Time-out',
    '414'    => 'Request-URI Too Large',
    '419'    => 'Bad Request',
    '500'    => 'Internal Server Error',
    '503' => 'Service Unavailable',
    'other'    => 'An error has occurred',
    'fixed_msg' => 'Sorry.<br>please confirm'
];
              
			

err_msg.php(ja)

              
<?php

return [
    '400'    => 'リクエストにエラーがあります',
    '401'    => '認証に失敗しました',
    '403'    => 'アクセス権がありません',
    '404'    => '該当アドレスのページはありません',
    '408'    => 'タイムアウトが発生しました',
    '414'    => 'リクエストURIが長すぎるため拒否しました',
    '419'    => '不正なリクエストです',
    '500'    => 'Internal Server Error',
    '503' => 'メンテナンス中です',
    'other'    => 'エラーが発生しました',
    'fixed_msg' => 'ご不便をおかけして申し訳ございません。<br>ご確認ください'
];
              
			

langファイルには改行を入れることも出来ます。改行を入れると画面表示時に改行されます

■ログとメンテナンス画面

ログ保持時にはIPアドレス、REQUEST_URI、SCRIPT_NAME、HTTP_USER_AGENT、メッセージが出ていると後から見たときに分かりやすいので任意のタイミングでログを出力します。

IPアドレスはロードバランサーのIPが入るので考慮し、ログを出力するサンプルです。

              
        //ロードバランサーのIPが入るので考慮
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ipArray = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = $ipArray[0];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        //エラーログ保持
        Log::error(@$ip." ".@$_SERVER["REQUEST_URI"]." ".@$_SERVER["SCRIPT_NAME"]." ".@$_SERVER["HTTP_USER_AGENT"]." {{".$msg."}}");
              
			

任意のタイミングでログを出力する場合、以下さまざまあります。

              
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);
              
			

システム改修中に全体的にメンテナンス画面に切り替えたい時があるので考慮します。

そのためミドルウェア(Middleware)を作成するので、そこでアクセス毎のアクセスログも出力する仕様にします。

Lalavelのバージョンが5.7の場合はデフォルトで機能があるのでミドルウェア(Middleware)作成不要!便利!

ssh接続し、Lalavelのプロジェクト配下でミドルウェア作成のartisanコマンドを実行します。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#ミドルウェア作成
% php artisan make:middleware CheckForMaintenanceMode
              
            

作成した/app/Http/Middleware/CheckForMaintenanceMode.phpを以下のように修正します。

CheckForMaintenanceMode.php

              
<?php

namespace App\Http\Middleware;

use Closure;
use Log;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\Exceptions\MaintenanceModeException;
use Request;

/**
 * メンテナンスモード実行とアクセスログ保持.
 * メンテナンスモードを回避するIP入れや、アクセスログをLOGに出力を変更時はここを修正
 * @author  yoshi
 *
 */
class CheckForMaintenanceMode
{
    /**
     * The application implementation.
     *
     * @var  \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * Create a new middleware instance.
     *
     * @param    \Illuminate\Contracts\Foundation\Application  $app
     * @return  void
     */
    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    /**
     * Handle an incoming request.
     *
     * @param    \Illuminate\Http\Request  $request
     * @param    \Closure  $next
     * @return  mixed
     *
     * @throws  \Symfony\Component\HttpKernel\Exception\HttpException
     */
    public function handle($request, Closure $next)
    {//$this->middleware('auth.very_basic');
        //ロードバランサーのIPが入るので考慮
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ipArray = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = $ipArray[0];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        //アクセスログ保持
        Log::info(@$ip." ".@$_SERVER["REQUEST_URI"]." ".@$_SERVER["SCRIPT_NAME"]." ".@$_SERVER["HTTP_USER_AGENT"]." {{Acsess}}");

        //メンテナンスモードでもスルーするIP
        $allow = ['111.11.111.111'];
        if ($this->app->isDownForMaintenance()) {
            if (!in_array($ip, $allow)) {
                $data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);

                throw new MaintenanceModeException($data['time'], $data['retry'], $data['message']);
            }
        }

        return $next($request);
    }
}
              
			

メンテナンスモードでも許可するIPが必要な場合はallow変数にセットします。この例では「111.11.111.111」のIPアドレスのみメンテナンスモードでも開けるようになります。開発者のIPを許可するとメンテナンス中に確認ができます。

次に作成したミドルウェアを読み込むように/app/Http/Kernel.phpのmiddlewareを変更します。

「Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,」の部分を修正しています。

Kernel.php

              
protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];
              
			

これで準備は完了です!あとは実際にメンテナンス中切り替えは、ssh接続し、Lalavelのプロジェクト配下でartisanコマンドを実行します。メンテナンス中は503エラー画面が表示されます。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#メンテナンスにしたいとき
% php artisan down
#メンテナンスを解除するとき
% php artisan up
              
            

■Laravelデバッグバー

Lalavelデバッグバーがあると便利なので設定します。

composerを使ってインストールします。ssh接続し、Lalavelのプロジェクト配下でcomposerコマンドを実行します。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#-vvvのオプションをつけて進捗を確認します。バージョン指定しないとエラーになったのでバージョン指定
% composer require barryvdh/laravel-debugbar:~2.4 -vvv
#Lalavel5.7の場合はバージョン3.4でその後のconfig/app.phpの変更が不要
% composer require barryvdh/laravel-debugbar:~3.4 -vvv
              
            

次にconfig/app.phpのサービスプロバイダーとファサードに追加します。

app.php

              
          'providers' => [
        :
  Barryvdh\Debugbar\ServiceProvider::class,
  ],
  'aliases' => [
        :
  'Debugbar' => Barryvdh\Debugbar\Facade::class,
  ],
              
            

.envファイルのAPP_DEBUGがtrueでない場合はtureに変更し、キャッシュをクリアします。

              
#Lalavelのプロジェクト配下に移動
% cd /home/failibere/www/sample
#キャッシュクリア
% php artisan config:cache
              
            

完成です!!