Повторно использовать область в запросе с отношением has_many к той же таблице STI
Child1 и Child2 имеют отношения STI с Entity, а Child2 has_many Child1. Child1 имеет столбец состояния, управляемый AASM.
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. Он должен вернуть все записи Child2, которые имеют одну или несколько записей Child1, которые находятся в этом состоянии. В идеале это будет использовать области, которые AASM создает автоматически, например
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.
1 ответ
Вы можете сделать это без использования каких-либо сторонних драгоценных камней с парой определенных отношений
class Child2 < Entity
has_many :child1s
has_many :owned_child1s, -> {owned}, class_name: "Child1"
has_many :sold_child1s, -> {sold}, class_name: "Child1"
end
затем, чтобы найти все Child2, которые владеют или продают:child1s соответственно, вы бы использовали следующие запросы
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.