Child1和Child2与Entity具有STI关系,Child2具有_many Child1。Child1有一个由AAM管理的状态列。
class Entity < ActiveRecord::Base
end
class Child1 < Entity
include AASM
aasm_column 'status' do
state :owned #also creates scope Child1.owned
state :sold
end
belongs_to :child2
end
class Child2 < Entity
has_many :child1s
end
我想在Child2上为Child1上的每个州创建一个范围。它应该返回具有一个或多个处于该状态的Child1记录的所有Child2记录。理想情况下,它将重用AAM自动创建的范围,例如
scope :owned, -> {joins(:child1s).merge(Child1.owned)} #in Child2
它既美观又干净又干燥。不幸的是,它生成的SQL被同一表上的联接混淆了:
irb(main):001:0> Child2.owned
Child2 Load (35.5ms) SELECT "entities".* FROM "entities"
INNER JOIN "entities" "child1_entities" ON "child1_entities"."child2_id" = "entities"."id" AND "child1_entities"."type" IN ('Child1')
WHERE "entities"."type" IN ('Child2') AND "entities"."status" = 'owned'
where子句的最后一部分应该是child1_entities.status = 'owned'
。
我可以用SQL或Arel编写整个查询,但我希望找到一些东西,即使我必须去那里为child1联接指定一个别名,我仍然可以重用child1中已有的作用域。
您可以在不使用任何具有两个作用域关系的第三方gem的情况下做到这一点
class Child2 < Entity
has_many :child1s
has_many :owned_child1s, -> {owned}, class_name: "Child1"
has_many :sold_child1s, -> {sold}, class_name: "Child1"
end
然后要查找所有拥有或出售的Child2:child1,您将使用以下查询
Child2.joins(:owned_child1s) #=> All Child2 having owned children
Child2.joins(:sold_child1s) #=> All Child2 having sold children
你可以在Child2上使用将其作为一个范围
class Child2 < Entity
has_many :child1s
has_many :owned_child1s, -> {owned}, class_name: "Child1"
has_many :sold_child1s, -> {sold}, class_name: "Child1"
scope :with_owned_child1s, -> {joins(:owned_child1s)}
scope :with_sold_child1s, -> {joins(:solid_child1s)}
end
然后,作用域关系使代码的可读性更强
Child2.with_owned_child1s.where(....) etc.