Railsではhas_many
な関連を持つモデルに対してRESTなルートを簡単に定義できる。
# routes.rb
resources :blogs, only: [:index]
resources :posts, only: [:index]
end
このようなルートが定義される。
% rake routes
Prefix Verb URI Pattern Controller#Action
blog_posts GET /blogs/:blog_id/posts(.:format) posts#index
blogs GET /blogs(.:format) blogs#index
このときのパラメータである:blog_id
にはデフォルトではBlog
モデルのidが使われる。そしてこれらのルートに相当するURLは/blogs/1/posts
などとなる。
id以外のカラムをパラメータにする
例えばBlog
モデルにはアプリケーション内でユニークなユーザー名のようなものを持っているとし、それをルートのパラメータにしたいというケースを考える。
これが実現すると/blogs/xoyip/posts
というURLが使えるようになる。個人的には先ほど書いたものよりかっこいいと思う。よくわからない数字が入るのは好きじゃない。
やり方
Blogモデルにto_param
メソッドを定義する。
class Blog < ActiveRecord::Base
def to_param
return user_name
end
end
to_param
はActiveRecord::IntegrationモジュールのメソッドでStringを返す。モデルのインスタンスからURLを生成するときに使われるメソッドで、ソースを見ると確かにデフォルトではidを文字列にして返しているのがわかる。
# ActiveRecord::Integration
def to_param
id && id.to_s
end
パラメータ名を変える
to_param
のオーバーライドによってURLに使うパラメータは変わったけれど、rake routes
が吐き出す一覧における表記はidのままとなっていて気持ち悪い。これを変えておいたほうがあとあとわかりやすいので、こちらも変更する。
routes.rbで使うresources
メソッドはparam
という引数を取ることができる。ここにシンボルを書いておくと、パラメータの名前が書いたものに変わるという仕組みになっている。
先ほどの例でいくとこのように書くことができ、
# routes.rb
resources :blogs, param: :user_name, only: [:index] do
resources :posts, only: [:index] do
end
end
rake routesの出力はこのように変わる。
% rake routes
Prefix Verb URI Pattern Controller#Action
blog_posts GET /blogs/:blog_user_name/posts(.:format) posts#index
blogs GET /blogs(.:format) blogs#index
コントローラ側でパラメータを扱うときには注意が必要で、それまでparam[:blog_id]
のように書いていたところをparam[:blog_user_name]
などと変更する必要がある。
少し手間かもしれないが、可読性のことを考えてパラメータ名も変更しておいたほうがいい。