速習Larave3(その5)、コントローラー

タグ: Laravel3  

コントローラーを使いはじめましょう。

さて、前回作成したroutes.phpをご覧ください。

随分コードが増えてきました。Laravelの特徴…というか、他のフレームワークでも採用されていますが、コントローラーを使用せず、無名関数を使用してルーティングに直接コードを記述できるようになっています。

これは、全部で数ページのサイトとか、ちょっとしたツールを作成するとか、機能を実験してみるとかには、とても便利に使用できます。

ですが、routes.phpはその名の通り、ルーティングの定義がメインです。まだ、3ページ分のルートしか定義していないのに、全部のルートを見通すのが難しくなっています。

つまり、ルートを調べるという事に関しては、可読性が落ちています。Laravelが折角、読みやすいPHPの世界を作ってくれているのに、その一部の機能を利用して壊しているのです。これはもったいない。対処しましょう。

ということで、今回からコントローラーを使いはじめましょう。どこに入れたらいいか迷うような小さなルーティングの内容ならともかく、可能な限りroutes.phpには、ルートの定義だけ記述して、すっきりさせます。それでなくてもフィルターとか、その他の定義が集まりごちゃごちゃしますからね。

Tips!! その内なるボヤキが聞こえたのか、Laravel4ではフィルターの定義は別ファイルになりました。Laravel3でもファイルを分け、routes.phpでrequireしてすっきりさせる方法は取れます。

まず、コントローラーを作成し、そこに記事に関する操作を移動します。application/controllers/post.phpを作成してください。

<?php

class Post_Controller extends Base_Controller
{
    // レストフル動作フラグ
    public $restful = true;

    /**
     * 一覧表示
     */
    public function get_index()
    {
        $posts = Post::order_by('created_at', 'desc')->get();
        return view('post.index')
                ->with('posts', $posts);
    }

    /**
     * 記事表示
     */
    public function get_show($id)
    {
        $post = Post::find($id);

        if ( $post === null )
        {
            return Redirect::error('404');
        }

        return View::make('post.show')
                ->with('title', $post->title)
                ->with('body', $post->body);
    }

    /**
     * 記事追加フォーム表示
     */
    public function get_add()
    {
        return view('post.add');
    }

    /**
     * 記事追加フォーム処理
     */
    public function post_add()
    {
        // バリディーションルール
        $rules = array(
            'title' => 'required|max:50',
            'body' => 'required|max:8000',
        );

        $input = Input::only(array( 'title', 'body' ));

        $val = Validator::make($input, $rules);

        if ( $val->passes() )
        {
            // バリデーション通過
            // 新ポストの追加
            $post = new Post($input);
            $post->save();

            // 続けて追加できるように、
            // フォームは空のままリダイレクト
            return Redirect::to_route('post-add')
                    ->with('message', '記事を追加しました。');
        }
        else
        {
            // バリデーション失敗
            // バリディーションで生成されたエラーと
            // フォームに入力された内容を
            // セッションのフラッシュデーターを通じ
            // フォームに送る
            return Redirect::to_route('post-add')
                    ->with_errors($val->errors)
                    ->with_input();
        }
    }

}

当然のことながら、前回まで積み上げてきたコードと同じですね。違っているのは関数名が付くことくらいでしょう。

関数名の命名規則は他のフレームワークとよく似ています。通常はプレフィックスにaction_を付けます。それ以降の名前がアクション名となります。アクション名とはルーティングで指定される名前のことです。

今回はRESTルーティング、RESTフルなルーティングを使用します。これはプレフィックスがリクエストのHTTP変数の名前になる命名規則を使用するという事です。例えばGETメソッドでのアクセスはget_が頭に付き、POSTメソッドならpost_です。これを使用するにはコントローラーの$restfulプロパティをtrueにセットします。

続いて、routes.phpを改良しましょう。

<?php

/*
 * ルーティング定義
 */

// トップページ:一覧表示
Route::get('/', array( 'as' => 'post-index', 'uses' => 'post@index' ));

// 記事表示
Route::get('/post/(:num)', array( 'as' => 'post-show', 'uses' => 'post@show' ));

// 記事追加
//  フォーム表示
Route::get('/post/add', array( 'as' => 'post-add', 'uses' => 'post@add' ));
//  フォーム処理
Route::post('/post/add', array( 'as' => 'post-add', 'before' => 'csrf', 'uses' => 'post@add' ));


/*
 * エラー処理
 */

Event::listen('404', function() {
        return Response::error('404');
    });

Event::listen('500', function() {
        return Response::error('500');
    });

/*
 * フィルター定義
 */
Route::filter('before', function() {
        // Do stuff before every request to your application...
    });

Route::filter('after', function($response) {
        // Do stuff after every request to your application...
    });

Route::filter('csrf', function() {
        if ( Request::forged() )
            return Response::error('500');
    });

Route::filter('auth', function() {
        if ( Auth::guest() )
            return Redirect::to('login');
    });

/*
 * ビュー・コンポーサー定義
 */
View::composer('template',
    function($view) {
        $view->warning = isset($view->warning) ? $view->warning : Session::get('warning', false);
        $view->message = isset($view->message) ? $view->message : Session::get('message', false);
    });

どうでしょうか。コードが少なくなり、すっきりしました。一番重要なのは先頭のルートの定義が読みやすくなったという事です。これで他の人が呼んでも分かりやすくなりました。他の人の中には、半年後の自分も含まれています。

ルーティングの定義では'as'を使いルート名、'uses'を使いコントローラーとアクションを定義しています。

もし全体をコントローラー/アクションだけで統一するのであれば、ルート名は無理に必要ありません。コントローラー/アクションの指定でリンクを生成できるからです。名前とコントローラー/アクションのどちらでもリンクやURL、URIを生成できるのですが、便利でも両方を混ぜて生成するとごちゃごちゃになりますので、どちらか一方を使用しましょう。

今回は無名関数でのルーティングを完全に使用しないとは決めず、必要に応じて使用し、無名関数のルーティングとコントローラーを両立させようと思います。ですから、ルートには全部名前を付け、その名前でリンクを生成する方針をとっていきます。