概要
LaravelのFormRequestクラスの使い方と機能のまとめです.
環境
Laravel 5.5
準備
まずは次のコマンドでFormRequestクラスを生成します.
artisan make:request TestRequest
ファイルは,app/Http/Requests/TestRequest.php に作成されます.作成されたファイルで
public function authorize()
{
return true; // 元はfalse
}
に変更することで使用できます.
使い方は,使用するコントローラなどで
use App/Http/Requests/TestRequest;
class ExampleController extends Controller
{
public function testMethod(TestRequest $request) {
/* 処理 */
}
}
とすることで使用できます.
機能
バリデーション
ruleメソッドに連想配列・文字列で記述します.
public function rules()
{
return [
'price' => ['required' , 'integer' , 'min:1'],
'discount' => 'required|integer',
];
}
このように,バリデーションルールを配列または文字列で指定できますが,配列で指定することをおすすめします.理由は後々の拡張性や可読性,一部ルールでは配列での指定が必須なので後々のことを考えると配列を使用したほうが良いからです.
使えるバリデーションルールはドキュメントにあります.
自分で作成したバリデーションを使う
Laravelでは用意してあるバリデーションルールを組み合わせれば,大抵のバリデーションができますが,それでも特殊なバリデーションを行いたいことが多々あります.その場合は
public function withValidator($validator) {
/* ここにバリデーションを書く */
}
を追加します.ドキュメントによれば
「このメソッドは完全に構築されたバリデータを受け取るため、バリデーションルールが実際に評価される前に、バリデータのどんなメソッドも呼び出すことができます。」
とあり,このメソッドではruleで指定したバリデーションルールが適用される前のバリデータにアクセスできます.
使い方は,例えば「割引額(discount)が支払金額(price)よりも小さい」ことをバリデートする場合は,
public function withValidator($validator) {
$validator->after(function ($validator) {
if($this->filled(['discount','price'])) {
if($this->input('discount') >= $this->input('price')) {
$validator->errors()->add('割引額', '合計金額より小さい額を指定してください.');
}
}
});
}
とします.ルールメソッドとは異なり,直接メッセージや項目名を指定します.もし,rules
で指定したチェックを通過した場合のみ追加チェックを行いたい場合は
public function withValidator($validator) {
if ($validator->fails()) return;
}
とすることで分岐することができます.
エラーメッセージの変更
エラー時の各項目のメッセージを
public function messages()
{
return [
'discount.integer' => '整数で指定してください.',
'price.integer' => '整数で指定してくださいませ.',
];
}
で変更できます.キーは「項目名 . ルール」となっています.
エラーメッセージの項目名の変更
ルールメソッドで指定したバリデーションで,エラーが返ってきたときにのメッセージの項目名を
public function attributes()
{
return [
'discount' => '割引額',
'price' => '合計金額',
];
}
で変更できます.
バリデーションデータの追加
apiなどではjson形式でデータを送信することがありますが,このようなときにデータを展開してバリデーションにかけるには,
//オーバーライド
protected function validationData()
{
$data = json_decode($this->input('json'), true);
return $this->all() + ['data' => $data];
}
とします.バリデーションは
public function rules()
{
return [
'data.price' => ['required' , 'integer' , 'min:1'],
'data.discount' => 'required|integer',
];
}
でかけれます.ネストにしたくない場合は,'data' => $data
を$data
にしたり
protected function validationData()
{
$inputs = $this->all();
$inputs['json'] = json_decode($this->input('json'), true);
return $inputs;
}
として,入力を上書きします.
これはバリデーションがかけられるデータがvalidationData
メソッドの返り値に対してかけられるため,元のデータにマージしてデータを追加することでバリデーションがかけられるデータを追加しています.したがってjsonにかかわらず別のデータを追加することもできます.例えば,ルートパラメータを追加する場合は
protected function validationData()
{
return $this->all() + $this->route()->parameters();
}
とします.
入力値の整形
例えば,日付の入力フォームが年・月・日で別れている場合など,それらを結合したデータを追加することができます.
protected function passedValidation()
{
$date = Date::createMidnightDate($this->input('year'), $this->input('month'), $this->input('day'));
$this->merge([
'date' => $date,
]);
}
また,年月日が一つのフォームになっていたとしても,そのままでは文字列型なので,
protected function passedValidation()
{
$this->merge([
'date' => Date::parse($this->input('date')),
]);
}
とすることで,コントローラ側で入力値をCarbon型で受け取ることができます.
$request->input('date'); // Carbon
メソッドの追加
FormRequestクラスに追加したメソッドは,呼び出すことができます.例えばCSVファイルを受け取る場合,
public function getCsvData()
{
$csvFile = $this->file('csv');
$csvData = array();
/* 受け取ったCSVファイルをからデータを取り出す */
return $csvData;
}
のようにメソッドを追加すれば,コントローラ側で
class ExampleController extends Controller
{
public function testMethod(TestRequest $request) {
$csvData = $request->getCsvData();
}
}
とすることで,CSVファイルのデータを受け取れます.他にも,json形式でデータを受け取る場合では
public function decodedData()
{
$data = $this->all();
$data['json'] = json_decode($data['json'], true);
return $data;
}
としてしまえば,コントローラ側でjson_decode
をかける必要がなくなります.前段のように
protected function passedValidation()
{
$this->merge([
'json' => json_decode($this->input('json'), true),
]);
}
としても良いと思います.
入力値の取得
FormRequestから入力値は以下のように取り出すことができます.
// すべて取り出す
$request->all();
$request->input();
// 一つだけ取り出す
$request->input('data', 'デフォルト値');
$request->data;
// 複数取り出す
$request->only(['data1', 'data2']);
// 指定したもの以外を取り出す
$request->except(['data3']);
これを使うことで,モデルの更新などを
$user = User::findOrNew($request->id);
$user->fill($request->except('id'));
$user->save();
このように書くことができます.
まとめ
FormRequestクラスを用いることで,バリデーションとコントローラを分離することができ,可読性や機能管理が容易になります.また,送信データに対する一次加工(json_decode
など)もこちらに含めてしまえば,コントローラ側でデータを加工する手間が省けます.