feat: sorting for primitive fields
# Conflicts: # frontend/src/App.vue
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AppendAssociations uses reflection to find all exported slice fields
|
||||
// in 'item' and then appends any elements in each slice to the association.
|
||||
func AppendAssociations(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)
|
||||
|
||||
// Process only exported fields (PkgPath == "") and slices.
|
||||
if fieldType.PkgPath == "" && fieldVal.Kind() == reflect.Slice {
|
||||
// The association name is the struct field name by default.
|
||||
assocName := fieldType.Name
|
||||
|
||||
// Append slice elements to the existing association.
|
||||
// You can add checks here if you want to skip empty slices, etc.
|
||||
if fieldVal.Len() > 0 {
|
||||
if err := db.Model(item).Association(assocName).Append(fieldVal.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,31 +4,30 @@ import (
|
||||
"app/internal/dal"
|
||||
"app/internal/database"
|
||||
"app/internal/models"
|
||||
"app/internal/utils"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AuthorService struct{}
|
||||
type AuthorService struct {
|
||||
}
|
||||
type Author = models.Author
|
||||
|
||||
func (service *AuthorService) Create(item Author) (Author, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.Author.Create(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = AppendAssociations(database.GetInstance(), &item)
|
||||
err = utils.AppendAssociations(database.GetInstance(), &item)
|
||||
return item, err
|
||||
}
|
||||
|
||||
func (service *AuthorService) GetAll() ([]*Author, error) {
|
||||
var authors []*Author
|
||||
authors, err := dal.Author.Preload(field.Associations).Find()
|
||||
return authors, err
|
||||
}
|
||||
|
||||
func (service *AuthorService) GetById(id uint) (*Author, error) {
|
||||
item, err := dal.Author.Preload(field.Associations).Where(dal.Author.Id.Eq(id)).First()
|
||||
if err != nil {
|
||||
@@ -40,16 +39,13 @@ func (service *AuthorService) GetById(id uint) (*Author, error) {
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (service *AuthorService) Update(item Author) (Author, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
_, err := dal.Author.Updates(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
err = UpdateAssociations(database.GetInstance(), &item)
|
||||
err = utils.UpdateAssociations(database.GetInstance(), &item)
|
||||
|
||||
if err != nil {
|
||||
return item, err
|
||||
@@ -57,13 +53,15 @@ func (service *AuthorService) Update(item Author) (Author, error) {
|
||||
|
||||
return item, err
|
||||
}
|
||||
|
||||
func (service *AuthorService) Delete(id uint) error {
|
||||
_, err := dal.Author.Unscoped().Where(dal.Author.Id.Eq(id)).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (service *AuthorService) Count() (int64, error) {
|
||||
amount, err := dal.Author.Count()
|
||||
return amount, err
|
||||
}
|
||||
|
||||
func (service *AuthorService) SortedByOrder(fieldsSortOrder map[string]string) ([]*Author, error) {
|
||||
return utils.SortByOrder(fieldsSortOrder, Author{})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"app/internal/dal"
|
||||
"app/internal/database"
|
||||
"app/internal/models"
|
||||
"app/internal/utils"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gen/field"
|
||||
@@ -15,12 +16,12 @@ type CommentService struct {
|
||||
type Comment = models.Comment
|
||||
|
||||
func (service *CommentService) Create(item Comment) (Comment, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.Comment.Preload(field.Associations).Create(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = AppendAssociations(database.GetInstance(), &item)
|
||||
err = utils.AppendAssociations(database.GetInstance(), &item)
|
||||
return item, err
|
||||
}
|
||||
|
||||
@@ -43,13 +44,13 @@ func (service *CommentService) GetById(id uint) (*Comment, error) {
|
||||
}
|
||||
|
||||
func (service *CommentService) Update(item Comment) (Comment, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.Comment.Preload(field.Associations).Save(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
err = UpdateAssociations(database.GetInstance(), &item)
|
||||
err = utils.UpdateAssociations(database.GetInstance(), &item)
|
||||
|
||||
if err != nil {
|
||||
return item, err
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"app/internal/dialogs"
|
||||
"app/internal/extras/excel"
|
||||
"app/internal/models"
|
||||
"app/internal/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -17,12 +18,12 @@ type PostService struct{}
|
||||
type Post = models.Post
|
||||
|
||||
func (service *PostService) Create(item Post) (Post, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.Post.Preload(field.Associations).Create(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = AppendAssociations(database.GetInstance(), &item)
|
||||
err = utils.AppendAssociations(database.GetInstance(), &item)
|
||||
return item, err
|
||||
}
|
||||
func (service *PostService) GetAll() ([]*Post, error) {
|
||||
@@ -43,12 +44,12 @@ func (service *PostService) GetById(id uint) (*Post, error) {
|
||||
}
|
||||
|
||||
func (service *PostService) Update(item Post) (Post, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.Post.Preload(field.Associations).Save(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = UpdateAssociations(database.GetInstance(), &item)
|
||||
err = utils.UpdateAssociations(database.GetInstance(), &item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"app/internal/dialogs"
|
||||
"app/internal/extras/excel"
|
||||
"app/internal/models"
|
||||
"app/internal/utils"
|
||||
"errors"
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm"
|
||||
@@ -17,12 +18,12 @@ type PostTypeService struct {
|
||||
type PostType = models.PostType
|
||||
|
||||
func (service *PostTypeService) Create(item PostType) (PostType, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.PostType.Preload(field.Associations).Create(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = AppendAssociations(database.GetInstance(), &item)
|
||||
err = utils.AppendAssociations(database.GetInstance(), &item)
|
||||
return item, err
|
||||
}
|
||||
func (service *PostTypeService) GetAll() ([]*PostType, error) {
|
||||
@@ -42,12 +43,12 @@ func (service *PostTypeService) GetById(id uint) (*PostType, error) {
|
||||
return item, nil
|
||||
}
|
||||
func (service *PostTypeService) Update(item PostType) (PostType, error) {
|
||||
ReplaceEmptySlicesWithNil(&item)
|
||||
utils.ReplaceEmptySlicesWithNil(&item)
|
||||
err := dal.PostType.Preload(field.Associations).Save(&item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
err = UpdateAssociations(database.GetInstance(), &item)
|
||||
err = utils.UpdateAssociations(database.GetInstance(), &item)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user