forked from gocaveman/tmeta
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrelations.go
217 lines (202 loc) · 6.5 KB
/
relations.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package tmeta
import "reflect"
// Relation is implemented by the supported types of relations, BelongsTo, HasMany, etc.
type Relation interface {
RelationName() string
RelationGoValueField() string
}
// RelationMap is a map of named relations to the relation data itself.
// Values must of one of the supported relation types.
type RelationMap map[string]Relation
// RelationNamed is a simple map lookup.
func (rm RelationMap) RelationNamed(n string) Relation {
return rm[n]
}
// RelationTargetPtr will find the named relation and use it's RelationGoValueField
// to obtain a pointer to the target field and return it. If this is not possible
// then nil is returned. If successful, the returned value will be a pointer to
// the type of the indicated field, e.g. if the field is of type `*Widget`, the
// return value will be of type `**Widget`, for type `[]Widget` it will
// return type `*[]Widget`.
func (rm RelationMap) RelationTargetPtr(o interface{}, n string) interface{} {
r := rm[n]
if r == nil {
return nil
}
fn := r.RelationGoValueField()
f := derefValue(reflect.ValueOf(o)).FieldByName(fn)
return f.Addr().Interface()
}
// BelongsTo is a relation for a single struct pointer where the
// ID of the linked row is stored on this table.
//
// Example using struct tags:
//
// type Book struct {
// // ...
//
// // This ID points to the row in the "author" table.
// AuthorID string `db:"author_id"`
//
// // This is the "author" relation that can be loaded from it.
// Author *Author `db:"-" tmeta:"belongs_to"`
// }
//
// Full form with all options:
//
// // The sql_id_field here must match the db struct tag in the field above.
// Author *Author `db:"-" tmeta:"belongs_to,relation_name=author,sql_id_field=author_id"`
//
// No options are required except the relation type ("belongs_to").
type BelongsTo struct {
Name string
GoValueField string // e.g. "Author" (of type *Author)
SQLIDField string // e.g. "author_id"
}
func (r *BelongsTo) RelationName() string {
return r.Name
}
func (r *BelongsTo) RelationGoValueField() string {
return r.GoValueField
}
// HasMany is a relation for a slice where the ID of the linked rows
// are stored on the other table.
//
// Example using struct tags:
//
// type Publisher struct {
// // ...
// BookList []Book `db:"-" tmeta:"has_many"`
// }
//
// type Book {
// // ...
// PublisherID string `db:"publisher_id"`
// }
//
// Full form with all options:
//
// // The sql_other_id_field here must match the ID field in the other table.
// BookList []Book `db:"-" tmeta:"has_many,relation_name=book_list,sql_other_id_field=publisher_id"`
//
// No options are required except the relation type ("has_many").
type HasMany struct {
Name string
GoValueField string // e.g. "Books" (of type []Book)
SQLOtherIDField string // e.g. "author_id" - on the other table
}
func (r *HasMany) RelationName() string {
return r.Name
}
func (r *HasMany) RelationGoValueField() string {
return r.GoValueField
}
// HasOne is a relation for a slice where the ID of the linked rows
// are stored on the other table.
//
// Example using struct tags:
//
// type Category struct {
// // ...
// CategoryInfo *CategoryInfo `db:"-" tmeta:"has_one"`
// }
//
// type CategoryInfo struct {
// // ...
// CategoryID string `db:"category_id"` // one to one relation
// }
//
// Full form with all options:
//
// CategoryInfo *CategoryInfo `db:"-" tmeta:"has_one,relation_name=category_info,sql_other_id_field=category_id"`
//
// No options are required except the relation type ("has_one").
type HasOne struct {
Name string
GoValueField string // e.g. "CategoryInfo" (of type *CategoryInfo)
SQLOtherIDField string // e.g. "category_id" - on the other table
}
func (r *HasOne) RelationName() string {
return r.Name
}
func (r *HasOne) RelationGoValueField() string {
return r.GoValueField
}
// BelongsToMany is a relation that uses a join table as a many to many relation.
//
// Example using struct tags:
//
// type Book struct {
// // ...
// CategoryList []Category `db:"-" tmeta:"belongs_to_many,join_name=book_category"`
// }
//
// // BookCategory is the join table.
// type BookCategory struct {
// BookID string `db:"book_id" tmeta:"pk"`
// CategoryID string `db:"category_id" tmeta:"pk"`
// }
//
// type Category struct {
// CategoryID string `db:"category_id" tmeta:"pk"`
// Name string `db:"name"`
// }
//
// Full form with all options:
//
// CategoryList []Category `db:"-" tmeta:"belongs_to_many,join_name=book_category,sql_id_field=book_id,sql_other_id_field=category_id"`
//
// The join_name option is required.
type BelongsToMany struct {
Name string
GoValueField string // e.g. "BookLists" (of type []Book)
JoinName string // the name of the join table (not necessarily the SQL name, it's Name()), e.g. ""
SQLIDField string // SQL ID field on join table corresponding to this side
SQLOtherIDField string // SQL ID field on join table corresponding to the other side
}
func (r *BelongsToMany) RelationName() string {
return r.Name
}
func (r *BelongsToMany) RelationGoValueField() string {
return r.GoValueField
}
// BelongsToManyIDs is a relation that uses a join table as a many to many relation
// but stores the IDs in a slice instead of the instances directly. Useful for
// easily updating the join table.
//
// Example using struct tags:
//
// type Book struct {
// // ...
// CategoryIDList []string `db:"-" tmeta:"belongs_to_many_ids,join_name=book_category"`
// }
//
// // BookCategory is the join table.
// type BookCategory struct {
// BookID string `db:"book_id" tmeta:"pk"`
// CategoryID string `db:"category_id" tmeta:"pk"`
// }
//
// type Category struct {
// CategoryID string `db:"category_id" tmeta:"pk"`
// Name string `db:"name"`
// }
//
// Full form with all options:
//
// CategoryIDList []string `db:"-" tmeta:"belongs_to_many_ids,join_name=book_category,sql_id_field=book_id,sql_other_id_field=category_id"`
//
// The join_name option is required.
type BelongsToManyIDs struct {
Name string
GoValueField string // e.g. "BookIDList" (of type []string)
JoinName string // the name of the join table (not necessarily the SQL name, it's Name()), e.g. ""
SQLIDField string // SQL ID field on join table corresponding to this side
SQLOtherIDField string // SQL ID field on join table corresponding to the other side
}
func (r *BelongsToManyIDs) RelationName() string {
return r.Name
}
func (r *BelongsToManyIDs) RelationGoValueField() string {
return r.GoValueField
}