🛤️

Railsのルーティング:Shallow Nestingの完全ガイド

に公開

はじめに

Railsでネストしたリソースのルーティングを設定する際、深くネストしすぎると「overly complex and cumbersome to maintain(過度に複雑でメンテナンスが面倒)」な状態に陥ることがあります。

例えば、以下のような3階層のネストを考えてみましょう。

/publishers/1/magazines/2/photos/3

このようなURLに対応するルートヘルパーは publisher_magazine_photo_url となり、3つのオブジェクトをすべて指定する必要があります。これは非常に冗長で、コードの可読性やメンテナンス性を低下させます。

一般的な経験則として、リソースのネストは1階層までに留めることが推奨されています。

本記事では、この問題を解決する「Shallow Nesting(シャローネスティング)」パターンについて詳しく解説します。

Shallow Nestingとは

Shallow Nestingは、ネストの深さを抑えつつ、リソース間の階層関係を適切に表現するルーティング手法です。

基本的な考え方

  • コレクションアクションindex, new, create):親リソースのスコープ下に配置し、階層関係を明示
  • メンバーアクションshow, edit, update, destroy):ネストさせず、リソースを一意に識別できる最小限の情報でルートを構築

**「メンバーアクション」**は、個別のリソースに対して作用し、そのリソースを特定するためにIDが必要なアクション(show, edit など)を指します。

**「コレクションアクション」**は、リソース全体に対して作用するアクション(index など)を指します。

実装方法

方法1::only オプションを使った明示的な指定

resources :articles do
  resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

この方法では、コレクションアクションとメンバーアクションを明示的に分けて定義します。:only オプションで、Railsが生成するルートを限定しています。

方法2::shallow オプションを使った簡潔な記述

resources :articles do
  resources :comments, shallow: true
end

この記述は方法1と全く同じルートを生成しますが、より簡潔で読みやすくなります。

方法3:親リソースレベルでの一括設定

resources :articles, shallow: true do
  resources :comments
  resources :quotes
end

親リソースに shallow: true を指定すると、その配下のすべての子リソースが自動的にshallowになります。複数のネストリソースがある場合に便利です。

生成されるルート

上記の設定により、以下のようなルートが生成されます。

コレクションアクション(親のIDが必要)

HTTPメソッド パス アクション ヘルパー
GET /articles/:article_id/comments comments#index article_comments_path
POST /articles/:article_id/comments comments#create article_comments_path
GET /articles/:article_id/comments/new comments#new new_article_comment_path

メンバーアクション(コメントのIDだけで識別可能)

HTTPメソッド パス アクション ヘルパー
GET /comments/:id/edit comments#edit edit_comment_path
GET /comments/:id comments#show comment_path
PATCH/PUT /comments/:id comments#update comment_path
DELETE /comments/:id comments#destroy comment_path

ポイント:既存のコメントを操作する際は、どの記事のものかをパスで明示する必要がありません。データベース上でコメントのIDは一意なので、それだけで特定できるためです。

CommentsControllerの実装例

Shallow Nestingを使用した場合のコントローラ実装例を見てみましょう。

class CommentsController < ApplicationController
  before_action :set_article, only: [:index, :new, :create]
  before_action :set_comment, only: [:show, :edit, :update, :destroy]

  # GET /articles/:article_id/comments
  def index
    @comments = @article.comments
  end

  # GET /articles/:article_id/comments/new
  def new
    @comment = @article.comments.build
  end

  # POST /articles/:article_id/comments
  def create
    @comment = @article.comments.build(comment_params)
    
    if @comment.save
      redirect_to @comment, notice: 'コメントを投稿しました。'
    else
      render :new, status: :unprocessable_entity
    end
  end

  # GET /comments/:id
  def show
  end

  # GET /comments/:id/edit
  def edit
  end

  # PATCH/PUT /comments/:id
  def update
    if @comment.update(comment_params)
      redirect_to @comment, notice: 'コメントを更新しました。'
    else
      render :edit, status: :unprocessable_entity
    end
  end

  # DELETE /comments/:id
  def destroy
    @comment.destroy
    redirect_to article_comments_url(@comment.article), notice: 'コメントを削除しました。'
  end

  private

  def set_article
    @article = Article.find(params[:article_id])
  end

  def set_comment
    @comment = Comment.find(params[:id])
  end

  def comment_params
    params.require(:comment).permit(:body, :author)
  end
end

実装のポイント

  • コレクションアクションindex, new, create):params[:article_id] から親リソースを取得
  • メンバーアクションshow, edit, update, destroy):params[:id] から直接コメントを取得
  • before_action で処理を分けることで、各アクションがシンプルになる

応用:shallowブロック

複数のリソースをまとめてshallowにする場合は、shallow ブロックを使うこともできます。

shallow do
  resources :articles do
    resources :comments
    resources :quotes
  end
end

この記述は、ブロック内のすべてのネストがshallowになります。

カスタマイズオプション

Shallow Nestingには2つのカスタマイズオプションがあります。

:shallow_path - パスのプレフィックスを追加

scope shallow_path: "sekret" do
  resources :articles do
    resources :comments, shallow: true
  end
end

メンバーアクションのパスが /sekret/comments/:id のようになります。

:shallow_prefix - ルートヘルパー名にプレフィックスを追加

scope shallow_prefix: "sekret" do
  resources :articles do
    resources :comments, shallow: true
  end
end

ヘルパー名が edit_sekret_comment_path のようになります(パス自体は変わりません)。

まとめ

Shallow Nestingを使うことで、以下のメリットが得られます。

  • URLがシンプルになり、理解しやすくなる
  • ルートヘルパーが簡潔になる
  • メンテナンス性が向上する
  • RESTfulな設計を保ちつつ、実用的なルーティングが実現できる

深くネストしたルーティングに悩んでいる場合は、ぜひShallow Nestingの導入を検討してみてください。

参考リンク

Discussion