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  }