Techioz Blog

入れ子になったルート内で Ruby on Rails ポリモーフィック関連付けを操作するにはどうすればよいですか

概要

私の質問をより明確に正しくするために編集されました。 私はポリモーフィックな関連付けを実装するためにいくつかのパターンを試しましたが、数年前に GoRails の Chris から選んだパターンに落ち着きました。これは堅牢であり、ポリモーフィックな関連付けを実装するほぼすべてのトップレベルのリソースで動作します。簡単な例は、Taskable を多態性関連付けとして公開する Tasks モデルです。プロジェクトなどの単一の最上位オブジェクトとの関連付けを簡単に設定できます。 例 (すべてのコードは当面の質問に限定されています):

app/models/task.rb

class Task < ApplicationRecord
  belongs_to :taskable, polymorphic: true
end

アプリ/モデル/プロジェクト.rb

class Project < ApplicationRecord
  has_many :phases
  has_many :tasks, as: :taskable
end

app/controllers/tasks_controller.rb

class TasksController < ApplicationController
  def new 
    @task = @taskable.tasks.new
  end
  # lots of other methods...
end

app/controllers/projects/tasks_controller.rb

class Projects::TasksController < TasksController 
  before_action :set_taskable

  private 
    def set_taskable
      @taskable = Project.find(params[:project_id])
    end
end

config/routes.rb

Rails.application.routes.draw do  
  resources :projects do
    resources :phases
    resources :tasks, module: :projects
  end
end

したがって、どのビューでも次のようにリンクを追加できます。 app/views/tasks/show.html.erb

<%= link_to 'Edit task', edit_polymorphic_path([@taskable, @task]) %>

このパターンを使用すると、コードの重複を最小限に抑えて (懸念事項を使用せずに)、あらゆるオブジェクトをタスクに簡単に接続できます。

問題は、ネストされたオブジェクトを接続する必要があるときに発生します。ルート ファイルでは、プロジェクトがフェーズと 1 対多の関係にあることに注意してください。ここで、フェーズをポリモーフィックな関連付けに接続する必要がある場合 (ここでは例としてタスクを使用します)、ポリモーフィック パスまたは URL を 3 つの部分からなる表記で作成する必要があります: <%= link_to ‘Edit task’, edit_polymorphic_path( [@プロジェクト、@タスク可能、@タスク]) %> 頭を悩ませてあちこち探しましたが、何も見つかりませんでした。どこから始めればよいのかさえ思いつきません。

最初にルートを次のように設定しました。

Rails.application.routes.draw do  
  resources :projects do
    resources :phases do
      resources :tasks, module: :phases
    end
    resources :tasks, module: :projects
  end
end

ルートは正しく出力されますが、パス呼び出しに 3 つのパラメータを渡す必要があります。 <%= link_to ‘タスクの編集’, edit_polymorphic_path([@project, @taskable, @task]) %> ただし、引数リストが異なるため、これは私のポリモーフィックなビューを壊します。したがって、ポリモーフィックな関連付けのルーティング レベルに関係なく、ビューをポリモーフィックに保つ方法を探しています。

私もこれを機能させようとしました: config/routes.rb

Rails.application.routes.draw do  
  resources :projects do
    resources :phases
    resources :tasks, module: :projects
  end
  resources :phases do
    resources :tasks, module: :phases
  end
end

もちろん、Rails はこれを好みません。両方の関連付けが 2 番目のブロックで表現されていないため、完全なルートを作成できないからです。

助けていただきありがとうございます。

解決策

ここで問題が何であるかを見逃しているかもしれませんが、ルートをネストし続けることができます。

Rails.application.routes.draw do  
  resources :projects do
    resources :phases do
      resources :tasks, module: :phases
    end
    resources :tasks, module: :projects
  end
end
#                Prefix Verb URI Pattern                                                     Controller#Action
edit_project_phase_task GET  /projects/:project_id/phases/:phase_id/tasks/:id/edit(.:format) phases/tasks#edit

アップデート

したがって、2 つのルートがあり、それをタスク用の 1 つのテンプレートから使用したいとします。

edit_polymorphic_path([@taskable, @task])           # @taskable is Project
edit_polymorphic_path([@project, @taskable, @task]) # @taskable is Phase

すべてに二重にネストされたルートをいつでも使用できます。

# this will work with @project or without
edit_polymorphic_path([@project, @taskable, @task])

例えば:

<%= edit_polymorphic_path([Project.first, Phase.first, Task.first]) %>
#=> /projects/1/phases/1/tasks/1/edit

<%= edit_polymorphic_path([nil, Project.first, Task.first]) %>
#=> /projects/1/tasks/1/edit