在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
方法中调用这些方法,但我觉得这可能会使我的控制器膨胀太多,并且当我试图验证我的参数时,可能会引起噩梦。
答案 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