我应该将我的自定义方法从控制器转移到模型中吗? [英] Should I move my custom methods to model from controller?

查看:78
本文介绍了我应该将我的自定义方法从控制器转移到模型中吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个产品型号和ProductsController。控制器具有所有标准的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
如果@po
如果@ order.save
format.html {render:check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order,flash:{success:哇!输入很好!}}
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:无法创建订单,某些验证失败}}
format.json {render status:400,json:{status::error,message:Order 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:无法创建订单,未生成订单编号}}
format.json {render status:400,json:{status::error,me ssage:po的问题,错误:@ 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:无法创建订单,库存低}}
format.json {render status:400,json:{status::error ,消息:产品问题,错误:@ product.errors}}
end
end
end
....
end

这只是给出一些操作的复杂性的想法。



现在的问题:所有这些善良应该被转化为模范?我正在处理业务逻辑,这应该在控制器中,但是随着Fat Models&薄控制器,在我看来,它应该被移开,如果是,那么剩下的是什么?



奖金问题:
我将来到需要在代码中使用某些功能的用例,而不是通过REST界面。 I.E.在运行耙子任务时,我需要使用check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order。就像生成一些基于低库存或电子邮件事件的订单一样。虽然我可以使用这里描述的选项:如何从Rails的控制台调用controller / view方法?将这个动作作为模型的一部分,那么在这样一种情况下,什么是Rails最佳实践的行动方案呢?



解决方案

考虑将您的逻辑移动到服务对象中。我认为将控制器逻辑推入模型是将问题转移到不同的位置。是的,你确实将逻辑隔离到一个单一的区域,但是有一些情况是你最终将逻辑移动到模型中,因为它是一个惯例,而不是真正属于它的事实。



服务对象可以帮助您减少重复并隔离您的业务逻辑,而无需让模型参与不需要了解的事项(例如,您重复的json响应)。

  class OrderService 
def initialize(legacy_alphanumeric_product_number)
#do stuff
end
#做更多的东西
end

从控制器可以调用

  def check_whatever 
@order = OrderService.new(params [:some_product_code])
@ order.check_something
#做更多的东西
结束

查看 7模式到Refac Fat FatRecord模型。我觉得这很有帮助在服务对象上还有一个 RailsCasts episode (需要预订)。


Let's say I have a Product model and ProductsController. Controller has all the standard CRUD method and Product does all kinds of validations etc.

Here is an issue. I have several custom very complex actions which also need to respond in multiple formats( json, html, xml, csv, pdf etc). Business logic reasons for this are beyond the scope of the question. Let's just way this is the way it has to be done. Also I use InheritedResources gem but I don't think it matters for the question.

For example ( this is a mock app, which is GREATLY simplified - I removed all kinds of if else statements and loops and localization, etc ):

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 

This is just to give an idea of complexity of some actions.

Now the question: should all of that goodness be moved into model? I'm dealing with business logic, which should be in the controller, but with trying keep with the rule of thumb Fat Models & Thin Controllers, it seems to me that it should be moved away, if so then what there is left to move?

Bonus Q: I'm coming accross use cases where I might need to use some of that functionality in the code, not through a REST interface. I.E. I need to use check_stock_using_legacy_identifier_and_create_a_unique_po_number_and_place_an_order, while running a rake task. Like generating some orders based on low stock, or from an email event, etc. While I can use options described here: How do I call controller/view methods from the console in Rails? , having this action as part of a model would make it easier wouldn't it?

So what is a Rails Best Practices course of action in a case like this?

解决方案

Consider moving your logic into a service object. I think shoving controller logic into the model is simply moving the problem to a different location. Yes, you do isolate the logic to a single area, but there are instances where you end up moving logic to models because of convention rather than the fact that it really belongs there.

A service object can help you reduce duplication and isolate your business logic without getting the model too involved in things it does not need to know about (your repeated json response, for example).

class OrderService
  def initialize(legacy_alphanumeric_product_number)
    # do stuff
  end
  # do more stuff
end

From the controller, you can just call

def check_whatever
  @order = OrderService.new(params[:some_product_code])
  @order.check_something
  # do more stuff
end

Take a look at 7 Patterns to Refactor Fat ActiveRecord Models. I found it very helpful. There is also a RailsCasts episode on service objects (requires pro subscription).

这篇关于我应该将我的自定义方法从控制器转移到模型中吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆