PIYO - Tech & Life -

routes.rbでresourcesを使ったときのパラメータをid以外にする

routes Rails

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]などと変更する必要がある。

少し手間かもしれないが、可読性のことを考えてパラメータ名も変更しておいたほうがいい。