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


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

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

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

```php
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');
	}
}
```

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

```php
if ($this->blood_type == 'B')
{
	return Redirect::to('/animal/g');
}
```
この部分の処理をgetA()とgetB()のメソッドでは走らせたいわけですが、
同じプログラムを何箇所にも書くのはよろしくないなとなります。

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

```php
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しているわけではないのでそのまま処理が続いてしまいます。
当たり前ですけど。

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

##使い方
まずはプログラムをば。

```php
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()以外で
フィルターが有効になるようにするなどで、対処が必要です。

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

* コントローラー内全てのアクション

```php
$this->beforeFilter('@filterGorilla');
```

* 指定したアクションのみ

```php
$this->beforeFilter('@filterGorilla', [
	'only' => ['getA', 'getB']
]);
```

* 指定したアクション以外全て

```php
$this->beforeFilter('@filterGorilla', [
	'except' => ['getG']
]);
```

* 指定したアクションタイプのみ

```php
$this->beforeFilter('@filterGorilla', [
	'on' => ['post']
]);
```
*POSTだけやGETだけなどの指定が可能になりますね。*

* 合わせ技

```php
$this->beforeFilter('@filterGorilla', [
	'on' => ['get'],
	'only' => ['getA', 'getB'],
]);
```

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

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

* アクション実行前にfilterGorillaを実行。

```php
	$this->beforeFilter('@filterGorilla');
```

* アクション実行後にfilterGorillaを実行。

```php
	$this->afterFilter('@filterGorilla');
```

##複数フィルターの指定

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

```php
$this->beforeFilter('@filterA');
$this->beforeFilter('@filterB');
```
という感じでフィルタリングしたい順番にフィルターをセットしていけばOKです。


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

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

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

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

##フィルターを書く場所

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

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

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

```
こんな感じで書きます。

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

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

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

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