コントーローラーとルーティング
今回はコントローラーが一体何をしているのか、そしてルーティングについて解説していきたいと思います。 Action Controller Overview と、 Rails Routing from the Outside In を参考にしました。
コントローラーの概要
まず、 Rails のコンポーネントの一つである、 Action Controller は MVC の C に当たることは初回に説明しました。 対してどのコントローラがリクエストの処理を行うか決めることをルーティングといいますが、これを決めるのは Action Routing の仕事です。
コントローラの仕事はリクエストの意味を理解し、それに合わせて適切な処理、出力を行うことです。
そのために Rails は様々な道具を用意してくれています。ここからはその道具について見ていきます。
メソッドとアクション
Rails でコントローラーを作成するには、 ApplicationController を継承したクラスを作成します。 コントローラー内にある公開されているメソッドはアクションと呼ばれていて、 ルーティングではどのコントローラでどのアクションを実行するべきなのかを決定します。 その後、 Rails はコントローラのインスタンスを作成し、ルーティングで決定されたメソッドをアクションとして実行します。
ApplicationController は Rails アプリケーションを作成する際に自動的に作成されるクラスで、 アプリケーション内のすべてのコントローラに適用したいこと(認証など)は ApplicationController に記述すると良いでしょう。
ApplicationController 自体は ActionController::Base を継承しているため、すべてのコントローラは ActionController::Base のサブクラスと言うことが出来ます。
class CustomersController < ApplicationController
def new
end
end
上記のコードは CustomersController というコントローラの中に new というアクションを定義する例です。 以上のようにメソッドを作成するだけでアクションを作ったことになります。
Rails では規約によりレンダリングするビューを指定していない場合は app/views/{コントローラ名}/{アクション名.html.erb}
をビューとして使用することになっています。今回の場合だと、 app/views/customers/new.html.erb
がビューとして使われます。
コントローラ内の公開されているメソッドのみがアクションとして扱われることに注意してください。 逆に言うと、外部に公開すると問題のあるメソッドは public にしないようにしましょう。
パラメータ
Web アプリケーションではユーザーからデータを受け取るために GET リクエストのクエリや、 POST リクエストのデータを利用します。 これらがどのような形式で渡されるかは HTML フォームの形式から決定されるのが普通です。
しかし、 Rails 内でパラメータを扱う際にはどちらのメソッドで送られてきたデータなのかを区別はしません。 GET のパラメータも POST のパラメータも params という連想配列(ハッシュ)に入っています。
リクエストが GET だったのか POST だったのか知りたい場合は request.post?
や
request.get?
を利用します。
例えばパラメータを利用して顧客の表示順を変えるコードは以下のものが考えられます。
class CustomersController < ApplicationController
def index
if params[:order] == "id_asc" # order=id_asc というクエリが含まれていた場合
@customers = Customer.order("customer_id ASC") # 顧客IDで昇順に並び替えた顧客の配列を取得
elsif params[:order] == "id_desc" # order=id_desc というクエリが含まれていた場合
@customers = Customer.order("customer_id DESC") # 顧客IDで降順に並び替えた顧客の配列を取得
else
@customers = Customer.order("created_at") # 顧客登録された順で顧客の配列を取得
end
end
end
?order=id_asc
などのクエリを含むリクエストを作成する HTML としては以下のようなものが考えられます。
<form action="/customers" method="get">
<select name="order">
<option value="created_date" selected>既定</option>
<option value="id_asc">▲ ID</option>
<option value="id_desc">▼ ID</option>
</select>
<input type="submit" value="並び替え">
</form>
params の構造
params に格納されるオブジェクトには大きく2種類あります。一つがクエリから生成されるクエリパラメータ(POSTの場合もこれに含む)。 もう一つが URL からルーティングルールに基づいて自動的に追加されるものルーティングパラメータです(このような文脈でのクエリパラメータという言葉の使い方は一般的ではないと思うので他の人に伝わらない可能性があります)。
params に格納するオブジェクトは文字列だけでなく、配列や連想配列にすることも出来ます。
たとえ文字列に含まれるのが数字だけだとしても、Rails はそれらを Integer に変換したりすることはありません。
クエリパラメータ
ここでは、クエリパラメータについて説明します。
クエリを name=val
とした場合は params[:name]
で val となっている文字列にアクセス出来ます。
配列を params に格納するためには以下のようなクエリを送信する必要があります。
?list[]=1&list[]=4&list[]=7&list[]=8
この配列には params[:list]
とすることでアクセスすることが出来、
これによって得られるのは ["1", "4", "7", "8"]
となります。
取得できるオブジェクトは数字ではなく文字列になっていることに気をつけてください。
params に連想配列を格納するためには以下のようなクエリが必要です。
?customer[customer_id]=93&customer[name]=John&customer[phone]=1234-567-8901&customer[email[]][email protected]&customer[email[]][email protected]
上記のようなクエリを渡すと params[:customer]
は {:customer_id => "93", :name => "John", :phone => "1234-567-8901", :email => ["[email protected]", "[email protected]"]}
のような連想配列に変換されます。ここでは連想配列の中に配列を持たせていますが、同様に連想配列の中に連想配列を入れることも出来ます。
上記のようなハッシュを含むクエリを生成する HTML の例は以下のとおりです。
<form action="customers" method="post">
<div><label>
顧客ID
<input type="number" name="customer[cuscustomer_id]">
</label></div>
<div><label>
顧客名
<input type="text" name="customer[name]">
</label></div>
<div><label>
電話番号
<input type="tel" name="customer[phone]">
</label></div>
<div><label>
メールアドレス
<input type="email" name="customer[email[]]">
</label></div>
<div><label>
メールアドレス(予備用)
<input type="email" name="customer[email[]]">
</label></div>
<div><input type="submit" value="登録"></div>
</form>
ルーティングパラメーター
params にはルーティングで定義された id が利用可能です。
例えば、 posts 一覧のから一つの post の詳細を閲覧したい場合は、URLにて posts/9517
のように id を指定します。このときの post の id を取得するのに params[:id] を利用することが出来ます。
セッション
セッションを使って小さなデータをリクエストをまたいで保存することが出来ます。一度使ったら利用しないようなものの場合は、フラッシュを利用します。
セッションを実現するための実装はいくつかありますがデフォルトだとクッキーを利用します。クッキーを利用したセッションでもデータが不正に変更されていないことを保証することが出来ます。しかし、保存されているクッキーは暗号化されていないので注意が必要です。また、クッキーを利用した場合は最大4kBまでしかデータを保存することが出来ません。また、クッキーなので消える場合があることに注意してください。
セッションへのアクセス
セッションを利用するのは簡単で、 session というハッシュのようなオブジェクトがあるのでそこにデータを入れたり出したりするだけです。
セッションに値を入れる時は普通にハッシュに値を入れるときのように、
session[:account_id] = params[:id]
といった形で利用します。
現在のセッションからオブジェクトを取り出すためには、
@account = Account.find(session[:account_id])
のような形で記述します。普通のハッシュオブジェクトと同じです。
セッションからデータを削除したい場合は、
session[:account_id] = nil
といったように、 nil
を代入します。
もしセッションをリセットしたくなった場合は reset_session
を使います。
フラッシュ
フラッシュはセッションの機能の一つで、リクエストが送られるたびに削除されます。つまり、次のリクエストでのみ値を取り出すことができます。
これは例えば警告メッセージを表示したりするのに役立ちます。
session と同様にハッシュのような形で利用します。例えば以下のようにして利用します。
flash[:notice] = "Welcome our website." # 代入
<%= flash[:notice] %> <!-- 値の利用 -->
もし次のリクエストでも現在と同じフラッシュを利用したい場合は flash.keep
を利用します。一部だけを持ち越したい場合は flash.keep(:notice)
のように引数に持ち越したい値のキーを指定します。
フィルター
フィルターはアクションの前、後、または前後に任意のメソッドを呼び出すための仕組みです。
フィルターは継承されるので、application_controller に記述したフィルターはアプリケーション全体に適用されることになります。
Before フィルター
Before フィルターでリダイレクトをするとリクエストの処理を早い段階で終えることが出来ます。
例えば、認証機能を追加するときに Before フィルターでログインの確認や権限を持っているかの確認を行い、必要ならばログインページにリダイレクトさせるといったことが可能になります。
上記の処理を記述した例を掲載します。
class ApplicationController < ActionController::Base
before_filter :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section."
redirect_to new_login_url
end
end
もし、before_filter を実行したくない場合は、
skip_before_filter :require_login, :only => [:new, :create]
のように記述します。これによって、 new と create 以外のアクションには require_login
フィルタが実行されなくなります。
After フィルターと Around フィルター
After フィルターはアクションの処理が終わった後に呼ばれるフィルターです。
After フィルターではアクションの実行をやめさせることは出来ません。その代わりにリスポンスデータにアクセスすることが出来ます。
Around フィルターを使うとアクションの前後に処理を入れることが出来ます。
Around フィルターは yield を使ってアクションを呼び出す責任があります。
リソースルーティング
リソースのルーティングを行うためには config/routes.rb
に設定を書き込む必要があります。といっても、 Rails のデフォルトのルーティングを行う場合は掲示板の作成行ったように、
resouces :posts
のような行を追加するだけで行うことが出来ます。
通常のリソース
一般的なリソースへのルーティングの設定を行うには以下のように記述します。
resources :posts
この場合、posts_controller.rb
のコントローラーに以下のようにルーティングされるようになります。
メソッド | パス | アクション | 利用用途 |
---|---|---|---|
GET | /posts | index | 投稿の一覧を表示する |
GET | /posts/new | new | 新しい投稿を作成するためのページを返す |
POST | /posts | create | 新しい投稿を作成する |
GET | /posts/:id | show | 指定した投稿を表示する |
GET | /posts/:id/edit | edit | 指定した投稿を編集するためのページを返す |
PUT | /posts/:id | update | 指定した投稿を更新する |
DELETE | /posts/:id | destroy | 指定した投稿を削除する |
また、以下のようなパスを返すヘルパーメソッドが自動的に追加されます。
- posts_path -> /posts
- new_post_path -> /posts/new
- edit_post_path(:id) -> /posts/:id/edit
- post_path(:id) -> /posts/:id
それぞれの _path ヘルパーメソッドに対応して _url ヘルパーメソッドも追加されます。
単一リソース
アプリケーションを作成していると一つしか存在しない情報にアクセスすることがあります。すなわちIDを必要としないリソースです。例えば、現在ログインしているユーザーの情報を表示するページにはIDが必要ありません。このリソースに /profile
でアクセスすることが出来るようにするためには以下のように記述します。
resource :profile
このような記述を行うことによって以下のように profiles_controller.rb
のアクションにルーティングされるようになります。
メソッド | パス | アクション | 利用用途 |
---|---|---|---|
GET | /profile/new | new | 新しいプロファイルを作成するためのページを返す |
POST | /profile | create | 新しいプロファイルを作成する |
GET | /profile | show | プロファイルを表示する |
GET | /profile/edit | edit | プロファイルを編集するためのページを返す |
PUT | /profile | update | プロファイルを更新する |
DELETE | /profile | destroy | プロファイルを削除する |
この方法でルートを作成すると以下のようなヘルパーメソッドでURLを生成できるようになります。
- new_profile_path -> /profile/new
- edit_profile_path -> /profile/edit
- profile_path -> /profile
ネストしたリソース
あるリソースが論理的にほかのリソースを持つという状況はよくあることだと思います。
例えばブログの記事にはふつうコメントを付けることができますが、これは論理的にはコメントはブログの子であるととらえることが出来ます。
このような構造を Rails で実現するためにはリソースのネストを利用します。掲示板の作成でも同様のことを行っています。
class Post < ActiveRecord::Base
has_many :comment
end
class Comment < ActiveRecord::Base
belongs_to :post
end
上記のように定義されたモデルをルーティング的にもネストさせるためには、以下のように config/routes.rb
に記述します。
resouces :posts do
resources :comment
end
このように記述することによって posts のルーティングに加えて以下のような CommentsController へのルーティングルールが作成されます。この時、コメントにアクセスするためには投稿のIDが必須になります。
メソッド | パス | アクション | 利用用途 |
---|---|---|---|
GET | /posts/:post_id/comments | index | ある投稿へのコメントの一覧を表示する |
GET | /posts/:post_id/comments/new | new | ある投稿への新しいコメントを作成するためのページを返す |
POST | /posts/:post_id/comments | create | ある投稿への新しいコメントを作成する |
GET | /posts/:post_id/comments/:id | show | ある投稿への指定したコメントを表示する |
GET | /posts/:post_id/comments/:id/edit | edit | ある投稿への指定したたコメントを編集するためのページを返す |
PUT | /posts/:post_id/comments/:id | update | ある投稿への指定したコメントを更新する |
DELETE | /posts/:post_id/comments/:id | destroy | ある投稿への指定したコメントを削除する |
これによって以下に挙げるようなヘルパーメソッドが追加されます。
- new_post_comment_path(:post_id) -> /posts/:post_id/comments/new
- edit_post_comment_path(:post_id, :comment_id) -> /posts/:post_id/comments/:comment_id/edit
- post_comments_path(:post_id) -> /posts/:post_id/comments
- post_comment_path(:post_id, :comment_id) -> /posts/:post_id/comments/:comment_id
オブジェクトからパスの生成
上記のようなネストしたルーティング状態において、以下のような方法でオブジェクトにアクセスするためのパスを生成することが出来ます。
まず、@post に Post のインスタンスと @comment に Comment のインスタンスが格納されているとします。
post_comment_path
を利用する際に引数にIDを指定する代わりに以下のように記述することが出来ます。
<%= link_to "View comment", post_comment_path(@post, @comment) %>
このような場合は url_for
を利用することも出来ます。
<%= link_to "View comment", url_for([@post, @comment]) %>
このように書くと Rails が自動的にどのヘルパーを使うべきなのかを判断してくれます。
さらに、link_to
などのメソッドでは url_for
も省略出来ます。
<%= link_to "View comment", [@post, @comment] %>
これが掲示板を作成する際に利用した形式です。
もし、投稿のページを表示したい場合は、
<%= link_to "View post", @post %>
といった形で書くことが出来ます。
RESTfulなアクションの追加
もし、リソースにデフォルト以外のアクションを追加したくなった場合は以下のように記述します。
resources :post do
member do
get :preview
end
resources :comment
end
上記のように書くことで、 /posts/:post_id/preview という GET メソッドを Post コントローラーの preview アクションにルーティングするようになります。
この時ヘルパーメソッドとして preview_post_url, preview_post_path が追加されます。
これは以下のように書くこともできます。
resources :post do
get 'preview', :on => :member
resources :comment
end
もしコレクション全体に対するルーティングを追加したい場合は以下のようにします。
resources :post do
collection do
get :search
end
resouces :comment
end
このように記述することによって、 /posts/search という GET メソッドを Post コントローラーの search アクションにルーティングすることが出来ます。
同様にヘルパーメソッドとして、 search_posts_url, search_posts_path が使えるようになります。
これは以下のように記述することもできます。
resources :post do
get 'search', :on => :collection
resouces :comment
end