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  }