Support association using join table such as Many to Many
- Dominant language
- Go
- Stars
- 786
- Forks
- 61
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 2
Description
## Idea 2
### Example
```go
type Channel struct {
ID int
Name string
// mapped to singular version of field name defined in "db" (subscriber) inside through association.
// impicitly trigger two preload: Preload("subscriptions") and Preload("subscriptions.subscriber")
// the result than flattened and mapped to subscribers.
Subscribers []User `db:"subscribers" through:subscriptions"`
Subscriptions []Subscription `ref:"id" fk:"channel_d"`
}
type User struct {
ID int
Name string
Subscriptions []Subscription
Channels []Channel `through:subscriptions"`
// self-referencing needs two intermediate reference to be set up.
// trigger Preload("user_followings") and Preload("user_followings.following")
Followings []User `through:"user_followings"` // map to following field
UserFollowings []Follow `ref:"id" fk:"follower_id"`
// trigger Preload("user_followers") and Preload("user_follwers.follower")
UserFollowers []Follow `ref:"id" fk:"following_id"`
Followers []User `through:"user_followers"` // map to follower field
}
type Follow struct {
Follower User
FollowerID int `db:",primary"`
Following User
FollowingID int `db:",primary"`
Accepted bool // this way, it may contains additional data
}
type Subscription struct {
ID int `db:",primary"`
Subscriber User
SubscriberID int
Channel Channel
ChannelID int
CreatedAt time.Time
UpdatedAt time.Time
}
```
### Specifications
- [x] Join association is defined using tag called`through` and doesn't support nested through.
- [x] It's a read only association (all modification will be ignored)
- [ ] Every preload of has through assoc will implicitly trigger two preload, the first one is the association defined by `through` tag, the second one is association inside `through` field that has the singular name as has through field. The result then flattened and mapped to the final association.
- [ ] Preload has many through
- [ ] Preload has one through
### Merit/Demerit
(+) Support has one and has many through
(+) Can be made read only and still accessible for update.
(+) We can have metadata on join table.
(-) Require additional association defined (especially verbose for self referencing association).
## ~Idea 1~
### Example:
```go
// Table subscription_users: user_id(int), subscription_id(int)
// Table followers: followed_id(int), following_id(int)
type Subscription struct {
ID int
Name string
// 1. basic declaration:
// subscription:id <- subscription_id:subscription_users:user_id -> user:id
Users []User `ref:"id:subscription_id" fk:"id:user_id" through:"subscription_users"`
}
type User struct {
ID int
Name string
// 2. back ref
// user:id <- user_id:subscription_users:subscription_id -> subscriptions:id
Subscriptions []Subscription `ref:"id:user_id" fk:"id:subscription_id" through:"subscription_users"`
// 3. omitting ref and fk tag, will be guessed based on table name:
// user:id <- user_id:subscription_users:subscription_id -> subscriptions:id (the same as 2)
// Subscriptions []Subscription through:"subscription_users"`
// 4. Self-referencing many to many
Followers []User `ref:"id:following_id" fk:"id:follower_id" through:"followers"`
Followings []User `ref:"id:follower_id" fk:"id:following_id" through:"followers"`
}
```
### Specifications
- Join table is defined using tag called`through`.
- `subscription:id <- subscription_id:subscription_users:user_id -> user:id`
is declared as:
`ref:"id:subscription_id" fk:"id:user_id" through:"subscription_users"`
- ref and fk can be completely omitted, and will be guessed as many to many when through tag is available, otherwise has many rule applied.
- Preloading support.
- ~Insert/Update support~ Edit: to complex and to magic to implement
### Merit/Demerit
(+) Can implement many to many without additional struct.
(-) Doesn't work for has one through.
(-) Can't be made fully readonly(can update but only the join data).
Contributor guide
Assessment
This issue has not been assessed yet.