ruby-on-rails - 我应该将我的自定义方法从 Controller 移动到模型吗?

标签 ruby-on-rails ruby design-patterns model-view-controller model

假设我有一个 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/

相关文章:

design-patterns - Unity MonoBehaviour 单例

ruby-on-rails - Rails 5 范围像多个部分字符串

ruby-on-rails - FactoryGirl 与 Refile gem

arrays - 当字符与前一个字符不同时,在 ruby​​ 中分割字符串

java - 如何组织 Java 业务对象类

java - 设计更好的 API?

ruby-on-rails - 适用于多语言站点的Rails CMS

ruby-on-rails - 为什么 ActiveRecord::Base 上的猴子修补方法会重新定义 Model.count 上不相关的方法

Ruby 递归问题

ruby-on-rails - 如何为 Rails View 返回一个字符串