diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go index 5958e238e4a..0e7a413fecd 100644 --- a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go @@ -691,3 +691,74 @@ func Test_Gen_Dao_Issue3459(t *testing.T) { } }) } + +// https://github.com/gogf/gf/issues/3749 +func Test_Gen_Dao_Issue3749(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + table = "table_user" + sqlContent = fmt.Sprintf( + gtest.DataContent(`issue`, `3749`, `user.tpl.sql`), + table, + ) + ) + dropTableWithDb(db, table) + array := gstr.SplitAndTrim(sqlContent, ";") + for _, v := range array { + if _, err = db.Exec(ctx, v); err != nil { + t.AssertNil(err) + } + } + defer dropTableWithDb(db, table) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link, + Group: group, + } + ) + + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + // for go mod import path auto retrieve. + err = gfile.Copy( + gtest.DataPath("gendao", "go.mod.txt"), + gfile.Join(path, "go.mod"), + ) + t.AssertNil(err) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + defer gfile.Remove(path) + + // files + files, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(files, []string{ + filepath.FromSlash(path + "/dao/internal/table_user.go"), + filepath.FromSlash(path + "/dao/table_user.go"), + filepath.FromSlash(path + "/model/do/table_user.go"), + filepath.FromSlash(path + "/model/entity/table_user.go"), + }) + // content + testPath := gtest.DataPath(`issue`, `3749`) + expectFiles := []string{ + filepath.FromSlash(testPath + "/dao/internal/table_user.go"), + filepath.FromSlash(testPath + "/dao/table_user.go"), + filepath.FromSlash(testPath + "/model/do/table_user.go"), + filepath.FromSlash(testPath + "/model/entity/table_user.go"), + } + for i, _ := range files { + t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) + } + }) +} diff --git a/cmd/gf/internal/cmd/gendao/gendao_dao.go b/cmd/gf/internal/cmd/gendao/gendao_dao.go index 3d3b8a4d641..753e6daa3b7 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_dao.go +++ b/cmd/gf/internal/cmd/gendao/gendao_dao.go @@ -58,8 +58,8 @@ func generateDaoSingle(ctx context.Context, in generateDaoSingleInput) { mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err) } var ( - tableNameCamelCase = gstr.CaseCamel(strings.ToLower(in.NewTableName)) - tableNameCamelLowerCase = gstr.CaseCamelLower(strings.ToLower(in.NewTableName)) + tableNameCamelCase = formatFieldName(in.NewTableName, FieldNameCaseCamel) + tableNameCamelLowerCase = formatFieldName(in.NewTableName, FieldNameCaseCamelLower) tableNameSnakeCase = gstr.CaseSnake(in.NewTableName) importPrefix = in.ImportPrefix ) @@ -179,7 +179,7 @@ func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField, removeFieldP } array[index] = []string{ - " #" + gstr.CaseCamel(strings.ToLower(newFiledName)) + ":", + " #" + formatFieldName(newFiledName, FieldNameCaseCamel) + ":", fmt.Sprintf(` #"%s",`, field.Name), } } @@ -219,7 +219,7 @@ func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField, removeF newFiledName = gstr.TrimLeftStr(newFiledName, v, 1) } array[index] = []string{ - " #" + gstr.CaseCamel(strings.ToLower(newFiledName)), + " #" + formatFieldName(newFiledName, FieldNameCaseCamel), " # " + "string", " #" + fmt.Sprintf(`// %s`, comment), } diff --git a/cmd/gf/internal/cmd/gendao/gendao_do.go b/cmd/gf/internal/cmd/gendao/gendao_do.go index 16a4c15ebb4..166f9c7cc6a 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_do.go +++ b/cmd/gf/internal/cmd/gendao/gendao_do.go @@ -40,7 +40,7 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) { structDefinition, _ = generateStructDefinition(ctx, generateStructDefinitionInput{ CGenDaoInternalInput: in, TableName: tableName, - StructName: gstr.CaseCamel(strings.ToLower(newTableName)), + StructName: formatFieldName(newTableName, FieldNameCaseCamel), FieldMap: fieldMap, IsDo: true, }) @@ -61,7 +61,7 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) { ctx, in, tableName, - gstr.CaseCamel(strings.ToLower(newTableName)), + formatFieldName(newTableName, FieldNameCaseCamel), structDefinition, ) in.genItems.AppendGeneratedFilePath(doFilePath) diff --git a/cmd/gf/internal/cmd/gendao/gendao_entity.go b/cmd/gf/internal/cmd/gendao/gendao_entity.go index 7a36bf428e7..cb9239dd1e1 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_entity.go +++ b/cmd/gf/internal/cmd/gendao/gendao_entity.go @@ -36,7 +36,7 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) { structDefinition, appendImports = generateStructDefinition(ctx, generateStructDefinitionInput{ CGenDaoInternalInput: in, TableName: tableName, - StructName: gstr.CaseCamel(strings.ToLower(newTableName)), + StructName: formatFieldName(newTableName, FieldNameCaseCamel), FieldMap: fieldMap, IsDo: false, }) @@ -44,7 +44,7 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) { ctx, in, newTableName, - gstr.CaseCamel(strings.ToLower(newTableName)), + formatFieldName(newTableName, FieldNameCaseCamel), structDefinition, appendImports, ) diff --git a/cmd/gf/internal/cmd/gendao/gendao_structure.go b/cmd/gf/internal/cmd/gendao/gendao_structure.go index 5603bd3a15a..01f334cb987 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_structure.go +++ b/cmd/gf/internal/cmd/gendao/gendao_structure.go @@ -140,7 +140,7 @@ func generateStructFieldDefinition( } attrLines = []string{ - " #" + gstr.CaseCamel(strings.ToLower(newFiledName)), + " #" + formatFieldName(newFiledName, FieldNameCaseCamel), " #" + localTypeNameStr, } attrLines = append(attrLines, fmt.Sprintf(` #%sjson:"%s"`, tagKey, jsonTag)) @@ -167,6 +167,43 @@ func generateStructFieldDefinition( return attrLines, appendImport } +type FieldNameCase string + +const ( + FieldNameCaseCamel FieldNameCase = "CaseCamel" + FieldNameCaseCamelLower FieldNameCase = "CaseCamelLower" +) + +// formatFieldName formats and returns a new field name that is used for golang codes generating. +func formatFieldName(fieldName string, nameCase FieldNameCase) string { + // For normal databases like mysql, pgsql, sqlite, + // field/table names of that are in normal case. + var newFieldName = fieldName + if isAllUpper(fieldName) { + // For special databases like dm, oracle, + // field/table names of that are in upper case. + newFieldName = strings.ToLower(fieldName) + } + switch nameCase { + case FieldNameCaseCamel: + return gstr.CaseCamel(newFieldName) + case FieldNameCaseCamelLower: + return gstr.CaseCamelLower(newFieldName) + default: + return "" + } +} + +// isAllUpper checks and returns whether given `fieldName` all letters are upper case. +func isAllUpper(fieldName string) bool { + for _, b := range fieldName { + if b >= 'a' && b <= 'z' { + return false + } + } + return true +} + // formatComment formats the comment string to fit the golang code without any lines. func formatComment(comment string) string { comment = gstr.ReplaceByArray(comment, g.SliceStr{ diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go new file mode 100644 index 00000000000..871b7d65748 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go @@ -0,0 +1,85 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// TableUserDao is the data access object for table table_user. +type TableUserDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. +} + +// TableUserColumns defines and stores column names for table table_user. +type TableUserColumns struct { + Id string // User ID + ParentId string // + Passport string // User Passport + PassWord string // User Password + Nickname2 string // User Nickname + CreateAt string // Created Time + UpdateAt string // Updated Time +} + +// tableUserColumns holds the columns for table table_user. +var tableUserColumns = TableUserColumns{ + Id: "Id", + ParentId: "parentId", + Passport: "PASSPORT", + PassWord: "PASS_WORD", + Nickname2: "NICKNAME2", + CreateAt: "create_at", + UpdateAt: "update_at", +} + +// NewTableUserDao creates and returns a new DAO object for table data access. +func NewTableUserDao() *TableUserDao { + return &TableUserDao{ + group: "test", + table: "table_user", + columns: tableUserColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *TableUserDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *TableUserDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *TableUserDao) Columns() TableUserColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *TableUserDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *TableUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go new file mode 100644 index 00000000000..9bc9cff6f7d --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "for-gendao-test/pkg/dao/internal" +) + +// internalTableUserDao is internal type for wrapping internal DAO implements. +type internalTableUserDao = *internal.TableUserDao + +// tableUserDao is the data access object for table table_user. +// You can define custom methods on it to extend its functionality as you wish. +type tableUserDao struct { + internalTableUserDao +} + +var ( + // TableUser is globally public accessible object for table table_user operations. + TableUser = tableUserDao{ + internal.NewTableUserDao(), + } +) + +// Fill with you ideas below. diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/model/do/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/model/do/table_user.go new file mode 100644 index 00000000000..6fe512a4401 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/3749/model/do/table_user.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// TableUser is the golang structure of table table_user for DAO operations like Where/Data. +type TableUser struct { + g.Meta `orm:"table:table_user, do:true"` + Id interface{} // User ID + ParentId interface{} // + Passport interface{} // User Passport + PassWord interface{} // User Password + Nickname2 interface{} // User Nickname + CreateAt *gtime.Time // Created Time + UpdateAt *gtime.Time // Updated Time +} diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/model/entity/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/model/entity/table_user.go new file mode 100644 index 00000000000..effe0ce2c54 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/3749/model/entity/table_user.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// TableUser is the golang structure for table table_user. +type TableUser struct { + Id uint `json:"id" orm:"Id" ` // User ID + ParentId string `json:"parentId" orm:"parentId" ` // + Passport string `json:"pASSPORT" orm:"PASSPORT" ` // User Passport + PassWord string `json:"pASSWORD" orm:"PASS_WORD" ` // User Password + Nickname2 string `json:"nICKNAME2" orm:"NICKNAME2" ` // User Nickname + CreateAt *gtime.Time `json:"createAt" orm:"create_at" ` // Created Time + UpdateAt *gtime.Time `json:"updateAt" orm:"update_at" ` // Updated Time +} diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/user.tpl.sql b/cmd/gf/internal/cmd/testdata/issue/3749/user.tpl.sql new file mode 100644 index 00000000000..59896315c69 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/issue/3749/user.tpl.sql @@ -0,0 +1,10 @@ +CREATE TABLE `%s` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `parentId` varchar(45) NOT NULL COMMENT '', + `PASSPORT` varchar(45) NOT NULL COMMENT 'User Passport', + `PASS_WORD` varchar(45) NOT NULL COMMENT 'User Password', + `NICKNAME2` varchar(45) NOT NULL COMMENT 'User Nickname', + `create_at` datetime DEFAULT NULL COMMENT 'Created Time', + `update_at` datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index 252db6195bb..88835dd0cdb 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -107,6 +107,7 @@ func CaseConvert(s string, caseType CaseType) string { // // Example: // CaseCamel("any_kind_of_string") -> AnyKindOfString +// CaseCamel("anyKindOfString") -> AnyKindOfString func CaseCamel(s string) string { return toCamelInitCase(s, true) } @@ -115,6 +116,7 @@ func CaseCamel(s string) string { // // Example: // CaseCamelLower("any_kind_of_string") -> anyKindOfString +// CaseCamelLower("AnyKindOfString") -> anyKindOfString func CaseCamelLower(s string) string { if s == "" { return s diff --git a/text/gstr/gstr_z_unit_case_test.go b/text/gstr/gstr_z_unit_case_test.go index ebf78b59e88..bdc033c0d19 100644 --- a/text/gstr/gstr_z_unit_case_test.go +++ b/text/gstr/gstr_z_unit_case_test.go @@ -18,6 +18,7 @@ func Test_CaseCamel(t *testing.T) { {"test_case", "TestCase"}, {"test", "Test"}, {"TestCase", "TestCase"}, + {"testCase", "TestCase"}, {" test case ", "TestCase"}, {"userLogin_log.bak", "UserLoginLogBak"}, {"", ""},