Rails 使用一个提交按钮提交多个嵌套表单

Rails submit multiple nested forms with one submit button

我正在为竞赛排行榜构建一个 rails 应用程序。数据模型为

class Tournament
  has_many :events
end

class User
  has_many :entries
  has_many :events, through: :entries
end

class Events
  has_many :entries
  belongs_to :tournament
end

class Entry
  belongs_to :user
  belongs_to :event
end

每个锦标赛有 2 个赛事,每个赛事用户最多可以输入 4 个参赛作品,因此每个锦标赛总共有 8 个参赛作品。我想允许用户使用一个提交按钮在一个表单中输入所有 8 个条目。

我的路线

resources :entries, except: [:index, :show] do
  collection do
    match 'create_collection',  via: [:create]
  end
end

我在 Entries 控制器中也有一个 create_collection 方法来处理条目,但我还没有做到这一点。我不确定如何让表格正常工作。

_form.html.haml(由 views/tournaments/index.html.haml 渲染并传递 next_events,这是锦标赛的 2 个事件实例。

.entry-form
  = form_tag create_collection_entries_path do |form|
    - next_events.each do |event|
      = event.name
      = fields_for "events[]", event do |f|
        - 4.times do 
          = f.fields_for :entries do |f|
            = f.label_tag 'player'
            = f.text_field 'player'

    = submit_tag "Submit", class: "btn btn-success"

表单显示如我所料,但是当我单击提交按钮时,只有最后 4 个条目在参数中提交并且事件 ID 未被传递

"events"=>[{"entries_attributes"=>
  {"0"=>{"player"=>"player5","id"=>"33"},
   "1"=>{"player"=>"player6", "id"=>"34"},
   "2"=>{"player"=>"player7", "id"=>"35"},
   "3"=>{"player"=>"player8", "id"=>"36"}}}],
"commit"=>"Submit"}

如何使用正确的事件 ID 获取要提交的两个事件的所有入口参数?

我的研究表明 rails 没有提供一种方法,可以通过一次提交以一种形式提交模型的多个实例。我查看了 this and this,这对我来说不太适用。所以我自己滚了。

以相当易于管理的方式提交参数哈希中所有值的表单。这不是最优雅的解决方案,需要在后端进行大量处理。但是我找不到更 railsy 的方式来做到这一点。

form.html.haml

.entry-form.on
  = form_tag create_collection_entries_path(@user.id) do
    - Tournament.next.events.each do |event|
      %div= event.name
      %div
        - @num_of_entry_forms.times do |n|
          %div
            = label_tag 'player'
            = text_field_tag "event[#{event.id}][entries][player_#{n}]" 

    = submit_tag "Submit", class: "btn btn-success"

routes.rb

resources :entries, except: [:index, :show] do
  collection do
    get '/new_collection/:user_id', to: 'entries#new_collection', as: 'new_collection'
    post '/create_collection/:user_id', to: 'entries#create_collection', as: 'create_collection'
  end
end

entries_controller.rb

def new_collection
  @num_of_entry_forms = EventEntryCollectionCreator::MAX_ENTRIES_PER_EVENT
  @user = User.find(params[:user_id])
end

def create_collection
  @eec = EventEntryCollection.new(params)
  if @eec.save
    redirect_to root_path
  else
    render :new_collection
  end
end

我仍在研究作为服务模型的 EventEntryCollectionCreator,但这是它的核心。

MAX_ENTRIES_PER_EVENT = 4

def save
  params["event"].each do |event|
    event.last["entries"]["player"].each do |entry|
      entry = Entry.new(event_id: event.first, user_id: user_id, player: entry.last)
      @entries_collection << entry
    end
  end
  save_collection
end

private
def save_collection
  @entries_collection.each do |entry|
    if valid_entry?
      entry.save
      @valid_models << entry
    else
      @invalid_models << entry
    end
  end
end

正如我所说,这不是最优雅的解决方案,但它解决了问题。好处是杂乱的后端处理从控制器中抽象出来,进入它自己的服务模型。消极方面是有一些代码味道,很多强制值进入散列和对象必须了解太多关于散列结构的信息。

我对其他解决方案或对正在进行的工作的改进持开放态度。