| 1 |
module ActiveRecord |
|---|
| 2 |
module Associations |
|---|
| 3 |
class HasAndBelongsToManyAssociation < AssociationCollection |
|---|
| 4 |
def initialize(owner, reflection) |
|---|
| 5 |
super |
|---|
| 6 |
construct_sql |
|---|
| 7 |
end |
|---|
| 8 |
|
|---|
| 9 |
def build(attributes = {}) |
|---|
| 10 |
load_target |
|---|
| 11 |
build_record(attributes) |
|---|
| 12 |
end |
|---|
| 13 |
|
|---|
| 14 |
def create(attributes = {}) |
|---|
| 15 |
create_record(attributes) { |record| insert_record(record) } |
|---|
| 16 |
end |
|---|
| 17 |
|
|---|
| 18 |
def create!(attributes = {}) |
|---|
| 19 |
create_record(attributes) { |record| insert_record(record, true) } |
|---|
| 20 |
end |
|---|
| 21 |
|
|---|
| 22 |
def find_first |
|---|
| 23 |
load_target.first |
|---|
| 24 |
end |
|---|
| 25 |
|
|---|
| 26 |
def find(*args) |
|---|
| 27 |
options = args.extract_options! |
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
if @reflection.options[:finder_sql] |
|---|
| 31 |
expects_array = args.first.kind_of?(Array) |
|---|
| 32 |
ids = args.flatten.compact.uniq |
|---|
| 33 |
|
|---|
| 34 |
if ids.size == 1 |
|---|
| 35 |
id = ids.first.to_i |
|---|
| 36 |
record = load_target.detect { |record| id == record.id } |
|---|
| 37 |
expects_array ? [record] : record |
|---|
| 38 |
else |
|---|
| 39 |
load_target.select { |record| ids.include?(record.id) } |
|---|
| 40 |
end |
|---|
| 41 |
else |
|---|
| 42 |
conditions = "#{@finder_sql}" |
|---|
| 43 |
|
|---|
| 44 |
if sanitized_conditions = sanitize_sql(options[:conditions]) |
|---|
| 45 |
conditions << " AND (#{sanitized_conditions})" |
|---|
| 46 |
end |
|---|
| 47 |
|
|---|
| 48 |
options[:conditions] = conditions |
|---|
| 49 |
options[:joins] = @join_sql |
|---|
| 50 |
options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select]) |
|---|
| 51 |
|
|---|
| 52 |
if options[:order] && @reflection.options[:order] |
|---|
| 53 |
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}" |
|---|
| 54 |
elsif @reflection.options[:order] |
|---|
| 55 |
options[:order] = @reflection.options[:order] |
|---|
| 56 |
end |
|---|
| 57 |
|
|---|
| 58 |
merge_options_from_reflection!(options) |
|---|
| 59 |
|
|---|
| 60 |
options[:select] ||= (@reflection.options[:select] || '*') |
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
args << options |
|---|
| 64 |
@reflection.klass.find(*args) |
|---|
| 65 |
end |
|---|
| 66 |
end |
|---|
| 67 |
|
|---|
| 68 |
protected |
|---|
| 69 |
def count_records |
|---|
| 70 |
load_target.size |
|---|
| 71 |
end |
|---|
| 72 |
|
|---|
| 73 |
def insert_record(record, force=true) |
|---|
| 74 |
if record.new_record? |
|---|
| 75 |
if force |
|---|
| 76 |
record.save! |
|---|
| 77 |
else |
|---|
| 78 |
return false unless record.save |
|---|
| 79 |
end |
|---|
| 80 |
end |
|---|
| 81 |
|
|---|
| 82 |
if @reflection.options[:insert_sql] |
|---|
| 83 |
@owner.connection.execute(interpolate_sql(@reflection.options[:insert_sql], record)) |
|---|
| 84 |
else |
|---|
| 85 |
columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns") |
|---|
| 86 |
|
|---|
| 87 |
attributes = columns.inject({}) do |attributes, column| |
|---|
| 88 |
case column.name |
|---|
| 89 |
when @reflection.primary_key_name |
|---|
| 90 |
attributes[column.name] = @owner.quoted_id |
|---|
| 91 |
when @reflection.association_foreign_key |
|---|
| 92 |
attributes[column.name] = record.quoted_id |
|---|
| 93 |
else |
|---|
| 94 |
if record.attributes.has_key?(column.name) |
|---|
| 95 |
value = @owner.send(:quote_value, record[column.name], column) |
|---|
| 96 |
attributes[column.name] = value unless value.nil? |
|---|
| 97 |
end |
|---|
| 98 |
end |
|---|
| 99 |
attributes |
|---|
| 100 |
end |
|---|
| 101 |
|
|---|
| 102 |
sql = |
|---|
| 103 |
"INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " + |
|---|
| 104 |
"VALUES (#{attributes.values.join(', ')})" |
|---|
| 105 |
|
|---|
| 106 |
@owner.connection.execute(sql) |
|---|
| 107 |
end |
|---|
| 108 |
|
|---|
| 109 |
return true |
|---|
| 110 |
end |
|---|
| 111 |
|
|---|
| 112 |
def delete_records(records) |
|---|
| 113 |
if sql = @reflection.options[:delete_sql] |
|---|
| 114 |
records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) } |
|---|
| 115 |
else |
|---|
| 116 |
ids = quoted_record_ids(records) |
|---|
| 117 |
sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" |
|---|
| 118 |
@owner.connection.execute(sql) |
|---|
| 119 |
end |
|---|
| 120 |
end |
|---|
| 121 |
|
|---|
| 122 |
def construct_sql |
|---|
| 123 |
interpolate_sql_options!(@reflection.options, :finder_sql) |
|---|
| 124 |
|
|---|
| 125 |
if @reflection.options[:finder_sql] |
|---|
| 126 |
@finder_sql = @reflection.options[:finder_sql] |
|---|
| 127 |
else |
|---|
| 128 |
@finder_sql = "#{@reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} " |
|---|
| 129 |
@finder_sql << " AND (#{conditions})" if conditions |
|---|
| 130 |
end |
|---|
| 131 |
|
|---|
| 132 |
@join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}" |
|---|
| 133 |
end |
|---|
| 134 |
|
|---|
| 135 |
def construct_scope |
|---|
| 136 |
{ :find => { :conditions => @finder_sql, |
|---|
| 137 |
:joins => @join_sql, |
|---|
| 138 |
:readonly => false, |
|---|
| 139 |
:order => @reflection.options[:order], |
|---|
| 140 |
:limit => @reflection.options[:limit] } } |
|---|
| 141 |
end |
|---|
| 142 |
|
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 |
|
|---|
| 146 |
def finding_with_ambiguous_select?(select_clause) |
|---|
| 147 |
!select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2 |
|---|
| 148 |
end |
|---|
| 149 |
|
|---|
| 150 |
private |
|---|
| 151 |
def create_record(attributes) |
|---|
| 152 |
|
|---|
| 153 |
ensure_owner_is_not_new |
|---|
| 154 |
if attributes.is_a?(Array) |
|---|
| 155 |
attributes.collect { |attr| create(attr) } |
|---|
| 156 |
else |
|---|
| 157 |
record = build(attributes) |
|---|
| 158 |
yield(record) |
|---|
| 159 |
record |
|---|
| 160 |
end |
|---|
| 161 |
end |
|---|
| 162 |
end |
|---|
| 163 |
end |
|---|
| 164 |
end |
|---|