LoginSignup
27
32

More than 5 years have passed since last update.

Laravelでアクション実行前・後などで共通の処理をさせたいあなたに!コントローラーフィルターまとめ

Last updated at Posted at 2015-09-11

概要

Laravelでサービスを作っていると、このコントローラー内ではアクション実行前に
この値をチェックして、だめなら別のページにリダイレクトさせたいとかって時があると思います。
そんな時に便利なのがコントローラーフィルターです。
しかしなぜかLaravel公式サイトでは一瞬しか触れられておらず、詳細が不明。もったいない。
ただしLaravel 5.1からこのコントローラーフィルターは非推奨となり、
HTTPミドルウェアを使うように推奨しています。
@localdisk さんよりご指摘。ありがとうございます!)

ということで今回は、4系を使っている人向けにコントローラーフィルターについて書きます。
HTTPミドルウェアについては次回記載したいと思います!

寄り道

概要で述べたようなことを実現しようと考えた時に
コントローラーフィルターを知る前はこんな感じのプログラムを書いてました。

class AnimalController extends BaseController {

    private $blood_type;

    public function getA()
    {
        if ($this->blood_type == 'B')
        {
            return Redirect::to('/animal/g');
        }

        return View::make('animal.a');
    }

    public function getB()
    {
        if ($this->blood_type == 'B')
        {
            return Redirect::to('/animal/g');
        }

        return View::make('animal.b');
    }

    public function getG()
    {
        return View::make('animal.g');
    }
}

という感じで、割と良くない感じのプログラムが出来上がりました。

if ($this->blood_type == 'B')
{
    return Redirect::to('/animal/g');
}

この部分の処理をgetA()とgetB()のメソッドでは走らせたいわけですが、
同じプログラムを何箇所にも書くのはよろしくないなとなります。

なので、こうやろうとするわけです。

class AnimalController extends BaseController {

    private $blood_type;

    public function getA()
    {
        $this->__checkGorilla();

        return View::make('animal.a');
    }

    public function getB()
    {
        $this->__checkGorilla();

        return View::make('animal.b');
    }

    public function getG()
    {
        return View::make('animal.g');
    }

    private function __checkGorilla()
    {
        if ($this->blood_type == 'B')
        {
            return Redirect::to('/animal/g');
        }
    }
}

しかしこれでは全然リダイレクトしてくれません。
__checkGorilla()のなかでどんなにリダイレクトさせようとしても
結局getA()、getB()の中ではそこでreturnしているわけではないのでそのまま処理が続いてしまいます。
当たり前ですけど。

で、これを解決できないかということで、コントローラーフィルターを使おうというわけです。
前置き長くなりました。

使い方

まずはプログラムをば。

class AnimalController extends BaseController {

    private $blood_type;

    public function __construct()
    {
        parent::__construct();
        $this->beforeFilter('@filterGorilla');
    }

    public function filterGorilla()
    {
        if ($this->blood_type == 'B')
        {
            return Redirect::to('/animal/g');
        }
    }

    public function getA()
    {
        return View::make('animal.a');
    }

    public function getB()
    {
        return View::make('animal.b');
    }

    public function getG()
    {
        return View::make('animal.g');
    }
}

どうでしょう?ゴリラフィルターを作ってみました。

これをconstructで上記のように指定してあげると、
getA()、getB()、getG()のアクションが呼ばれる直前にfilterGorilla()が実行され、
この中でリダイレクトの処理を書くと、ちゃんとリダイレクトしてくれます!

この時の注意点はfilterGorillaという名前のフィルターを作った場合、指定するときは
$this->beforeFilter('@filterGorilla');
のように 先頭に@をつけてください。

また、 フィルターのメソッドはpublicで作る必要があります。
private function filterGorilla()
のようにprivateで作ってしまうと、filterGorilla()なんて知りませんと怒られます。

そして、もう一つ注意点、
上記のプログラムのままだと、getG()を呼び出した時もgetG()にまたリダイレクトしようとして、
結果、無限リダイレクトループに陥ります。
そんな時は、下で紹介する'フィルタリング対象'を参考にgetG()以外で
フィルターが有効になるようにするなどで、対処が必要です。

フィルタリング対象

フィルターを有効にしたいアクションがコントローラー内全てなら、
これまでのやり方で問題無いですが、このアクションの時だけ有効にとかやりたくなります。
以下を参考にどうぞ!

  • コントローラー内全てのアクション
$this->beforeFilter('@filterGorilla');
  • 指定したアクションのみ
$this->beforeFilter('@filterGorilla', [
    'only' => ['getA', 'getB']
]);
  • 指定したアクション以外全て
$this->beforeFilter('@filterGorilla', [
    'except' => ['getG']
]);
  • 指定したアクションタイプのみ
$this->beforeFilter('@filterGorilla', [
    'on' => ['post']
]);

POSTだけやGETだけなどの指定が可能になりますね。

  • 合わせ技
$this->beforeFilter('@filterGorilla', [
    'on' => ['get'],
    'only' => ['getA', 'getB'],
]);

色々組み合わせできます。

フィルタリングタイミング

  • アクション実行前にfilterGorillaを実行。
    $this->beforeFilter('@filterGorilla');
  • アクション実行後にfilterGorillaを実行。
    $this->afterFilter('@filterGorilla');

複数フィルターの指定

複数フィルターをセットしたいときは

$this->beforeFilter('@filterA');
$this->beforeFilter('@filterB');

という感じでフィルタリングしたい順番にフィルターをセットしていけばOKです。

ちなみ↓こんな感じで配列で一括で指定できるかなーと思いましたが、無理でした。怒られます。

$this->beforeFilter(['@filterA', '@filterB']);

フィルターの引数

フィルター内でRouteやRequestを受け取りたいって事あります。
受け取れます。

public function filterGorilla($route, $request)
{
    if ($request->input('name') == 'gorilla')
    {
        return Redirect::to('/animal/g');
    }
}

フィルターを書く場所

作ったフィルターを1つのコントローラー内でしか使わないなら
そのコントローラー内に書けばいいと思います。
では、いろんなコントローラーで使いたいならどうしましょう?
ぱっと思いつくのはBaseControllerにフィルターを書いちゃう方法です。
でも他にもっと適切な場所があります。
よくみるとappディレクトリの中にfilters.phpってファイルがあります。
そうです。ここに書けばいいんです。

Route::filter('filterGorilla', function()
{
    // フィルター内の処理いろいろ
});

// ちなみにここでRouteやRequestを受け取りたいときはこんな感じ
Route::filter('filterGorilla', function($route, $request)
{
    // フィルター内の処理いろいろ
});

こんな感じで書きます。

注意点としては filters.phpにフィルターを書いた場合は、
フィルター指定時に@マークを付けないでください。
つけると怒られます。

// 良い例
$this->beforeFilter('filterGorilla');

// 悪い例
$this->beforeFilter('@filterGorilla');

まとめ

フィルターは便利。
毎回何かのチェックさせるとか、何かの処理させるとかをいい感じにまとめられます。
でもやり過ぎると、いろんなフィルターに引っかかりまくって
逆にソースが見づらくなるので使いすぎに注意されたし。

27
32
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
32