github.com/gogf/gf@v1.16.9/database/gdb/gdb_model_utility.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gdb 8 9 import ( 10 "time" 11 12 "github.com/gogf/gf/container/gset" 13 "github.com/gogf/gf/internal/empty" 14 "github.com/gogf/gf/os/gtime" 15 "github.com/gogf/gf/text/gregex" 16 "github.com/gogf/gf/text/gstr" 17 "github.com/gogf/gf/util/gutil" 18 ) 19 20 // TableFields retrieves and returns the fields information of specified table of current 21 // schema. 22 // 23 // Also see DriverMysql.TableFields. 24 func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) { 25 useSchema := m.schema 26 if len(schema) > 0 && schema[0] != "" { 27 useSchema = schema[0] 28 } 29 return m.db.TableFields(m.GetCtx(), m.guessPrimaryTableName(tableStr), useSchema) 30 } 31 32 // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns 33 // the current model. 34 func (m *Model) getModel() *Model { 35 if !m.safe { 36 return m 37 } else { 38 return m.Clone() 39 } 40 } 41 42 // mappingAndFilterToTableFields mappings and changes given field name to really table field name. 43 // Eg: 44 // ID -> id 45 // NICK_Name -> nickname 46 func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string { 47 fieldsMap, err := m.TableFields(m.tablesInit) 48 if err != nil || len(fieldsMap) == 0 { 49 return fields 50 } 51 var ( 52 inputFieldsArray = gstr.SplitAndTrim(gstr.Join(fields, ","), ",") 53 outputFieldsArray = make([]string, 0, len(inputFieldsArray)) 54 ) 55 fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) 56 for k, _ := range fieldsMap { 57 fieldsKeyMap[k] = nil 58 } 59 for _, field := range inputFieldsArray { 60 if _, ok := fieldsKeyMap[field]; !ok { 61 if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, field) { 62 // Eg: user.id, user.name 63 outputFieldsArray = append(outputFieldsArray, field) 64 continue 65 } else { 66 // Eg: id, name 67 if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" { 68 outputFieldsArray = append(outputFieldsArray, foundKey) 69 } else if !filter { 70 outputFieldsArray = append(outputFieldsArray, field) 71 } 72 } 73 } else { 74 outputFieldsArray = append(outputFieldsArray, field) 75 } 76 } 77 return outputFieldsArray 78 } 79 80 // filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations. 81 // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. 82 func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, error) { 83 var err error 84 switch value := data.(type) { 85 case List: 86 for k, item := range value { 87 value[k], err = m.doMappingAndFilterForInsertOrUpdateDataMap(item, false) 88 if err != nil { 89 return nil, err 90 } 91 } 92 return value, nil 93 94 case Map: 95 return m.doMappingAndFilterForInsertOrUpdateDataMap(value, true) 96 97 default: 98 return data, nil 99 } 100 } 101 102 // doMappingAndFilterForInsertOrUpdateDataMap does the filter features for map. 103 // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. 104 func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) { 105 var err error 106 data, err = m.db.GetCore().mappingAndFilterData( 107 m.schema, m.guessPrimaryTableName(m.tablesInit), data, m.filter, 108 ) 109 if err != nil { 110 return nil, err 111 } 112 // Remove key-value pairs of which the value is nil. 113 if allowOmitEmpty && m.option&optionOmitNilData > 0 { 114 tempMap := make(Map, len(data)) 115 for k, v := range data { 116 if empty.IsNil(v) { 117 continue 118 } 119 tempMap[k] = v 120 } 121 data = tempMap 122 } 123 124 // Remove key-value pairs of which the value is empty. 125 if allowOmitEmpty && m.option&optionOmitEmptyData > 0 { 126 tempMap := make(Map, len(data)) 127 for k, v := range data { 128 if empty.IsEmpty(v) { 129 continue 130 } 131 // Special type filtering. 132 switch r := v.(type) { 133 case time.Time: 134 if r.IsZero() { 135 continue 136 } 137 case *time.Time: 138 if r.IsZero() { 139 continue 140 } 141 case gtime.Time: 142 if r.IsZero() { 143 continue 144 } 145 case *gtime.Time: 146 if r.IsZero() { 147 continue 148 } 149 } 150 tempMap[k] = v 151 } 152 data = tempMap 153 } 154 155 if len(m.fields) > 0 && m.fields != "*" { 156 // Keep specified fields. 157 var ( 158 set = gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ",")) 159 charL, charR = m.db.GetChars() 160 chars = charL + charR 161 ) 162 set.Walk(func(item string) string { 163 return gstr.Trim(item, chars) 164 }) 165 for k := range data { 166 k = gstr.Trim(k, chars) 167 if !set.Contains(k) { 168 delete(data, k) 169 } 170 } 171 } else if len(m.fieldsEx) > 0 { 172 // Filter specified fields. 173 for _, v := range gstr.SplitAndTrim(m.fieldsEx, ",") { 174 delete(data, v) 175 } 176 } 177 return data, nil 178 } 179 180 // getLink returns the underlying database link object with configured `linkType` attribute. 181 // The parameter `master` specifies whether using the master node if master-slave configured. 182 func (m *Model) getLink(master bool) Link { 183 if m.tx != nil { 184 return &txLink{m.tx.tx} 185 } 186 linkType := m.linkType 187 if linkType == 0 { 188 if master { 189 linkType = linkTypeMaster 190 } else { 191 linkType = linkTypeSlave 192 } 193 } 194 switch linkType { 195 case linkTypeMaster: 196 link, err := m.db.GetCore().MasterLink(m.schema) 197 if err != nil { 198 panic(err) 199 } 200 return link 201 case linkTypeSlave: 202 link, err := m.db.GetCore().SlaveLink(m.schema) 203 if err != nil { 204 panic(err) 205 } 206 return link 207 } 208 return nil 209 } 210 211 // getPrimaryKey retrieves and returns the primary key name of the model table. 212 // It parses m.tables to retrieve the primary table name, supporting m.tables like: 213 // "user", "user u", "user as u, user_detail as ud". 214 func (m *Model) getPrimaryKey() string { 215 table := gstr.SplitAndTrim(m.tablesInit, " ")[0] 216 tableFields, err := m.TableFields(table) 217 if err != nil { 218 return "" 219 } 220 for name, field := range tableFields { 221 if gstr.ContainsI(field.Key, "pri") { 222 return name 223 } 224 } 225 return "" 226 } 227 228 // mergeArguments creates and returns new arguments by merging <m.extraArgs> and given `args`. 229 func (m *Model) mergeArguments(args []interface{}) []interface{} { 230 if len(m.extraArgs) > 0 { 231 newArgs := make([]interface{}, len(m.extraArgs)+len(args)) 232 copy(newArgs, m.extraArgs) 233 copy(newArgs[len(m.extraArgs):], args) 234 return newArgs 235 } 236 return args 237 }