mabots' blog

知のレバレッジを最大化せよ (旧はてなダイアリーから移転しました。)

Nested Resource のルーティング、Member、Collection、Shallow の使いこなし

コミュニティーっぽいサービスで、 User has many Microposts *1 のような関係が設立する場合のルーティングを resource を用いてよしなに作る tips をまとめてみました。

Nested Resource

Resrouce を利用すれば基本的なルーティングを作成してくれますが、 ActiveRecord Model をネストした構造のルーティングを設定してみましょう。

  resources :users do
    resources :microposts
  end

作成されるルーティングは下記のようになります。

URI Pattern                                   Controller#Action
GET    /users/:user_id/microposts(.:format)          microposts#index
POST   /users/:user_id/microposts(.:format)          microposts#create
GET    /users/:user_id/microposts/new(.:format)      microposts#new
GET    /users/:user_id/microposts/:id/edit(.:format) microposts#edit
GET    /users/:user_id/microposts/:id(.:format)      microposts#show
PATCH  /users/:user_id/microposts/:id(.:format)      microposts#update
PUT    /users/:user_id/microposts/:id(.:format)      microposts#update
DELETE /users/:user_id/microposts/:id(.:format)      microposts#destroy

Shallow で microposts 単体の URL を定義する

前述の Nested Resource の場合、microposts 単体の perma-link を用意しようとすると /users/:user_id/microposts/:id となり冗長であるため shallow を指定することによって

  resources :users, shallow: true  do
    resources :microposts
  end

作成される routing は下記のように変化して、micropost 単体の Permalink とよしなに解釈するアクションと、リレーション上必ず :user_id が必要なアクションはネストされるように解釈してくれます。

URI Pattern                              Controller#Action
GET    /users/:user_id/microposts(.:format)     microposts#index
POST   /users/:user_id/microposts(.:format)     microposts#create
GET    /users/:user_id/microposts/new(.:format) microposts#new
GET    /microposts/:id/edit(.:format)           microposts#edit
GET    /microposts/:id(.:format)                microposts#show
PATCH  /microposts/:id(.:format)                microposts#update
PUT    /microposts/:id(.:format)                microposts#update
DELETE /microposts/:id(.:format)                microposts#destroy

Member

ネストされたアクションから User の :id を取り扱うためには :user_id として参照していましたが、さらに Member を指定することによって

  resources :users, shallow: true  do
    member do
      resources :microposts
    end
  end

ルーティングは下記のようになり、:user_id を :id として取り扱うことができます。User の :id がわかれば、その User が所有する microposts は自明である場合などは、より自然に取り扱うことができるでしょう。

URI Pattern                                   Controller#Action
GET    /users/:id/microposts(.:format)     microposts#index
POST   /users/:id/microposts(.:format)     microposts#create
GET    /users/:id/microposts/new(.:format) microposts#new

Collection

User :id にひもづく当該 User の「プロフィールもっとみる画面」を作る場合と、そもそも「新着のユーザー一覧」を出したい場合などの使い分けをするには

たとえば下記のように collection を指定してみると

  resources :users, shallow: true  do
    member do
      resources :microposts
      get 'profile_more' #当該ユーザのプロフィールをもっとみる画面を出したい
    end
     collection do
      get 'new_users' #そもそも新着のユーザー一覧を出したい
    end
  end

「プロフィールもっとみる」(profile_more)は :id あり、「新着ユーザー一覧」は :user_id 無しのルーティングとなります。今回設定してみた /users/:id/profile_more のルーティングをみると、前述の Member のありがたみというのも同時にわかりますね。

  profile_more_user GET    /users/:id/profile_more(.:format)   users#profile_more
  new_users_users GET    /users/new_users(.:format)          users#new_users

*1:Tutorial で作る model をそのままつかっています http://ruby.railstutorial.org/chapters/a-demo-app#sec-demo_user_has_many_microposts