relationship.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package schema
  2. import (
  3. "fmt"
  4. "reflect"
  5. "regexp"
  6. "strings"
  7. "github.com/jinzhu/inflection"
  8. )
  9. // RelationshipType relationship type
  10. type RelationshipType string
  11. const (
  12. HasOne RelationshipType = "has_one" // HasOneRel has one relationship
  13. HasMany RelationshipType = "has_many" // HasManyRel has many relationship
  14. BelongsTo RelationshipType = "belongs_to" // BelongsToRel belongs to relationship
  15. Many2Many RelationshipType = "many_to_many" // Many2ManyRel many to many relationship
  16. )
  17. type Relationships struct {
  18. HasOne []*Relationship
  19. BelongsTo []*Relationship
  20. HasMany []*Relationship
  21. Many2Many []*Relationship
  22. Relations map[string]*Relationship
  23. }
  24. type Relationship struct {
  25. Name string
  26. Type RelationshipType
  27. Field *Field
  28. Polymorphic *Polymorphic
  29. References []Reference
  30. Schema *Schema
  31. FieldSchema *Schema
  32. JoinTable *Schema
  33. foreignKeys, primaryKeys []string
  34. }
  35. type Polymorphic struct {
  36. PolymorphicID *Field
  37. PolymorphicType *Field
  38. Value string
  39. }
  40. type Reference struct {
  41. PrimaryKey *Field
  42. PrimaryValue string
  43. ForeignKey *Field
  44. OwnPrimaryKey bool
  45. }
  46. func (schema *Schema) parseRelation(field *Field) {
  47. var (
  48. err error
  49. fieldValue = reflect.New(field.IndirectFieldType).Interface()
  50. relation = &Relationship{
  51. Name: field.Name,
  52. Field: field,
  53. Schema: schema,
  54. foreignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
  55. primaryKeys: toColumns(field.TagSettings["REFERENCES"]),
  56. }
  57. )
  58. if relation.FieldSchema, err = Parse(fieldValue, schema.cacheStore, schema.namer); err != nil {
  59. schema.err = err
  60. return
  61. }
  62. if polymorphic, _ := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
  63. schema.buildPolymorphicRelation(relation, field, polymorphic)
  64. } else if many2many, _ := field.TagSettings["MANY2MANY"]; many2many != "" {
  65. schema.buildMany2ManyRelation(relation, field, many2many)
  66. } else {
  67. switch field.IndirectFieldType.Kind() {
  68. case reflect.Struct, reflect.Slice:
  69. schema.guessRelation(relation, field, true)
  70. default:
  71. schema.err = fmt.Errorf("unsupported data type %v for %v on field %v", relation.FieldSchema, schema, field.Name)
  72. }
  73. }
  74. if relation.Type == "has" {
  75. switch field.IndirectFieldType.Kind() {
  76. case reflect.Struct:
  77. relation.Type = HasOne
  78. case reflect.Slice:
  79. relation.Type = HasMany
  80. }
  81. }
  82. if schema.err == nil {
  83. schema.Relationships.Relations[relation.Name] = relation
  84. switch relation.Type {
  85. case HasOne:
  86. schema.Relationships.HasOne = append(schema.Relationships.HasOne, relation)
  87. case HasMany:
  88. schema.Relationships.HasMany = append(schema.Relationships.HasMany, relation)
  89. case BelongsTo:
  90. schema.Relationships.BelongsTo = append(schema.Relationships.BelongsTo, relation)
  91. case Many2Many:
  92. schema.Relationships.Many2Many = append(schema.Relationships.Many2Many, relation)
  93. }
  94. }
  95. }
  96. // User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
  97. // type User struct {
  98. // Toys []Toy `gorm:"polymorphic:Owner;"`
  99. // }
  100. // type Pet struct {
  101. // Toy Toy `gorm:"polymorphic:Owner;"`
  102. // }
  103. // type Toy struct {
  104. // OwnerID int
  105. // OwnerType string
  106. // }
  107. func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field, polymorphic string) {
  108. relation.Polymorphic = &Polymorphic{
  109. Value: schema.Table,
  110. PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"],
  111. PolymorphicID: relation.FieldSchema.FieldsByName[polymorphic+"ID"],
  112. }
  113. if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
  114. relation.Polymorphic.Value = strings.TrimSpace(value)
  115. }
  116. if relation.Polymorphic.PolymorphicType == nil {
  117. schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
  118. }
  119. if relation.Polymorphic.PolymorphicID == nil {
  120. schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %v, missing field %v", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
  121. }
  122. if schema.err == nil {
  123. relation.References = append(relation.References, Reference{
  124. PrimaryValue: relation.Polymorphic.Value,
  125. ForeignKey: relation.Polymorphic.PolymorphicType,
  126. })
  127. primaryKeyField := schema.PrioritizedPrimaryField
  128. if len(relation.foreignKeys) > 0 {
  129. if primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 {
  130. schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %v", relation.foreignKeys, schema, field.Name)
  131. }
  132. }
  133. relation.References = append(relation.References, Reference{
  134. PrimaryKey: primaryKeyField,
  135. ForeignKey: relation.Polymorphic.PolymorphicID,
  136. OwnPrimaryKey: true,
  137. })
  138. }
  139. relation.Type = "has"
  140. }
  141. func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Field, many2many string) {
  142. relation.Type = Many2Many
  143. var (
  144. err error
  145. joinTableFields []reflect.StructField
  146. fieldsMap = map[string]*Field{}
  147. ownFieldsMap = map[string]bool{} // fix self join many2many
  148. )
  149. for _, s := range []*Schema{schema, relation.FieldSchema} {
  150. for _, primaryField := range s.PrimaryFields {
  151. fieldName := s.Name + primaryField.Name
  152. if _, ok := fieldsMap[fieldName]; ok {
  153. if field.Name != s.Name {
  154. fieldName = inflection.Singular(field.Name) + primaryField.Name
  155. } else {
  156. fieldName = s.Name + primaryField.Name + "Reference"
  157. }
  158. } else {
  159. ownFieldsMap[fieldName] = true
  160. }
  161. fieldsMap[fieldName] = primaryField
  162. joinTableFields = append(joinTableFields, reflect.StructField{
  163. Name: fieldName,
  164. PkgPath: primaryField.StructField.PkgPath,
  165. Type: primaryField.StructField.Type,
  166. Tag: removeSettingFromTag(primaryField.StructField.Tag, "column"),
  167. })
  168. }
  169. }
  170. if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, schema.namer); err != nil {
  171. schema.err = err
  172. }
  173. relation.JoinTable.Name = many2many
  174. relation.JoinTable.Table = schema.namer.JoinTableName(many2many)
  175. // build references
  176. for _, f := range relation.JoinTable.Fields {
  177. relation.References = append(relation.References, Reference{
  178. PrimaryKey: fieldsMap[f.Name],
  179. ForeignKey: f,
  180. OwnPrimaryKey: schema == fieldsMap[f.Name].Schema && ownFieldsMap[f.Name],
  181. })
  182. }
  183. return
  184. }
  185. func (schema *Schema) guessRelation(relation *Relationship, field *Field, guessHas bool) {
  186. var (
  187. primaryFields, foreignFields []*Field
  188. primarySchema, foreignSchema = schema, relation.FieldSchema
  189. )
  190. if !guessHas {
  191. primarySchema, foreignSchema = relation.FieldSchema, schema
  192. }
  193. reguessOrErr := func(err string, args ...interface{}) {
  194. if guessHas {
  195. schema.guessRelation(relation, field, false)
  196. } else {
  197. schema.err = fmt.Errorf(err, args...)
  198. }
  199. }
  200. if len(relation.foreignKeys) > 0 {
  201. for _, foreignKey := range relation.foreignKeys {
  202. if f := foreignSchema.LookUpField(foreignKey); f != nil {
  203. foreignFields = append(foreignFields, f)
  204. } else {
  205. reguessOrErr("unsupported relations %v for %v on field %v with foreign keys %v", relation.FieldSchema, schema, field.Name, relation.foreignKeys)
  206. return
  207. }
  208. }
  209. } else {
  210. for _, primaryField := range primarySchema.PrimaryFields {
  211. lookUpName := schema.Name + primaryField.Name
  212. if !guessHas {
  213. lookUpName = field.Name + primaryField.Name
  214. }
  215. if f := foreignSchema.LookUpField(lookUpName); f != nil {
  216. foreignFields = append(foreignFields, f)
  217. primaryFields = append(primaryFields, primaryField)
  218. }
  219. }
  220. }
  221. if len(foreignFields) == 0 {
  222. reguessOrErr("failed to guess %v's relations with %v's field %v 1 g %v", relation.FieldSchema, schema, field.Name, guessHas)
  223. return
  224. } else if len(relation.primaryKeys) > 0 {
  225. for idx, primaryKey := range relation.primaryKeys {
  226. if f := primarySchema.LookUpField(primaryKey); f != nil {
  227. if len(primaryFields) < idx+1 {
  228. primaryFields = append(primaryFields, f)
  229. } else if f != primaryFields[idx] {
  230. reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.primaryKeys)
  231. return
  232. }
  233. } else {
  234. reguessOrErr("unsupported relations %v for %v on field %v with primary keys %v", relation.FieldSchema, schema, field.Name, relation.primaryKeys)
  235. return
  236. }
  237. }
  238. } else if len(primaryFields) == 0 {
  239. if len(foreignFields) == 1 {
  240. primaryFields = append(primaryFields, primarySchema.PrioritizedPrimaryField)
  241. } else if len(primarySchema.PrimaryFields) == len(foreignFields) {
  242. primaryFields = append(primaryFields, primarySchema.PrimaryFields...)
  243. } else {
  244. reguessOrErr("unsupported relations %v for %v on field %v", relation.FieldSchema, schema, field.Name)
  245. return
  246. }
  247. }
  248. // build references
  249. for idx, foreignField := range foreignFields {
  250. relation.References = append(relation.References, Reference{
  251. PrimaryKey: primaryFields[idx],
  252. ForeignKey: foreignField,
  253. OwnPrimaryKey: schema == primarySchema && guessHas,
  254. })
  255. }
  256. if guessHas {
  257. relation.Type = "has"
  258. } else {
  259. relation.Type = BelongsTo
  260. }
  261. }
  262. type Constraint struct {
  263. Name string
  264. Field *Field
  265. Schema *Schema
  266. ForeignKeys []*Field
  267. ReferenceSchema *Schema
  268. References []*Field
  269. OnDelete string
  270. OnUpdate string
  271. }
  272. func (rel *Relationship) ParseConstraint() *Constraint {
  273. str := rel.Field.TagSettings["CONSTRAINT"]
  274. if str == "-" {
  275. return nil
  276. }
  277. var (
  278. name string
  279. idx = strings.Index(str, ",")
  280. settings = ParseTagSetting(str, ",")
  281. )
  282. if idx != -1 && regexp.MustCompile("^[A-Za-z-_]+$").MatchString(str[0:idx]) {
  283. name = str[0:idx]
  284. } else {
  285. name = rel.Schema.namer.RelationshipFKName(*rel)
  286. }
  287. constraint := Constraint{
  288. Name: name,
  289. Field: rel.Field,
  290. OnUpdate: settings["ONUPDATE"],
  291. OnDelete: settings["ONDELETE"],
  292. Schema: rel.Schema,
  293. }
  294. for _, ref := range rel.References {
  295. if ref.PrimaryKey != nil && !ref.OwnPrimaryKey {
  296. constraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey)
  297. constraint.References = append(constraint.References, ref.PrimaryKey)
  298. constraint.ReferenceSchema = ref.PrimaryKey.Schema
  299. }
  300. }
  301. if constraint.ReferenceSchema == nil {
  302. return nil
  303. }
  304. return &constraint
  305. }