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

@ -25,15 +25,15 @@ type Relationships struct {
} }
type Relationship struct { type Relationship struct {
Name string Name string
Type RelationshipType Type RelationshipType
Field *Field Field *Field
Polymorphic *Polymorphic Polymorphic *Polymorphic
References []Reference References []Reference
Schema *Schema Schema *Schema
FieldSchema *Schema FieldSchema *Schema
JoinTable *Schema JoinTable *Schema
ForeignKeys, AssociationForeignKeys []string ForeignKeys, PrimaryKeys []string
} }
type Polymorphic struct { type Polymorphic struct {
@ -53,12 +53,11 @@ func (schema *Schema) parseRelation(field *Field) {
var ( var (
fieldValue = reflect.New(field.FieldType).Interface() fieldValue = reflect.New(field.FieldType).Interface()
relation = &Relationship{ relation = &Relationship{
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"]), PrimaryKeys: toColumns(field.TagSettings["PRIMARYKEY"]),
AssociationForeignKeys: toColumns(field.TagSettings["ASSOCIATION_FOREIGNKEY"]),
} }
) )
@ -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
}
}
} else {
for _, primaryField := range primarySchema.PrimaryFields {
if f := foreignSchema.LookUpField(field.Name + primaryField.Name); f != nil {
foreignFields = append(foreignFields, f)
primaryFields = append(primaryFields, primaryField)
}
}
}
if len(foreignFields) == 0 {
reguessOrErr("failed to guess %v's relations with %v's field %v", relation.FieldSchema, schema, field.Name)
return return
} else if len(relation.PrimaryKeys) > 0 {
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
}
} }
switch field.FieldType.Kind() { // build references
case reflect.Struct: for idx, foreignField := range foreignFields {
schema.parseStructRelation(relation, field) relation.References = append(relation.References, Reference{
case reflect.Slice: PriamryKey: primaryFields[idx],
schema.parseSliceRelation(relation, field) ForeignKey: foreignField,
default: OwnPriamryKey: schema == primarySchema,
schema.err = fmt.Errorf("unsupported data type: %v (in %v#%v ", field.FieldType.PkgPath(), schema, field.Name) })
}
if guessHas {
relation.Type = "has"
} else {
relation.Type = "belongs_to"
} }
} }
func (schema *Schema) parseStructRelation(relation *Relationship, field *Field) error { func (schema *Schema) parseMany2ManyRelation(relation *Relationship, field *Field) error {
return nil
}
func (schema *Schema) parseSliceRelation(relation *Relationship, field *Field) error {
return nil return nil
} }