gorm/model/field.go
2018-02-27 09:20:09 +08:00

135 lines
3.4 KiB
Go

package model
import (
"database/sql"
"errors"
"fmt"
"reflect"
"sort"
"strings"
"github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/builder"
"github.com/jinzhu/gorm/schema"
)
// Field GORM model field
type Field struct {
*schema.Field
IsBlank bool
Value reflect.Value
}
// GetAssignments get assignments
func GetAssignments(tx *gorm.DB) chan [][]*Field {
fieldChan := make(chan [][]*Field)
go func() {
// TODO handle select, omit, protected
switch dest := tx.Statement.Dest.(type) {
case map[string]interface{}:
fieldChan <- [][]*Field{mapToFields(dest, tx.Statement, schema.Parse(tx.Statement.Table))}
case []map[string]interface{}:
fields := [][]*Field{}
tableSchema := schema.Parse(tx.Statement.Table)
for _, v := range dest {
fields = append(fields, mapToFields(v, tx.Statement, tableSchema))
}
fieldChan <- fields
default:
if s := schema.Parse(tx.Statement.Dest); s != nil {
results := indirect(reflect.ValueOf(tx.Statement.Dest))
switch results.Kind() {
case reflect.Slice:
fields := [][]*Field{}
for i := 0; i < results.Len(); i++ {
fields = append(fields, structToField(results.Index(i), tx.Statement, s))
}
fieldChan <- fields
case reflect.Struct:
fieldChan <- [][]*Field{structToField(results, tx.Statement, s)}
}
}
}
}()
return fieldChan
}
// Set set a value to the field
func (field *Field) Set(value interface{}) (err error) {
if !field.Value.IsValid() {
return errors.New("field value not valid")
}
if !field.Value.CanAddr() {
return ErrUnaddressable
}
reflectValue, ok := value.(reflect.Value)
if !ok {
reflectValue = reflect.ValueOf(value)
}
fieldValue := field.Value
if reflectValue.IsValid() {
if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
} else {
if fieldValue.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
fieldValue.Set(reflect.New(field.StructField.Type.Elem()))
}
fieldValue = fieldValue.Elem()
}
if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
} else if scanner, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
err = scanner.Scan(reflectValue.Interface())
} else {
err = fmt.Errorf("could not convert argument of field %s from %s to %s", field.Name, reflectValue.Type(), fieldValue.Type())
}
}
} else {
field.Value.Set(reflect.Zero(fieldValue.Type()))
}
field.IsBlank = isBlank(field.Value)
return err
}
func mapToFields(value map[string]interface{}, stmt *builder.Statement, s *schema.Schema) (fields []*Field) {
// sort
// TODO assign those value to dest
for k, v := range value {
if s != nil {
if f := s.FieldByName(k); f != nil {
fields = append(fields, &Field{Field: f, Value: reflect.ValueOf(v)})
continue
}
}
fields = append(fields, &Field{Field: &schema.Field{DBName: k}, Value: reflect.ValueOf(v)})
}
sort.SliceStable(fields, func(i, j int) bool {
return strings.Compare(fields[i].Field.DBName, fields[j].Field.DBName) < 0
})
return
}
func structToField(value reflect.Value, stmt *builder.Statement, s *schema.Schema) (fields []*Field) {
// TODO use Offset to replace FieldByName?
for _, sf := range s.Fields {
obj := value
for _, bn := range sf.BindNames {
obj = value.FieldByName(bn)
}
fields = append(fields, &Field{Field: sf, Value: obj, IsBlank: isBlank(obj)})
}
return
}