aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/through_association_scope.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations/through_association_scope.rb')
-rw-r--r--activerecord/lib/active_record/associations/through_association_scope.rb108
1 files changed, 81 insertions, 27 deletions
diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb
index cabb33c4a8..90ebadda89 100644
--- a/activerecord/lib/active_record/associations/through_association_scope.rb
+++ b/activerecord/lib/active_record/associations/through_association_scope.rb
@@ -1,3 +1,5 @@
+require 'enumerator'
+
module ActiveRecord
# = Active Record Through Association Scope
module Associations
@@ -19,9 +21,9 @@ module ActiveRecord
# Build SQL conditions from attributes, qualified by table name.
def construct_conditions
- table_name = @reflection.through_reflection.quoted_table_name
- conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
- "#{table_name}.#{attr} = #{value}"
+ reflection = @reflection.through_reflection_chain.last
+ conditions = construct_quoted_owner_attributes(reflection).map do |attr, value|
+ "#{table_aliases[reflection]}.#{attr} = #{value}"
end
conditions << sql_conditions if sql_conditions
"(" + conditions.join(') AND (') + ")"
@@ -49,35 +51,87 @@ module ActiveRecord
distinct = "DISTINCT " if @reflection.options[:uniq]
selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
end
-
+
def construct_joins(custom_joins = nil)
- polymorphic_join = nil
- if @reflection.source_reflection.macro == :belongs_to
- reflection_primary_key = @reflection.klass.primary_key
- source_primary_key = @reflection.source_reflection.primary_key_name
- if @reflection.options[:source_type]
- polymorphic_join = "AND %s.%s = %s" % [
- @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}",
- @owner.class.quote_value(@reflection.options[:source_type])
- ]
+ # puts @reflection.through_reflection_chain.map(&:inspect)
+
+ "#{construct_through_joins} #{@reflection.options[:joins]} #{custom_joins}"
+ end
+
+ def construct_through_joins
+ joins = []
+
+ # Iterate over each pair in the through reflection chain, joining them together
+ @reflection.through_reflection_chain.each_cons(2) do |left, right|
+ polymorphic_join = nil
+
+ case
+ when left.source_reflection.nil?
+ left_primary_key = left.primary_key_name
+ right_primary_key = right.klass.primary_key
+
+ if left.options[:as]
+ polymorphic_join = "AND %s.%s = %s" % [
+ table_aliases[left], "#{left.options[:as]}_type",
+ @owner.class.quote_value(right.klass.name)
+ ]
+ end
+ when left.source_reflection.macro == :belongs_to
+ left_primary_key = left.klass.primary_key
+ right_primary_key = left.source_reflection.primary_key_name
+
+ if left.options[:source_type]
+ polymorphic_join = "AND %s.%s = %s" % [
+ table_aliases[right],
+ left.source_reflection.options[:foreign_type].to_s,
+ @owner.class.quote_value(left.options[:source_type])
+ ]
+ end
+ else
+ left_primary_key = left.source_reflection.primary_key_name
+ right_primary_key = right.klass.primary_key
+
+ if left.source_reflection.options[:as]
+ polymorphic_join = "AND %s.%s = %s" % [
+ table_aliases[left],
+ "#{left.source_reflection.options[:as]}_type",
+ @owner.class.quote_value(right.klass.name)
+ ]
+ end
end
- else
- reflection_primary_key = @reflection.source_reflection.primary_key_name
- source_primary_key = @reflection.through_reflection.klass.primary_key
- if @reflection.source_reflection.options[:as]
- polymorphic_join = "AND %s.%s = %s" % [
- @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type",
- @owner.class.quote_value(@reflection.through_reflection.klass.name)
- ]
+
+ if right.quoted_table_name == table_aliases[right]
+ table = right.quoted_table_name
+ else
+ table = "#{right.quoted_table_name} #{table_aliases[right]}"
end
+
+ joins << "INNER JOIN %s ON %s.%s = %s.%s %s" % [
+ table,
+ table_aliases[left], left_primary_key,
+ table_aliases[right], right_primary_key,
+ polymorphic_join
+ ]
end
+
+ joins.join(" ")
+ end
- "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
- @reflection.through_reflection.quoted_table_name,
- @reflection.quoted_table_name, reflection_primary_key,
- @reflection.through_reflection.quoted_table_name, source_primary_key,
- polymorphic_join
- ]
+ def table_aliases
+ @table_aliases ||= begin
+ tally = {}
+ @reflection.through_reflection_chain.inject({}) do |aliases, reflection|
+ if tally[reflection.table_name].nil?
+ tally[reflection.table_name] = 1
+ aliases[reflection] = reflection.quoted_table_name
+ else
+ tally[reflection.table_name] += 1
+ aliased_table_name = reflection.table_name + "_#{tally[reflection.table_name]}"
+ aliases[reflection] = reflection.klass.connection.quote_table_name(aliased_table_name)
+ end
+ aliases
+ end
+ end
end
# Construct attributes for associate pointing to owner.