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