LaravelのRequestとResponse
前回、LaravelでフォームデータをPOSTして結果を表示するコードを紹介しました。
バックグラウンドで使われているコード意識せずに使えるところもLaravelの優れたところなのですが、今回はその部分を取り上げることでLaravelのコード力をアップさせたいと思います。
POSTされたリクエストの処理はLaravelではRequestオブジェクトを通じて取得することができ、結果の表示はResponseオブジェクトの機能により表示されています。
Request
ルーターやコントローラーで「依存性の注入(DI)」をすることによりRequestクラスのインスタンスが利用できるようになります。またその作業はタイプヒント(引数に型を定義する)その作業はLaravelのサービスコンテナが自動的に処理してくれます。
Laravelではサーバーに送信されるHTTPリクエストへのインターフェースをRequestクラスで提供しています。
Requestからは、リクエストのパス、やPOST、GETパラメータ、HTTPヘッダー、Cookieなどが取得できます。
Route::get('/test/{id}', function (Request $req, $id) {
return $req;
});
注意したいのは、ルートパラメータ―はRequest内では処理されずにそのままURIに入るところです。後から自力でURIからルートパラメータを取得することも可能ですが、ルートパラメータを事前に切り分けておきたい場合は、上記のようにidは別途引数として設定する必要があります。
またルーター処理で、関数でなくコントローラーを設定した際は、コントローラーのメソッドで同じように引数を定義すれば(ここでは$id)受け取ることができます。
Requestオブジェクトに存在するプロパティやメソッドの代表的なものは次の通りです。
- all
「$request->all()」で、受信リクエストのすべてのデータを配列として取得できます。
- collect
すべてのデータをコレクションで取得できます。allと違うのは引数にキーを設定することができ、サブセットをコレクションとして取得できます。
$request->collect('colors')->each(function ($color) { })
- input
単一の値を取得したい場合はinputメソッドを使います。第2引数を渡すことで、キーが存在しない時のデフォルト値を指定できます。
$name = $request->input('name', null);
また、このinputメソッドを引数なしで実行すると、すべての値を連想配列として取得します。
もし値がJSON(application/json)としてPOSTされていた場合は、input('main.sub')という風にJSONオブジェクト内のプロパティを指定することが可能です。
- query
queryはinputと違いクエリパラメータの値だけを対象とします。使用方法はinputと同じです。
- boolean
入力値をbooleanに変換します。1,"1",true,"true","on","yes"に対してtrueを返し、他はfalseとなります。
- date
dateメソッドを使うと日付(Carbonインスタンス)として値を取得します。値が存在しない場合はnullとなります、値が存在してインスタンスにキャストできない場合は例外がスローされます。
- only
onlyメソッドでは、リクエストの中のデータの一部だけを取得します。キー値を、文字配列か、可変長パラメータ式でメソッドに渡します。
指定したキーと値のセットがみ配列として返ります。
- except
onlyの逆で、指定したキーのみ取得しないようにします。
- has
hasメソッドに指定したキーが存在するか否かをboolean値で返します。配列で複数指定することができますが、その際はすべてが存在した場合にtrueとなります。ひとつでも存在した場合にtrueとしたい場合はhasAnyを使います。
また、whenHasメソッドでは第2引数に存在した場合の関数、第3引数(省略可)に存在しなかった場合の関数を指定し、値の有無による処理分岐が可能です。
- merge
mergeを使うと、引数に指定した連想配列をパラメータとして上書きで統合します。もしキー値が存在しない時だけ設定したい場合はmergeIfMissingメソッドが用意されています。
- flash
フラッシュはセッション変数へ現在の値を書き込む処理です。この値は$request->old('key')とすることで呼び出せます。またbladeテンプレート内ではold('key')とするだけで利用可能です。
flashにもflashOnlyとflashExeptが存在し、指定したものだけ保存したり、指定したものだけ対象外にしたりすることができます。
またこのflash処理は、リダイレクト時にwithInputメソッドを指定する事で自動的に処理されます。
return redirect('input-form')->withInput(); return redirect()->route('route-name')->withInput(); return redirect('form')->withInput( $request->except('password') );
- hasFile
リクエストに指定した名前でファイルがアップロードされているか確認できます。アップロードされたファイルは次に紹介するfileメソッドから取得できます。
- file
fileメソッドは引数にtype="file"のinput要素に指定した名前を渡すことで、フォームからPOSTされたファイルを取得することができます。(名前はそのままrequestのプロパティにもなるのでそちら経由でも利用できます)
fileメソッドやプロパティから取得できるインスタンスは、UploadFileクラスで次のようなメソッドを持ちます。
- isValid
アップロードに問題がなかったかチェックすることができます。
- path
ファイルパスを取得します。
- extension
拡張子を取得します。これはファイルの内容に基づいた推定で、名称から取得するものではありません。
- store
ファイルを指定したフォルダに保存します。フォルダへのパスは第1引数に設定します。ファイル名は自動的に設定されるため指定することはできません。
名前を指定して保存したい場合は、storeAsメソッドを利用し、第1引数にフォルダ、第2引数にファイル名を渡します。
fileから得られる、UploadFileクラスのインスタンスはSymfonyパッケージのクラスを継承したものです。そのためアップロード時のファイル名を取得するgetClientOriginalNameなどといったメソッドは、LaravelのAPIには記載がなく、vendor¥symfony¥http-foundation¥File¥UploadedFile.phpのコード内に説明があります。さらに上流ではPHPのSplFileInfoも継承していてこのクラスのメソッドも利用できます。
筆者の覚書として、アップロードしたファイルを、Requestからテーブル(この例ではattachment_files)に挿入する際のコードを残しておきます。
controller.php
... if($request->hasFile('attachment_files')) { foreach($request->file('attachment_files') as $value) { if($value->isValid()) { $afile = new AttachmentFile(); $afile->file_name = $value->getClientOriginalName(); $afile->raw_data = $value->get(); $afile->save(); } } } ...
- isValid
またRequestのインスタンスからはデータの検証を行うvalidateメソッドを呼び出せます。この処理では、現在値の保存(後からoldヘルパ関数で呼び出せます)や、エラーメッセージの出力、元のページへのリダイレクトまで処理してくれます。
次の例では、fromは入力必須かつ日付形式であること、toはformの条件に加えてfromより後の日になっていることを条件とするルールを設定しています。
$request->validate([
'from' => 'required|date',
'to'=>['required','date','after:from'],
]);
バリデーションのルールを自作したいケースについてはLaravelのバリデーションのカスタマイズの記事にて紹介していますので、よろしかったら合わせてご覧ください。
Response
Laravelのルーターやコントローラーでは通常ビューを返すことでページを表示します。また、このビューの代わりに文字列を返すとContent-Typeがtext/htmlのHTTPレスポンスになります。同様にオブジェクトや配列を返すとJSON(Application/json)に変換されます。
Laravel内部では、ルーターやコントローラーでビューや文字列、配列等が返された場合、それを適切なResponseに変換します。
ルーターでviewヘルパ関数でViewを指定して返していた戻り値を、responseヘルパ関数を使って次のように書くことができます。
web.php
...
Route::get('/',function(){
//return view('home',['data'=>$param]); とほぼ同じ
return response()->view('home', $data, 200)->header('Content-Type', 'text/html');
});...
# 処理をコントローラに委譲する場合
Route::get('/page1',[SampleController::class,"sample"]);
ビューを返す方が一般的になっていますが、Http¥Responseクラスを継承した、インスタンスを作成して返すこともできます。
たとえば、次のようにすることでプレーンテキストを返すことができます。
web.php
...
Route::get('/test1',function(){
return response('This is raw text',200)->header('Content-Type','text/plain');
});...
responseメソッドの第1引数に文字列、第2引数にステータスコードをセットます。
このresponseテキストの部分は、先ほど同様に配列やビューを渡すこともできます。たとえばカスタム404を返したければ「response(view('notfound'),404)」などとすることができます。
レスポンスヘッダーを付与したい場合は、headerメソッドで指定します。複数ある場合は最初のheaderメソッドの後に追加していきます。また、withHeadersメソッドを使えば、ヘッダを連想配列の形で指定することもできます。
HTTPヘッダだけでなくcookieも付与することができます。
ただしLaravelが生成するCookieはデフォルトでは暗号化されていて、Laravel以外からは読み取れません。
それを解除したい場合は、App¥MiddleWare¥EncryptCookiesに、除外対象のCookie名を$exceptプロパティに事前に追加します。
Laravelのミドルウェアに関しては別記事もありますので、よろしければ参考にしていただければ幸いです。
また、モデルを返すと、レスポンスは$hiddenプロパティを反映した形のJSONとなります。$hiddenをはじめとするLaravelのモデルの機能についても別記事設けてあります。
次の例ではルートパラメータをとモデルにクラスにタイプヒントした変数名が同じ時暗黙バインディングが行われ、$userにはルートパラメータ―に指定したidをもつUserモデルが入ります。
web.php
...
Route::get('/test2/{user}', function (User $user) {
return $user;
});...
たとえば、上記のルーティングの設定で「test/2」にアクセスした場合、次のような文字列がブラウザに表示されることになります。
例では、デフォルトで設定されているUser利用しています。これは$hiddenプロパティに、「password」と「remember_token」が設定してあるのでその列をぬかした結果が出力されています。
その他、responseオブジェクトを使って、JSONを生成したり、ファイルダウンロードを開始したりすることも可能です。
// JSONレスポンスを作成
return $response->json(['param1'=>'value1','param2'=>'value2']);
// ダウンロードを開始
return $response()->download($pathToFile, $name, $headers);
リダイレクト
リダイレクトもまたレスポンスなので合わせて触れておきます。
redirectヘルパ関数にURLを渡すと、RedicrectResponseクラスが返りますので、これを戻り値とすることでリダイレクト処理ができます。
この時、リダイレクト先へセッションパラメーターを付与することもできます。これは通常のセッション変数同様に、blade.phpからsession('キー名')で利用できます。
ルートに名前がつけてある場合は、それを指定することもできます。この時第2引数に連想配列またはモデルを渡す事ができます。
web.php
...
Route::get('/ridirect-to-home', function () {
return redirect('/home');
#return redirect('/home')->with('param','value');
#return redirect()->route('home',['param'=>'value']);
#return redirect()->route('home',[$model]);
});...
ひとつ前のページにリダイレクトしたい場合は、back()メソッドが用意されています。これは、StartSession.phpというミドルウェアによって機能が成立します。そのため、このミドルウェアが適用されていないルートでは利用できません。このミドルウェアはデフォルトではweb.phpにルートを記述すると適用されるようになっています。
web.php
...
Route::get('/post-any-data', function () {
return back()->withInput();
});...
withInput()メソッドを付与すると、前の画面で入力した値を戻せます。この時、前の値はoldヘルパ関数を使って呼び出すことができます。
外部サイトにリダイレクトしたい場合は、awayメソッドを使います。
web.php
...
return redirect()->away('https://www.google.com');
...