Refactor GetModelStruct

This commit is contained in:
Jinzhu 2016-01-03 11:11:30 +08:00
parent 4e45e6dc2d
commit 6a5a2dbc55
2 changed files with 164 additions and 191 deletions

View File

@ -75,9 +75,7 @@ func (scope *Scope) Fields() map[string]*Field {
} }
} }
if modelStruct.cached { scope.fields = fields
scope.fields = fields
}
return fields return fields
} }
return scope.fields return scope.fields

View File

@ -45,7 +45,6 @@ type ModelStruct struct {
StructFields []*StructField StructFields []*StructField
ModelType reflect.Type ModelType reflect.Type
defaultTableName string defaultTableName string
cached bool
} }
func (s *ModelStruct) TableName(db *DB) string { func (s *ModelStruct) TableName(db *DB) string {
@ -96,6 +95,15 @@ type Relationship struct {
JoinTableHandler JoinTableHandlerInterface JoinTableHandler JoinTableHandlerInterface
} }
func getForeignField(column string, fields []*StructField) *StructField {
for _, field := range fields {
if field.Name == column || field.DBName == ToDBName(column) {
return field
}
}
return nil
}
func (scope *Scope) GetModelStruct() *ModelStruct { func (scope *Scope) GetModelStruct() *ModelStruct {
var modelStruct ModelStruct var modelStruct ModelStruct
// Scope value can't be nil // Scope value can't be nil
@ -132,7 +140,6 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} }
// Get all fields // Get all fields
fields := []*StructField{}
for i := 0; i < reflectType.NumField(); i++ { for i := 0; i < reflectType.NumField(); i++ {
if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) { if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) {
field := &StructField{ field := &StructField{
@ -145,189 +152,155 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
if fieldStruct.Tag.Get("sql") == "-" { if fieldStruct.Tag.Get("sql") == "-" {
field.IsIgnored = true field.IsIgnored = true
}
if _, ok := field.TagSettings["PRIMARY_KEY"]; ok {
field.IsPrimaryKey = true
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
}
if _, ok := field.TagSettings["DEFAULT"]; ok {
field.HasDefaultValue = true
}
if value, ok := field.TagSettings["COLUMN"]; ok {
field.DBName = value
} else { } else {
field.DBName = ToDBName(fieldStruct.Name) if _, ok := field.TagSettings["PRIMARY_KEY"]; ok {
} field.IsPrimaryKey = true
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
fields = append(fields, field)
}
}
var finished = make(chan bool)
go func(finished chan bool) {
for _, field := range fields {
if !field.IsIgnored {
fieldStruct := field.Struct
indirectType := fieldStruct.Type
if indirectType.Kind() == reflect.Ptr {
indirectType = indirectType.Elem()
} }
if _, isScanner := reflect.New(indirectType).Interface().(sql.Scanner); isScanner { if _, ok := field.TagSettings["DEFAULT"]; ok {
field.HasDefaultValue = true
}
fieldValue := reflect.New(fieldStruct.Type).Interface()
if _, isScanner := fieldValue.(sql.Scanner); isScanner {
// is scanner
field.IsScanner, field.IsNormal = true, true field.IsScanner, field.IsNormal = true, true
} } else if _, isTime := fieldValue.(*time.Time); isTime {
// is time
if _, isTime := reflect.New(indirectType).Interface().(*time.Time); isTime {
field.IsNormal = true field.IsNormal = true
} } else if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
// embedded struct
if !field.IsNormal { for _, subField := range scope.New(fieldValue).GetStructFields() {
toScope := scope.New(reflect.New(fieldStruct.Type).Interface()) subField = subField.clone()
subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
getForeignField := func(column string, fields []*StructField) *StructField { if subField.IsPrimaryKey {
for _, field := range fields { modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
if field.Name == column || field.DBName == ToDBName(column) {
return field
}
} }
return nil modelStruct.StructFields = append(modelStruct.StructFields, subField)
}
continue
} else {
indirectType := fieldStruct.Type
for indirectType.Kind() == reflect.Ptr {
indirectType = indirectType.Elem()
} }
var relationship = &Relationship{}
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
if polymorphicField := getForeignField(polymorphic+"Id", toScope.GetStructFields()); polymorphicField != nil {
if polymorphicType := getForeignField(polymorphic+"Type", toScope.GetStructFields()); polymorphicType != nil {
relationship.ForeignFieldNames = []string{polymorphicField.Name}
relationship.ForeignDBNames = []string{polymorphicField.DBName}
relationship.AssociationForeignFieldNames = []string{scope.PrimaryField().Name}
relationship.AssociationForeignDBNames = []string{scope.PrimaryField().DBName}
relationship.PolymorphicType = polymorphicType.Name
relationship.PolymorphicDBName = polymorphicType.DBName
polymorphicType.IsForeignKey = true
polymorphicField.IsForeignKey = true
}
}
}
var foreignKeys []string
if foreignKey, ok := field.TagSettings["FOREIGNKEY"]; ok {
foreignKeys = append(foreignKeys, foreignKey)
}
switch indirectType.Kind() { switch indirectType.Kind() {
case reflect.Slice: case reflect.Slice:
elemType := indirectType.Elem() defer func(field *StructField) {
if elemType.Kind() == reflect.Ptr { var (
elemType = elemType.Elem() relationship = &Relationship{}
} toScope = scope.New(reflect.New(field.Struct.Type).Interface())
foreignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ";")
associationForeignKeys = strings.Split(field.TagSettings["ASSOCIATIONFOREIGNKEY"], ";")
elemType = field.Struct.Type
)
if elemType.Kind() == reflect.Struct { for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr {
if many2many := field.TagSettings["MANY2MANY"]; many2many != "" { elemType = elemType.Elem()
relationship.Kind = "many_to_many" }
// foreign keys if elemType.Kind() == reflect.Struct {
if len(foreignKeys) == 0 { if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
for _, field := range scope.PrimaryFields() { relationship.Kind = "many_to_many"
foreignKeys = append(foreignKeys, field.DBName)
}
}
for _, foreignKey := range foreignKeys { // if no foreign keys
if field, ok := scope.FieldByName(foreignKey); ok { if len(foreignKeys) == 0 {
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, field.DBName) for _, field := range scope.PrimaryFields() { // FIXME
joinTableDBName := ToDBName(reflectType.Name()) + "_" + field.DBName foreignKeys = append(foreignKeys, field.DBName)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName)
}
}
// association foreign keys
var associationForeignKeys []string
if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
associationForeignKeys = []string{foreignKey}
} else {
for _, field := range toScope.PrimaryFields() {
associationForeignKeys = append(associationForeignKeys, field.DBName)
}
}
for _, name := range associationForeignKeys {
if field, ok := toScope.FieldByName(name); ok {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
}
}
joinTableHandler := JoinTableHandler{}
joinTableHandler.Setup(relationship, many2many, reflectType, elemType)
relationship.JoinTableHandler = &joinTableHandler
field.Relationship = relationship
} else {
relationship.Kind = "has_many"
if len(foreignKeys) == 0 {
for _, field := range scope.PrimaryFields() {
if foreignField := getForeignField(reflectType.Name()+field.Name, toScope.GetStructFields()); foreignField != nil {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.Name)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, field.DBName)
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
} }
} }
for _, foreignKey := range foreignKeys {
if field, ok := scope.FieldByName(foreignKey); ok { // FIXME
// source foreign keys (db names)
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, field.DBName)
// join table foreign keys for source
joinTableDBName := ToDBName(reflectType.Name()) + "_" + field.DBName
relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName)
}
}
// if no association foreign keys
if len(associationForeignKeys) == 0 {
for _, field := range toScope.PrimaryFields() {
associationForeignKeys = append(associationForeignKeys, field.DBName)
}
}
for _, name := range associationForeignKeys {
if field, ok := toScope.FieldByName(name); ok {
// association foreign keys (db names)
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
// join table foreign keys for association
joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
}
}
joinTableHandler := JoinTableHandler{}
joinTableHandler.Setup(relationship, many2many, reflectType, elemType)
relationship.JoinTableHandler = &joinTableHandler
field.Relationship = relationship
} else { } else {
relationship.Kind = "has_many"
// if no foreign keys
if len(foreignKeys) == 0 {
for _, field := range scope.PrimaryFields() { // FIXME
foreignKeys = append(foreignKeys, reflectType.Name()+field.Name)
}
}
for _, foreignKey := range foreignKeys { for _, foreignKey := range foreignKeys {
if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scope.PrimaryField().Name) // source foreign keys
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scope.PrimaryField().DBName) foreignField.IsForeignKey = true
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.Name)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, field.DBName)
// association foreign keys
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
} }
} }
}
if len(relationship.ForeignFieldNames) != 0 { if len(relationship.ForeignFieldNames) != 0 {
field.Relationship = relationship field.Relationship = relationship
}
}
} else {
field.IsNormal = true
}
case reflect.Struct:
if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
for _, toField := range toScope.GetStructFields() {
toField = toField.clone()
toField.Names = append([]string{fieldStruct.Name}, toField.Names...)
modelStruct.StructFields = append(modelStruct.StructFields, toField)
if toField.IsPrimaryKey {
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, toField)
}
}
continue
} else {
if len(foreignKeys) == 0 {
for _, f := range scope.PrimaryFields() {
if foreignField := getForeignField(modelStruct.ModelType.Name()+f.Name, toScope.GetStructFields()); foreignField != nil {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, f.Name)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, f.DBName)
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
} }
} }
} else { } else {
for _, foreignKey := range foreignKeys { field.IsNormal = true
if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { }
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scope.PrimaryField().Name) }(field)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scope.PrimaryField().DBName) case reflect.Struct:
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) defer func(field *StructField) {
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) var (
foreignField.IsForeignKey = true relationship = &Relationship{}
} toScope = scope.New(reflect.New(field.Struct.Type).Interface())
tagForeignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ";")
foreignKeys = tagForeignKeys
)
if len(tagForeignKeys) == 0 {
for _, primaryField := range scope.PrimaryFields() {
foreignKeys = append(foreignKeys, modelStruct.ModelType.Name()+primaryField.Name)
}
}
// if has one
for _, foreignKey := range foreignKeys {
if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil {
foreignField.IsForeignKey = true
// source foreign keys
scopeField := getForeignField(foreignKey, modelStruct.StructFields)
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scopeField.Name)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scopeField.DBName)
// association foreign keys
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
} }
} }
@ -335,25 +308,23 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
relationship.Kind = "has_one" relationship.Kind = "has_one"
field.Relationship = relationship field.Relationship = relationship
} else { } else {
foreignKeys = tagForeignKeys
if len(foreignKeys) == 0 { if len(foreignKeys) == 0 {
for _, f := range toScope.PrimaryFields() { for _, f := range toScope.PrimaryFields() {
if foreignField := getForeignField(field.Name+f.Name, fields); foreignField != nil { foreignKeys = append(foreignKeys, field.Name+f.Name)
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, f.Name)
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, f.DBName)
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
}
} }
} else { }
for _, foreignKey := range foreignKeys {
if foreignField := getForeignField(foreignKey, fields); foreignField != nil { for _, foreignKey := range foreignKeys {
relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, toScope.PrimaryField().Name) if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, toScope.PrimaryField().DBName) // association foreign keys
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, toScope.PrimaryField().Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, toScope.PrimaryField().DBName)
foreignField.IsForeignKey = true
} // source foreign keys
relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name)
relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName)
foreignField.IsForeignKey = true
} }
} }
@ -362,29 +333,33 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
field.Relationship = relationship field.Relationship = relationship
} }
} }
} }(field)
default: default:
field.IsNormal = true field.IsNormal = true
} }
} }
if field.IsNormal {
if len(modelStruct.PrimaryFields) == 0 && field.DBName == "id" {
field.IsPrimaryKey = true
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
}
}
} }
// Even it is ignored, also possible to decode db value into the field
if value, ok := field.TagSettings["COLUMN"]; ok {
field.DBName = value
} else {
field.DBName = ToDBName(fieldStruct.Name)
}
modelStruct.StructFields = append(modelStruct.StructFields, field) modelStruct.StructFields = append(modelStruct.StructFields, field)
} }
finished <- true }
}(finished)
if len(modelStruct.PrimaryFields) == 0 {
if field := getForeignField("id", modelStruct.StructFields); field != nil {
field.IsPrimaryKey = true
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
}
}
modelStructsMap.Set(reflectType, &modelStruct) modelStructsMap.Set(reflectType, &modelStruct)
<-finished
modelStruct.cached = true
return &modelStruct return &modelStruct
} }