github.com/go-courier/sqlx/v2@v2.23.13/generator/model_crud.go (about)

     1  package generator
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/go-courier/codegen"
     9  	"github.com/go-courier/packagesx"
    10  	"github.com/go-courier/sqlx/v2/builder"
    11  )
    12  
    13  func (m *Model) snippetSetDeletedAtIfNeedForFieldValues(file *codegen.File) codegen.Snippet {
    14  	if m.HasDeletedAt {
    15  		return codegen.Expr(`if _, ok := fieldValues["`+m.FieldKeyDeletedAt+`"]; !ok {
    16  			fieldValues["`+m.FieldKeyDeletedAt+`"] = ?(`+file.Use("time", "Now")+`())
    17  		}`,
    18  			m.FieldType(file, m.FieldKeyDeletedAt),
    19  		)
    20  	}
    21  	return nil
    22  }
    23  
    24  func (m *Model) snippetSetCreatedAtIfNeed(file *codegen.File) codegen.Snippet {
    25  	if m.HasCreatedAt {
    26  		return codegen.Expr(`
    27  if m.`+m.FieldKeyCreatedAt+`.IsZero() {
    28  	m.`+m.FieldKeyCreatedAt+` = ?(`+file.Use("time", "Now")+`())
    29  }
    30  `,
    31  			m.FieldType(file, m.FieldKeyCreatedAt),
    32  		)
    33  	}
    34  
    35  	return nil
    36  }
    37  
    38  func (m *Model) snippetSetUpdatedAtIfNeed(file *codegen.File) codegen.Snippet {
    39  	if m.HasUpdatedAt {
    40  		return codegen.Expr(`
    41  if m.`+m.FieldKeyUpdatedAt+`.IsZero() {
    42  	m.`+m.FieldKeyUpdatedAt+` = ?(`+file.Use("time", "Now")+`())
    43  }
    44  `,
    45  			m.FieldType(file, m.FieldKeyUpdatedAt),
    46  		)
    47  	}
    48  
    49  	return codegen.Expr("")
    50  }
    51  
    52  func (m *Model) snippetSetUpdatedAtIfNeedForFieldValues(file *codegen.File) codegen.Snippet {
    53  	if m.HasUpdatedAt {
    54  		return codegen.Expr(`
    55  if _, ok := fieldValues[?]; !ok {
    56  	fieldValues[?] = ?(?())
    57  }
    58  `,
    59  			codegen.Val(m.FieldKeyUpdatedAt),
    60  			codegen.Val(m.FieldKeyUpdatedAt),
    61  			m.FieldType(file, m.FieldKeyUpdatedAt),
    62  			codegen.Id(file.Use("time", "Now")))
    63  	}
    64  	return nil
    65  }
    66  
    67  func (m *Model) WriteCreate(file *codegen.File) {
    68  	file.WriteBlock(
    69  		codegen.Func(codegen.Var(
    70  			codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
    71  			Named("Create").
    72  			MethodOf(codegen.Var(m.PtrType(), "m")).
    73  			Return(codegen.Var(codegen.Error)).
    74  			Do(
    75  				m.snippetSetCreatedAtIfNeed(file),
    76  				m.snippetSetUpdatedAtIfNeed(file),
    77  
    78  				codegen.Expr(`
    79  _, err := db.ExecExpr(?(db, m, nil))
    80  return err
    81  `,
    82  					codegen.Id(file.Use("github.com/go-courier/sqlx/v2", "InsertToDB")),
    83  				),
    84  			),
    85  	)
    86  
    87  	if len(m.Keys.UniqueIndexes) > 0 {
    88  
    89  		file.WriteBlock(
    90  			codegen.Func(
    91  				codegen.Var(codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db"),
    92  				codegen.Var(codegen.Slice(codegen.String), "updateFields"),
    93  			).
    94  				Named("CreateOnDuplicateWithUpdateFields").
    95  				MethodOf(codegen.Var(m.PtrType(), "m")).
    96  				Return(codegen.Var(codegen.Error)).
    97  				Do(
    98  					codegen.Expr(`
    99  if len(updateFields) == 0 {
   100  	panic(`+file.Use("fmt", "Errorf")+`("must have update fields"))
   101  }
   102  `),
   103  
   104  					m.snippetSetCreatedAtIfNeed(file),
   105  					m.snippetSetUpdatedAtIfNeed(file),
   106  
   107  					codegen.Expr(`
   108  fieldValues := `+file.Use("github.com/go-courier/sqlx/v2/builder", "FieldValuesFromStructByNonZero")+`(m, updateFields...)
   109  `),
   110  					func() codegen.Snippet {
   111  						if m.HasAutoIncrement {
   112  							return codegen.Expr(
   113  								`delete(fieldValues, ?)`,
   114  								file.Val(m.FieldKeyAutoIncrement),
   115  							)
   116  						}
   117  						return nil
   118  					}(),
   119  
   120  					codegen.Expr(`
   121  table := db.T(m)
   122  
   123  cols, vals := table.ColumnsAndValuesByFieldValues(fieldValues)
   124  
   125  fields := make(map[string]bool, len(updateFields))
   126  for _, field := range updateFields {
   127  	fields[field] = true
   128  }
   129  `),
   130  					codegen.Expr(`
   131  for _, fieldNames := range m.UniqueIndexes() {
   132  	for _, field := range fieldNames {
   133  		delete(fields, field)
   134  	}
   135  }
   136  
   137  if len(fields) == 0 {
   138  	panic(`+file.Use("fmt", "Errorf")+`("no fields for updates"))
   139  }
   140  
   141  for field := range fieldValues {
   142  	if !fields[field] {
   143  		delete(fieldValues, field)
   144  	}
   145  }
   146  
   147  additions := `+file.Use("github.com/go-courier/sqlx/v2/builder", "Additions")+`{}
   148  
   149  switch db.Dialect().DriverName() {
   150  case "mysql":	
   151  	additions = append(additions, `+file.Use("github.com/go-courier/sqlx/v2/builder", "OnDuplicateKeyUpdate")+`(table.AssignmentsByFieldValues(fieldValues)...))
   152  case "postgres":
   153  	indexes := m.UniqueIndexes()
   154  	fields := make([]string, 0)
   155  	for _, fs := range indexes {
   156  		fields = append(fields, fs...)
   157  	}
   158  	indexFields, _ := db.T(m).Fields(fields...)
   159  
   160  	additions = append(additions,
   161  			`+file.Use("github.com/go-courier/sqlx/v2/builder", "OnConflict")+`(indexFields).
   162  				DoUpdateSet(table.AssignmentsByFieldValues(fieldValues)...))
   163  }
   164  
   165  additions = append(additions, `+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`("User.CreateOnDuplicateWithUpdateFields"))
   166  
   167  expr := `+file.Use("github.com/go-courier/sqlx/v2/builder", "Insert")+`().Into(table, additions...).Values(cols, vals...)
   168  
   169  _, err := db.ExecExpr(expr)
   170  return err
   171  `,
   172  						file.Val(m.StructName+".CreateOnDuplicateWithUpdateFields"),
   173  						file.Val(m.StructName+".CreateOnDuplicateWithUpdateFields"),
   174  					),
   175  				),
   176  		)
   177  	}
   178  }
   179  
   180  func (m *Model) WriteDelete(file *codegen.File) {
   181  	file.WriteBlock(
   182  		codegen.Func(codegen.Var(
   183  			codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
   184  			Named("DeleteByStruct").
   185  			MethodOf(codegen.Var(m.PtrType(), "m")).
   186  			Return(codegen.Var(codegen.Error)).
   187  			Do(
   188  				codegen.Expr(`
   189  _, err := db.ExecExpr(
   190  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Delete")+`().
   191  From(
   192  	db.T(m),
   193  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Where")+`(m.ConditionByStruct(db)),
   194  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(?),
   195  ),
   196  )
   197  
   198  `, file.Val(m.StructName+".DeleteByStruct")),
   199  
   200  				codegen.Return(codegen.Expr("err")),
   201  			),
   202  	)
   203  }
   204  
   205  func (m *Model) WriteByKey(file *codegen.File) {
   206  	m.Table.Keys.Range(func(key *builder.Key, idx int) {
   207  		fieldNames := key.Def.FieldNames
   208  
   209  		fieldNamesWithoutEnabled := stringFilter(fieldNames, func(item string, i int) bool {
   210  			if m.HasDeletedAt {
   211  				return item != m.FieldKeyDeletedAt
   212  			}
   213  			return true
   214  		})
   215  
   216  		if m.HasDeletedAt && key.IsPrimary() {
   217  			fieldNames = append(fieldNames, m.FieldKeyDeletedAt)
   218  		}
   219  
   220  		if key.IsUnique {
   221  			{
   222  				methodForFetch := createMethod("FetchBy%s", fieldNamesWithoutEnabled...)
   223  
   224  				file.WriteBlock(
   225  					codegen.Func(codegen.Var(
   226  						codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
   227  						Named(methodForFetch).
   228  						MethodOf(codegen.Var(m.PtrType(), "m")).
   229  						Return(codegen.Var(codegen.Error)).
   230  						Do(
   231  							codegen.Expr(`
   232  table := db.T(m)
   233  
   234  err := db.QueryExprAndScan(
   235  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Select")+`(nil).
   236  From(
   237  	db.T(m),
   238  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Where")+`(`+toExactlyConditionFrom(file, fieldNames...)+`),
   239  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(?),
   240  ),
   241  m,
   242  )
   243  `,
   244  								file.Val(m.StructName+"."+methodForFetch),
   245  							),
   246  
   247  							codegen.Return(codegen.Expr("err")),
   248  						),
   249  				)
   250  
   251  				methodForUpdateWithMap := createMethod("UpdateBy%sWithMap", fieldNamesWithoutEnabled...)
   252  
   253  				file.WriteBlock(
   254  					codegen.Func(
   255  						codegen.Var(codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db"),
   256  						codegen.Var(codegen.Type(file.Use("github.com/go-courier/sqlx/v2/builder", "FieldValues")), "fieldValues"),
   257  					).
   258  						Named(methodForUpdateWithMap).
   259  						MethodOf(codegen.Var(m.PtrType(), "m")).
   260  						Return(codegen.Var(codegen.Error)).
   261  						Do(
   262  							m.snippetSetUpdatedAtIfNeedForFieldValues(file),
   263  							codegen.Expr(`
   264  table := db.T(m)
   265  
   266  result, err := db.ExecExpr(
   267  	`+file.Use("github.com/go-courier/sqlx/v2/builder", "Update")+`(db.T(m)).
   268  		Where(
   269  			`+toExactlyConditionFrom(file, fieldNames...)+`,
   270  			`+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(?),
   271  		).
   272  		Set(table.AssignmentsByFieldValues(fieldValues)...),
   273  	)
   274  
   275  if err != nil {
   276  	return err
   277  }
   278  
   279  rowsAffected, _ := result.RowsAffected()
   280  if rowsAffected == 0 {
   281    return m.`+methodForFetch+`(db)
   282  }
   283  
   284  return nil
   285  `,
   286  								file.Val(m.StructName+"."+methodForUpdateWithMap),
   287  							),
   288  						),
   289  				)
   290  
   291  				methodForUpdateWithStruct := createMethod("UpdateBy%sWithStruct", fieldNamesWithoutEnabled...)
   292  
   293  				file.WriteBlock(
   294  					codegen.Func(
   295  						codegen.Var(codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db"),
   296  						codegen.Var(codegen.Ellipsis(codegen.String), "zeroFields"),
   297  					).
   298  						Named(methodForUpdateWithStruct).
   299  						MethodOf(codegen.Var(m.PtrType(), "m")).
   300  						Return(codegen.Var(codegen.Error)).
   301  						Do(
   302  							codegen.Expr(`
   303  fieldValues := ` + file.Use("github.com/go-courier/sqlx/v2/builder", "FieldValuesFromStructByNonZero") + `(m, zeroFields...)
   304  return m.` + methodForUpdateWithMap + `(db, fieldValues)
   305  `),
   306  						),
   307  				)
   308  			}
   309  
   310  			{
   311  
   312  				method := createMethod("FetchBy%sForUpdate", fieldNamesWithoutEnabled...)
   313  
   314  				file.WriteBlock(
   315  					codegen.Func(codegen.Var(
   316  						codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
   317  						Named(method).
   318  						MethodOf(codegen.Var(m.PtrType(), "m")).
   319  						Return(codegen.Var(codegen.Error)).
   320  						Do(
   321  							codegen.Expr(`
   322  table := db.T(m)
   323  
   324  err := db.QueryExprAndScan(
   325  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Select")+`(nil).
   326  From(
   327  	db.T(m),
   328  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Where")+`(`+toExactlyConditionFrom(file, fieldNames...)+`),
   329  `+file.Use("github.com/go-courier/sqlx/v2/builder", "ForUpdate")+`(),
   330  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(?),
   331  ),
   332  m,
   333  )
   334  `,
   335  								file.Val(m.StructName+"."+method),
   336  							),
   337  
   338  							codegen.Return(codegen.Expr("err")),
   339  						),
   340  				)
   341  			}
   342  
   343  			{
   344  				methodForDelete := createMethod("DeleteBy%s", fieldNamesWithoutEnabled...)
   345  
   346  				file.WriteBlock(
   347  					codegen.Func(codegen.Var(
   348  						codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
   349  						Named(methodForDelete).
   350  						MethodOf(codegen.Var(m.PtrType(), "m")).
   351  						Return(codegen.Var(codegen.Error)).
   352  						Do(
   353  							codegen.Expr(`
   354  table := db.T(m)
   355  
   356  _, err := db.ExecExpr(
   357  `+file.Use("github.com/go-courier/sqlx/v2/builder", "Delete")+`().
   358  	From(db.T(m),
   359  	`+file.Use("github.com/go-courier/sqlx/v2/builder", "Where")+`(`+toExactlyConditionFrom(file, fieldNames...)+`),
   360  	`+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(`+string(file.Val(m.StructName+"."+methodForDelete).Bytes())+`),
   361  ))
   362  `,
   363  								file.Val(m.StructName+"."+methodForDelete),
   364  							),
   365  
   366  							codegen.Return(codegen.Expr("err")),
   367  						),
   368  				)
   369  
   370  				if m.HasDeletedAt {
   371  
   372  					methodForSoftDelete := createMethod("SoftDeleteBy%s", fieldNamesWithoutEnabled...)
   373  
   374  					file.WriteBlock(
   375  						codegen.Func(codegen.Var(
   376  							codegen.Type(file.Use("github.com/go-courier/sqlx/v2", "DBExecutor")), "db")).
   377  							Named(methodForSoftDelete).
   378  							MethodOf(codegen.Var(m.PtrType(), "m")).
   379  							Return(codegen.Var(codegen.Error)).
   380  							Do(
   381  								codegen.Expr(`
   382  table := db.T(m)
   383  
   384  fieldValues := `+file.Use("github.com/go-courier/sqlx/v2/builder", "FieldValues")+`{}`),
   385  
   386  								m.snippetSetDeletedAtIfNeedForFieldValues(file),
   387  								m.snippetSetUpdatedAtIfNeedForFieldValues(file),
   388  
   389  								codegen.Expr(`
   390  _, err := db.ExecExpr(
   391  	`+file.Use("github.com/go-courier/sqlx/v2/builder", "Update")+`(db.T(m)).
   392  		Where(
   393  			`+toExactlyConditionFrom(file, fieldNames...)+`,
   394  			`+file.Use("github.com/go-courier/sqlx/v2/builder", "Comment")+`(`+string(file.Val(m.StructName+"."+methodForSoftDelete).Bytes())+`),
   395  		).
   396  		Set(table.AssignmentsByFieldValues(fieldValues)...),
   397  )
   398  
   399  return err
   400  `),
   401  							),
   402  					)
   403  				}
   404  			}
   405  		}
   406  	})
   407  }
   408  
   409  func (m *Model) WriteCRUD(file *codegen.File) {
   410  	m.WriteCreate(file)
   411  	m.WriteDelete(file)
   412  	m.WriteByKey(file)
   413  }
   414  
   415  func toExactlyConditionFrom(file *codegen.File, fieldNames ...string) string {
   416  	buf := &bytes.Buffer{}
   417  	buf.WriteString(file.Use("github.com/go-courier/sqlx/v2/builder", "And"))
   418  	buf.WriteString(`(
   419  `)
   420  
   421  	for _, fieldName := range fieldNames {
   422  		buf.WriteString(fmt.Sprintf(`table.F("%s").Eq(m.%s),
   423  		`, fieldName, fieldName))
   424  	}
   425  
   426  	buf.WriteString(`
   427  )`)
   428  
   429  	return buf.String()
   430  }
   431  
   432  func createMethod(method string, fieldNames ...string) string {
   433  	return fmt.Sprintf(method, strings.Join(fieldNames, "And"))
   434  }
   435  
   436  func (m *Model) FieldType(file *codegen.File, fieldName string) codegen.SnippetType {
   437  	if field, ok := m.Fields[fieldName]; ok {
   438  		typ := field.Type().String()
   439  		if strings.Contains(typ, ".") {
   440  			importPath, name := packagesx.GetPkgImportPathAndExpose(typ)
   441  			if importPath != m.TypeName.Pkg().Path() {
   442  				return codegen.Type(file.Use(importPath, name))
   443  			}
   444  			return codegen.Type(name)
   445  		}
   446  		return codegen.BuiltInType(typ)
   447  	}
   448  	return nil
   449  }