github.com/goravel/framework@v1.13.9/database/gorm/cursor.go (about)

     1  package gorm
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/mitchellh/mapstructure"
     9  	"gorm.io/gorm"
    10  
    11  	"github.com/goravel/framework/support/carbon"
    12  	"github.com/goravel/framework/support/str"
    13  )
    14  
    15  type CursorImpl struct {
    16  	query *QueryImpl
    17  	row   map[string]any
    18  }
    19  
    20  func (c *CursorImpl) Scan(value any) error {
    21  	msConfig := &mapstructure.DecoderConfig{
    22  		DecodeHook: mapstructure.ComposeDecodeHookFunc(
    23  			ToTimeHookFunc(), ToCarbonHookFunc(), ToDeletedAtHookFunc(),
    24  		),
    25  		Squash: true,
    26  		Result: value,
    27  		MatchName: func(mapKey, fieldName string) bool {
    28  			return str.Case2Camel(mapKey) == fieldName || strings.EqualFold(mapKey, fieldName)
    29  		},
    30  	}
    31  
    32  	decoder, err := mapstructure.NewDecoder(msConfig)
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	if err := decoder.Decode(c.row); err != nil {
    38  		return err
    39  	}
    40  
    41  	for _, item := range c.query.conditions.with {
    42  		// Need to new a query, avoid to clear the conditions
    43  		query := c.query.new(c.query.instance)
    44  		// The new query must be cleared
    45  		query.clearConditions()
    46  		if err := query.Load(value, item.query, item.args...); err != nil {
    47  			return err
    48  		}
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  func ToTimeHookFunc() mapstructure.DecodeHookFunc {
    55  	return func(f reflect.Type, t reflect.Type, data any) (any, error) {
    56  		if t != reflect.TypeOf(time.Time{}) {
    57  			return data, nil
    58  		}
    59  
    60  		switch f.Kind() {
    61  		case reflect.String:
    62  			return time.Parse(time.RFC3339, data.(string))
    63  		case reflect.Float64:
    64  			return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
    65  		case reflect.Int64:
    66  			return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
    67  		default:
    68  			return data, nil
    69  		}
    70  	}
    71  }
    72  
    73  func ToCarbonHookFunc() mapstructure.DecodeHookFunc {
    74  	return func(f reflect.Type, t reflect.Type, data any) (any, error) {
    75  		if f == reflect.TypeOf(time.Time{}) {
    76  			switch t {
    77  			case reflect.TypeOf(carbon.DateTime{}):
    78  				return carbon.NewDateTime(carbon.FromStdTime(data.(time.Time))), nil
    79  			case reflect.TypeOf(carbon.DateTimeMilli{}):
    80  				return carbon.NewDateTimeMilli(carbon.FromStdTime(data.(time.Time))), nil
    81  			case reflect.TypeOf(carbon.DateTimeMicro{}):
    82  				return carbon.NewDateTimeMicro(carbon.FromStdTime(data.(time.Time))), nil
    83  			case reflect.TypeOf(carbon.DateTimeNano{}):
    84  				return carbon.NewDateTimeNano(carbon.FromStdTime(data.(time.Time))), nil
    85  			case reflect.TypeOf(carbon.Date{}):
    86  				return carbon.NewDate(carbon.FromStdTime(data.(time.Time))), nil
    87  			case reflect.TypeOf(carbon.DateMilli{}):
    88  				return carbon.NewDateMilli(carbon.FromStdTime(data.(time.Time))), nil
    89  			case reflect.TypeOf(carbon.DateMicro{}):
    90  				return carbon.NewDateMicro(carbon.FromStdTime(data.(time.Time))), nil
    91  			case reflect.TypeOf(carbon.DateNano{}):
    92  				return carbon.NewDateNano(carbon.FromStdTime(data.(time.Time))), nil
    93  			case reflect.TypeOf(carbon.Timestamp{}):
    94  				return carbon.NewTimestamp(carbon.FromStdTime(data.(time.Time))), nil
    95  			case reflect.TypeOf(carbon.TimestampMilli{}):
    96  				return carbon.NewTimestampMilli(carbon.FromStdTime(data.(time.Time))), nil
    97  			case reflect.TypeOf(carbon.TimestampMicro{}):
    98  				return carbon.NewTimestampMicro(carbon.FromStdTime(data.(time.Time))), nil
    99  			case reflect.TypeOf(carbon.TimestampNano{}):
   100  				return carbon.NewTimestampNano(carbon.FromStdTime(data.(time.Time))), nil
   101  			}
   102  		}
   103  		if f.Kind() == reflect.String {
   104  			switch t {
   105  			case reflect.TypeOf(carbon.DateTime{}):
   106  				return carbon.NewDateTime(carbon.Parse(data.(string))), nil
   107  			case reflect.TypeOf(carbon.DateTimeMilli{}):
   108  				return carbon.NewDateTimeMilli(carbon.Parse(data.(string))), nil
   109  			case reflect.TypeOf(carbon.DateTimeMicro{}):
   110  				return carbon.NewDateTimeMicro(carbon.Parse(data.(string))), nil
   111  			case reflect.TypeOf(carbon.DateTimeNano{}):
   112  				return carbon.NewDateTimeNano(carbon.Parse(data.(string))), nil
   113  			case reflect.TypeOf(carbon.Date{}):
   114  				return carbon.NewDate(carbon.Parse(data.(string))), nil
   115  			case reflect.TypeOf(carbon.DateMilli{}):
   116  				return carbon.NewDateMilli(carbon.Parse(data.(string))), nil
   117  			case reflect.TypeOf(carbon.DateMicro{}):
   118  				return carbon.NewDateMicro(carbon.Parse(data.(string))), nil
   119  			case reflect.TypeOf(carbon.DateNano{}):
   120  				return carbon.NewDateNano(carbon.Parse(data.(string))), nil
   121  			case reflect.TypeOf(carbon.Timestamp{}):
   122  				return carbon.NewTimestamp(carbon.Parse(data.(string))), nil
   123  			case reflect.TypeOf(carbon.TimestampMilli{}):
   124  				return carbon.NewTimestampMilli(carbon.Parse(data.(string))), nil
   125  			case reflect.TypeOf(carbon.TimestampMicro{}):
   126  				return carbon.NewTimestampMicro(carbon.Parse(data.(string))), nil
   127  			case reflect.TypeOf(carbon.TimestampNano{}):
   128  				return carbon.NewTimestampNano(carbon.Parse(data.(string))), nil
   129  			}
   130  		}
   131  
   132  		return data, nil
   133  	}
   134  }
   135  
   136  func ToDeletedAtHookFunc() mapstructure.DecodeHookFunc {
   137  	return func(f reflect.Type, t reflect.Type, data any) (any, error) {
   138  		if t != reflect.TypeOf(gorm.DeletedAt{}) {
   139  			return data, nil
   140  		}
   141  
   142  		if f == reflect.TypeOf(time.Time{}) {
   143  			return gorm.DeletedAt{Time: data.(time.Time), Valid: true}, nil
   144  		}
   145  
   146  		if f.Kind() == reflect.String {
   147  			return gorm.DeletedAt{Time: carbon.Parse(data.(string)).ToStdTime(), Valid: true}, nil
   148  		}
   149  
   150  		return data, nil
   151  	}
   152  }