gorm/preload.go

133 lines
4.2 KiB
Go
Raw Normal View History

package gorm
import (
"database/sql/driver"
"errors"
"fmt"
"reflect"
)
2015-02-17 17:55:14 +03:00
func getRealValue(value reflect.Value, field string) interface{} {
2015-02-11 12:58:19 +03:00
result := reflect.Indirect(value).FieldByName(field).Interface()
if r, ok := result.(driver.Valuer); ok {
result, _ = r.Value()
}
return result
}
func equalAsString(a interface{}, b interface{}) bool {
return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b)
}
func Preload(scope *Scope) {
2015-02-17 17:55:14 +03:00
fields := scope.Fields()
isSlice := scope.IndirectValue().Kind() == reflect.Slice
if scope.Search.Preload != nil {
for key, conditions := range scope.Search.Preload {
for _, field := range fields {
if field.Name == key && field.Relationship != nil {
2015-02-17 17:55:14 +03:00
results := makeSlice(field.Struct.Type)
relation := field.Relationship
primaryName := scope.PrimaryKeyField().Name
associationPrimaryKey := scope.New(results).PrimaryKeyField().Name
switch relation.Kind {
case "has_one":
2015-02-16 11:35:26 +03:00
condition := fmt.Sprintf("%v IN (?)", scope.Quote(relation.ForeignDBName))
scope.NewDB().Where(condition, scope.getColumnAsArray(primaryName)).Find(results, conditions...)
resultValues := reflect.Indirect(reflect.ValueOf(results))
for i := 0; i < resultValues.Len(); i++ {
result := resultValues.Index(i)
if isSlice {
2015-02-17 17:55:14 +03:00
value := getRealValue(result, relation.ForeignFieldName)
objects := scope.IndirectValue()
for j := 0; j < objects.Len(); j++ {
2015-02-17 17:55:14 +03:00
if equalAsString(getRealValue(objects.Index(j), primaryName), value) {
2015-02-11 12:58:19 +03:00
reflect.Indirect(objects.Index(j)).FieldByName(field.Name).Set(result)
break
}
}
} else {
scope.SetColumn(field, result)
}
}
case "has_many":
2015-02-16 11:35:26 +03:00
condition := fmt.Sprintf("%v IN (?)", scope.Quote(relation.ForeignDBName))
scope.NewDB().Where(condition, scope.getColumnAsArray(primaryName)).Find(results, conditions...)
resultValues := reflect.Indirect(reflect.ValueOf(results))
if isSlice {
for i := 0; i < resultValues.Len(); i++ {
result := resultValues.Index(i)
2015-02-17 17:55:14 +03:00
value := getRealValue(result, relation.ForeignFieldName)
objects := scope.IndirectValue()
for j := 0; j < objects.Len(); j++ {
2015-02-11 12:58:19 +03:00
object := reflect.Indirect(objects.Index(j))
2015-02-17 17:55:14 +03:00
if equalAsString(getRealValue(object, primaryName), value) {
f := object.FieldByName(field.Name)
f.Set(reflect.Append(f, result))
break
}
}
}
} else {
scope.SetColumn(field, resultValues)
}
case "belongs_to":
2015-02-16 11:35:26 +03:00
scope.NewDB().Where(scope.getColumnAsArray(relation.ForeignFieldName)).Find(results, conditions...)
resultValues := reflect.Indirect(reflect.ValueOf(results))
for i := 0; i < resultValues.Len(); i++ {
result := resultValues.Index(i)
if isSlice {
2015-02-17 17:55:14 +03:00
value := getRealValue(result, associationPrimaryKey)
objects := scope.IndirectValue()
for j := 0; j < objects.Len(); j++ {
2015-02-11 12:58:19 +03:00
object := reflect.Indirect(objects.Index(j))
2015-02-17 17:55:14 +03:00
if equalAsString(getRealValue(object, relation.ForeignFieldName), value) {
object.FieldByName(field.Name).Set(result)
}
}
} else {
scope.SetColumn(field, result)
}
}
case "many_to_many":
scope.Err(errors.New("not supported relation"))
default:
scope.Err(errors.New("not supported relation"))
}
break
}
}
}
}
}
2015-02-17 17:55:14 +03:00
func makeSlice(typ reflect.Type) interface{} {
if typ.Kind() == reflect.Slice {
typ = typ.Elem()
}
sliceType := reflect.SliceOf(typ)
slice := reflect.New(sliceType)
slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
return slice.Interface()
}
func (scope *Scope) getColumnAsArray(column string) (primaryKeys []interface{}) {
values := scope.IndirectValue()
switch values.Kind() {
case reflect.Slice:
2015-02-24 10:50:02 +03:00
primaryKeyMap := map[interface{}]bool{}
for i := 0; i < values.Len(); i++ {
2015-02-24 10:50:02 +03:00
primaryKeyMap[reflect.Indirect(values.Index(i)).FieldByName(column).Interface()] = true
}
for key := range primaryKeyMap {
primaryKeys = append(primaryKeys, key)
}
case reflect.Struct:
return []interface{}{values.FieldByName(column).Interface()}
}
return
}