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 def construct_find_options
!(options
)
14 options
[:joins] = @join_sql
15 options
[:readonly] = finding_with_ambiguous_select
?(options
[:select] || @reflection.options
[:select])
16 options
[:select] ||= (@reflection.options
[:select] || '*')
23 def insert_record(record
, force
=true)
28 return false unless record
.save
32 if @reflection.options
[:insert_sql]
33 @owner.connection
.insert(interpolate_sql(@reflection.options
[:insert_sql], record
))
35 columns
= @owner.connection
.columns(@reflection.options
[:join_table], "#{@reflection.options[:join_table]} Columns")
37 attributes
= columns
.inject({}) do |attrs
, column
|
39 when @reflection.primary_key_name
.to_s
40 attrs
[column
.name
] = owner_quoted_id
41 when @reflection.association_foreign_key
.to_s
42 attrs
[column
.name
] = record
.quoted_id
44 if record
.has_attribute
?(column
.name
)
45 value
= @owner.send(:quote_value, record
[column
.name
], column
)
46 attrs
[column
.name
] = value
unless value
.nil?
53 "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
54 "VALUES (#{attributes.values.join(', ')})"
56 @owner.connection
.insert(sql
)
62 def delete_records(records
)
63 if sql
= @reflection.options
[:delete_sql]
64 records
.each
{ |record
| @owner.connection
.delete(interpolate_sql(sql
, record
)) }
66 ids
= quoted_record_ids(records
)
67 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})"
68 @owner.connection
.delete(sql
)
73 if @reflection.options
[:finder_sql]
74 @finder_sql = interpolate_sql(@reflection.options
[:finder_sql])
76 @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
77 @finder_sql << " AND (#{conditions})" if conditions
80 @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}"
82 if @reflection.options
[:counter_sql]
83 @counter_sql = interpolate_sql(@reflection.options
[:counter_sql])
84 elsif @reflection.options
[:finder_sql]
85 # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
86 @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])
89 @counter_sql = @finder_sql
94 { :find => { :conditions => @finder_sql,
97 :order => @reflection.options
[:order],
98 :include => @reflection.options
[:include],
99 :limit => @reflection.options
[:limit] } }
102 # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
103 # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
104 # an id column. This will then overwrite the id column of the records coming back.
105 def finding_with_ambiguous_select
?(select_clause
)
106 !select_clause
&& @owner.connection
.columns(@reflection.options
[:join_table], "Join Table Columns").size
!= 2
110 def create_record(attributes
, &block
)
111 # Can't use Base.create because the foreign key may be a protected attribute.
112 ensure_owner_is_not_new
113 if attributes
.is_a
?(Array
)
114 attributes
.collect
{ |attr
| create(attr
) }
116 build_record(attributes
, &block
)