Implement guess relation

This commit is contained in:
Jinzhu 2020-02-01 15:23:45 +08:00
parent eea78f3f30
commit a9c20291e4
1 changed files with 110 additions and 30 deletions

View File

@ -33,7 +33,7 @@ type Relationship struct {
Schema *Schema Schema *Schema
FieldSchema *Schema FieldSchema *Schema
JoinTable *Schema JoinTable *Schema
ForeignKeys, AssociationForeignKeys []string ForeignKeys, PrimaryKeys []string
} }
type Polymorphic struct { type Polymorphic struct {
@ -56,9 +56,8 @@ func (schema *Schema) parseRelation(field *Field) {
Name: field.Name, Name: field.Name,
Field: field, Field: field,
Schema: schema, Schema: schema,
Type: RelationshipType(strings.ToLower(strings.TrimSpace(field.TagSettings["REL"]))),
ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]), ForeignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
AssociationForeignKeys: toColumns(field.TagSettings["ASSOCIATION_FOREIGNKEY"]), PrimaryKeys: toColumns(field.TagSettings["PRIMARYKEY"]),
} }
) )
@ -66,6 +65,8 @@ 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` // User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
// type User struct { // type User struct {
// Toys []Toy `gorm:"polymorphic:Owner;"` // Toys []Toy `gorm:"polymorphic:Owner;"`
@ -89,11 +90,11 @@ func (schema *Schema) parseRelation(field *Field) {
} }
if relation.Polymorphic.PolymorphicType == nil { 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") 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 { 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") 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 { if schema.err == nil {
@ -105,7 +106,7 @@ func (schema *Schema) parseRelation(field *Field) {
primaryKeyField := schema.PrioritizedPrimaryField primaryKeyField := schema.PrioritizedPrimaryField
if len(relation.ForeignKeys) > 0 { if len(relation.ForeignKeys) > 0 {
if primaryKeyField = schema.LookUpField(relation.ForeignKeys[0]); primaryKeyField == nil || len(relation.ForeignKeys) > 1 { if primaryKeyField = schema.LookUpField(relation.ForeignKeys[0]); primaryKeyField == nil || len(relation.ForeignKeys) > 1 {
schema.err = fmt.Errorf("invalid polymorphic foreign key: %+v for %v on field %v", relation.ForeignKeys, schema, field.Name) 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{ relation.References = append(relation.References, Reference{
@ -115,29 +116,108 @@ func (schema *Schema) parseRelation(field *Field) {
}) })
} }
relation.Type = "has"
} else {
switch field.FieldType.Kind() {
case reflect.Struct:
schema.guessRelation(relation, field, true)
case reflect.Slice:
schema.guessRelation(relation, field, true)
default:
schema.err = fmt.Errorf("unsupported data type %v for %v on field %v", relation.FieldSchema, schema, field.Name)
}
}
if relation.Type == "has" {
switch field.FieldType.Kind() { switch field.FieldType.Kind() {
case reflect.Struct: case reflect.Struct:
relation.Type = HasOne relation.Type = HasOne
case reflect.Slice: case reflect.Slice:
relation.Type = HasMany relation.Type = HasMany
} }
}
}
func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessHas bool) {
var (
primaryFields, foreignFields []*Field
primarySchema, foreignSchema = schema, relation.FieldSchema
)
if !guessHas {
primarySchema, foreignSchema = relation.FieldSchema, schema
}
reguessOrErr := func(err string, args ...interface{}) {
if guessHas {
schema.guessRelation(relation, field, false)
} else {
schema.err = fmt.Errorf(err, args...)
}
}
if len(relation.ForeignKeys) > 0 {
for _, foreignKey := range relation.ForeignKeys {
if f := foreignSchema.LookUpField(foreignKey); f != nil {
foreignFields = append(foreignFields, f)
} else {
reguessOrErr("unsupported relations %v for %v on field %v with foreign keys %v", relation.FieldSchema, schema, field.Name, relation.ForeignKeys)
return return
} }
}
switch field.FieldType.Kind() { } else {
case reflect.Struct: for _, primaryField := range primarySchema.PrimaryFields {
schema.parseStructRelation(relation, field) if f := foreignSchema.LookUpField(field.Name + primaryField.Name); f != nil {
case reflect.Slice: foreignFields = append(foreignFields, f)
schema.parseSliceRelation(relation, field) primaryFields = append(primaryFields, primaryField)
default: }
schema.err = fmt.Errorf("unsupported data type: %v (in %v#%v ", field.FieldType.PkgPath(), schema, field.Name)
} }
} }
func (schema *Schema) parseStructRelation(relation *Relationship, field *Field) error { if len(foreignFields) == 0 {
return nil reguessOrErr("failed to guess %v's relations with %v's field %v", relation.FieldSchema, schema, field.Name)
} return
} else if len(relation.PrimaryKeys) > 0 {
func (schema *Schema) parseSliceRelation(relation *Relationship, field *Field) error { for idx, primaryKey := range relation.PrimaryKeys {
if f := primarySchema.LookUpField(primaryKey); f != nil {
if len(primaryFields) < idx+1 {
primaryFields = append(primaryFields, f)
} else if f != primaryFields[idx] {
reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.PrimaryKeys)
return
}
} else {
reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.PrimaryKeys)
return
}
}
} else if len(primaryFields) == 0 {
if len(foreignFields) == 1 {
primaryFields = append(primaryFields, primarySchema.PrioritizedPrimaryField)
} else if len(primarySchema.PrimaryFields) == len(foreignFields) {
primaryFields = append(primaryFields, primarySchema.PrimaryFields...)
} else {
reguessOrErr("unsupported relations %v for %v on field %v", relation.FieldSchema, schema, field.Name)
return
}
}
// build references
for idx, foreignField := range foreignFields {
relation.References = append(relation.References, Reference{
PriamryKey: primaryFields[idx],
ForeignKey: foreignField,
OwnPriamryKey: schema == primarySchema,
})
}
if guessHas {
relation.Type = "has"
} else {
relation.Type = "belongs_to"
}
}
func (schema *Schema) parseMany2ManyRelation(relation *Relationship, field *Field) error {
return nil return nil
} }