Reputation: 6129
Child1 and Child2 have an STI relationship with Entity, and Child2 has_many Child1. Child1 has a status column managed by 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
I would like to create a scope on Child2 for each state on Child1. It should return all Child2 records that have one or more Child1 records that are in that state. Ideally it would reuse the scopes that AASM creates automatically e.g.
scope :owned, -> {joins(:child1s).merge(Child1.owned)} #in Child2
...which is nice and clean and DRY. Unfortunately the SQL this generates is confused by the join on the same table:
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'
The last part of the where clause should be child1_entities.status = 'owned'
.
I could write the entire query in SQL or Arel but I'm hoping to find something that even if I have to go there to specify an alias for the child1 join, I can still reuse the scopes I already have in Child1.
Upvotes: 1
Views: 580
Reputation: 4618
You can do this without using any third party gems with a couple of scoped relationships
class Child2 < Entity
has_many :child1s
has_many :owned_child1s, -> {owned}, class_name: "Child1"
has_many :sold_child1s, -> {sold}, class_name: "Child1"
end
then to find all Child2 which have owned or sold :child1s respectively you would use the following queries
Child2.joins(:owned_child1s) #=> All Child2 having owned children
Child2.joins(:sold_child1s) #=> All Child2 having sold children
You could make this a scope on Child2 using:
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
The scoped relationships then make the code a bit more readable
Child2.with_owned_child1s.where(....) etc.
Upvotes: 3