github.com/gogf/gf@v1.16.9/database/gdb/gdb_model.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 "context" 11 "fmt" 12 "github.com/gogf/gf/util/gconv" 13 "time" 14 15 "github.com/gogf/gf/text/gregex" 16 17 "github.com/gogf/gf/text/gstr" 18 ) 19 20 // Model is core struct implementing the DAO for ORM. 21 type Model struct { 22 db DB // Underlying DB interface. 23 tx *TX // Underlying TX interface. 24 rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. 25 schema string // Custom database schema. 26 linkType int // Mark for operation on master or slave. 27 tablesInit string // Table names when model initialization. 28 tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". 29 fields string // Operation fields, multiple fields joined using char ','. 30 fieldsEx string // Excluded operation fields, multiple fields joined using char ','. 31 withArray []interface{} // Arguments for With feature. 32 withAll bool // Enable model association operations on all objects that have "with" tag in the struct. 33 extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. 34 whereHolder []ModelWhereHolder // Condition strings for where operation. 35 groupBy string // Used for "group by" statement. 36 orderBy string // Used for "order by" statement. 37 having []interface{} // Used for "having..." statement. 38 start int // Used for "select ... start, limit ..." statement. 39 limit int // Used for "select ... start, limit ..." statement. 40 option int // Option for extra operation features. 41 offset int // Offset statement for some databases grammar. 42 data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. 43 batch int // Batch number for batch Insert/Replace/Save operations. 44 filter bool // Filter data and where key-value pairs according to the fields of the table. 45 distinct string // Force the query to only return distinct results. 46 lockInfo string // Lock for update or in shared lock. 47 cacheEnabled bool // Enable sql result cache feature. 48 cacheDuration time.Duration // Cache TTL duration (< 1 for removing cache, >= 0 for saving cache). 49 cacheName string // Cache name for custom operation. 50 unscoped bool // Disables soft deleting features when select/delete operations. 51 safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. 52 onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. 53 onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. 54 } 55 56 // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. 57 type ModelHandler func(m *Model) *Model 58 59 // ChunkHandler is a function that is used in function Chunk, which handles given Result and error. 60 // It returns true if it wants continue chunking, or else it returns false to stop chunking. 61 type ChunkHandler func(result Result, err error) bool 62 63 // ModelWhereHolder is the holder for where condition preparing. 64 type ModelWhereHolder struct { 65 Operator int // Operator for this holder. 66 Where interface{} // Where parameter, which can commonly be type of string/map/struct. 67 Args []interface{} // Arguments for where parameter. 68 } 69 70 const ( 71 linkTypeMaster = 1 72 linkTypeSlave = 2 73 whereHolderOperatorWhere = 1 74 whereHolderOperatorAnd = 2 75 whereHolderOperatorOr = 3 76 defaultFields = "*" 77 ) 78 79 // Table is alias of Core.Model. 80 // See Core.Model. 81 // Deprecated, use Model instead. 82 func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model { 83 return c.db.Model(tableNameQueryOrStruct...) 84 } 85 86 // Model creates and returns a new ORM model from given schema. 87 // The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like: 88 // 1. Model names: 89 // db.Model("user") 90 // db.Model("user u") 91 // db.Model("user, user_detail") 92 // db.Model("user u, user_detail ud") 93 // 2. Model name with alias: 94 // db.Model("user", "u") 95 // 3. Model name with sub-query: 96 // db.Model("? AS a, ? AS b", subQuery1, subQuery2) 97 func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { 98 var ( 99 tableStr string 100 tableName string 101 extraArgs []interface{} 102 ) 103 // Model creation with sub-query. 104 if len(tableNameQueryOrStruct) > 1 { 105 conditionStr := gconv.String(tableNameQueryOrStruct[0]) 106 if gstr.Contains(conditionStr, "?") { 107 tableStr, extraArgs = formatWhere(c.db, formatWhereInput{ 108 Where: conditionStr, 109 Args: tableNameQueryOrStruct[1:], 110 OmitNil: false, 111 OmitEmpty: false, 112 Schema: "", 113 Table: "", 114 }) 115 } 116 } 117 // Normal model creation. 118 if tableStr == "" { 119 tableNames := make([]string, len(tableNameQueryOrStruct)) 120 for k, v := range tableNameQueryOrStruct { 121 if s, ok := v.(string); ok { 122 tableNames[k] = s 123 } else if tableName = getTableNameFromOrmTag(v); tableName != "" { 124 tableNames[k] = tableName 125 } 126 } 127 if len(tableNames) > 1 { 128 tableStr = fmt.Sprintf( 129 `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), 130 ) 131 } else if len(tableNames) == 1 { 132 tableStr = c.QuotePrefixTableName(tableNames[0]) 133 } 134 } 135 return &Model{ 136 db: c.db, 137 tablesInit: tableStr, 138 tables: tableStr, 139 fields: defaultFields, 140 start: -1, 141 offset: -1, 142 filter: true, 143 extraArgs: extraArgs, 144 } 145 } 146 147 // Raw creates and returns a model based on a raw sql not a table. 148 // Example: 149 // db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) 150 func (c *Core) Raw(rawSql string, args ...interface{}) *Model { 151 model := c.Model() 152 model.rawSql = rawSql 153 model.extraArgs = args 154 return model 155 } 156 157 // Raw creates and returns a model based on a raw sql not a table. 158 // Example: 159 // db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) 160 // See Core.Raw. 161 func (m *Model) Raw(rawSql string, args ...interface{}) *Model { 162 model := m.db.Raw(rawSql, args...) 163 model.db = m.db 164 model.tx = m.tx 165 return model 166 } 167 168 func (tx *TX) Raw(rawSql string, args ...interface{}) *Model { 169 return tx.Model().Raw(rawSql, args...) 170 } 171 172 // With creates and returns an ORM model based on meta data of given object. 173 func (c *Core) With(objects ...interface{}) *Model { 174 return c.db.Model().With(objects...) 175 } 176 177 // Model acts like Core.Model except it operates on transaction. 178 // See Core.Model. 179 func (tx *TX) Model(tableNameQueryOrStruct ...interface{}) *Model { 180 model := tx.db.Model(tableNameQueryOrStruct...) 181 model.db = tx.db 182 model.tx = tx 183 return model 184 } 185 186 // With acts like Core.With except it operates on transaction. 187 // See Core.With. 188 func (tx *TX) With(object interface{}) *Model { 189 return tx.Model().With(object) 190 } 191 192 // Ctx sets the context for current operation. 193 func (m *Model) Ctx(ctx context.Context) *Model { 194 if ctx == nil { 195 return m 196 } 197 model := m.getModel() 198 model.db = model.db.Ctx(ctx) 199 if m.tx != nil { 200 model.tx = model.tx.Ctx(ctx) 201 } 202 return model 203 } 204 205 // GetCtx returns the context for current Model. 206 // It returns `context.Background()` is there's no context previously set. 207 func (m *Model) GetCtx() context.Context { 208 if m.tx != nil && m.tx.ctx != nil { 209 return m.tx.ctx 210 } 211 return m.db.GetCtx() 212 } 213 214 // As sets an alias name for current table. 215 func (m *Model) As(as string) *Model { 216 if m.tables != "" { 217 model := m.getModel() 218 split := " JOIN " 219 if gstr.ContainsI(model.tables, split) { 220 // For join table. 221 array := gstr.Split(model.tables, split) 222 array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1]) 223 model.tables = gstr.Join(array, split) 224 } else { 225 // For base table. 226 model.tables = gstr.TrimRight(model.tables) + " AS " + as 227 } 228 return model 229 } 230 return m 231 } 232 233 // DB sets/changes the db object for current operation. 234 func (m *Model) DB(db DB) *Model { 235 model := m.getModel() 236 model.db = db 237 return model 238 } 239 240 // TX sets/changes the transaction for current operation. 241 func (m *Model) TX(tx *TX) *Model { 242 model := m.getModel() 243 model.db = tx.db 244 model.tx = tx 245 return model 246 } 247 248 // Schema sets the schema for current operation. 249 func (m *Model) Schema(schema string) *Model { 250 model := m.getModel() 251 model.schema = schema 252 return model 253 } 254 255 // Clone creates and returns a new model which is a clone of current model. 256 // Note that it uses deep-copy for the clone. 257 func (m *Model) Clone() *Model { 258 newModel := (*Model)(nil) 259 if m.tx != nil { 260 newModel = m.tx.Model(m.tablesInit) 261 } else { 262 newModel = m.db.Model(m.tablesInit) 263 } 264 *newModel = *m 265 // Shallow copy slice attributes. 266 if n := len(m.extraArgs); n > 0 { 267 newModel.extraArgs = make([]interface{}, n) 268 copy(newModel.extraArgs, m.extraArgs) 269 } 270 if n := len(m.whereHolder); n > 0 { 271 newModel.whereHolder = make([]ModelWhereHolder, n) 272 copy(newModel.whereHolder, m.whereHolder) 273 } 274 if n := len(m.withArray); n > 0 { 275 newModel.withArray = make([]interface{}, n) 276 copy(newModel.withArray, m.withArray) 277 } 278 return newModel 279 } 280 281 // Master marks the following operation on master node. 282 func (m *Model) Master() *Model { 283 model := m.getModel() 284 model.linkType = linkTypeMaster 285 return model 286 } 287 288 // Slave marks the following operation on slave node. 289 // Note that it makes sense only if there's any slave node configured. 290 func (m *Model) Slave() *Model { 291 model := m.getModel() 292 model.linkType = linkTypeSlave 293 return model 294 } 295 296 // Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object 297 // whenever the operation done, or else it changes the attribute of current model. 298 func (m *Model) Safe(safe ...bool) *Model { 299 if len(safe) > 0 { 300 m.safe = safe[0] 301 } else { 302 m.safe = true 303 } 304 return m 305 } 306 307 // Args sets custom arguments for model operation. 308 func (m *Model) Args(args ...interface{}) *Model { 309 model := m.getModel() 310 model.extraArgs = append(model.extraArgs, args) 311 return model 312 } 313 314 // Handler calls each of `handlers` on current Model and returns a new Model. 315 // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. 316 func (m *Model) Handler(handlers ...ModelHandler) *Model { 317 model := m.getModel() 318 for _, handler := range handlers { 319 model = handler(model) 320 } 321 return model 322 }