Ruby on rails 为了瘦控制器,rails模型应该考虑其他模型吗?
我到处都读到,业务逻辑属于模型,而不属于控制器,但限制在哪里? 我在玩弄个人会计应用程序Ruby on rails 为了瘦控制器,rails模型应该考虑其他模型吗?,ruby-on-rails,ruby,Ruby On Rails,Ruby,我到处都读到,业务逻辑属于模型,而不属于控制器,但限制在哪里? 我在玩弄个人会计应用程序 Account Entry Operation 创建操作时,仅当创建了相应的条目并将其链接到帐户以使操作平衡时才有效,例如购买6包: o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"}) o.entries.build({:account_id=>1, :amount=&g
Account
Entry
Operation
创建操作时,仅当创建了相应的条目并将其链接到帐户以使操作平衡时才有效,例如购买6包:
o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"})
o.entries.build({:account_id=>1, :amount=>15})
o.valid? #=>false
o.entries.build({:account_id=>2, :amount=>-15})
o.valid? #=>true
现在,在基本操作的情况下向用户显示的表单被简化为隐藏条目详细信息,根据用户请求的操作类型从5个默认帐户中选择帐户(初始化账户->权益到账户,支出资产->支出,赚取收入->资产,借贷负债->资产,支付债务资产->负债…)我希望根据默认值创建条目
我还希望能够创建更复杂的操作(多于2个条目)。对于第二个用例,我将有一个不同的表单,其中暴露了额外的复杂性。第二个用例阻止我在操作中包含借方和贷方字段,并删除条目链接
最好的形式是什么?像我现在这样在SimpleOperationController中使用上述代码,或者在Operation类上定义一个新方法以便调用Operation.new\u simple\u Operation(params[:Operation])
从操作类实际创建和操作条目对象不是打破了关注点的分离吗
我不是在寻求关于我扭曲的会计原则的建议:)
编辑——我似乎没有把自己表达得太清楚。
我不太关心验证。我更关心的是创建逻辑代码应该放在哪里:
假设控制器上的操作名为“花费”,则在使用“花费”时,参数散列将包含:金额、日期、描述。借方和贷方帐户将从调用的操作中派生,但随后我必须创建所有对象。有没有更好
#error and transaction handling is left out for the sake of clarity
def spend
amount=params[:operation].delete(:amount)#remove non existent Operation attribute
op=Operation.new(params[:operation])
#select accounts in some way
...
#build entries
op.entries.build(...)
op.entries.build(...)
op.save
end
或者在操作上创建一个方法,使上面的
def spend
op=Operation.new_simple_operation(params)
op.save
end
这无疑会带来更薄的控制器和更胖的模型,但该模型将创建和存储其他模型的实例,这就是我的问题所在。更容易从每个实体验证自身以及相互依赖的实体将其状态委托给其关联项的状态的角度来考虑。例如,在您的情况下:
class Operation < ActiveRecord::Base
has_many :entries
validates_associated :entries
end
类操作
validates_associated将检查每个关联实体是否有效(在这种情况下,如果操作有效,则应检查所有条目)
尝试从整体上验证模型的整个层次结构是非常诱人的,但正如您所说,最容易做到这一点的地方是控制器,它应该更像是请求和响应的路由器,而不是处理业务逻辑。我认为,控制器应该反映最终用户视图,并将请求转换为模型操作和响应,同时还要进行格式化。在您的案例中,有两种操作表示具有默认帐户/条目的简单操作,以及具有用户选择的条目和帐户的更复杂的操作。表单应该反映用户视图(两个表单具有不同的字段),并且控制器中应该有两个要匹配的操作。然而,控制器不应具有与数据操作方式相关的逻辑,而应具有如何接收和响应的逻辑。我会在Operation类上使用类方法,从表单中获取适当的数据,并根据需要创建一个或多个对象,或者将这些类方法放在支持类上,支持类不是AR模型,但具有跨模型边界的业务逻辑。单独的实用程序类的优点是它使每个模型都集中在一个目的上,缺点是实用程序类没有定义的居住场所。我把它们放在lib/中,但是Rails并没有为模型助手指定一个位置。虚拟属性(更多信息和)将非常有帮助。将整个参数传递回模型可以使控制器中的事情变得简单。这将允许您动态构建表单并轻松构建条目对象
class Operation
has_many :entries
def entry_attributes=(entry_attributes)
entry_attributes.each do |entry|
entries.build(entry)
end
end
end
class OperationController < ApplicationController
def create
@operation = Operation.new(params[:opertaion])
if @operation.save
flash[:notice] = "Successfully saved operation."
redirect_to operations_path
else
render :action => 'new'
end
end
end
现在,您将收到一条错误消息,告诉用户金额已关闭,他们应该解决问题。你可以在这里得到真正的幻想,并通过具体的问题来增加很多价值,比如告诉他们他们有多少折扣
但是模型将创建和存储其他模型的实例,这就是我的问题所在
这有什么问题
如果您的“业务逻辑”声明一个操作必须有一组有效的条目,那么Operation类肯定知道并处理您的条目对象没有错
如果你做得太过分,让你的模型处理一些他们不需要知道的事情,比如EntryHtmlFormBuilder或其他什么东西,你只会遇到问题:-)如果你想把这个逻辑嵌入到任何特定的模型中,为什么不把它们放到一个observer类中呢,这将使您创建关联项的逻辑与所观察的类分开。I har最初接受您的答案,但它假设条目的参数已定义,而事实并非如此。然后,我必须在控制器中创建正确的参数,这与创建对象相同:),但是您的技巧在其他地方会很有用,如果IRRC的话,在rails 2.2中会得到修复
class Operation
# methods from above
protected
def validate
total = 0
entries.each { |e| t += e.amount }
errors.add("entries", "unbalanced transfers") unless total == 0
end
end