首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >rails模型是否应该为了瘦控制器而关注其他模型?

rails模型是否应该为了瘦控制器而关注其他模型?
EN

Stack Overflow用户
提问于 2008-09-15 16:01:15
回答 5查看 872关注 0票数 6

我到处读到业务逻辑属于模型,而不是控制器,但是限制在哪里呢?我在玩个人会计应用程序。

代码语言:javascript
复制
Account
Entry
Operation

创建操作时,只有在创建相应条目并将其链接到帐户以便操作平衡时,才有效,例如购买一个6包:

代码语言:javascript
复制
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个条目)。对于第二个用例,我将有一个不同的形式,其中额外的复杂性是exposed.This,第二个用例阻止了我在操作中包括一个借方和信用字段,并去掉了条目链接。

哪一种是最好的形式?像现在一样在SimpleOperationController中使用上面的代码,或者在操作类上定义一个新方法,这样我就可以调用SimpleOperationController了

它不是打破了关注点的分离,实际上创建和操作了操作类中的条目对象吗?

(我不想就我扭曲的会计原则征求意见:)

编辑--我好像表达得不太清楚。我不太关心验证问题。我更关心的是创建逻辑代码应该去哪里:

假设控制器上的操作称为spend,当使用spend时,params散列将包含: amount、date、description。借方和贷方帐户将从被调用的操作中派生出来,但是我必须创建所有的对象。如果你能

代码语言:javascript
复制
#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

或者在操作上创建一个方法,使上面的内容看起来像

代码语言:javascript
复制
def spend
  op=Operation.new_simple_operation(params)
  op.save
end

这肯定会给出一个更薄的控制器和一个更胖的模型,但是这个模型会创建和存储其他模型的实例,这正是我的问题所在。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2008-09-15 21:19:57

但是,该模型将创建和存储其他模型的实例,这正是我的问题所在。

这是怎么回事?

如果您的“业务逻辑”声明一个操作必须有一组有效的条目,那么操作类当然没有任何问题需要了解,并处理您的条目对象。

只有当你走得太远,让你的模型操纵他们不需要知道的事情时,你才会遇到问题,比如EntryHtmlFormBuilder之类的:-)

票数 6
EN

Stack Overflow用户

发布于 2008-09-15 21:08:43

虚拟属性(更多信息,这里这里)将对此有很大帮助。将整个参数传递回模型,在控制器中保持简单。这将允许您动态地构建表单并轻松地构建条目对象。

代码语言:javascript
复制
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中重写alone:

代码语言:javascript
复制
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

现在,您将收到一条错误消息,通知用户金额已关闭,他们应修复此问题。你可以在这里得到真正的花哨,通过对问题的具体说明来增加很多价值,比如告诉他们他们有多少钱。

票数 2
EN

Stack Overflow用户

发布于 2008-09-15 16:23:46

从验证自身的每个实体以及相互依赖的实体将其状态委托给其关联条目的状态来考虑,这是比较容易的。例如,在你的例子中:

代码语言:javascript
复制
class Operation < ActiveRecord::Base
  has_many :entries
  validates_associated :entries
end

validates_associated将检查每个关联实体是否有效(在本例中,如果操作有效,所有条目都应该有效)。

尝试验证整个模型的层次结构是很有诱惑力的,但正如您所说,最容易做到的地方是控制器,它应该更像一个请求和响应路由器,而不是处理业务逻辑。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64214

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档