// CheckRelationship // 1. checks if every resource in HasOne and HasMany exists // 2. CheckRelationships func (model *Model) CheckRelationship(seed map[string]interface{}) error { for _, resoureName := range model.HasOne { if _, ok := model.router.apiFaker.Routers[inflection.Plural(resoureName)]; !ok { return HasOneErrorf("use unknown reource %s in file: %s", resoureName, model.router.filePath) } } for _, resoureName := range model.HasMany { if _, ok := model.router.apiFaker.Routers[inflection.Plural(resoureName)]; !ok { return HasManyErrorf("use unknown reource \"%s\" in file: %s", resoureName, model.router.filePath) } } for _, column := range model.Columns { if err := column.CheckRelationships(seed[column.Name], model); err != nil { return err } } return nil }
func getFuncMap(scope *gorm.Scope, field *gorm.Field, filename string) template.FuncMap { hash := func() string { return strings.Replace(time.Now().Format("20060102150506.000000000"), ".", "", -1) } return template.FuncMap{ "class": func() string { return inflection.Plural(utils.ToParamString(scope.GetModelStruct().ModelType.Name())) }, "primary_key": func() string { return fmt.Sprintf("%v", scope.PrimaryKeyValue()) }, "column": func() string { return strings.ToLower(field.Name) }, "filename": func() string { return filename }, "basename": func() string { return strings.TrimSuffix(path.Base(filename), path.Ext(filename)) }, "hash": hash, "filename_with_hash": func() string { return urlReplacer.ReplaceAllString(fmt.Sprintf("%v.%v%v", strings.TrimSuffix(filename, path.Ext(filename)), hash(), path.Ext(filename)), "-") }, "extension": func() string { return strings.TrimPrefix(path.Ext(filename), ".") }, } }
func (admin *Admin) AddResource(value interface{}, config ...*Config) *Resource { res := admin.NewResource(value, config...) if !res.Config.Invisible { var menuName string if res.Config.Singleton { menuName = res.Name } else { menuName = inflection.Plural(res.Name) } menu := &Menu{rawPath: res.ToParam(), Name: menuName} admin.menus = appendMenu(admin.menus, res.Config.Menu, menu) } admin.resources = append(admin.resources, res) return res }
// InsertRelatedData allocates and returns a new LineItem, // it will has all data of the caller LineItem, // it will insert all related data if the given Model's has any Column named xxx_id func (li LineItem) InsertRelatedData(model *Model) LineItem { // has one relationship newLi := NewLineItemWithMap(li.ToMap()) singularName := inflection.Singular(model.Name) // HasOne for _, resName := range model.HasOne { resStruct := map[string]interface{}{} if resRouter, ok := model.router.apiFaker.Routers[inflection.Plural(resName)]; ok { resLis := resRouter.Model.ToLineItems() sort.Sort(resLis) for _, resLi := range resLis { curId, ok := resLi.Get(fmt.Sprintf("%s_id", singularName)) if ok && curId == newLi.Id() { resStruct = resLi.ToMap() break } } } if len(resStruct) > 0 { newLi.Set(inflection.Singular(resName), resStruct) } } // HasMany for _, resName := range model.HasMany { resSlice := []interface{}{} if resRouter, ok := model.router.apiFaker.Routers[resName]; ok { resLis := resRouter.Model.ToLineItems() sort.Sort(resLis) for _, resLi := range resLis { curId, ok := resLi.Get(fmt.Sprintf("%s_id", singularName)) if ok && curId == newLi.Id() { resSlice = append(resSlice, resLi.ToMap()) } } } if len(resSlice) > 0 { newLi.Set(resName, resSlice) } } return newLi }
// CheckRelationships checks the if resource exists with the xxx_id func (column *Column) CheckRelationships(seedVal interface{}, model *Model) error { if !strings.HasSuffix(column.Name, "_id") { return nil } columnLogName := fmt.Sprintf("column[name=\"%s\"]", column.Name) resName := strings.TrimSuffix(column.Name, "_id") resPluralName := inflection.Plural(resName) router, ok := model.router.apiFaker.Routers[resPluralName] if ok { for _, li := range router.Model.ToLineItems() { id, ok := li.Get("id") if ok && id == seedVal { return nil } } } return ColumnsErrorf("%s has no item[id=%v] of resource[resource_name=\"%s\"]", columnLogName, seedVal, resPluralName) }
func (res Resource) ToParam() string { if res.Config.Singleton == true { return utils.ToParamString(res.Name) } return utils.ToParamString(inflection.Plural(res.Name)) }
// GetModelStruct get value's model struct, relationships based on struct and tag definition func (scope *Scope) GetModelStruct() *ModelStruct { var modelStruct ModelStruct // Scope value can't be nil if scope.Value == nil { return &modelStruct } reflectType := reflect.ValueOf(scope.Value).Type() for reflectType.Kind() == reflect.Slice || reflectType.Kind() == reflect.Ptr { reflectType = reflectType.Elem() } // Scope value need to be a struct if reflectType.Kind() != reflect.Struct { return &modelStruct } // Get Cached model struct if value := modelStructsMap.Get(reflectType); value != nil { return value } modelStruct.ModelType = reflectType // Set default table name if tabler, ok := reflect.New(reflectType).Interface().(tabler); ok { modelStruct.defaultTableName = tabler.TableName() } else { tableName := ToDBName(reflectType.Name()) if scope.db == nil || !scope.db.parent.singularTable { tableName = inflection.Plural(tableName) } modelStruct.defaultTableName = tableName } // Get all fields for i := 0; i < reflectType.NumField(); i++ { if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) { field := &StructField{ Struct: fieldStruct, Name: fieldStruct.Name, Names: []string{fieldStruct.Name}, Tag: fieldStruct.Tag, TagSettings: parseTagSetting(fieldStruct.Tag), } // is ignored field if _, ok := field.TagSettings["-"]; ok { field.IsIgnored = true } else { 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 _, ok := field.TagSettings["AUTO_INCREMENT"]; ok { field.HasDefaultValue = true } indirectType := fieldStruct.Type for indirectType.Kind() == reflect.Ptr { indirectType = indirectType.Elem() } fieldValue := reflect.New(indirectType).Interface() if _, isScanner := fieldValue.(sql.Scanner); isScanner { // is scanner field.IsScanner, field.IsNormal = true, true } else if _, isTime := fieldValue.(*time.Time); isTime { // is time field.IsNormal = true } else if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous { // is embedded struct for _, subField := range scope.New(fieldValue).GetStructFields() { subField = subField.clone() subField.Names = append([]string{fieldStruct.Name}, subField.Names...) if subField.IsPrimaryKey { modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField) } modelStruct.StructFields = append(modelStruct.StructFields, subField) } continue } else { // build relationships switch indirectType.Kind() { case reflect.Slice: defer func(field *StructField) { var ( relationship = &Relationship{} toScope = scope.New(reflect.New(field.Struct.Type).Interface()) foreignKeys []string associationForeignKeys []string elemType = field.Struct.Type ) if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" { foreignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ",") } if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" { associationForeignKeys = strings.Split(field.TagSettings["ASSOCIATIONFOREIGNKEY"], ",") } for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() } if elemType.Kind() == reflect.Struct { if many2many := field.TagSettings["MANY2MANY"]; many2many != "" { relationship.Kind = "many_to_many" // if no foreign keys defined with tag if len(foreignKeys) == 0 { for _, field := range modelStruct.PrimaryFields { foreignKeys = append(foreignKeys, field.DBName) } } for _, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil { // source foreign keys (db names) relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.DBName) // join table foreign keys for source joinTableDBName := ToDBName(reflectType.Name()) + "_" + foreignField.DBName relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName) } } // if no association foreign keys defined with tag 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 { // User has many comments, associationType is User, comment use UserID as foreign key var associationType = reflectType.Name() var toFields = toScope.GetStructFields() relationship.Kind = "has_many" if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" { // Dog has many toys, tag polymorphic is Owner, then associationType is Owner // Toy use OwnerID, OwnerType ('dogs') as foreign key if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil { associationType = polymorphic relationship.PolymorphicType = polymorphicType.Name relationship.PolymorphicDBName = polymorphicType.DBName polymorphicType.IsForeignKey = true } } // if no foreign keys defined with tag if len(foreignKeys) == 0 { // if no association foreign keys defined with tag if len(associationForeignKeys) == 0 { for _, field := range modelStruct.PrimaryFields { foreignKeys = append(foreignKeys, associationType+field.Name) associationForeignKeys = append(associationForeignKeys, field.Name) } } else { // generate foreign keys from defined association foreign keys for _, scopeFieldName := range associationForeignKeys { if foreignField := getForeignField(scopeFieldName, modelStruct.StructFields); foreignField != nil { foreignKeys = append(foreignKeys, associationType+foreignField.Name) associationForeignKeys = append(associationForeignKeys, foreignField.Name) } } } } else { // generate association foreign keys from foreign keys if len(associationForeignKeys) == 0 { for _, foreignKey := range foreignKeys { if strings.HasPrefix(foreignKey, associationType) { associationForeignKey := strings.TrimPrefix(foreignKey, associationType) if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { associationForeignKeys = append(associationForeignKeys, associationForeignKey) } } } if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 { associationForeignKeys = []string{scope.PrimaryKey()} } } else if len(foreignKeys) != len(associationForeignKeys) { scope.Err(errors.New("invalid foreign keys, should have same length")) return } } for idx, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, toFields); foreignField != nil { if associationField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); associationField != nil { // source foreign keys foreignField.IsForeignKey = true relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName) // association foreign keys relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) } } } if len(relationship.ForeignFieldNames) != 0 { field.Relationship = relationship } } } else { field.IsNormal = true } }(field) case reflect.Struct: defer func(field *StructField) { var ( // user has one profile, associationType is User, profile use UserID as foreign key // user belongs to profile, associationType is Profile, user use ProfileID as foreign key associationType = reflectType.Name() relationship = &Relationship{} toScope = scope.New(reflect.New(field.Struct.Type).Interface()) toFields = toScope.GetStructFields() tagForeignKeys []string tagAssociationForeignKeys []string ) if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" { tagForeignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ",") } if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" { tagAssociationForeignKeys = strings.Split(field.TagSettings["ASSOCIATIONFOREIGNKEY"], ",") } if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" { // Cat has one toy, tag polymorphic is Owner, then associationType is Owner // Toy use OwnerID, OwnerType ('cats') as foreign key if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil { associationType = polymorphic relationship.PolymorphicType = polymorphicType.Name relationship.PolymorphicDBName = polymorphicType.DBName polymorphicType.IsForeignKey = true } } // Has One { var foreignKeys = tagForeignKeys var associationForeignKeys = tagAssociationForeignKeys // if no foreign keys defined with tag if len(foreignKeys) == 0 { // if no association foreign keys defined with tag if len(associationForeignKeys) == 0 { for _, primaryField := range modelStruct.PrimaryFields { foreignKeys = append(foreignKeys, associationType+primaryField.Name) associationForeignKeys = append(associationForeignKeys, primaryField.Name) } } else { // generate foreign keys form association foreign keys for _, associationForeignKey := range tagAssociationForeignKeys { if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { foreignKeys = append(foreignKeys, associationType+foreignField.Name) associationForeignKeys = append(associationForeignKeys, foreignField.Name) } } } } else { // generate association foreign keys from foreign keys if len(associationForeignKeys) == 0 { for _, foreignKey := range foreignKeys { if strings.HasPrefix(foreignKey, associationType) { associationForeignKey := strings.TrimPrefix(foreignKey, associationType) if foreignField := getForeignField(associationForeignKey, modelStruct.StructFields); foreignField != nil { associationForeignKeys = append(associationForeignKeys, associationForeignKey) } } } if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 { associationForeignKeys = []string{scope.PrimaryKey()} } } else if len(foreignKeys) != len(associationForeignKeys) { scope.Err(errors.New("invalid foreign keys, should have same length")) return } } for idx, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, toFields); foreignField != nil { if scopeField := getForeignField(associationForeignKeys[idx], modelStruct.StructFields); scopeField != nil { foreignField.IsForeignKey = true // source foreign keys 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) } } } } if len(relationship.ForeignFieldNames) != 0 { relationship.Kind = "has_one" field.Relationship = relationship } else { var foreignKeys = tagForeignKeys var associationForeignKeys = tagAssociationForeignKeys if len(foreignKeys) == 0 { // generate foreign keys & association foreign keys if len(associationForeignKeys) == 0 { for _, primaryField := range toScope.PrimaryFields() { foreignKeys = append(foreignKeys, field.Name+primaryField.Name) associationForeignKeys = append(associationForeignKeys, primaryField.Name) } } else { // generate foreign keys with association foreign keys for _, associationForeignKey := range associationForeignKeys { if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil { foreignKeys = append(foreignKeys, field.Name+foreignField.Name) associationForeignKeys = append(associationForeignKeys, foreignField.Name) } } } } else { // generate foreign keys & association foreign keys if len(associationForeignKeys) == 0 { for _, foreignKey := range foreignKeys { if strings.HasPrefix(foreignKey, field.Name) { associationForeignKey := strings.TrimPrefix(foreignKey, field.Name) if foreignField := getForeignField(associationForeignKey, toFields); foreignField != nil { associationForeignKeys = append(associationForeignKeys, associationForeignKey) } } } if len(associationForeignKeys) == 0 && len(foreignKeys) == 1 { associationForeignKeys = []string{toScope.PrimaryKey()} } } else if len(foreignKeys) != len(associationForeignKeys) { scope.Err(errors.New("invalid foreign keys, should have same length")) return } } for idx, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil { if associationField := getForeignField(associationForeignKeys[idx], toFields); associationField != nil { foreignField.IsForeignKey = true // association foreign keys relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, associationField.Name) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationField.DBName) // source foreign keys relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) } } } if len(relationship.ForeignFieldNames) != 0 { relationship.Kind = "belongs_to" field.Relationship = relationship } } }(field) default: field.IsNormal = true } } } // 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) } } 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) return &modelStruct }
func (scope *Scope) GetModelStruct() *ModelStruct { var modelStruct ModelStruct reflectValue := reflect.Indirect(reflect.ValueOf(scope.Value)) if !reflectValue.IsValid() { return &modelStruct } if reflectValue.Kind() == reflect.Slice { reflectValue = reflect.Indirect(reflect.New(reflectValue.Type().Elem())) } scopeType := reflectValue.Type() if scopeType.Kind() == reflect.Ptr { scopeType = scopeType.Elem() } if value := modelStructsMap.Get(scopeType); value != nil { return value } modelStruct.ModelType = scopeType if scopeType.Kind() != reflect.Struct { return &modelStruct } if tabler, ok := reflect.New(scopeType).Interface().(interface { TableName() string }); ok { modelStruct.defaultTableName = tabler.TableName() } else { name := ToDBName(scopeType.Name()) if scope.db == nil || !scope.db.parent.singularTable { name = inflection.Plural(name) } modelStruct.defaultTableName = name } // Get all fields fields := []*StructField{} for i := 0; i < scopeType.NumField(); i++ { if fieldStruct := scopeType.Field(i); ast.IsExported(fieldStruct.Name) { field := &StructField{ Struct: fieldStruct, Name: fieldStruct.Name, Names: []string{fieldStruct.Name}, Tag: fieldStruct.Tag, } if fieldStruct.Tag.Get("sql") == "-" { field.IsIgnored = true } sqlSettings := parseTagSetting(field.Tag.Get("sql")) gormSettings := parseTagSetting(field.Tag.Get("gorm")) if _, ok := gormSettings["PRIMARY_KEY"]; ok { field.IsPrimaryKey = true modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field) } if _, ok := sqlSettings["DEFAULT"]; ok { field.HasDefaultValue = true } if value, ok := gormSettings["COLUMN"]; ok { field.DBName = value } else { field.DBName = ToDBName(fieldStruct.Name) } 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 { field.IsScanner, field.IsNormal = true, true } if _, isTime := reflect.New(indirectType).Interface().(*time.Time); isTime { field.IsNormal = true } if !field.IsNormal { gormSettings := parseTagSetting(field.Tag.Get("gorm")) toScope := scope.New(reflect.New(fieldStruct.Type).Interface()) getForeignField := func(column string, fields []*StructField) *StructField { for _, field := range fields { if field.Name == column || field.DBName == ToDBName(column) { return field } } return nil } var relationship = &Relationship{} if polymorphic := gormSettings["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 := gormSettings["FOREIGNKEY"]; ok { foreignKeys = append(foreignKeys, foreignKey) } switch indirectType.Kind() { case reflect.Slice: elemType := indirectType.Elem() if elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() } if elemType.Kind() == reflect.Struct { if many2many := gormSettings["MANY2MANY"]; many2many != "" { relationship.Kind = "many_to_many" // foreign keys if len(foreignKeys) == 0 { for _, field := range scope.PrimaryFields() { foreignKeys = append(foreignKeys, field.DBName) } } for _, foreignKey := range foreignKeys { if field, ok := scope.FieldByName(foreignKey); ok { relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, field.DBName) joinTableDBName := ToDBName(scopeType.Name()) + "_" + field.DBName relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName) } } // association foreign keys var associationForeignKeys []string if foreignKey := gormSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" { associationForeignKeys = []string{gormSettings["ASSOCIATIONFOREIGNKEY"]} } 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) relationship.AssociationForeignStructFieldNames = append(relationship.AssociationForeignFieldNames, field.Name) joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName) } } joinTableHandler := JoinTableHandler{} joinTableHandler.Setup(relationship, many2many, scopeType, elemType) relationship.JoinTableHandler = &joinTableHandler field.Relationship = relationship } else { relationship.Kind = "has_many" if len(foreignKeys) == 0 { for _, field := range scope.PrimaryFields() { if foreignField := getForeignField(scopeType.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 } } } else { for _, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scope.PrimaryField().Name) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scope.PrimaryField().DBName) relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) foreignField.IsForeignKey = true } } } if len(relationship.ForeignFieldNames) != 0 { field.Relationship = relationship } } } else { field.IsNormal = true } case reflect.Struct: if _, ok := gormSettings["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 { for _, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, toScope.GetStructFields()); foreignField != nil { relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, scope.PrimaryField().Name) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, scope.PrimaryField().DBName) relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) foreignField.IsForeignKey = true } } } if len(relationship.ForeignFieldNames) != 0 { relationship.Kind = "has_one" field.Relationship = relationship } else { if len(foreignKeys) == 0 { for _, f := range toScope.PrimaryFields() { if foreignField := getForeignField(field.Name+f.Name, fields); 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 { for _, foreignKey := range foreignKeys { if foreignField := getForeignField(foreignKey, fields); foreignField != nil { relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, toScope.PrimaryField().Name) relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, toScope.PrimaryField().DBName) relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.Name) relationship.ForeignDBNames = append(relationship.ForeignDBNames, foreignField.DBName) foreignField.IsForeignKey = true } } } if len(relationship.ForeignFieldNames) != 0 { relationship.Kind = "belongs_to" field.Relationship = relationship } } } default: field.IsNormal = true } } if field.IsNormal { if len(modelStruct.PrimaryFields) == 0 && field.DBName == "id" { field.IsPrimaryKey = true modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field) } } } modelStruct.StructFields = append(modelStruct.StructFields, field) } finished <- true }(finished) modelStructsMap.Set(scopeType, &modelStruct) <-finished modelStruct.cached = true return &modelStruct }
func newTable(typ reflect.Type) *Table { table, ok := Tables.tables[typ] if ok { return table } table, ok = Tables.inFlight[typ] if ok { return table } tableName := Underscore(typ.Name()) table = &Table{ Name: inflection.Plural(tableName), ModelName: tableName, Type: typ, Fields: make([]*Field, 0, typ.NumField()), FieldsMap: make(map[string]*Field, typ.NumField()), } Tables.inFlight[typ] = table for i := 0; i < typ.NumField(); i++ { f := typ.Field(i) if f.Anonymous { typ := f.Type if typ.Kind() == reflect.Ptr { typ = typ.Elem() } for _, ff := range newTable(typ).Fields { ff = ff.Copy() ff.Index = append(f.Index, ff.Index...) table.AddField(ff) } continue } if f.PkgPath != "" && !f.Anonymous { continue } field := table.newField(typ, f) if field != nil { table.AddField(field) } } typ = reflect.PtrTo(typ) table.Methods = make(map[string]*method) for i := 0; i < typ.NumMethod(); i++ { m := typ.Method(i) if m.PkgPath != "" { continue } if m.Type.NumIn() > 1 { continue } if m.Type.NumOut() != 1 { continue } method := &method{ Index: m.Index, appender: types.Appender(m.Type.Out(0)), } table.Methods[m.Name] = method } Tables.tables[typ] = table delete(Tables.inFlight, typ) return table }