Help us understand the problem. What is going on with this article?

Laravel5でメンテナンス画面を実装する

More than 3 years have passed since last update.

はじめに

メンテナンスモードを即時実行
resources/views/errors/503.blade.php の内容を表示する

php artisan down

メンテナンスモードを即時解除

php artisan up

以下の手順をする際、手違いでメンテナンスモードになったらupしてください

要件

  • 管理画面でメンテナンス画面に切り替えれるスイッチをつける
  • オンにすると全ページで503ページが表示される
  • ただし、許可したIPアドレスからのアクセスのみ、オンにしていても全ページが通常通り表示される
  • 管理画面でメンテナンス画面に表示するメッセージを入力できる
  • 入力した内容はメンテナンス画面で表示される

実装

必要ファイルの用意
touch resources/views/admin/maintenance/index.blade.php
php artisan make:controller MaintenanceController
php artisan make:model Maintenance
php artisan make:migrate create_maintenances
php artisan make:middleware CheckForMaintenanceMode
ルーティング
app/Http/routes.php
Route::resource('maintenance', 'MaintenanceController', ['only' => ['index', 'store']]);
View

※ここではLaravelCollectiveを使用しています。
使用していない場合はふつうにHTMLの書き方でformタグとか書いていけばいいです。
やってるのはラジオボタンとテキストエリアの作成です。

resources/views/admin/maintenance/index.blade.php
{{ Form::open(['url' => route('admin.maintenance.store')]) }}
{{ Form::radio('mode', 1, $maintenance->mode, ['class' => 'maintenance', 'id' => 'mode_1']) }}
{{ Form::label('mode_1', 'オン') !!}
{{ Form::radio('mode', 0, !$maintenance->mode, ['class' => 'maintenance', 'id' => 'mode_0']) }}
{{ Form::label('mode_0', 'オフ') !!}
@if($errors -> maintenance -> has('mode'))
{{ $errors -> maintenance -> first('mode') }}
@endif
{{ Form::textarea('body', $maintenance -> body, ['class' => 'maintenance', 'id' => 'body']) }}
@if($errors -> maintenance -> has('body'))
{{ $errors -> maintenance -> first('body') }}
@endif
{{ Form::submit('設定') }}
{{ Form::close() }}

普通にLaravelを構築してたら最初から503のViewファイルがあるはずです。なかったら作って

resources/views/errors/503.blade.php
<h1>メンテナンス中</h1>
<p>{!! nl2br($maintenance -> body) !!}</p>
Controller
app/Http/Controllers/MaintenanceController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Maintenance;

class MaintenanceController extends Controller
{
    public function index() {
        if(count(Maintenance::all()) == 0) {
            $maintenance = new Maintenance();
        }
        else {
            $maintenance = Maintenance::orderBy('updated_at','desc')->first();
        }

        $this -> mode();

        return view('admin.maintenance.index')->with(['maintenance' => $maintenance]);
    }

    public function store(Request $request)
    {
        if(count(Maintenance::all()) == 0) {
            $maintenance = new Maintenance();
        }
        else {
            $maintenance = Maintenance::orderBy('updated_at','desc')->first();
        }

        $rules = [
            'mode' => 'required|boolean',
            'body' => 'required|string',
        ];

        $validator = \Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            return back()->withErrors($validator,'maintenance');
        }

        $maintenance -> mode = $request -> mode;
        $maintenance -> body = $request -> body;
        $maintenance -> save();
        return redirect()->back();
    }

    public function mode()
    {
        $maintenance = Maintenance::orderBy('updated_at','desc')->first();

        if($maintenance -> mode == 1){
            return \Artisan::call('down');
        }
        else {
            return \Artisan::call('up');
        }
    }
}
Model

app/Maintenance.php

何もしない

Migrate
database/migrations/2016_10_19_100000_create_maintenances.php
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMaintenances extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('maintenances', function($table) {
            $table->increments('id');
            $table->boolean('mode');
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('maintenances');
    }
}

DBにテーブル情報を登録

php artisan migrate
Middleware

ここを参考にしました。
http://www.larajapan.com/2016/01/10/%E3%83%A1%E3%83%B3%E3%83%86%E3%83%8A%E3%83%B3%E3%82%B9%E7%94%BB%E9%9D%A2%E3%81%AE%E8%A3%8F%E5%8F%A3/

app/Http/Middleware/CheckForMaintenanceMode.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\HttpKernel\Exception\HttpException;

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
     */
    public function handle($request, Closure $next)
    {
        $allow = ['***.***.***.***']; // ここにアクセス許可したいIPアドレスを書く。複数の場合は['hoge','hoge']のように区切る

        if ($this->app->isDownForMaintenance()) {
            if (!in_array($request->getClientIp(), $allow)) {
                throw new HttpException(503);
            }
        }

        return $next($request);
    }
}
Kernel
app/Http/Kernel.php
    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
    ];
503画面に変数を持たせる

404画面や503画面などの例外処理のページは、Controllerでwithメソッドで
どうこうするみたいなやり方で変数を渡すことができません。(やり方あったら教えてください)

これらの例外処理のページは以下のやり方で変数を持たせることができます。

これを参考にしました。
http://stackoverflow.com/questions/39217513/how-to-pass-variables-to-http-error-layouts-in-laravel

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        view()->composer('errors.503', function($view)
        {
            $maintenance = \App\Maintenance::orderBy('updated_at','desc')->first();

            $view->with(['maintenance' => $maintenance]);
        });
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

おわり

おわり

おまけ

上記のやり方でメンテナンス画面の実装は完了しましたが、
許可したIPアドレスでは503ページが表示されない仕様なので、
実際にユーザーから503ページがどのように見えてるかが確認できません。
なので、503ページのプレビュー機能を追加します。

ルーティングを修正
app/Http/routes.php
Route::resource('maintenance', 'MaintenanceController', ['only' => ['index', 'store']]);
Route::get('maintenance/preview', function(){
    return view('errors.503');
});
Viewを修正

これをどっか適当なところに入れてください。これで終わり。簡単ですね

resources/views/admin/maintenance/index.blade.php
<a href="/admin/maintenance/preview" target="_blank">プレビュー</a>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした