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

     1  package gorm
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"gorm.io/gorm"
     9  
    10  	"github.com/goravel/framework/contracts/database/orm"
    11  	"github.com/goravel/framework/support/str"
    12  )
    13  
    14  type Event struct {
    15  	columnNamesWithDbColumnNames map[string]string
    16  	dest                         any
    17  	destOfMap                    map[string]any
    18  	model                        any
    19  	modelOfMap                   map[string]any
    20  	query                        *QueryImpl
    21  }
    22  
    23  func NewEvent(query *QueryImpl, model, dest any) *Event {
    24  	return &Event{
    25  		dest:  dest,
    26  		model: model,
    27  		query: query,
    28  	}
    29  }
    30  
    31  func (e *Event) ColumnNamesWithDbColumnNames() map[string]string {
    32  	if e.columnNamesWithDbColumnNames != nil {
    33  		return e.columnNamesWithDbColumnNames
    34  	}
    35  
    36  	res := make(map[string]string)
    37  	var modelType reflect.Type
    38  	var modelValue reflect.Value
    39  
    40  	if e.model != nil {
    41  		modelType = reflect.TypeOf(e.model)
    42  		modelValue = reflect.ValueOf(e.model)
    43  	} else {
    44  		modelType = reflect.TypeOf(e.dest)
    45  		modelValue = reflect.ValueOf(e.dest)
    46  	}
    47  	if modelType.Kind() == reflect.Pointer {
    48  		modelType = modelType.Elem()
    49  		modelValue = modelValue.Elem()
    50  	}
    51  
    52  	for i := 0; i < modelType.NumField(); i++ {
    53  		if !modelType.Field(i).IsExported() {
    54  			continue
    55  		}
    56  		if modelType.Field(i).Name == "Model" && modelValue.Field(i).Type().Kind() == reflect.Struct {
    57  			structField := modelValue.Field(i).Type()
    58  			for j := 0; j < structField.NumField(); j++ {
    59  				if !structField.Field(i).IsExported() {
    60  					continue
    61  				}
    62  				dbColumn := structNameToDbColumnName(structField.Field(j).Name, structField.Field(j).Tag.Get("gorm"))
    63  				res[structField.Field(j).Name] = dbColumn
    64  				res[dbColumn] = dbColumn
    65  			}
    66  		}
    67  
    68  		dbColumn := structNameToDbColumnName(modelType.Field(i).Name, modelType.Field(i).Tag.Get("gorm"))
    69  		res[modelType.Field(i).Name] = dbColumn
    70  		res[dbColumn] = dbColumn
    71  	}
    72  
    73  	return res
    74  }
    75  
    76  func (e *Event) Context() context.Context {
    77  	return e.query.ctx
    78  }
    79  
    80  func (e *Event) DestOfMap() map[string]any {
    81  	if e.destOfMap != nil {
    82  		return e.destOfMap
    83  	}
    84  
    85  	var destOfMap map[string]any
    86  	if destMap, ok := e.dest.(map[string]any); ok {
    87  		destOfMap = destMap
    88  	} else {
    89  		destType := reflect.TypeOf(e.dest)
    90  		if destType.Kind() == reflect.Pointer {
    91  			destType = destType.Elem()
    92  		}
    93  		if destType.Kind() == reflect.Struct {
    94  			destOfMap = structToMap(e.dest)
    95  		}
    96  	}
    97  
    98  	e.destOfMap = destOfMap
    99  
   100  	return e.destOfMap
   101  }
   102  
   103  func (e *Event) GetAttribute(key string) any {
   104  	destOfMap := e.DestOfMap()
   105  	value, exist := destOfMap[e.toDBColumnName(key)]
   106  	if exist && e.validColumn(key) && e.validValue(key, value) {
   107  		return value
   108  	}
   109  
   110  	return e.GetOriginal(key)
   111  }
   112  
   113  func (e *Event) GetOriginal(key string, def ...any) any {
   114  	modelOfMap := e.ModelOfMap()
   115  	value, exist := modelOfMap[e.toDBColumnName(key)]
   116  	if exist {
   117  		return value
   118  	}
   119  
   120  	if len(def) > 0 {
   121  		return def[0]
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func (e *Event) IsDirty(columns ...string) bool {
   128  	destOfMap := e.DestOfMap()
   129  
   130  	if len(columns) == 0 {
   131  		for destColumn, destValue := range destOfMap {
   132  			if !(e.validColumn(destColumn) && e.validValue(destColumn, destValue)) {
   133  				continue
   134  			}
   135  			if e.dirty(destColumn, destValue) {
   136  				return true
   137  			}
   138  		}
   139  	} else {
   140  		for _, column := range columns {
   141  			if !e.validColumn(column) {
   142  				continue
   143  			}
   144  			for destColumn, destValue := range destOfMap {
   145  				if !(e.validColumn(destColumn) && e.validValue(destColumn, destValue)) {
   146  					continue
   147  				}
   148  				if e.equalColumnName(column, destColumn) && e.dirty(destColumn, destValue) {
   149  					return true
   150  				}
   151  			}
   152  		}
   153  	}
   154  
   155  	return false
   156  }
   157  
   158  func (e *Event) IsClean(fields ...string) bool {
   159  	return !e.IsDirty(fields...)
   160  }
   161  
   162  func (e *Event) ModelOfMap() map[string]any {
   163  	if e.modelOfMap != nil {
   164  		return e.modelOfMap
   165  	}
   166  
   167  	if e.model == nil {
   168  		return map[string]any{}
   169  	}
   170  
   171  	e.modelOfMap = structToMap(e.model)
   172  
   173  	return e.modelOfMap
   174  }
   175  
   176  func (e *Event) Query() orm.Query {
   177  	return NewQueryImpl(e.query.ctx, e.query.config, e.query.connection, e.query.instance.Session(&gorm.Session{NewDB: true}), nil)
   178  }
   179  
   180  func (e *Event) SetAttribute(key string, value any) {
   181  	destOfMap := e.DestOfMap()
   182  	destOfMap[e.toDBColumnName(key)] = value
   183  	e.destOfMap = destOfMap
   184  
   185  	if m, ok := e.dest.(map[string]any); ok {
   186  		m[key] = value
   187  	} else {
   188  		destType := reflect.TypeOf(e.dest)
   189  		destValue := reflect.ValueOf(e.dest)
   190  		if destType.Kind() == reflect.Pointer {
   191  			destType = destType.Elem()
   192  			destValue = destValue.Elem()
   193  		}
   194  
   195  		if !destValue.CanAddr() {
   196  			destValueCanAddr := reflect.New(destValue.Type())
   197  			destValueCanAddr.Elem().Set(destValue)
   198  			e.dest = destValueCanAddr.Interface()
   199  			e.query.instance.Statement.Dest = e.dest
   200  			destValue = destValueCanAddr.Elem()
   201  		}
   202  
   203  		for i := 0; i < destType.NumField(); i++ {
   204  			if !destType.Field(i).IsExported() {
   205  				continue
   206  			}
   207  			if e.equalColumnName(destType.Field(i).Name, key) {
   208  				if value == nil {
   209  					destValue.Field(i).Set(reflect.Zero(destValue.Field(i).Type()))
   210  				} else {
   211  					valueValue := reflect.ValueOf(value)
   212  					destValue.Field(i).Set(valueValue)
   213  				}
   214  			}
   215  		}
   216  	}
   217  }
   218  
   219  func (e *Event) dirty(destColumn string, destValue any) bool {
   220  	modelOfMap := e.ModelOfMap()
   221  	dbDestColumn := e.toDBColumnName(destColumn)
   222  
   223  	if modelValue, exist := modelOfMap[dbDestColumn]; exist {
   224  		return !reflect.DeepEqual(modelValue, destValue)
   225  	}
   226  
   227  	return true
   228  }
   229  
   230  func (e *Event) equalColumnName(origin, source string) bool {
   231  	originDbColumnName := e.toDBColumnName(origin)
   232  	sourceDbColumnName := e.toDBColumnName(source)
   233  
   234  	if originDbColumnName == "" || sourceDbColumnName == "" {
   235  		return false
   236  	}
   237  
   238  	return originDbColumnName == sourceDbColumnName
   239  }
   240  
   241  func (e *Event) toDBColumnName(name string) string {
   242  	dbColumnName, exist := e.ColumnNamesWithDbColumnNames()[name]
   243  	if exist {
   244  		return dbColumnName
   245  	}
   246  
   247  	return ""
   248  }
   249  
   250  func (e *Event) validColumn(name string) bool {
   251  	dbColumn := e.toDBColumnName(name)
   252  	if dbColumn == "" {
   253  		return false
   254  	}
   255  
   256  	selectColumns := e.query.instance.Statement.Selects
   257  	omitColumns := e.query.instance.Statement.Omits
   258  	if len(selectColumns) > 0 {
   259  		for _, selectColumn := range selectColumns {
   260  			dbSelectColumn := e.toDBColumnName(selectColumn)
   261  			if dbSelectColumn == "" {
   262  				continue
   263  			}
   264  
   265  			if dbSelectColumn == dbColumn {
   266  				return true
   267  			}
   268  		}
   269  
   270  		return false
   271  	}
   272  	if len(omitColumns) > 0 {
   273  		for _, omitColumn := range omitColumns {
   274  			dbOmitColumn := e.toDBColumnName(omitColumn)
   275  			if dbOmitColumn == "" {
   276  				continue
   277  			}
   278  
   279  			if dbOmitColumn == dbColumn {
   280  				return false
   281  			}
   282  		}
   283  
   284  		return true
   285  	}
   286  
   287  	return true
   288  }
   289  
   290  func (e *Event) validValue(name string, value any) bool {
   291  	dbColumn := e.toDBColumnName(name)
   292  	if dbColumn == "" {
   293  		return false
   294  	}
   295  
   296  	selectColumns := e.query.instance.Statement.Selects
   297  	if len(selectColumns) > 0 {
   298  		return e.validColumn(name)
   299  	}
   300  
   301  	if value == nil {
   302  		return false
   303  	}
   304  
   305  	valueValue := reflect.ValueOf(value)
   306  
   307  	return !valueValue.IsZero()
   308  }
   309  
   310  func structToMap(data any) map[string]any {
   311  	res := make(map[string]any)
   312  	modelType := reflect.TypeOf(data)
   313  	modelValue := reflect.ValueOf(data)
   314  
   315  	if modelType.Kind() == reflect.Pointer {
   316  		modelType = modelType.Elem()
   317  		modelValue = modelValue.Elem()
   318  	}
   319  
   320  	for i := 0; i < modelType.NumField(); i++ {
   321  		if !modelType.Field(i).IsExported() {
   322  			continue
   323  		}
   324  
   325  		dbColumn := structNameToDbColumnName(modelType.Field(i).Name, modelType.Field(i).Tag.Get("gorm"))
   326  		if modelValue.Field(i).Kind() == reflect.Pointer {
   327  			if modelValue.Field(i).IsNil() {
   328  				res[dbColumn] = nil
   329  			} else {
   330  				res[dbColumn] = modelValue.Field(i).Elem().Interface()
   331  			}
   332  		} else {
   333  			res[dbColumn] = modelValue.Field(i).Interface()
   334  		}
   335  	}
   336  
   337  	return res
   338  }
   339  
   340  func structNameToDbColumnName(structName, tag string) string {
   341  	if strings.Contains(tag, "column:") {
   342  		tags := strings.Split(tag, ";")
   343  		for _, item := range tags {
   344  			if strings.Contains(item, "column:") {
   345  				return strings.Trim(strings.ReplaceAll(item, "column:", ""), " ")
   346  			}
   347  		}
   348  	}
   349  
   350  	return str.Camel2Case(structName)
   351  }