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