6

我有一个游戏模型,它 has_many :texts。问题是我必须根据它们所属的游戏对文本进行不同的排序(是的,丑陋,但它是遗留数据)。我创建了一个Text.in_game_order_query(game)方法,它返回适当的顺序。

我最喜欢的解决方案是在 Text 模型中放置一个默认范围,但这需要知道它们属于哪个游戏。我也不想为每个游戏的文本创建单独的类 - 有很多游戏,而且还会有更多游戏,而且所有较新的游戏都将使用相同的顺序。所以我有了另一个想法:在 has_many 中排序文本,当我知道它们属于哪个游戏时:

has_many :texts, :order => Text.in_game_order_query(self)

但是, self 是这里的类,所以这不起作用。

@game.texts.in_game_order(@game)除了每次打电话真的没有其他解决方案吗?

4

6 回答 6

12

我最近遇到了一个非常相似的问题,我确信这在 Rails 中是不可能的,但我学到了一些非常有趣的东西。

你可以为作用域声明一个参数,然后不传入它,默认情况下它会传入父对象!

所以,你可以这样做:

class Game < ActiveRecord
  has_many :texts, -> (game) { Text.in_game_order_query(game) }

信不信由你,你不必在游戏中通过。Rails 会为你神奇地做到这一点。你可以简单地做:

game.texts

不过,有一个警告。如果您启用了预加载,这目前在 Rails 中不起作用。如果这样做,您可能会收到以下警告:

弃用警告:关联范围“文本”是实例相关的(范围块接受一个参数)。预加载发生在创建单个实例之前。这意味着没有实例被传递到关联范围。这很可能会导致损坏或不正确的行为。这些关联的加入、预加载和预先加载已被弃用,将来将被删除。

于 2016-01-11T21:16:52.190 回答
4

跟进使用 PradeepKumar 的想法,我发现以下解决方案可行

假设一个类Block有一个属性block_type和一个容器类(比如 Page),你可以有这样的东西:

class Page
  ...

  has_many :blocks do
    def ordered_by_type
      # self is the array of blocks
      self.sort_by(&:block_type)
    end
  end

  ...
end

然后当你打电话

page.blocks.ordered_by_type

你得到你想要的 - 由 Proc 定义。显然,Proc 可能要复杂得多,并且在 SQL 调用中不工作,但在编译结果集之后。

更新:
经过一段时间后,我重新阅读了这篇文章和我的答案,我想知道你是否可以做一些简单的事情,就像你在帖子中基本上建议自己的另一种方法一样。

如果你添加了一个方法来Game调用ordered_texts

  def ordered_texts
    texts.in_game_order(self)
  end

这能解决问题吗?或者这个方法是否需要与其他Game关系方法链接?

于 2013-01-03T20:12:08.123 回答
1

协会扩展是否有可能?

看来您可以完成这项工作:

module Legacy
  def legacy_game_order
    order(proxy_association.owner.custom_texts_order)
  end
end

class Game << ActiveRecord::Base
  includes Legacy
  has_many :texts, :extend => Legacy

  def custom_texts_order
    # your custom query logic goes here
  end
end

这样,给定一个游戏实例,您应该能够访问实例的自定义查询,而无需传入 self:

g = Game.find(123)
g.texts.legacy_game_order
于 2012-08-24T21:04:49.223 回答
0

这是您可以做到的一种方法,

   has_many :texts, :order => lambda { Text.in_game_order_query(self) }

这是我通常不推荐的另一种方式(但会起作用),

has_many :texts do
  def game_order(game)
    find(:all, :order => Text.in_game_order_query(game))
  end
end

你可以打电话给他们,

game.texts.game_order(game)
于 2012-08-14T10:12:07.340 回答
0

我不确定您的订单/查询在 in_game_order_query 类方法中是什么样的,但我相信您可以做到这一点

has_many :texts, :finder_sql => proc{Text.in_game_order_query(self)}

只是让您知道我以前从未使用过它,但如果您让我知道这是否适合您,我将不胜感激。

查看http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many了解更多关于 :finder_sql 的文档

于 2012-08-15T02:59:42.283 回答
-1

我认为如果您想要处理运行时信息,您应该通过以下方式完成:

has_many :texts, :order => proc{ {Text.in_game_order_query(self)} }
于 2012-08-14T10:08:45.967 回答