😸

[Rails] ルーティングをシンプルにする方法(as・shallowについて)

2025/01/30に公開

はじめに

引き続き以前書いたコメント機能について、冗長性を修正したいと思います。

今回直したいのは、下記部分。

<%= link_to "削除", task_task_comment_path(task_comment.task, task_comment), method: :delete %>

task_task_comment_path が長くてわかりにくいので、短縮したい。


asオプションを使う

config/routes.rb で asオプションを使用し、パスを短縮する。

resources :tasks do
  resources :task_comments, as: "comments"
end

これにより、task_task_comments_pathtask_comments_path に変わる。

<%= link_to "削除", task_comment_path(task_comment.task, task_comment), method: :delete %>

asオプションでのエラー

しかし、下記エラーが発生してしまった!

ActionView::Template::Error (undefined method `task_task_comments_path' for #<ActionView::Base:0x0000000003aca0>

              recipient.public_send(method, *args, options)
                       ^^^^^^^^^^^^
Did you mean?  task_comments_path
               task_comment_path):
    80:         <% end %>
    81:         <!-- コメント投稿フォーム -->
    82:
    83:         <%= form_with model:[@task, @task_comment] do |f| %>
    84:           <%= f.text_area :comment, placeholder: "コメントを入力", class: "form-input-comment" %>     
    85:           <%= button_tag(type: 'submit', class: 'submit-btn') do %>
    86:             <i class="fa-solid fa-paper-plane"></i>

app/views/public/tasks/show.html.erb:83

なぜエラーが起きたか?

resources :tasks do
  resources :task_comments, as: "comments"
end

上記で、task_comments というリソースを comments という名前に変更している。
その結果、削除リンク内にある通常の task_task_comments_path パスが task_comments_path というパスに変更される。

下記が問題でエラーが起きてる。

<%= form_with model:[@task, @task_comment] do |f| %>
  • @task が親リソース。
  • @task_comment がその子リソース。
    親子リソースの場合、親リソースのIDを含んだURLヘルパーを生成する。
    つまり、task_task_comments_path というパスヘルパーを生成する。
    このヘルパーパスは as で変更したので、form_with内も修正が必要。

解決策

<%= form_with model: @task_comment, url: task_comments_path(@task), local: true do |f| %>
  • 送信先を task_comments_path(@task) を指定する
  • model: @task_comment を記載することで、@task_comment オブジェクトに関連付ける。これにより、フォームの送信後、task_comment モデルから生成されたインスタンスとして、データが保存される。

shallow の使用

routes.rbで、shallow: true を使用するやり方でも、task_comment_path が簡潔になる。

resources :tasks do
  resources :task_comments, shallow: true
end
<%= form_with model: @task_comment, url: task_comments_path, local: true do |f| %>

shallow: true オプションは、親リソースのIDを省略して書くことができる。

「shallow: true」は非推奨なのか?
「shallow: true」は賛否両論があり、プロジェクトによって使いどころが異なる。
深いネストを避けたいときは有効だが、一貫性を重視するなら使わない方がよい場合もある。
そのため、今回は as を使用しました。(shallow でも動いたけど、ルートの統一性を考えて as にした)

非推奨という意見

  • ルーティングの一貫性が失われる
    ndex や new では /tasks/:task_id/task_comments なのに、show や edit では /task_comments/:id になり、URL の構造が統一されない。
  • リソースの所属が不明確になる
    task_comments/:id のように task_id を省略すると、「このコメントはどのタスクに属しているのか?」が URL だけでは分からなくなる。
    API の場合、 /tasks/:task_id/task_comments/:id のような構造の方が、データの関連性を明確にできる。
  • RESTful な設計の原則から外れる
    「子リソースは親リソースに従属すべき」という考えに基づくと、show や edit でも task_id を持つべき。

しかし、「shallow: true」はRails の標準機能であり、公式に非推奨になってはいないらしい。
普通に使用してるコミュニティもあるっぽいので、これはケースバイケースでやっていくしかないなと思いました🤔

as: は ルート名(パスヘルパー)をカスタマイズするだけなので、ルーティングの構造を変えるshallow: とは異なり、一貫性問題は起きにくい。
(↑ここはもう少し理解を深めたい。長くなりすぎるので、明日調べようかな)


参考文献

Discussion