ActiveRecord保持范围封装

时间:2011-01-20 02:58:30

标签: ruby-on-rails activerecord named-scope arel

我有两个模型,foobarfoo有很多bars

Bar是在给定时间段内发生的事件,所以我想要一个返回ActiveRecord::Relation的方法或范围来表示当前有效条形图的foos。

在具有范围的Foo类中这很容易:

class Foo < ActiveRecord::Base
has_many :bars

scope :has_current_bars, joins(:bars).where('bar.foo_id IS NOT NULL').where('bar.starts_at <= ?', DateTime.now).where('bar.ends_at >= ?', DateTime.now)

我不喜欢这一点,foo需要了解bar内部的很多内容。

是否可以通过在bar上添加范围来重写,因此foo不需要了解bar属性?

3 个答案:

答案 0 :(得分:1)

绝对。您可以而且应该将范围移至Bar

class Bar < ActiveRecord::Base
  belongs_to :foo

  scope :current, where('starts_at <= ? AND ends_at >= ?', DateTime.now, DateTime.now)
end

foo = Foo.first
foo.bars.current # Will return all of foo's bars which match the scope

# EDIT:
bars.current.map(&:foo) # Will return all foos that have current bars

答案 1 :(得分:0)

class Foo < ActiveRecord::Base
  has_many :bars

  def self.has_current_bars
    joins(:bars).merge(Bar.current)
  end

  # or
  scope :has_current_bars, joins(:bars).merge(Bar.current)
end

class Bar < ActiveRecord::Base
  scope :current, where('bar.starts_at <= ?', DateTime.now).where('bar.ends_at >= ?', DateTime.now)
end

foos = Foo.has_current_bars

答案 2 :(得分:0)

如果要封装查询对象,我编写了一个微型库,可以非常简单地将复杂的查询逻辑移到模型和控制器之外。

https://github.com/ElMassimo/queryable

它负责使您的作用域可链接,并将每个方法委托给实际查询。

对于这种情况,您可以有两个查询对象,FooQuery和BarQuery,并使这些对象协作,以便每个查询对象负责封装与其相应模型相关的逻辑。