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
FieldSchema *Schema
JoinTable *Schema
ForeignKeys, AssociationForeignKeys []string
ForeignKeys, PrimaryKeys []string
}
type Polymorphic struct {
@ -56,9 +56,8 @@ func (schema *Schema) parseRelation(field *Field) {
Name: field.Name,
Field: field,
Schema: schema,
Type: RelationshipType(strings.ToLower(strings.TrimSpace(field.TagSettings["REL"]))),
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
}
// 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;"`
@ -89,11 +90,11 @@ func (schema *Schema) parseRelation(field *Field) {
}
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 {
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 {
@ -105,7 +106,7 @@ func (schema *Schema) parseRelation(field *Field) {
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 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{
@ -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() {
case reflect.Struct:
relation.Type = HasOne
case reflect.Slice:
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
}
switch field.FieldType.Kind() {
case reflect.Struct:
schema.parseStructRelation(relation, field)
case reflect.Slice:
schema.parseSliceRelation(relation, field)
default:
schema.err = fmt.Errorf("unsupported data type: %v (in %v#%v ", field.FieldType.PkgPath(), schema, field.Name)
}
} else {
for _, primaryField := range primarySchema.PrimaryFields {
if f := foreignSchema.LookUpField(field.Name + primaryField.Name); f != nil {
foreignFields = append(foreignFields, f)
primaryFields = append(primaryFields, primaryField)
}
}
}
func (schema *Schema) parseStructRelation(relation *Relationship, field *Field) error {
return nil
}
func (schema *Schema) parseSliceRelation(relation *Relationship, field *Field) error {
if len(foreignFields) == 0 {
reguessOrErr("failed to guess %v's relations with %v's field %v", relation.FieldSchema, schema, field.Name)
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
}
}
// 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
}