So I do have a struct A which has a map of B where the key of the map is the id of B, where B also contains the id of it self.
type A struct {
gorm.Model
bValues map[string]*B
}
type B struct {
gorm.Model
SomeValue string
}
How could I make grom treat this maps as relation like if they just where slices? It doesn't really matter if gorm takes the foreign key from the struct or the maps key.
There is no way to do this out of the box with Gorm, but you could try to implement something like this with some hooks.
The strategy would be to represent the HasMany relationship as is standard, and then keep some unexported (thus hidden to Gorm) field which is the map:
type A struct {
gorm.Model
Bs []*B
bsAsMap map[uint]*B
}
type B struct {
gorm.Model
AID uint
SomeValue string
}
Note the lowercase b in bsAsMany
means the field is unexported, so invisible to gorm. I used uint type keys as that's what gorm.Model uses. AID is the ID of A that has this B.
The AfterFind
hook gets run after any operation that queries the entity from the database.
func (a *A) AfterFind(tx *gorm.DB) error {
a.bsAsMap = make(map[uint]*B, len(a.Bs))
for _, b := range a.Bs {
a.bsAsMap[b.ID] = b
}
return nil
}
You'll also want to use a BeforeSave
hook in order to save any changes in the bsAsMap
field to the Bs
slice:
func (a *A) BeforeSave(tx *gorm.DB) error {
a.Bs = make([]*B, 0, len(a.bsAsMap))
for _, b := range a.bsAsMap {
a.Bs = append(a.Bs, b)
}
return nil
}
This means that bsAsMap
becomes the source of truth for the relationship from the point of view of your app code and any changes to Bs
will be undone on save. Because bsAsMap
is an unexported field, you won't be able to access it in other packages, but you can overcome that by adding accessor methods like GetB(id uint) *B
and SetB(*B)
.