速習Larave3(その7)、更新処理No.1

タグ: Laravel3  

さて、次に更新処理を行いましょう。

更新処理だけを作成するのでしたら、そう手間はかかりません。追加と更新を全く別のルート、別のアクションで処理するならば、ロジックは簡単です。ですから、初心者の方にはお勧めです。

ですが、やっぱり素人くさいですよね。なにせ大抵の場合ページ構成も全く一緒です。

そのため、今回は追加と更新を一つのコントローラー/アクションとして実現します。

実は、良い方法がないかと色々あさって見たのですが、海外のチュートリアルも「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?)とオプション付きで指定した場合、それを受け取る引数にはデフォルト値を指定し、引数が無かった場合の値を設定することに注意してください。

表示処理がフォームの処理と同じくらい複雑になっています。ですから、追加と更新を分けたほうが簡単にはなるんですよ。でも、複雑に見えてもたいしたことありません。次の記事からゆっくり解説していきます。

次の記事:速習Larave3(その7)、更新処理No.2