有多个答案可以解释如何拥有嵌套资源,但是,我的用例有些不同。
批次属于订单,一个订单有很多批次。
如果您有订单的表单并可以在该表单中创建批次,我可以理解它是如何工作的,但是无法理解我的处境。
我有一个嵌套资源(批处理)的表单,其中父项(订单)可能存在或不存在。他们可以通过单选按钮选择是否存在。如果存在,那么他们只需选择它所属的顺序即可。如果不存在,我将显示订单字段并提交订单参数以及批处理参数。如果批次未保存,我想确保回滚订单创建。
这是我到目前为止的代码。
def create
@batch = Batch.new(batch_params)
Batch.transaction do
if params[:new_order] == "newOrder"
@order = Order.new(order_params)
@order.project_id = params[:batch][:project_id]
begin
@order.save!
rescue
respond_to do |format|
format.html { render action: 'new' }
format.json { render json: {order: @order.errors}, status: :unprocessable_entity }
format.js { render json: {order: @order.errors}, status: :unprocessable_entity }
end
raise ActiveRecord::Rollback
return
end
#@batch.order_id = @order.id
end
respond_to do |format|
begin
@batch.save!
format.html { redirect_to @batch, notice: 'Batch was successfully created.' }
format.json { render json: @batch }
format.js { render json: @batch }
rescue
binding.pry
raise ActiveRecord.Rollback
format.html { render action: 'new' }
format.json { render json: {batch: @batch.errors}, status: :unprocessable_entity }
format.js { render json: {batch: @batch.errors}, status: :unprocessable_entity }
end
end
end
end
这并不像我想要的那样,而且看起来很丑。我有种感觉,我正在变得比我所需要的更加困难。在这种情况下最好的方法是什么?非常感谢!
答案 0 :(得分:2)
似乎这是使用服务对象https://www.engineyard.com/blog/keeping-your-rails-controllers-dry-with-services的绝佳机会。
此模式对于保持模型和控制器整洁,并确保应用程序的那些部分遵守“单一职责原则”非常有用。
在这种情况下,我要做的是创建一个名为CreateBatch
的服务类,该服务类接受参数并针对每种情况执行正确的逻辑。然后,您可以在控制器中呈现正确的输出。这也将有助于清理您的条件和提早退货。
例如:
# app/controllers/batches_controller.rb
def create
project_id = params[:batch][:project_id]
new_order = params[:new_order]
result = CreateBatch.new(new_order, batch_params, order_params, project_id).call
if result.errors
# handle errors with correct format
else
# handle successful response with correct format
end
end
# app/services/create_batch.rb
class CreateBatch
def initialize(new_order, batch_params, order_params, project_id)
@new_order = new_order
@batch_params = batch_params
@order_params = order_params
@project_id = project_id
end
def call
if new_order?
create_new_order
else
add_batch_to_existing_order
end
end
private
def new_order?
@new_order
end
def create_new_order
order_params = @order_params.merge(project_id: @project_id)
Order.save(order_params)
end
def add_batch_to_existing_order
Batch.create(@batch_params)
end
end
我没有执行此操作,因此可能需要一些调整才能工作,但是,我希望这是一个很好的起点。关于此重构的令人敬畏的事情之一是,您现在有1个条件逻辑和1个条件响应,无需添加Transaction
块,并且无需提前返回。将call
方法分解为两种可从控制器调用的方法可能很有意义。使用这样的服务类也使代码更容易进行单元测试。
答案 1 :(得分:1)
为什么不将错误处理和响应呈现移到事务之外?
def create
@batch = Batch.new(batch_params)
Batch.transaction do
if params[:new_order] == "newOrder"
@order = Order.new(order_params)
@order.project_id = params[:batch][:project_id]
@order.save!
@batch.order_id = @order.id
@batch.save!
end
end
respond_to do |format|
format.html { redirect_to @batch, notice: 'Batch was successfully created.' }
format.json { render json: @batch }
format.js { render json: @batch }
end
rescue StandardError => error
@error = error
format.html { render action: 'new' }
format.json { render json: {error: @error, batch: @batch.errors}, status: :unprocessable_entity }
format.js { render json: {error: @error, batch: @batch.errors}, status: :unprocessable_entity }
end
它仍然很复杂,但是绝对可读。下一步是将整个交易块提取到服务中。