速習Larave3(その7)、更新処理No.1
タグ: Laravel3
さて、次に更新処理を行いましょう。
- ソース:github
更新処理だけを作成するのでしたら、そう手間はかかりません。追加と更新を全く別のルート、別のアクションで処理するならば、ロジックは簡単です。ですから、初心者の方にはお勧めです。
ですが、やっぱり素人くさいですよね。なにせ大抵の場合ページ構成も全く一緒です。
そのため、今回は追加と更新を一つのコントローラー/アクションとして実現します。
実は、良い方法がないかと色々あさって見たのですが、海外のチュートリアルも「Laravelはこんなに簡単なんだよ」と表現するために、難しい部分、複雑な部分を避けているようで、実用性に欠けています。ですから、良い例が見つからなかったのです。
今回は色々な考え方ができるので、多少「こういう考え方もできるよ」と説明しながら、進みます。
実は、可能な限りベストな方法を考えるために半日以上使いました。色々試しました。とりあえず、今のところ思いついたベストな方法で、更新と追加を一緒のロジックで処理するやり方を紹介します。実際、このサイトでやっているやり方よりスマートになりました。
ルーティングをデザインする
まず考えるのがこれです。
| 処理 | ルート |
|---|---|
| 追加 | /post/add |
| 更新 | /post/update/(ポストID) |
この方法は、認識性に優れています。ルートを読めば何をするのか一目瞭然です。もうひとつの候補はこれです。
| 処理 | ルート |
|---|---|
| 追加 | /post/edit |
| 更新 | /post/edit/(ポストID) |
エディターを考えてください。コマンドラインから起動する時、何も指定しなければ、新規として開きます。ファイルを指定すればそのファイルの更新処理として起動します。それと同じ考えです。
何も指定されなければ、新規として扱い、ポストIDが指定されたら、そのIDの記事を更新処理と考えます。
好みの問題です。どちらを選んでも構いません。フレームワークを使用する利点の一つが、こうしたルートと実際に動作するスクリプトを自由に結び付けられることです。
今回は二番目の方法を取ります。実はルートの勉強もしようと思っています。
Route::any('post/edit', ...);
Route::any('post/edit/(:num)', ...);
このように分けて書いても間違いではありません。実際、追加と更新を別のアクションで処理するのでしたら、こう書くことになります。
今回は同じアクションで処理します。すると上記のように二つに分けて書くのは二度手間です。こう書きましょう。
Route::any('post/edit/(:num?)', ...);
クエスションマーク付きは公式ドキュメントでは「オプション」という表現を使っていますが、要するにあってもなくても一致するという意味です。これですっきり書く方法が身につきました。
実際のroutes.phpは次のようになります。追加処理だけを行なっていたルート名post-addは削除してください。addは追加ですが、今回は更新にも使用しますからね。代わりに以下のコードで書き換えてください。
// 記事追加/編集
// フォーム表示
Route::get('/post/edit/(:num?)', array( 'as' => 'post-edit', 'uses' => 'post@edit' ));
// フォーム処理
Route::post('/post/edit/(:num?)', array( 'as' => 'post-edit', 'before' => 'csrf', 'uses' => 'post@edit' ));
any()で登録するのではなく、HTTP変数のGETとPOSTでルートを分けています。CSRFのフィルターはPOSTのみに適用したいですからね。(初心者はRoute::anyにCSRFフィルター付けて、はまりがちです。;) )
チラッ、ちっら(なんだかアクションの名前も変わっているような…)
ご察しの通り。addからeditに変えました。両方を担当させるにはaddには荷が重過ぎます。言葉の意味がせま過ぎますからね。というわけで、コントローラーに移ります。
コントローラーの処理
post.phpです。get_add()とpost_add()を削除し、以下のコードに置き換えてください。
/**
* 記事追加/編集フォーム表示
*/
public function get_edit($id = null)
{
// フラッシュデーターとしてセッションに
// 保存されている入力を取得
$inputs = array_only(
Input::old(), array( 'title', 'body' ));
// フラッシュデータが存在しない場合
// フォーム処理からのリダイレクトではなく
// 初めて呼び出されたということ
if ( empty($inputs) )
{
// 初回処理
if ( $id === null )
{
// 新規記事追加
$data = array(
// ページ出力は空文字列
'title' => '',
'body' => '',
);
}
else
{
// 更新処理
$post = Post::find($id);
if ( $post === null )
{
return Redirect::error('404');
}
$data = array(
// ページ出力は既存のレコード
'title' => $post->title,
'body' => $post->body,
);
}
}
else
{
// フォーム更新時
$data = array(
// ページ出力はフォームの入力データー
'title' => $inputs['title'],
'body' => $inputs['body'],
);
}
return view('post.form')
->with('title', $data['title'])
->with('body', $data['body']);
}
/**
* 記事追加フォーム処理
*/
public function post_edit($id = null)
{
// バリディーションルール
$rules = array(
'title' => 'required|max:50',
'body' => 'required|max:8000',
);
$input = Input::only(array( 'title', 'body' ));
$val = Validator::make($input, $rules);
if ( $val->passes() )
{
// バリデーション通過
if ( $id === null )
{
// 新ポストの追加
$post = new Post($input);
$post->save();
// 続けて投稿しやすいように
// 再度追加処理へ(IDを付けないでリダイレクト)
return Redirect::to_route('post-edit')
->with('message', '記事を投稿しました。');
}
else
{
// 既存ポストの更新
$post = Post::find($id);
if ( $post === null )
{
return Redirect::error('404');
}
$post->title = $input['title'];
$post->body = $input['body'];
$post->save();
// 同じ記事を修正しやすいように
// 再度記事を表示させる
return Redirect::to_route('post-edit', array( $id ))
->with_input()
->with('message', '記事を編集しました。');
// with_input()が無くても同じ記事が表示されるが
// よけいなDBアクセスをさせない
}
}
else
{
// バリデーション失敗
return Redirect::to_route('post-edit', array( $id ))
->with_input()
->with_errors($val);
}
}
まず、ルートに(:num?)とオプション付きで指定した場合、それを受け取る引数にはデフォルト値を指定し、引数が無かった場合の値を設定することに注意してください。
表示処理がフォームの処理と同じくらい複雑になっています。ですから、追加と更新を分けたほうが簡単にはなるんですよ。でも、複雑に見えてもたいしたことありません。次の記事からゆっくり解説していきます。