Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions database/gorm/conditions.go

@hwbrzzl hwbrzzl Nov 27, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solve the problem: The conditions will be chaotic when switching connections through the old logic.

Before, the Where method, etc. calls the Gorm method directly and gets a new Gorm instance.

Now, the conditions will be saved in Conditions, and will be built when calling a final method, like: Get.

After this change, we can control the conditions and the Gorm instance more freely.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package gorm

import (
ormcontract "github.com/goravel/framework/contracts/database/orm"
)

type Conditions struct {
distinct []any
group string
having *Having
join []Join
limit *int
lockForUpdate bool
model any
offset *int
omit []string
order []any
scopes []func(ormcontract.Query) ormcontract.Query
selectColumns *Select
sharedLock bool
table *Table
where []Where
with []With
withoutEvents bool
withTrashed bool
}

type Having struct {
query any
args []any
}

type Join struct {
query string
args []any
}

type Select struct {
query any
args []any
}

type Table struct {
name string
args []any
}

type Where struct {
query any
args []any
or bool
}

type With struct {
query string
args []any
}
8 changes: 6 additions & 2 deletions database/gorm/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ func (c *CursorImpl) Scan(value any) error {
return err
}

for relation, args := range c.query.with {
if err := c.query.origin.Load(value, relation, args...); err != nil {
for _, item := range c.query.conditions.with {
// Need to new a query, avoid to clear the conditions
query := c.query.new(c.query.instance)
// The new query must be cleared
query.clearConditions()
if err := query.Load(value, item.query, item.args...); err != nil {
return err
}
}
Expand Down
265 changes: 131 additions & 134 deletions database/gorm/event.go

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no changes for this file, just order the methods.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,102 @@ func NewEvent(query *QueryImpl, model, dest any) *Event {
}
}

func (e *Event) ColumnNamesWithDbColumnNames() map[string]string {
if e.columnNamesWithDbColumnNames != nil {
return e.columnNamesWithDbColumnNames
}

res := make(map[string]string)
var modelType reflect.Type
var modelValue reflect.Value

if e.model != nil {
modelType = reflect.TypeOf(e.model)
modelValue = reflect.ValueOf(e.model)
} else {
modelType = reflect.TypeOf(e.dest)
modelValue = reflect.ValueOf(e.dest)
}
if modelType.Kind() == reflect.Pointer {
modelType = modelType.Elem()
modelValue = modelValue.Elem()
}

for i := 0; i < modelType.NumField(); i++ {
if !modelType.Field(i).IsExported() {
continue
}
if modelType.Field(i).Name == "Model" && modelValue.Field(i).Type().Kind() == reflect.Struct {
structField := modelValue.Field(i).Type()
for j := 0; j < structField.NumField(); j++ {
if !structField.Field(i).IsExported() {
continue
}
dbColumn := structNameToDbColumnName(structField.Field(j).Name, structField.Field(j).Tag.Get("gorm"))
res[structField.Field(j).Name] = dbColumn
res[dbColumn] = dbColumn
}
}

dbColumn := structNameToDbColumnName(modelType.Field(i).Name, modelType.Field(i).Tag.Get("gorm"))
res[modelType.Field(i).Name] = dbColumn
res[dbColumn] = dbColumn
}

return res
}

func (e *Event) Context() context.Context {
return e.query.instance.Statement.Context
}

func (e *Event) DestOfMap() map[string]any {
if e.destOfMap != nil {
return e.destOfMap
}

var destOfMap map[string]any
if destMap, ok := e.dest.(map[string]any); ok {
destOfMap = destMap
} else {
destType := reflect.TypeOf(e.dest)
if destType.Kind() == reflect.Pointer {
destType = destType.Elem()
}
if destType.Kind() == reflect.Struct {
destOfMap = structToMap(e.dest)
}
}

e.destOfMap = destOfMap

return e.destOfMap
}

func (e *Event) GetAttribute(key string) any {
destOfMap := e.DestOfMap()
value, exist := destOfMap[e.toDBColumnName(key)]
if exist && e.validColumn(key) && e.validValue(key, value) {
return value
}

return e.GetOriginal(key)
}

func (e *Event) GetOriginal(key string, def ...any) any {
modelOfMap := e.ModelOfMap()
value, exist := modelOfMap[e.toDBColumnName(key)]
if exist {
return value
}

if len(def) > 0 {
return def[0]
}

return nil
}

func (e *Event) IsDirty(columns ...string) bool {
destOfMap := e.DestOfMap()

Expand Down Expand Up @@ -63,15 +159,22 @@ func (e *Event) IsClean(fields ...string) bool {
return !e.IsDirty(fields...)
}

func (e *Event) Query() orm.Query {
return NewQueryImplByInstance(e.query.instance.Session(&gorm.Session{NewDB: true}), &QueryImpl{
config: e.query.config,
withoutEvents: false,
})
func (e *Event) ModelOfMap() map[string]any {
if e.modelOfMap != nil {
return e.modelOfMap
}

if e.model == nil {
return map[string]any{}
}

e.modelOfMap = structToMap(e.model)

return e.modelOfMap
}

func (e *Event) Context() context.Context {
return e.query.instance.Statement.Context
func (e *Event) Query() orm.Query {
return NewQueryImpl(e.query.ctx, e.query.config, e.query.connection, e.query.instance.Session(&gorm.Session{NewDB: true}), nil)
}

func (e *Event) SetAttribute(key string, value any) {
Expand Down Expand Up @@ -113,28 +216,35 @@ func (e *Event) SetAttribute(key string, value any) {
}
}

func (e *Event) GetAttribute(key string) any {
destOfMap := e.DestOfMap()
value, exist := destOfMap[e.toDBColumnName(key)]
if exist && e.validColumn(key) && e.validValue(key, value) {
return value
func (e *Event) dirty(destColumn string, destValue any) bool {
modelOfMap := e.ModelOfMap()
dbDestColumn := e.toDBColumnName(destColumn)

if modelValue, exist := modelOfMap[dbDestColumn]; exist {
return !reflect.DeepEqual(modelValue, destValue)
}

return e.GetOriginal(key)
return true
}

func (e *Event) GetOriginal(key string, def ...any) any {
modelOfMap := e.ModelOfMap()
value, exist := modelOfMap[e.toDBColumnName(key)]
if exist {
return value
func (e *Event) equalColumnName(origin, source string) bool {
originDbColumnName := e.toDBColumnName(origin)
sourceDbColumnName := e.toDBColumnName(source)

if originDbColumnName == "" || sourceDbColumnName == "" {
return false
}

if len(def) > 0 {
return def[0]
return originDbColumnName == sourceDbColumnName
}

func (e *Event) toDBColumnName(name string) string {
dbColumnName, exist := e.ColumnNamesWithDbColumnNames()[name]
if exist {
return dbColumnName
}

return nil
return ""
}

func (e *Event) validColumn(name string) bool {
Expand Down Expand Up @@ -197,119 +307,6 @@ func (e *Event) validValue(name string, value any) bool {
return !valueValue.IsZero()
}

func (e *Event) dirty(destColumn string, destValue any) bool {
modelOfMap := e.ModelOfMap()
dbDestColumn := e.toDBColumnName(destColumn)

if modelValue, exist := modelOfMap[dbDestColumn]; exist {
return !reflect.DeepEqual(modelValue, destValue)
}

return true
}

func (e *Event) equalColumnName(origin, source string) bool {
originDbColumnName := e.toDBColumnName(origin)
sourceDbColumnName := e.toDBColumnName(source)

if originDbColumnName == "" || sourceDbColumnName == "" {
return false
}

return originDbColumnName == sourceDbColumnName
}

func (e *Event) toDBColumnName(name string) string {
dbColumnName, exist := e.ColumnNamesWithDbColumnNames()[name]
if exist {
return dbColumnName
}

return ""
}

func (e *Event) ModelOfMap() map[string]any {
if e.modelOfMap != nil {
return e.modelOfMap
}

if e.model == nil {
return map[string]any{}
}

e.modelOfMap = structToMap(e.model)

return e.modelOfMap
}

func (e *Event) DestOfMap() map[string]any {
if e.destOfMap != nil {
return e.destOfMap
}

var destOfMap map[string]any
if destMap, ok := e.dest.(map[string]any); ok {
destOfMap = destMap
} else {
destType := reflect.TypeOf(e.dest)
if destType.Kind() == reflect.Pointer {
destType = destType.Elem()
}
if destType.Kind() == reflect.Struct {
destOfMap = structToMap(e.dest)
}
}

e.destOfMap = destOfMap

return e.destOfMap
}

func (e *Event) ColumnNamesWithDbColumnNames() map[string]string {
if e.columnNamesWithDbColumnNames != nil {
return e.columnNamesWithDbColumnNames
}

res := make(map[string]string)
var modelType reflect.Type
var modelValue reflect.Value

if e.model != nil {
modelType = reflect.TypeOf(e.model)
modelValue = reflect.ValueOf(e.model)
} else {
modelType = reflect.TypeOf(e.dest)
modelValue = reflect.ValueOf(e.dest)
}
if modelType.Kind() == reflect.Pointer {
modelType = modelType.Elem()
modelValue = modelValue.Elem()
}

for i := 0; i < modelType.NumField(); i++ {
if !modelType.Field(i).IsExported() {
continue
}
if modelType.Field(i).Name == "Model" && modelValue.Field(i).Type().Kind() == reflect.Struct {
structField := modelValue.Field(i).Type()
for j := 0; j < structField.NumField(); j++ {
if !structField.Field(i).IsExported() {
continue
}
dbColumn := structNameToDbColumnName(structField.Field(j).Name, structField.Field(j).Tag.Get("gorm"))
res[structField.Field(j).Name] = dbColumn
res[dbColumn] = dbColumn
}
}

dbColumn := structNameToDbColumnName(modelType.Field(i).Name, modelType.Field(i).Tag.Get("gorm"))
res[modelType.Field(i).Name] = dbColumn
res[dbColumn] = dbColumn
}

return res
}

func structToMap(data any) map[string]any {
res := make(map[string]any)
modelType := reflect.TypeOf(data)
Expand Down
Loading