has_many_association.rb 3.8 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3 4
module ActiveRecord
  module Associations
    class HasManyAssociation < AssociationCollection #:nodoc:
      protected
5 6 7 8 9 10 11 12
        def owner_quoted_id
          if @reflection.options[:primary_key]
            quote_value(@owner.send(@reflection.options[:primary_key]))
          else
            @owner.quoted_id
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
13
        def count_records
14
          count = if has_cached_counter?
D
Initial  
David Heinemeier Hansson 已提交
15
            @owner.send(:read_attribute, cached_counter_attribute_name)
16 17
          elsif @reflection.options[:counter_sql]
            @reflection.klass.count_by_sql(@counter_sql)
D
Initial  
David Heinemeier Hansson 已提交
18
          else
19
            @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
D
Initial  
David Heinemeier Hansson 已提交
20
          end
21 22 23 24 25

          # If there's nothing in the database and @target has no new records
          # we are certain the current target is an empty array. This is a
          # documented side-effect of the method that may avoid an extra SELECT.
          @target ||= [] and loaded if count == 0
26
          
27 28 29 30
          if @reflection.options[:limit]
            count = [ @reflection.options[:limit], count ].min
          end
          
31
          return count
D
Initial  
David Heinemeier Hansson 已提交
32
        end
33

D
Initial  
David Heinemeier Hansson 已提交
34 35 36
        def has_cached_counter?
          @owner.attribute_present?(cached_counter_attribute_name)
        end
37

D
Initial  
David Heinemeier Hansson 已提交
38
        def cached_counter_attribute_name
39
          "#{@reflection.name}_count"
D
Initial  
David Heinemeier Hansson 已提交
40 41 42
        end

        def insert_record(record)
43
          set_belongs_to_association_for(record)
44
          record.save
D
Initial  
David Heinemeier Hansson 已提交
45 46 47
        end

        def delete_records(records)
48 49
          case @reflection.options[:dependent]
            when :destroy
50
              records.each { |r| r.destroy }
51
            when :delete_all
52
              @reflection.klass.delete(records.map { |record| record.id })
53 54 55 56
            else
              ids = quoted_record_ids(records)
              @reflection.klass.update_all(
                "#{@reflection.primary_key_name} = NULL", 
57
                "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
58
              )
59
          end
D
Initial  
David Heinemeier Hansson 已提交
60
        end
61 62 63 64 65 66

        def target_obsolete?
          false
        end

        def construct_sql
67
          case
68 69 70 71
            when @reflection.options[:finder_sql]
              @finder_sql = interpolate_sql(@reflection.options[:finder_sql])

            when @reflection.options[:as]
72
              @finder_sql = 
73
                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
74
                "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
75
              @finder_sql << " AND (#{conditions})" if conditions
76
            
77
            else
78
              @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
79
              @finder_sql << " AND (#{conditions})" if conditions
80 81
          end

82 83 84
          if @reflection.options[:counter_sql]
            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
          elsif @reflection.options[:finder_sql]
85 86
            # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
            @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
87
            @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
88
          else
89
            @counter_sql = @finder_sql
90 91
          end
        end
92 93 94 95

        def construct_scope
          create_scoping = {}
          set_belongs_to_association_for(create_scoping)
96
          {
97
            :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
98 99
            :create => create_scoping
          }
100
        end
D
Initial  
David Heinemeier Hansson 已提交
101 102 103
    end
  end
end