Example #1
0
func TestPlural(t *testing.T) {
	for key, value := range inflections {
		if v := inflection.Plural(strings.ToUpper(key)); v != strings.ToUpper(value) {
			t.Errorf("%v's plural should be %v, but got %v", strings.ToUpper(key), strings.ToUpper(value), v)
		}

		if v := inflection.Plural(strings.Title(key)); v != strings.Title(value) {
			t.Errorf("%v's plural should be %v, but got %v", strings.Title(key), strings.Title(value), v)
		}

		if v := inflection.Plural(key); v != value {
			t.Errorf("%v's plural should be %v, but got %v", key, value, v)
		}
	}
}
Example #2
0
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
			} else {
				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
}