Implement parse many2many relation
This commit is contained in:
parent
a9c20291e4
commit
fd9b688084
@ -103,11 +103,11 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
|||||||
field.DBName = dbName
|
field.DBName = dbName
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := field.TagSettings["PRIMARY_KEY"]; ok && checkTruth(val) {
|
if val, ok := field.TagSettings["PRIMARYKEY"]; ok && checkTruth(val) {
|
||||||
field.PrimaryKey = true
|
field.PrimaryKey = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := field.TagSettings["AUTO_INCREMENT"]; ok && checkTruth(val) {
|
if val, ok := field.TagSettings["AUTOINCREMENT"]; ok && checkTruth(val) {
|
||||||
field.AutoIncrement = true
|
field.AutoIncrement = true
|
||||||
field.HasDefaultValue = true
|
field.HasDefaultValue = true
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
|||||||
for _, ef := range field.EmbeddedSchema.Fields {
|
for _, ef := range field.EmbeddedSchema.Fields {
|
||||||
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)
|
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)
|
||||||
|
|
||||||
if prefix, ok := field.TagSettings["EMBEDDED_PREFIX"]; ok {
|
if prefix, ok := field.TagSettings["EMBEDDEDPREFIX"]; ok {
|
||||||
ef.DBName = prefix + ef.DBName
|
ef.DBName = prefix + ef.DBName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ type Namer interface {
|
|||||||
TableName(table string) string
|
TableName(table string) string
|
||||||
ColumnName(column string) string
|
ColumnName(column string) string
|
||||||
JoinTableName(table string) string
|
JoinTableName(table string) string
|
||||||
JoinTableColumnName(table, column string) string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamingStrategy tables, columns naming strategy
|
// NamingStrategy tables, columns naming strategy
|
||||||
@ -40,11 +39,6 @@ func (ns NamingStrategy) JoinTableName(str string) string {
|
|||||||
return ns.TablePrefix + toDBName(str)
|
return ns.TablePrefix + toDBName(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinTableColumnName convert string to join table column name
|
|
||||||
func (ns NamingStrategy) JoinTableColumnName(referenceTable, referenceColumn string) string {
|
|
||||||
return inflection.Singular(toDBName(referenceTable)) + toDBName(referenceColumn)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
smap sync.Map
|
smap sync.Map
|
||||||
// https://github.com/golang/lint/blob/master/lint.go#L770
|
// https://github.com/golang/lint/blob/master/lint.go#L770
|
||||||
|
@ -57,7 +57,7 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||||||
Field: field,
|
Field: field,
|
||||||
Schema: schema,
|
Schema: schema,
|
||||||
ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
|
ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
|
||||||
PrimaryKeys: toColumns(field.TagSettings["PRIMARYKEY"]),
|
PrimaryKeys: toColumns(field.TagSettings["REFERENCES"]),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,63 +65,13 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse Polymorphic relations
|
|
||||||
//
|
|
||||||
// User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
|
|
||||||
// type User struct {
|
|
||||||
// Toys []Toy `gorm:"polymorphic:Owner;"`
|
|
||||||
// }
|
|
||||||
// type Pet struct {
|
|
||||||
// Toy Toy `gorm:"polymorphic:Owner;"`
|
|
||||||
// }
|
|
||||||
// type Toy struct {
|
|
||||||
// OwnerID int
|
|
||||||
// OwnerType string
|
|
||||||
// }
|
|
||||||
if polymorphic, _ := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
|
if polymorphic, _ := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
|
||||||
relation.Polymorphic = &Polymorphic{
|
schema.buildPolymorphicRelation(relation, field, polymorphic)
|
||||||
Value: schema.Table,
|
} else if many2many, _ := field.TagSettings["MANY2MANY"]; many2many != "" {
|
||||||
PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"],
|
schema.buildMany2ManyRelation(relation, field, many2many)
|
||||||
PolymorphicID: relation.FieldSchema.FieldsByName[polymorphic+"ID"],
|
|
||||||
}
|
|
||||||
|
|
||||||
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
|
|
||||||
relation.Polymorphic.Value = strings.TrimSpace(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if relation.Polymorphic.PolymorphicType == nil {
|
|
||||||
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if relation.Polymorphic.PolymorphicID == nil {
|
|
||||||
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
if schema.err == nil {
|
|
||||||
relation.References = append(relation.References, Reference{
|
|
||||||
PriamryValue: relation.Polymorphic.Value,
|
|
||||||
ForeignKey: relation.Polymorphic.PolymorphicType,
|
|
||||||
})
|
|
||||||
|
|
||||||
primaryKeyField := schema.PrioritizedPrimaryField
|
|
||||||
if len(relation.ForeignKeys) > 0 {
|
|
||||||
if primaryKeyField = schema.LookUpField(relation.ForeignKeys[0]); primaryKeyField == nil || len(relation.ForeignKeys) > 1 {
|
|
||||||
schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %v", relation.ForeignKeys, schema, field.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relation.References = append(relation.References, Reference{
|
|
||||||
PriamryKey: primaryKeyField,
|
|
||||||
ForeignKey: relation.Polymorphic.PolymorphicType,
|
|
||||||
OwnPriamryKey: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
relation.Type = "has"
|
|
||||||
} else {
|
} else {
|
||||||
switch field.FieldType.Kind() {
|
switch field.FieldType.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct, reflect.Slice:
|
||||||
schema.guessRelation(relation, field, true)
|
|
||||||
case reflect.Slice:
|
|
||||||
schema.guessRelation(relation, field, true)
|
schema.guessRelation(relation, field, true)
|
||||||
default:
|
default:
|
||||||
schema.err = fmt.Errorf("unsupported data type %v for %v on field %v", relation.FieldSchema, schema, field.Name)
|
schema.err = fmt.Errorf("unsupported data type %v for %v on field %v", relation.FieldSchema, schema, field.Name)
|
||||||
@ -138,6 +88,102 @@ func (schema *Schema) parseRelation(field *Field) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
|
||||||
|
// type User struct {
|
||||||
|
// Toys []Toy `gorm:"polymorphic:Owner;"`
|
||||||
|
// }
|
||||||
|
// type Pet struct {
|
||||||
|
// Toy Toy `gorm:"polymorphic:Owner;"`
|
||||||
|
// }
|
||||||
|
// type Toy struct {
|
||||||
|
// OwnerID int
|
||||||
|
// OwnerType string
|
||||||
|
// }
|
||||||
|
func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field, polymorphic string) {
|
||||||
|
relation.Polymorphic = &Polymorphic{
|
||||||
|
Value: schema.Table,
|
||||||
|
PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"],
|
||||||
|
PolymorphicID: relation.FieldSchema.FieldsByName[polymorphic+"ID"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
|
||||||
|
relation.Polymorphic.Value = strings.TrimSpace(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if relation.Polymorphic.PolymorphicType == nil {
|
||||||
|
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if relation.Polymorphic.PolymorphicID == nil {
|
||||||
|
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema.err == nil {
|
||||||
|
relation.References = append(relation.References, Reference{
|
||||||
|
PriamryValue: relation.Polymorphic.Value,
|
||||||
|
ForeignKey: relation.Polymorphic.PolymorphicType,
|
||||||
|
})
|
||||||
|
|
||||||
|
primaryKeyField := schema.PrioritizedPrimaryField
|
||||||
|
if len(relation.ForeignKeys) > 0 {
|
||||||
|
if primaryKeyField = schema.LookUpField(relation.ForeignKeys[0]); primaryKeyField == nil || len(relation.ForeignKeys) > 1 {
|
||||||
|
schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %v", relation.ForeignKeys, schema, field.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relation.References = append(relation.References, Reference{
|
||||||
|
PriamryKey: primaryKeyField,
|
||||||
|
ForeignKey: relation.Polymorphic.PolymorphicType,
|
||||||
|
OwnPriamryKey: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
relation.Type = "has"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Field, many2many string) {
|
||||||
|
relation.Type = Many2Many
|
||||||
|
|
||||||
|
var (
|
||||||
|
joinTableFields []reflect.StructField
|
||||||
|
fieldsMap = map[string]*Field{}
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, s := range []*Schema{schema, relation.Schema} {
|
||||||
|
for _, primaryField := range s.PrimaryFields {
|
||||||
|
fieldName := s.Name + primaryField.Name
|
||||||
|
if _, ok := fieldsMap[fieldName]; ok {
|
||||||
|
if field.Name != s.Name {
|
||||||
|
fieldName = field.Name + primaryField.Name
|
||||||
|
} else {
|
||||||
|
fieldName = s.Name + primaryField.Name + "Reference"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsMap[fieldName] = primaryField
|
||||||
|
joinTableFields = append(joinTableFields, reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: primaryField.StructField.PkgPath,
|
||||||
|
Type: primaryField.StructField.Type,
|
||||||
|
Tag: removeSettingFromTag(primaryField.StructField.Tag, "column"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relation.JoinTable, schema.err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, schema.namer)
|
||||||
|
relation.JoinTable.Name = many2many
|
||||||
|
relation.JoinTable.Table = schema.namer.JoinTableName(many2many)
|
||||||
|
|
||||||
|
// build references
|
||||||
|
for _, f := range relation.JoinTable.Fields {
|
||||||
|
relation.References = append(relation.References, Reference{
|
||||||
|
PriamryKey: fieldsMap[f.Name],
|
||||||
|
ForeignKey: f,
|
||||||
|
OwnPriamryKey: schema == fieldsMap[f.Name].Schema,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessHas bool) {
|
func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessHas bool) {
|
||||||
var (
|
var (
|
||||||
primaryFields, foreignFields []*Field
|
primaryFields, foreignFields []*Field
|
||||||
@ -214,10 +260,6 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessH
|
|||||||
if guessHas {
|
if guessHas {
|
||||||
relation.Type = "has"
|
relation.Type = "has"
|
||||||
} else {
|
} else {
|
||||||
relation.Type = "belongs_to"
|
relation.Type = BelongsTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (schema *Schema) parseMany2ManyRelation(relation *Relationship, field *Field) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@ package schema
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,3 +39,7 @@ func toColumns(val string) (results []string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeSettingFromTag(tag reflect.StructTag, name string) reflect.StructTag {
|
||||||
|
return reflect.StructTag(regexp.MustCompile(`(?i)(gorm:.*?)(`+name+`:.*?)(;|("))`).ReplaceAllString(string(tag), "${1}${4}"))
|
||||||
|
}
|
||||||
|
23
schema/utils_test.go
Normal file
23
schema/utils_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoveSettingFromTag(t *testing.T) {
|
||||||
|
tags := map[string]string{
|
||||||
|
`gorm:"before:value;column:db;after:value" other:"before:value;column:db;after:value"`: `gorm:"before:value;after:value" other:"before:value;column:db;after:value"`,
|
||||||
|
`gorm:"before:value;column:db;" other:"before:value;column:db;after:value"`: `gorm:"before:value;" other:"before:value;column:db;after:value"`,
|
||||||
|
`gorm:"before:value;column:db" other:"before:value;column:db;after:value"`: `gorm:"before:value;" other:"before:value;column:db;after:value"`,
|
||||||
|
`gorm:"column:db" other:"before:value;column:db;after:value"`: `gorm:"" other:"before:value;column:db;after:value"`,
|
||||||
|
`gorm:"before:value;column:db ;after:value" other:"before:value;column:db;after:value"`: `gorm:"before:value;after:value" other:"before:value;column:db;after:value"`,
|
||||||
|
`gorm:"before:value;column:db; after:value" other:"before:value;column:db;after:value"`: `gorm:"before:value; after:value" other:"before:value;column:db;after:value"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tags {
|
||||||
|
if string(removeSettingFromTag(reflect.StructTag(k), "column")) != v {
|
||||||
|
t.Errorf("%v after removeSettingFromTag should equal %v, but got %v", k, v, removeSettingFromTag(reflect.StructTag(k), "column"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user