OSDN Git Service

Add db vendor (#45)
[bytom/vapor.git] / vendor / github.com / jinzhu / gorm / association.go
1 package gorm
2
3 import (
4         "errors"
5         "fmt"
6         "reflect"
7 )
8
9 // Association Mode contains some helper methods to handle relationship things easily.
10 type Association struct {
11         Error  error
12         scope  *Scope
13         column string
14         field  *Field
15 }
16
17 // Find find out all related associations
18 func (association *Association) Find(value interface{}) *Association {
19         association.scope.related(value, association.column)
20         return association.setErr(association.scope.db.Error)
21 }
22
23 // Append append new associations for many2many, has_many, replace current association for has_one, belongs_to
24 func (association *Association) Append(values ...interface{}) *Association {
25         if association.Error != nil {
26                 return association
27         }
28
29         if relationship := association.field.Relationship; relationship.Kind == "has_one" {
30                 return association.Replace(values...)
31         }
32         return association.saveAssociations(values...)
33 }
34
35 // Replace replace current associations with new one
36 func (association *Association) Replace(values ...interface{}) *Association {
37         if association.Error != nil {
38                 return association
39         }
40
41         var (
42                 relationship = association.field.Relationship
43                 scope        = association.scope
44                 field        = association.field.Field
45                 newDB        = scope.NewDB()
46         )
47
48         // Append new values
49         association.field.Set(reflect.Zero(association.field.Field.Type()))
50         association.saveAssociations(values...)
51
52         // Belongs To
53         if relationship.Kind == "belongs_to" {
54                 // Set foreign key to be null when clearing value (length equals 0)
55                 if len(values) == 0 {
56                         // Set foreign key to be nil
57                         var foreignKeyMap = map[string]interface{}{}
58                         for _, foreignKey := range relationship.ForeignDBNames {
59                                 foreignKeyMap[foreignKey] = nil
60                         }
61                         association.setErr(newDB.Model(scope.Value).UpdateColumn(foreignKeyMap).Error)
62                 }
63         } else {
64                 // Polymorphic Relations
65                 if relationship.PolymorphicDBName != "" {
66                         newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(relationship.PolymorphicDBName)), relationship.PolymorphicValue)
67                 }
68
69                 // Delete Relations except new created
70                 if len(values) > 0 {
71                         var associationForeignFieldNames, associationForeignDBNames []string
72                         if relationship.Kind == "many_to_many" {
73                                 // if many to many relations, get association fields name from association foreign keys
74                                 associationScope := scope.New(reflect.New(field.Type()).Interface())
75                                 for idx, dbName := range relationship.AssociationForeignFieldNames {
76                                         if field, ok := associationScope.FieldByName(dbName); ok {
77                                                 associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
78                                                 associationForeignDBNames = append(associationForeignDBNames, relationship.AssociationForeignDBNames[idx])
79                                         }
80                                 }
81                         } else {
82                                 // If has one/many relations, use primary keys
83                                 for _, field := range scope.New(reflect.New(field.Type()).Interface()).PrimaryFields() {
84                                         associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
85                                         associationForeignDBNames = append(associationForeignDBNames, field.DBName)
86                                 }
87                         }
88
89                         newPrimaryKeys := scope.getColumnAsArray(associationForeignFieldNames, field.Interface())
90
91                         if len(newPrimaryKeys) > 0 {
92                                 sql := fmt.Sprintf("%v NOT IN (%v)", toQueryCondition(scope, associationForeignDBNames), toQueryMarks(newPrimaryKeys))
93                                 newDB = newDB.Where(sql, toQueryValues(newPrimaryKeys)...)
94                         }
95                 }
96
97                 if relationship.Kind == "many_to_many" {
98                         // if many to many relations, delete related relations from join table
99                         var sourceForeignFieldNames []string
100
101                         for _, dbName := range relationship.ForeignFieldNames {
102                                 if field, ok := scope.FieldByName(dbName); ok {
103                                         sourceForeignFieldNames = append(sourceForeignFieldNames, field.Name)
104                                 }
105                         }
106
107                         if sourcePrimaryKeys := scope.getColumnAsArray(sourceForeignFieldNames, scope.Value); len(sourcePrimaryKeys) > 0 {
108                                 newDB = newDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(sourcePrimaryKeys)), toQueryValues(sourcePrimaryKeys)...)
109
110                                 association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB))
111                         }
112                 } else if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
113                         // has_one or has_many relations, set foreign key to be nil (TODO or delete them?)
114                         var foreignKeyMap = map[string]interface{}{}
115                         for idx, foreignKey := range relationship.ForeignDBNames {
116                                 foreignKeyMap[foreignKey] = nil
117                                 if field, ok := scope.FieldByName(relationship.AssociationForeignFieldNames[idx]); ok {
118                                         newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface())
119                                 }
120                         }
121
122                         fieldValue := reflect.New(association.field.Field.Type()).Interface()
123                         association.setErr(newDB.Model(fieldValue).UpdateColumn(foreignKeyMap).Error)
124                 }
125         }
126         return association
127 }
128
129 // Delete remove relationship between source & passed arguments, but won't delete those arguments
130 func (association *Association) Delete(values ...interface{}) *Association {
131         if association.Error != nil {
132                 return association
133         }
134
135         var (
136                 relationship = association.field.Relationship
137                 scope        = association.scope
138                 field        = association.field.Field
139                 newDB        = scope.NewDB()
140         )
141
142         if len(values) == 0 {
143                 return association
144         }
145
146         var deletingResourcePrimaryFieldNames, deletingResourcePrimaryDBNames []string
147         for _, field := range scope.New(reflect.New(field.Type()).Interface()).PrimaryFields() {
148                 deletingResourcePrimaryFieldNames = append(deletingResourcePrimaryFieldNames, field.Name)
149                 deletingResourcePrimaryDBNames = append(deletingResourcePrimaryDBNames, field.DBName)
150         }
151
152         deletingPrimaryKeys := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, values...)
153
154         if relationship.Kind == "many_to_many" {
155                 // source value's foreign keys
156                 for idx, foreignKey := range relationship.ForeignDBNames {
157                         if field, ok := scope.FieldByName(relationship.ForeignFieldNames[idx]); ok {
158                                 newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface())
159                         }
160                 }
161
162                 // get association's foreign fields name
163                 var associationScope = scope.New(reflect.New(field.Type()).Interface())
164                 var associationForeignFieldNames []string
165                 for _, associationDBName := range relationship.AssociationForeignFieldNames {
166                         if field, ok := associationScope.FieldByName(associationDBName); ok {
167                                 associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
168                         }
169                 }
170
171                 // association value's foreign keys
172                 deletingPrimaryKeys := scope.getColumnAsArray(associationForeignFieldNames, values...)
173                 sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(deletingPrimaryKeys))
174                 newDB = newDB.Where(sql, toQueryValues(deletingPrimaryKeys)...)
175
176                 association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB))
177         } else {
178                 var foreignKeyMap = map[string]interface{}{}
179                 for _, foreignKey := range relationship.ForeignDBNames {
180                         foreignKeyMap[foreignKey] = nil
181                 }
182
183                 if relationship.Kind == "belongs_to" {
184                         // find with deleting relation's foreign keys
185                         primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, values...)
186                         newDB = newDB.Where(
187                                 fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
188                                 toQueryValues(primaryKeys)...,
189                         )
190
191                         // set foreign key to be null if there are some records affected
192                         modelValue := reflect.New(scope.GetModelStruct().ModelType).Interface()
193                         if results := newDB.Model(modelValue).UpdateColumn(foreignKeyMap); results.Error == nil {
194                                 if results.RowsAffected > 0 {
195                                         scope.updatedAttrsWithValues(foreignKeyMap)
196                                 }
197                         } else {
198                                 association.setErr(results.Error)
199                         }
200                 } else if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
201                         // find all relations
202                         primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
203                         newDB = newDB.Where(
204                                 fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
205                                 toQueryValues(primaryKeys)...,
206                         )
207
208                         // only include those deleting relations
209                         newDB = newDB.Where(
210                                 fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, deletingResourcePrimaryDBNames), toQueryMarks(deletingPrimaryKeys)),
211                                 toQueryValues(deletingPrimaryKeys)...,
212                         )
213
214                         // set matched relation's foreign key to be null
215                         fieldValue := reflect.New(association.field.Field.Type()).Interface()
216                         association.setErr(newDB.Model(fieldValue).UpdateColumn(foreignKeyMap).Error)
217                 }
218         }
219
220         // Remove deleted records from source's field
221         if association.Error == nil {
222                 if field.Kind() == reflect.Slice {
223                         leftValues := reflect.Zero(field.Type())
224
225                         for i := 0; i < field.Len(); i++ {
226                                 reflectValue := field.Index(i)
227                                 primaryKey := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, reflectValue.Interface())[0]
228                                 var isDeleted = false
229                                 for _, pk := range deletingPrimaryKeys {
230                                         if equalAsString(primaryKey, pk) {
231                                                 isDeleted = true
232                                                 break
233                                         }
234                                 }
235                                 if !isDeleted {
236                                         leftValues = reflect.Append(leftValues, reflectValue)
237                                 }
238                         }
239
240                         association.field.Set(leftValues)
241                 } else if field.Kind() == reflect.Struct {
242                         primaryKey := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, field.Interface())[0]
243                         for _, pk := range deletingPrimaryKeys {
244                                 if equalAsString(primaryKey, pk) {
245                                         association.field.Set(reflect.Zero(field.Type()))
246                                         break
247                                 }
248                         }
249                 }
250         }
251
252         return association
253 }
254
255 // Clear remove relationship between source & current associations, won't delete those associations
256 func (association *Association) Clear() *Association {
257         return association.Replace()
258 }
259
260 // Count return the count of current associations
261 func (association *Association) Count() int {
262         var (
263                 count        = 0
264                 relationship = association.field.Relationship
265                 scope        = association.scope
266                 fieldValue   = association.field.Field.Interface()
267                 query        = scope.DB()
268         )
269
270         switch relationship.Kind {
271         case "many_to_many":
272                 query = relationship.JoinTableHandler.JoinWith(relationship.JoinTableHandler, query, scope.Value)
273         case "has_many", "has_one":
274                 primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
275                 query = query.Where(
276                         fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
277                         toQueryValues(primaryKeys)...,
278                 )
279         case "belongs_to":
280                 primaryKeys := scope.getColumnAsArray(relationship.ForeignFieldNames, scope.Value)
281                 query = query.Where(
282                         fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(primaryKeys)),
283                         toQueryValues(primaryKeys)...,
284                 )
285         }
286
287         if relationship.PolymorphicType != "" {
288                 query = query.Where(
289                         fmt.Sprintf("%v.%v = ?", scope.New(fieldValue).QuotedTableName(), scope.Quote(relationship.PolymorphicDBName)),
290                         relationship.PolymorphicValue,
291                 )
292         }
293
294         if err := query.Model(fieldValue).Count(&count).Error; err != nil {
295                 association.Error = err
296         }
297         return count
298 }
299
300 // saveAssociations save passed values as associations
301 func (association *Association) saveAssociations(values ...interface{}) *Association {
302         var (
303                 scope        = association.scope
304                 field        = association.field
305                 relationship = field.Relationship
306         )
307
308         saveAssociation := func(reflectValue reflect.Value) {
309                 // value has to been pointer
310                 if reflectValue.Kind() != reflect.Ptr {
311                         reflectPtr := reflect.New(reflectValue.Type())
312                         reflectPtr.Elem().Set(reflectValue)
313                         reflectValue = reflectPtr
314                 }
315
316                 // value has to been saved for many2many
317                 if relationship.Kind == "many_to_many" {
318                         if scope.New(reflectValue.Interface()).PrimaryKeyZero() {
319                                 association.setErr(scope.NewDB().Save(reflectValue.Interface()).Error)
320                         }
321                 }
322
323                 // Assign Fields
324                 var fieldType = field.Field.Type()
325                 var setFieldBackToValue, setSliceFieldBackToValue bool
326                 if reflectValue.Type().AssignableTo(fieldType) {
327                         field.Set(reflectValue)
328                 } else if reflectValue.Type().Elem().AssignableTo(fieldType) {
329                         // if field's type is struct, then need to set value back to argument after save
330                         setFieldBackToValue = true
331                         field.Set(reflectValue.Elem())
332                 } else if fieldType.Kind() == reflect.Slice {
333                         if reflectValue.Type().AssignableTo(fieldType.Elem()) {
334                                 field.Set(reflect.Append(field.Field, reflectValue))
335                         } else if reflectValue.Type().Elem().AssignableTo(fieldType.Elem()) {
336                                 // if field's type is slice of struct, then need to set value back to argument after save
337                                 setSliceFieldBackToValue = true
338                                 field.Set(reflect.Append(field.Field, reflectValue.Elem()))
339                         }
340                 }
341
342                 if relationship.Kind == "many_to_many" {
343                         association.setErr(relationship.JoinTableHandler.Add(relationship.JoinTableHandler, scope.NewDB(), scope.Value, reflectValue.Interface()))
344                 } else {
345                         association.setErr(scope.NewDB().Select(field.Name).Save(scope.Value).Error)
346
347                         if setFieldBackToValue {
348                                 reflectValue.Elem().Set(field.Field)
349                         } else if setSliceFieldBackToValue {
350                                 reflectValue.Elem().Set(field.Field.Index(field.Field.Len() - 1))
351                         }
352                 }
353         }
354
355         for _, value := range values {
356                 reflectValue := reflect.ValueOf(value)
357                 indirectReflectValue := reflect.Indirect(reflectValue)
358                 if indirectReflectValue.Kind() == reflect.Struct {
359                         saveAssociation(reflectValue)
360                 } else if indirectReflectValue.Kind() == reflect.Slice {
361                         for i := 0; i < indirectReflectValue.Len(); i++ {
362                                 saveAssociation(indirectReflectValue.Index(i))
363                         }
364                 } else {
365                         association.setErr(errors.New("invalid value type"))
366                 }
367         }
368         return association
369 }
370
371 // setErr set error when the error is not nil. And return Association.
372 func (association *Association) setErr(err error) *Association {
373         if err != nil {
374                 association.Error = err
375         }
376         return association
377 }