【问题标题】:Which MVC layer should handle a child claiming a toy?哪个 MVC 层应该处理声称玩具的孩子?
【发布时间】:2012-12-12 02:02:36
【问题描述】:

我的应用中有两个模型:childhas_many :toystoy belongs_to :child。我进行了为此工作所需的数据库迁移(将child_id 添加到toys 表中)。

起初,孩子独立存在,玩具独立存在(没有关联)。在幼儿园的每一天开始时,没有孩子拥有任何玩具。要玩玩具,孩子必须先认领它,然后成为它的主人。所以,现在我需要以某种方式实现一个child.claim(toy) 方法,在这里我被卡住了。具体来说:

  1. 应该进入child 控制器或模型吗?或者也许应该以某种方式将两者分开?
  2. 如果它应该进入控制器,它应该对应于 CRUD 操作之一还是它自己的东西,如 def claim(toy)

编辑 1: 孩子是用户,通过浏览器登录。 (今天的孩子可以做一些了不起的事情)

【问题讨论】:

  • 谁是系统孩子的用户,还是kinder的园长?
  • 孩子是用户。我会在问题中说明这一点。

标签: ruby-on-rails model-view-controller associations


【解决方案1】:

我会构建一个单独的类来处理有关儿童和玩具的所有逻辑。称其为上下文,称其为关注,但要做到这一点。

class ToyChild # or ToyInteraction or ChildContext::Toys...

  attr_reader :toy, :child

  def initialize(toy, chid)
    @toy   = toy
    @child = child
  end

  def associate
    toy.child = child
    # could be more difficult: you should check if the child has not enough toys, if the toy is not already assigned to another child etc...
    #I'd avoid saving here since you may want to perform other operations
  end

  def foo
    #code here
  end

end

在控制器中:

assoc = ToyChild.new(toy, child).associate
assoc.save

这种编码风格:

  • 更容易测试

  • 职责分工明确

  • 保持干燥(控制器中没有特定代码)

【讨论】:

  • 这是一个有趣的方法,但我不知道如何去做:我将把这个文件放在哪里?我将如何要求它?我想它以后还会包含当孩子厌倦玩具时的“放弃”方法,对吧?
  • 我遵循clean-ruby.com 中解释的约定我的所有上下文都在/app/contexts 中,因为它们在/app 下,它们是自动加载的。是的,我会把所有的交互逻辑放在这个类中
  • 明白,谢谢。我认为这可能是一个好方法,但我仍然不知道如何实际实现它,所以现在必须通过子控制器来认领玩具。
  • 阅读推荐的书籍,你会在维护方面获得很多时间
  • 老实说,目前 42 美元对我来说有点贵——我才刚刚开始使用整个 Rails。
【解决方案2】:

孩子是玩具的外键

当然还有其他方法可以做到这一点,但根据您最初的问题,最简单的解决方案是让您的行为与儿童与玩具表中的玩具相关联这一事实保持一致。

最简单的解决方案

抛开关于在完美的 OOP/MVC 设计中应该做什么的争论,做出改变的明智的地方是在控制器中,因为当孩子认领玩具时,该认领由 #update 处理(或者也许甚至是玩具控制器上的 #claim) 操作。

孩子是用户,通过浏览器登录。

在您的情况下,会话已经知道孩子是谁,因此将外键添加到 Toy 模型是微不足道的。控制器是接收关联模型参数的实体,因此它是告诉玩具模型为给定玩具更新哪些属性的正确位置。

当然还有更复杂的解决方案,但根据您原始帖子中提供的信息,它们的价值值得怀疑。与往常一样,您的里程可能会有所不同。

【讨论】:

  • 太棒了,谢谢。在阅读了您的答案并观看了 RailsCast 398 之后,我对直接在控制器上实现这一点感觉并没有那么糟糕。
【解决方案3】:

实际上,如果 child 是用户,则不需要声明方法。您可以在控制器中使用claim_toy 方法。在每个玩具的玩具索引视图中,您可以提供如下链接。

<%= link_to "claim", claim_toy_path(:toy_id => toy.id) %>

您的控制器方法将如下所示。

def claim_toy
  toy = Toy.find(params[:toy_id])
  current_child.toys << toy
end

足够简单。顺便说一句,这不是一个安宁的解决方案。

【讨论】:

  • 我的印象是让控制器对数据库做任何事情是个坏主意(如find 等)。不是这样吗?
  • find 对控制器没问题,一般检索数据没问题。但更愿意提取复杂的逻辑。
  • 你看看你的控制器中是否有太多的逻辑那么它很糟糕。这就是为什么活动记录为您提供诸如查找、创建、更新和其他一些方法之类的方法的原因。在模型中创建一个两行方法并在控制器中调用它只会使它变得多余。
  • 谢谢,这很有说服力。接受了你的回答。最后一点,您能否为我提供一些测试控制器的帮助?我的意思是,如何在 rspec 测试中设置这些变量?手动设置 params 哈希?
  • 对不起,伙计,我什至对测试一无所知。
猜你喜欢
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-20
  • 1970-01-01
  • 2018-03-02
  • 1970-01-01
相关资源
最近更新 更多