3 class HasAndBelongsToManyAssociation
< AssociationCollection
#:nodoc:
4 def create(attributes
= {})
5 create_record(attributes
) { |record
| insert_record(record
) }
8 def create
!(attributes
= {})
9 create_record(attributes
) { |record
| insert_record(record
, true) }
13 @reflection.columns(@reflection.options
[:join_table], "#{@reflection.options[:join_table]} Columns")
16 def reset_column_information
17 @reflection.reset_column_information
21 def construct_find_options
!(options
)
22 options
[:joins] = @join_sql
23 options
[:readonly] = finding_with_ambiguous_select
?(options
[:select] || @reflection.options
[:select])
24 options
[:select] ||= (@reflection.options
[:select] || '*')
31 def insert_record(record
, force
= true, validate
= true)
36 return false unless record
.save(validate
)
40 if @reflection.options
[:insert_sql]
41 @owner.connection
.insert(interpolate_sql(@reflection.options
[:insert_sql], record
))
43 attributes
= columns
.inject({}) do |attrs
, column
|
45 when @reflection.primary_key_name
.to_s
46 attrs
[column
.name
] = owner_quoted_id
47 when @reflection.association_foreign_key
.to_s
48 attrs
[column
.name
] = record
.quoted_id
50 if record
.has_attribute
?(column
.name
)
51 value
= @owner.send(:quote_value, record
[column
.name
], column
)
52 attrs
[column
.name
] = value
unless value
.nil?
59 "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
60 "VALUES (#{attributes.values.join(', ')})"
62 @owner.connection
.insert(sql
)
68 def delete_records(records
)
69 if sql
= @reflection.options
[:delete_sql]
70 records
.each
{ |record
| @owner.connection
.delete(interpolate_sql(sql
, record
)) }
72 ids
= quoted_record_ids(records
)
73 sql
= "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
74 @owner.connection
.delete(sql
)
79 if @reflection.options
[:finder_sql]
80 @finder_sql = interpolate_sql(@reflection.options
[:finder_sql])
82 @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
83 @finder_sql << " AND (#{conditions})" if conditions
86 @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
88 if @reflection.options
[:counter_sql]
89 @counter_sql = interpolate_sql(@reflection.options
[:counter_sql])
90 elsif @reflection.options
[:finder_sql]
91 # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
92 @reflection.options
[:counter_sql] = @reflection.options
[:finder_sql].sub(/SELECT (\/\
*.*?\
*\
/ )?(.*)\bFROM\b/im
) { "SELECT #{$1}COUNT(*) FROM" }
93 @counter_sql = interpolate_sql(@reflection.options
[:counter_sql])
95 @counter_sql = @finder_sql
100 { :find => { :conditions => @finder_sql,
103 :order => @reflection.options
[:order],
104 :include => @reflection.options
[:include],
105 :limit => @reflection.options
[:limit] } }
108 # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
109 # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
110 # an id column. This will then overwrite the id column of the records coming back.
111 def finding_with_ambiguous_select
?(select_clause
)
112 !select_clause
&& columns
.size
!= 2
116 def create_record(attributes
, &block
)
117 # Can't use Base.create because the foreign key may be a protected attribute.
118 ensure_owner_is_not_new
119 if attributes
.is_a
?(Array
)
120 attributes
.collect
{ |attr
| create(attr
) }
122 build_record(attributes
, &block
)