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