Railsでwebアプリケーションを開発する際に、あるリソースの配下に別のリソースを入れ子にさせたいことはよくあると思います。
つい最近、この部分で自分が少しハマったため、まとめておきたいと思います。
ネストしたリソースとは?
例えば、以下のようにブログとコメントのmodelがあるとします。
# app/models/blog.rb class Blog < ApplicationRecord has_many :comment end # app/models/comment.rb class Comment < ApplicationRecord belongs_to :blog end
routes.rb
でルーティングを以下のように設定するとブログとそれに紐づくコメントという親子関係を表せるようになります。
resources :blogs do resources :comments end
発行されるパスは以下のようになります。
HTTPメソッド | パス | コントローラ#アクション |
---|---|---|
GET | /blogs/:blog_id/comments | comments#index |
GET | /blogs/:blog_id/comments/new | comments#new |
POST | /blogs/:blog_id/comments | comments#create |
GET | /blogs/:blog_id/comments/:id | comments#show |
GET | /blogs/:blog_id/comments/:id/edit | comments#edit |
PATCH/PUT | /blogs/:blog_id/comments/:id | comments#update |
DELETE | /blogs/:blog_id/comments/:id | comments#destroy |
上記を例にネストしたリソースを定義する目的の一つは親のリソースのidを入れ子になっている子のコントローラ内で外部キーとして利用が可能な点かなと考えています。
namespaceとは何が違うのか
私が整理できていなかったことの一つに namespace
とネストしたリソースの使い分けがあります。
namespace
でルーティングをグループ化するときは例えば以下のように宣言できます。
namespace :blogs do resources :comments end
発行されるパスは以下になります。
HTTPメソッド | パス | コントローラ#アクション |
---|---|---|
GET | /blogs/comments | blogs/comments#index |
GET | /blogs/comments/new | blogs/comments#new |
POST | /blogs/comments | blogs/comments#create |
GET | /blogs/comments/:id | blogs/comments#show |
GET | /blogs/comments/:id/edit | blogs/comments#edit |
PATCH/PUT | /blogs/comments/:id | bogs/comments#update |
DELETE | /blogs/comments/:id | blogs/comments#destroy |
ネストしたリソースの宣言との結果の違いが分かるでしょうか?
(私はこれが整理できていなかったせいでハマりました・・・)
パスとコントローラのアクションを見ていただくと
- パスはblogsのidを持っていない
- コントローラのアクションはグループ化したルーティングに基づいてファイルが構成されている
という違いがあります。
ハマったこと
ここまで読んでいただいた方は、なんとなくお察しかもしれませんが、私は最近ネストしたリソースを宣言して意図したコントローラに処理が入っていないことでハマりました・・・!
やっていたこととしては以下の内容です。(実際の実装ではなく例です)
HTTPメソッド | パス | コントローラ#アクション |
---|---|---|
GET | /blogs/:blog_id/comments | comments#index |
この時だけ外部からのリクエストの際にログイン認証をスキップするという実装をしたかったのですが、ログイン認証に弾かれてログイン画面にリダイレクトしてしまうという事象が起きていました。
実はファイルの構成で
- app/controllers/blogs/comments_controller.rb
- app/ controllers/comments_controller.rb
というように comments_controller.rb
はファイル構成の違いで2個あり、一つ目は認証をスキップしていて二つ目は認証が必要な実装をしていました。
もう一度表をよく見てみるとちゃんと書いてありますね。
comments#index
ネストしたリソースを宣言した際、コントローラのアクションはネストしません。
つまり、app/controllers/comments_controller.rb
のアクションが実行されるということです。
Railsガイドもrails routes
の結果も見ていたはずなのに・・・
ネストしたリソースで app/controllers/blogs/comments_controller.rb
のアクションが実行されるようにするには以下のようにしてあげれば解決できます。
resources :blogs do resources :comments, controller: "blogs/comments" end
終わりに
Railsでwebアプリケーションを開発する際に初心者は特にroutes.rbを触る機会はあまりないのではないでしょうか?
私は技術書やチュートリアルで何度か宣言を書いたことがある程度で、Railsのルーティングについては理解が怪しいままでした。
その結果、ネストしたリソースと namespace
とが混ざってしまい、問題を解決するのに少し時間がかかりました。
こちらのブログが同じようにルーティングの理解が怪しい・・・という方の参考に少しでもなれば嬉しいです!
もしも内容に誤りがありましたら、コメント等をいただけると幸いです。