假设我有一个 Product 模型和 ProductsController。 Controller 具有所有标准的 CRUD 方法,Product 进行各种验证等。
这里有个问题。 我有几个自定义的非常复杂的操作,它们也需要以多种格式(json、html、xml、csv、pdf 等)进行响应。这样做的业务逻辑原因超出了问题的范围。让我们这样吧,这是必须完成的方式。 我也使用 InheritedResources gem,但我认为这对这个问题并不重要。
例如(这是一个模拟应用程序,大大简化了 - 我删除了所有类型的 if else 语句和循环以及本地化等):
class ProductController < InheritedResources::Base
....
def check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order
@order = Order.new
@product = Product.find(params[:legacy_alphanumeric_product_number])
if @product.stock > 5
@po = LegacyOrder.create_po
if @po
if @order.save
format.html{ render :check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order, flash: {success: "Wow! Input was good!"}}
format.json{ render status: 400, json: {status: :success, message: "Order created"}}
else
format.html{ render :check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order, flash: {error: "Can't create order, some validations failed"}}
format.json{ render status: 400, json: {status: :error, message: "Problem with order", errors: @order.errors}}
end
else
format.html{ render :check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order, flash: {error: "Can't create order, PO number wasn't generated"}}
format.json{ render status: 400, json: {status: :error, message: "Problem with po", errors: @po.errors}}
end
else
respond_to do |format|
format.html{ render :check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order, flash: {error: "Can't create order, stock is low"}}
format.json{ render status: 400, json: {status: :error, message: "Problem with product", errors: @product.errors}}
end
end
end
....
end
这只是为了让您了解一些操作的复杂性。
现在的问题是:是否应该将所有这些优点都转移到模型中?我正在处理业务逻辑,它应该在 Controller 中,但是在尝试遵循 Fat Models & Thin Controllers 的经验法则时,在我看来它应该被移走,如果是这样那么还有什么可以移动?
奖金问题: 我遇到了一些用例,在这些用例中我可能需要在代码中使用某些功能,而不是通过 REST 接口(interface)。 IE。我需要在运行 rake 任务时使用 check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order。比如根据低库存或电子邮件事件等生成一些订单。虽然我可以使用此处描述的选项:How do I call controller/view methods from the console in Rails? ,将此操作作为模型的一部分会更容易,不是吗?
那么在这种情况下,Rails 最佳实践行动方案是什么?
最佳答案
考虑将您的逻辑移至服务对象中。我认为将 Controller 逻辑插入模型只是将问题转移到不同的位置。是的,您确实将逻辑隔离到一个区域,但在某些情况下,您最终将逻辑移至模型是因为约定而不是它真正属于那里的事实。
服务对象可以帮助您减少重复并隔离您的业务逻辑,而不会让模型过多地涉及它不需要知道的事情(例如,您重复的 json 响应)。
class OrderService
def initialize(legacy_alphanumeric_product_number)
# do stuff
end
# do more stuff
end
在 Controller 中,你可以直接调用
def check_whatever
@order = OrderService.new(params[:some_product_code])
@order.check_something
# do more stuff
end
看看7 Patterns to Refactor Fat ActiveRecord Models .我发现它很有帮助。还有一个RailsCasts episode在服务对象上(需要专业版订阅)。
关于ruby-on-rails - 我应该将我的自定义方法从 Controller 移动到模型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16907578/