fix: update associations

This commit is contained in:
2025-03-09 20:10:33 +07:00
parent d149aebf12
commit 7c7c8e6cc5
6 changed files with 124 additions and 19 deletions

View File

@@ -2,13 +2,14 @@ package database
import (
"app/internal/dal"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"sync"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
@@ -31,7 +32,8 @@ func initialize() error {
},
)
db, err = gorm.Open(sqlite.Open("file:"+Path+"?_fk=1"), &gorm.Config{
Logger: newLogger,
Logger: newLogger,
FullSaveAssociations: false,
})
if err != nil {
return err

View File

@@ -1,17 +1,18 @@
package main
import (
"gorm.io/gen"
"app/internal/models"
"app/internal/models"
"gorm.io/gen"
)
func main() {
g := gen.NewGenerator(gen.Config{
OutPath: "../dal", // output directory, default value is ./query
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
FieldNullable: true,
WithUnitTest: true,
})
g.ApplyBasic(models.Entities...)
g.Execute()
g := gen.NewGenerator(gen.Config{
OutPath: "../dal", // output directory, default value is ./query
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
FieldNullable: true,
WithUnitTest: true,
})
g.ApplyBasic(models.Entities...)
g.Execute()
}

View File

@@ -2,6 +2,7 @@ package services
import (
"app/internal/dal"
"app/internal/database"
"app/internal/models"
"errors"
@@ -13,6 +14,7 @@ type AuthorService struct{}
type Author = models.Author
func (service *AuthorService) Create(item Author) (Author, error) {
ReplaceEmptySlicesWithNil(&item)
err := dal.Author.Preload(field.Associations).Create(&item)
return item, err
}
@@ -32,15 +34,24 @@ func (service *AuthorService) GetById(id uint) (*Author, error) {
}
return item, nil
}
func (service *AuthorService) Update(item Author) (Author, error) {
var posts []*models.Post
for _, post := range item.Posts {
posts = append(posts, &post)
ReplaceEmptySlicesWithNil(&item)
_, err := dal.Author.Updates(&item)
if err != nil {
return item, err
}
err := dal.Author.Posts.Model(&item).Replace(posts...)
_ = dal.Author.Save(&item)
err = UpdateAssociations(database.GetInstance(), &item)
if err != nil {
return item, err
}
return item, err
}
func (service *AuthorService) Delete(id uint) error {
_, err := dal.Author.Unscoped().Where(dal.Author.Id.Eq(id)).Delete()
return err

View File

@@ -0,0 +1,49 @@
package services
import "reflect"
// ReplaceEmptySlicesWithNil takes a pointer to any struct (or struct-like type)
// and replaces all empty slices with nil, traversing nested fields recursively.
func ReplaceEmptySlicesWithNil(v interface{}) {
// Must be a pointer so we can set fields
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
panic("ReplaceEmptySlicesWithNil expects a pointer to a struct")
}
replaceEmptySlices(rv.Elem())
}
// replaceEmptySlices is the recursive helper that does the actual reflection work.
func replaceEmptySlices(v reflect.Value) {
switch v.Kind() {
case reflect.Ptr:
// If it is a pointer, recurse on the element it points to.
if !v.IsNil() {
replaceEmptySlices(v.Elem())
}
case reflect.Struct:
// For a struct, iterate each field and process it.
for i := 0; i < v.NumField(); i++ {
fieldVal := v.Field(i)
if !fieldVal.CanSet() {
// If we can't set the field (e.g. unexported), skip it
continue
}
replaceEmptySlices(fieldVal)
}
case reflect.Slice:
// If it is a slice, check if it's empty; if it is, set it to nil.
if v.Len() == 0 && v.CanSet() {
// v.Set(reflect.Zero(v.Type())) sets slice to nil
v.Set(reflect.Zero(v.Type()))
} else {
// If not empty, we should recurse on each element in case it's a struct.
for i := 0; i < v.Len(); i++ {
replaceEmptySlices(v.Index(i))
}
}
}
}

View File

@@ -0,0 +1,41 @@
package services
import (
"errors"
"reflect"
"gorm.io/gorm"
)
func UpdateAssociations(db *gorm.DB, item interface{}) error {
// We expect a pointer to a struct so we can do db.Model(item).
val := reflect.ValueOf(item)
if val.Kind() != reflect.Ptr {
return errors.New("item must be a pointer to a struct")
}
elem := val.Elem()
if elem.Kind() != reflect.Struct {
return errors.New("item must be a pointer to a struct")
}
t := elem.Type()
for i := 0; i < elem.NumField(); i++ {
fieldVal := elem.Field(i)
fieldType := t.Field(i)
// Only process exported fields (capitalized) and slices.
if fieldType.PkgPath == "" && fieldVal.Kind() == reflect.Slice {
// For clarity, the association name is the struct field name by default.
assocName := fieldType.Name
// Replace the association with the current slice value.
// If you only want to replace on non-nil or non-empty slices, you can add extra checks here.
if err := db.Model(item).Association(assocName).Replace(fieldVal.Interface()); err != nil {
return err
}
}
}
return nil
}