edit(更新処理) で put メソッドを使う CakePHP

v 2.5.6 より

乗り越えるべき課題

CakePHP では

X. View で $this->Form->Create を定義したとき、デフォルトで method="post" が定義される
Y. Model が更新値を save する時
primary key が request->data['Model'] になかった場合
更新処理(update)ではなく作成処理(insert)になる

X-1. Form::Create で method の種類を指定しなかった場合

1 <?php 
2   echo $this->Form->Create('Post');

次の通りに HTML 出力される

<form id="PostEditForm" accept-charset="utf-8" method="post" action="/cake/posts/edit/1">

Y-1.

posts db の属性が -> [id(PK), title, body, created_at, updated_at] で

app/View/Posts/edit.cpt 一部が次の通りとする

1 <?php 
2   echo $this->Form->Create('Post');
3   echo $this->Form->input('title');
4   echo $this->Form->input('body'));
5   echo $this->Form->end('Update');
6 ?>

app/Controller/PostsController/edit.php 一部が次の通りとする

34  public function edit($id = null) {
35    if($this->request->is('put')) {
36      if($this->Post->save($this->request->data)) {

フォームに入力された値が正常な場合でも、save では新規登録処理(insert) が行われる

課題への対策

X. FormHelper に type オプションとアクションを明示する
Y. primary key の属性に対するフィールドを hidden で埋め込み、request->data から参照できるようにする
1 <?php 
2   echo $this->Form->input('id', array('hiddenField' => true));
3   echo $this->Form->Create('Post',  array('type' => 'put', 'action' => 'edit'));
4   echo $this->Form->input('title');
5   echo $this->Form->input('body'));
6   echo $this->Form->end('Update');
7 ?>

X. に付随して routing を修正する

例として次の通り

32 Router::connect('/:controller/:id', array('controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT'), array('id' => '[0-9]'));

以下、個人的な止め書き(知ったことを列挙しているだけ)

Session パッケージを使わず、path parameter のみで edit 処理をできるだけ安全に処理させる

(curl などでリクエストされた時は危ないだろう)

app/View/Posts/edit.cpt

1 <?php 
2   echo $this->Form->Create('Post', array('type' => 'put', 'action' => 'edit'));
3   echo $this->Form->input('id',    array('default' => $put['Post']['id'], 'hiddenField' => true));
4   echo $this->Form->input('title', array('default' => $put['Post']['title']));
5   echo $this->Form->input('body',  array('default' => $put['Post']['body'], 'rows' => 3));
6   echo $this->Form->end('Update');
7 ?>

app/Controller/PostsController/edit.php

29  public function edit($id = null) {
30    if(is_null($id)) { /* put 処理が失敗したときは $id が null になる
31                          L42 で Post が再び findById できるように hidden で得た id を $id へ割り当てる */
32      $id = $this->request->data['Post']['id'];
33    } 
34    if($this->request->is('put')) {
35      if($this->Post->save($this->request->data)) {
36        $this->Session->setFlash('Success!');
37        return $this->redirect(array('action' => 'index'));
38      } else { 
39        $this->Session->setFlash('Failed!');
40      } 
41    } 
42    $this->set('put', $this->Post->findById($id));
43  } 

app/Config/routes.php

32 Router::connect('/:controller/:id', array('controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT'), array('id' => '[0-9]'));