从Rails表单创建批量对象 - 使用整数设置对象的数量

时间:2018-02-01 21:21:55

标签: ruby-on-rails ruby forms

在Rails 5.1中,我想使用一个表单,该表单将根据表单中指定的对象数创建对象。例如,我有以下字段:

<%= form_for(@object, remote: true, html: {"data-type" => :json, id: "object_new"}) do |f| %>

  <%= f.label :name, "Object Name", for: "object_name" %>
  <%= f.text_field :name, type: "text", id: "object_name" %>

  <%= f.label :amount, "Amount of objects", for: "object_amount" %>
  <%= f.number_field :amount, type: "number", id: "object_amount" %>

  <%= f.label :start_at, "Start Count At...", for: "object_start_at" %>
  <%= f.number_field :start_at, type: "number", id: "object_start_at" %>

  <%= f.button 'Create New Object(s)', id: 'new_objects_submit' %>

<%end %>

我的对象模型如下所示:

class Object < ApplicationRecord
  attr_accessor :amount, :start_at
  validates :name, uniqueness: true
end

基本思想是您为要创建name的对象提供,并使用amount字段设置要创建的对象数量。 start_at字段表示要开始的数字。

所以说名称为Basic,金额为3,start_at为2。创建的对象是:

Basic02
Basic03
Basic04

我的问题是如何以最友好的Rails方式实现这一目标?我想在控制器中添加私有方法来解析数据以满足我的需要,并在控制器的create方法中调用这些方法,但我觉得这可能会使我的控制器膨胀太多,并且当我试图验证我的参数时,可能会引起噩梦。

1 个答案:

答案 0 :(得分:2)

在您的创建操作中,您希望执行以下操作:

def create

  @object.amount.times do |i|
    object = Object.new do |object|
      name = @object.name.strip # => "Basic"
      number = (@object.start_at + i + 1) # => 2
      formatted_number = number < 10 ? "0#{number}" : number # => "02"
      object.name = @object.name.strip + formatted_number # => "Basic02"
    end
    object.save!
  end

end

我做了几次类似的事情,这种事情往往会变成难以理解的混乱。

我建议将此逻辑粘贴到服务对象中。这样你就可以抽象出任何相关的逻辑和错误处理,而不会使控制器膨胀,即:

ObjectsController#create

def create
  if CreateObjects.call(@object)
    respond_to do |format|
      format ... #success
    end
  else
    respond_to do |format|
      format ... #failure
    end
  end
end

CreateObjects服务:

# app/services/create_objects.rb 

class CreateObjects
  def self.call(object)
    new(object).call
  end

  def initialize(object)
    @object = object
  end

  def call
    ActiveRecord::Base.transaction do # will only save if _all_ the objects are saved
      @object.amount.times do |i|
        create_object(i)
      end
    end
  end

  def create_object(i)
    object = Object.new do |object|
      name = @object.name.strip # => "Basic"
      object.name = @object.name.strip + formatted_number(i) # => "Basic02"
    end
    object.save!
  end

  def formatted_number(i)
    number = (@object.start_at + i + 1) # => 2
    number < 10 ? "0#{number}" : number # => "02"
  end
end