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 }