Skip to content

Commit

Permalink
Merge pull request #11 from go-advanced-admin/pointers
Browse files Browse the repository at this point in the history
feat: enhance pointer handling in models and forms
  • Loading branch information
YidiDev authored Oct 7, 2024
2 parents 372ff0f + 2ff36cc commit 29424f1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 39 deletions.
5 changes: 3 additions & 2 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type TestModel3 struct {

type TestModel4 struct {
ID uint `gorm:"primarykey"`
Title string
Title *string
}

func main() {
Expand Down Expand Up @@ -119,8 +119,9 @@ func populateTestModels(db *gorm.DB) {
}

for i := 0; i < 100; i++ {
title := fmt.Sprintf("Post Title %d", i)
db.Create(&TestModel4{
Title: fmt.Sprintf("Post Title %d", i),
Title: &title,
})
}
}
45 changes: 27 additions & 18 deletions internal/adminpanel/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
for i := 0; i < modelType.NumField(); i++ {
field := modelType.Field(i)
fieldName := field.Name

fieldType := field.Type

isPointer := false
underlyingType := fieldType
if fieldType.Kind() == reflect.Ptr {
isPointer = true
underlyingType = fieldType.Elem()
}

fieldDisplayName := utils.HumanizeName(fieldName)
includeInList := true
includeInFetch := true
Expand Down Expand Up @@ -162,11 +172,9 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
}
}

fieldType := field.Type

var formField form.Field
if includeInAddForm || includeInEditForm {
switch fieldType.Kind() {
switch underlyingType.Kind() {
case reflect.String:
formField = &fields.TextField{}
if tag != "" {
Expand All @@ -190,21 +198,21 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
case "maxLength":
maxLengthInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(uint(0)))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
maxLength, ok := maxLengthInterface.(uint)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.TextField).MaxLength = &maxLength
case "minLength":
minLengthInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(uint(0)))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
minLength, ok := minLengthInterface.(uint)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.TextField).MinLength = &minLength
}
Expand All @@ -229,21 +237,21 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
case "max":
maxInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(0))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
maxValue, ok := maxInterface.(int)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.IntegerField).MaxValue = &maxValue
case "min":
minInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(0))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
minValue, ok := minInterface.(int)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.IntegerField).MinValue = &minValue
}
Expand All @@ -268,21 +276,21 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
case "max":
maxInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(float64(0)))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
maxValue, ok := maxInterface.(float64)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.FloatField).MaxValue = &maxValue
case "min":
minInterface, err := utils.ConvertStringToType(value, reflect.TypeOf(float64(0)))
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
minValue, ok := minInterface.(float64)
if !ok {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.(*fields.FloatField).MinValue = &minValue
}
Expand Down Expand Up @@ -332,9 +340,9 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error

switch key {
case "initial":
convertedValue, err := utils.ConvertStringToType(value, fieldType)
convertedValue, err := utils.ConvertStringToType(value, underlyingType)
if err != nil {
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, fieldType.Name(), err)
return nil, fmt.Errorf("error converting value '%s' to type '%s': %w", value, underlyingType.Name(), err)
}
formField.RegisterInitialValue(convertedValue)
}
Expand Down Expand Up @@ -364,7 +372,8 @@ func (a *App) RegisterModel(model interface{}, orm ORMIntegrator) (*Model, error
fieldConfigs = append(fieldConfigs, FieldConfig{
Name: fieldName,
DisplayName: fieldDisplayName,
FieldType: fieldType,
FieldType: underlyingType,
IsPointer: isPointer,
IncludeInListDisplay: includeInList,
IncludeInListFetch: includeInFetch,
IncludeInSearch: includeInSearch,
Expand Down
1 change: 1 addition & 0 deletions internal/adminpanel/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type FieldConfig struct {
Name string
DisplayName string
FieldType reflect.Type
IsPointer bool
IncludeInListFetch bool
IncludeInListDisplay bool
IncludeInSearch bool
Expand Down
89 changes: 70 additions & 19 deletions internal/adminpanel/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,21 +226,35 @@ func (f *ModelAddForm) Save(values map[string]form.HTMLType) (interface{}, error
return nil, fmt.Errorf("field %s is not settable", fieldName)
}

if value == nil {
if fieldVal.Kind() == reflect.Ptr {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
}
continue
}

val := reflect.ValueOf(value)

if val.Type().AssignableTo(fieldVal.Type()) {
fieldVal.Set(val)
} else if val.Type().ConvertibleTo(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
if fieldVal.Kind() == reflect.Ptr {
if value == nil {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
continue
}
elemType := fieldVal.Type().Elem()
newVal := reflect.New(elemType)
if val.Type().AssignableTo(elemType) {
newVal.Elem().Set(val)
} else if val.Type().ConvertibleTo(elemType) {
newVal.Elem().Set(val.Convert(elemType))
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
}
fieldVal.Set(newVal)
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
if value == nil {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
continue
}
if val.Type().AssignableTo(fieldVal.Type()) {
fieldVal.Set(val)
} else if val.Type().ConvertibleTo(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
}
}
}

Expand Down Expand Up @@ -279,19 +293,44 @@ func (f *ModelEditForm) Save(values map[string]form.HTMLType) (interface{}, erro

for fieldName, value := range cleanValues {
fieldVal := instanceVal.FieldByName(fieldName)

if !fieldVal.IsValid() {
continue
return nil, fmt.Errorf("field %s not found in model", fieldName)
}

if !fieldVal.CanSet() {
return nil, fmt.Errorf("field %s is not settable", fieldName)
}

val := reflect.ValueOf(value)
if val.Type().AssignableTo(fieldVal.Type()) {
fieldVal.Set(val)
} else if val.Type().ConvertibleTo(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))

if fieldVal.Kind() == reflect.Ptr {
if value == nil {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
continue
}
elemType := fieldVal.Type().Elem()
newVal := reflect.New(elemType)
if val.Type().AssignableTo(elemType) {
newVal.Elem().Set(val)
} else if val.Type().ConvertibleTo(elemType) {
newVal.Elem().Set(val.Convert(elemType))
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
}
fieldVal.Set(newVal)
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
if value == nil {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
continue
}
if val.Type().AssignableTo(fieldVal.Type()) {
fieldVal.Set(val)
} else if val.Type().ConvertibleTo(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
} else {
return nil, fmt.Errorf("field %s has invalid type", fieldName)
}
}
}

Expand Down Expand Up @@ -520,7 +559,19 @@ func (m *Model) GetEditHandler() HandlerFunc {
if field.EditFormField == nil {
continue
}
initialValuesMap[field.Name] = reflect.ValueOf(instanceData).Elem().FieldByName(field.Name).Interface()
fieldValue := reflect.ValueOf(instanceData).Elem().FieldByName(field.Name)
var value interface{}
if field.IsPointer {
if fieldValue.IsNil() {
value = nil
} else {
value = fieldValue.Elem().Interface()
}
} else {
value = fieldValue.Interface()
}

initialValuesMap[field.Name] = value
}

err = formInstance.RegisterInitialValues(initialValuesMap)
Expand Down

0 comments on commit 29424f1

Please sign in to comment.