たとえば1.入力画面→2.確認画面→3.保存完了画面というフローがあったとする。
1.から2.へはSessionでデータを引き継がせるようにしていたとして、
2.の入力内容から「修正する」ボタンを押してGetリクエストにて1.の入力画面へ戻ってきた時、引き継いだ入力情報をどうやって入力画面へ反映させるとスマートなのか?
で悩んだことがある。
その時に使ったのが、$this->requestでおなじみのServerRequestオブジェクトに用意されているwithParsedBody()メソッドだ。これ、結構使える関数なので紹介していく。検証したCakePHPのバージョンは3.6.13。
$this->request->withParsedBody()は、GETやPOSTにてデータが渡された状態のリクエストを意図的に作ることができる
↑で書いた通りのままだが、withParsedBody()は画面からGETやPOSTにてデータが渡された状態のリクエストを意図的に作ることができるメソッドだ。内部的には下記のように、自分のServerRequestインスタンスを複製した上でデータをセットし、それを返してくれる:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Update the parsed body and get a new instance. * * @param null|array|object $data The deserialized body data. This will * typically be in an array or object. * @return static */ public function withParsedBody($data) { $new = clone $this; $new->data = $data; return $new; } |
もし1カラムずつデータをセットさせたい場合はwithData()を使うと良いらしい:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * Update the request with a new request data element. * * Returns an updated request object. This method returns * a *new* request object and does not mutate the request in-place. * * Use `withParsedBody()` if you need to replace the all request data. * * @param string $name The dot separated path to insert $value at. * @param mixed $value The value to insert into the request data. * @return static */ public function withData($name, $value) { $copy = clone $this; $copy->data = Hash::insert($copy->data, $name, $value); return $copy; } |
withParsedBody()の使いどころ
確認画面から入力画面に戻ってくる時の処理を例に出すと、こんな感じ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?php namespace App\Controller; use App\Controller\AppController; use App\Form\HogeForm; use Cake\View\View; use Cake\Network\Exception\BadRequestException; class HogeController extends AppController { public function initialize() { parent::initialize(); } /** * 入力画面 */ public function input() { $hoge = new HogeForm(); // モデルのないフォーム if ($this->request->is('post')) { $requestData = $this->request->getData(); if ($hoge->validate($requestData)) { $this->request->session()->write('requestData', $requestData); return $this->redirect(['action' => 'confirm']); } } else { // 確認画面から「修正する」ボタンを押して、GETで入力画面に戻ってきた時の処理 if ($this->request->session()->check('requestData')) { $requestData = $this->request->session()->read('requestData'); // 本来ならread()ではなく、consume()を使うほうが好ましい $this->request = $this->request->withParsedBody($requestData); } } $this->set(compact('hoge')); } /** * 確認画面 */ public function confirm() { if (!$this->request->session()->check('requestData')) { throw new BadRequestException('不正なリクエストです'); } $requestData = $this->request->session()->read('requestData'); $this->set(compact('requestData')); } } |
Line:29~35の処理がキモだ。
ただ個人的にまだ未解決なのが、セッションに引き継がせた情報をread()で取得した上でwithParsedBody()に渡すのは上手くいくのに、consume()で取得した情報をwithParsedBody()に渡すと何も渡せてない状態になること。consume()もwithParsedBody()も同じ$this->requestを使っているあたりで、何らか予期しないことが起こっているのかもしれない。今後要調査。
上記以外にwithParsedBody()が活躍しそうな場所としては、Controllerのテストコード。なぜなら画面から入力されたリクエストデータを再現することができるので。
参考)https://api.cakephp.org/3.6/class-Cake.Http.ServerRequest.html#_withParsedBody