Skip to content

yyle88/gormcngen

Repository files navigation

GitHub Workflow Status (branch) GoDoc Coverage Status Supported Go Versions GitHub Release Go Report Card

gormcngen

gormcngen: Provides a Columns() Function to Retrieve Column Names from GORM Models

Like MyBatis Plus in the Java ecosystem, which allows developers to get column names using expressions like Example::getName.

Like SQLAlchemy in the Python ecosystem, which allows developers to access column names using a class function, like Example.name.

gormcngen also brings type-safe column referencing to Go models.


Ecosystem

GORM Type-Safe Ecosystem


CHINESE README

中文说明


Language Ecosystem Comparison

Language ORM Type-Safe Columns Example
Java MyBatis Plus Example::getName wrapper.eq(Example::getName, "alice")
Python SQLAlchemy Example.name query.filter(Example.name == "alice")
Go GORMCNGEN cls.Name.Eq() db.Where(cls.Name.Eq("alice"))

Installation

go get github.com/yyle88/gormcngen

Important Note

gormcngen is a Go package, not a CLI application. It requires a test-code-driven generation workflow.

Quick Start

1. Create Project Structure

Set up the basic project structure and create dedicated DIR to hold models and generated code:

# Create models DIR
mkdir -p internal/models

2. Define GORM Models

Define data models - gormcngen generates column access methods from these models:

Create internal/models/models.go:

package models

type Account struct {
    ID       uint   `gorm:"primaryKey"`
    Username string `gorm:"uniqueIndex;size:100"`
    Mailbox  string `gorm:"index;size:255"`
    Age      int    `gorm:"column:age"`
    IsActive bool   `gorm:"default:true"`
}

3. Create Generation Files

Create the target file to hold generated code and the test file containing generation logic:

# Create target file to hold generated code with package declaration
echo "package models" > internal/models/ngen.go

# Create test file containing generation logic with package declaration
echo "package models" > internal/models/ngen_test.go

4. Write Generation Logic

Write the code generation logic in the test file, configure generation options and set the models to process.

Note: In Go, using test files to generate source code is a common practice.

Create internal/models/ngen_test.go:

package models

import (
    "testing"
    
    "github.com/yyle88/gormcngen"
    "github.com/yyle88/osexistpath/osmustexist"
    "github.com/yyle88/runpath/runtestpath"
)

//go:generate go test -run ^TestGenerate$
func TestGenerate(t *testing.T) {
    // Get absolute path to target file (ngen.go)
    absPath := osmustexist.FILE(runtestpath.SrcPath(t))
    t.Log(absPath)
    
    // Configure generation options
    options := gormcngen.NewOptions().
        WithColumnClassExportable(true).
        WithColumnsMethodRecvName("c").
        WithColumnsCheckFieldType(true)
    
    // Define models to process
    models := []interface{}{
		&Account{},
	}
    
    // Create config and generate
    cfg := gormcngen.NewConfigs(models, options, absPath)
    cfg.WithIsGenPreventEdit(true)  // Add "DO NOT EDIT" warning headers (default: true)
    cfg.WithGeneratedFromPos(gormcngen.GetGenPosFuncMark(0))  // Show generation source location (default: show)
    cfg.Gen()
}

5. Execute Generation

Run the test to initiate code generation - the generated code gets auto written to the target file:

# Clean up dependencies
go mod tidy

# Run generation test
cd internal/models
go test -v ./...

🎉 Generation Complete! The ngen.go file now contains the generated column access methods.

The generated ngen.go contains:

// Code generated using gormcngen. DO NOT EDIT.
// This file was auto generated via github.com/yyle88/gormcngen
// Generated from: ngen_test.go:20 -> models.TestGenerate
// ========== GORMCNGEN:DO-NOT-EDIT-MARKER:END ==========

func (c *Account) Columns() *AccountColumns {
    return &AccountColumns{
        ID:       gormcnm.Cnm(c.ID, "id"),
        Username: gormcnm.Cnm(c.Username, "username"),
        Mailbox:  gormcnm.Cnm(c.Mailbox, "mailbox"),
        Age:      gormcnm.Cnm(c.Age, "age"),
        IsActive: gormcnm.Cnm(c.IsActive, "is_active"),
    }
}

type AccountColumns struct {
    gormcnm.ColumnOperationClass
    ID       gormcnm.ColumnName[uint]
    Username gormcnm.ColumnName[string]
    Mailbox  gormcnm.ColumnName[string]
    Age      gormcnm.ColumnName[int]
    IsActive gormcnm.ColumnName[bool]
}

🚀 Setup Complete! You now have type-safe column access methods to work with models.

6. Use in Business Logic

Now when writing business code, you can use the generated type-safe column methods to build database queries:

var account Account
cls := account.Columns()

// Perfect type protection with zero boilerplate
err := db.Where(cls.Username.Eq("alice")).
         Where(cls.Age.Gte(18)).
         Where(cls.IsActive.Eq(true)).
         First(&account).Error

Advanced Usage

// Basic configuration (matches internal examples)
options := gormcngen.NewOptions().
    WithColumnClassExportable(true).           // Generate exported ExampleColumns struct
    WithEmbedColumnOperations(false)           // Don't embed operation methods

// Chinese field name support
chineseOptions := gormcngen.NewOptions().
    WithUseTagName(true).                      // Use cnm tag values as field names  
    WithTagKeyName("cnm").                     // Set 'cnm' as the tag name
    WithColumnClassExportable(true)

// Advanced features (from example6)
advancedOptions := gormcngen.NewOptions().
    WithColumnClassExportable(true).           // Exported struct names
    WithColumnsMethodRecvName("one").          // Custom method argument
    WithColumnsCheckFieldType(true).           // Type checking (recommended)
    WithIsGenFuncTableColumns(true)            // Generate TableColumns function

// Batch processing multiple models
allModels := []interface{}{&Account{}, &Product{}, &Item{}, &Client{}}
configs := gormcngen.NewConfigs(allModels, options, "models_gen.go")
configs.WithIsGenPreventEdit(true)  // Add "DO NOT EDIT" headers to generated files
configs.WithGeneratedFromPos(gormcngen.GetGenPosFuncMark(0))  // Show generation source location (default: show)
configs.Gen()

Advanced Features

Multi-Language Field Support

The cnm tag lets you define Chinese aliases to use as field names, which are generated as extra struct fields:

type Product struct {
    ID          uint          `gorm:"primaryKey"`
    Name        string        `gorm:"size:255;not null" cnm:"V产品名称"`
    Price       decimal.Decimal `gorm:"type:decimal(10,2)"`
    CategoryID  uint          `gorm:"index"`
    CreatedAt   time.Time     `gorm:"autoCreateTime"`
    UpdatedAt   time.Time     `gorm:"autoUpdateTime"`
}

Generated Result:

type ProductColumns struct {
    gormcnm.ColumnOperationClass
    // The column names and types of the model's columns
    ID       gormcnm.ColumnName[uint]
    Name     gormcnm.ColumnName[string]           // Maps to "name"
    V产品名称   gormcnm.ColumnName[string]           // Chinese alias mapping to Name field
    Price    gormcnm.ColumnName[decimal.Decimal]
    CategoryID gormcnm.ColumnName[uint]
    CreatedAt gormcnm.ColumnName[time.Time]
    UpdatedAt gormcnm.ColumnName[time.Time]
}

func (*Product) Columns() *ProductColumns {
    return &ProductColumns{
        ID:       "id",
        Name:     "name",
        V产品名称:   "name",      // Chinese alias pointing to same column
        Price:    "price",
        CategoryID: "category_id",
        CreatedAt: "created_at",
        UpdatedAt: "updated_at",
    }
}

Using Chinese Field Names in Queries:

With the generated Chinese aliases, you can write queries using native language:

var product Product
var cls = product.Columns()

// Query using Chinese field names - same database column, different Go field name
if err := db.Where(cls.V产品名称.Eq("iPhone")).
    Where(cls.Price.Gte(5000.00)).
    First(&product).Error; err != nil {
    panic(errors.WithMessage(err, "product not found"))
}

fmt.Println("Found product:", product.Name)

This allows developers to write more readable code in native language while maintaining complete type protection and database support.

Go Generate Integration

Create a generation script:

scripts/generate_columns.go:

package main

import (
    "github.com/yyle88/gormcngen"
    "project-name/models"
)

func main() {
    models := []interface{}{&models.Account{}}
    options := gormcngen.NewOptions()
    configs := gormcngen.NewConfigs(models, options, "models/account_columns_gen.go")
    configs.Gen()
}

Then use in the target files:

//go:generate go run scripts/generate_columns.go

type Account struct {
    ID       uint   `gorm:"primaryKey"`
    Username string `gorm:"uniqueIndex"`
    Mailbox  string `gorm:"index"`
}

🔗 Using with gormrepo

Combine gormcngen with gormrepo to get type-safe CRUD operations.

Quick Preview

// Create repo with columns
repo := gormrepo.NewRepo(&Account{}, (&Account{}).Columns())

// Concise approach with gormrepo/gormclass
repo := gormrepo.NewRepo(gormclass.Use(&Account{}))

// Type-safe queries
account, err := repo.With(ctx, db).First(func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
    return db.Where(cls.Username.Eq("alice"))
})

// Find with conditions
accounts, err := repo.With(ctx, db).Find(func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
    return db.Where(cls.Age.Gte(18)).Where(cls.Age.Lte(65))
})

// Type-safe updates
err := repo.With(ctx, db).Updates(
    func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
        return db.Where(cls.ID.Eq(1))
    },
    func(cls *AccountColumns) map[string]interface{} {
        return cls.Kw(cls.Age.Kv(26)).Kw(cls.Nickname.Kv("NewNick")).AsMap()
    },
)

👉 See gormrepo to get complete documentation and more examples.


Examples

See examples and demos directories.

Related Projects

Explore the complete GORM ecosystem with these integrated packages:

Core Ecosystem

  • gormcnm - GORM foundation providing type-safe column operations and query builders
  • gormcngen - Code generation using AST enabling type-safe GORM operations (this project)
  • gormrepo - Repository pattern implementation with GORM best practices
  • gormmom - Native language GORM tag generation engine with smart column naming
  • gormzhcn - Complete Chinese programming interface with GORM

Each package targets different aspects of GORM development, from localization to type safety and code generation.


📄 License

MIT License - see LICENSE.


💬 Contact & Feedback

Contributions are welcome! Report bugs, suggest features, and contribute code:

  • 🐛 Mistake reports? Open an issue on GitHub with reproduction steps
  • 💡 Fresh ideas? Create an issue to discuss
  • 📖 Documentation confusing? Report it so we can improve
  • 🚀 Need new features? Share the use cases to help us understand requirements
  • Performance issue? Help us optimize through reporting slow operations
  • 🔧 Configuration problem? Ask questions about complex setups
  • 📢 Follow project progress? Watch the repo to get new releases and features
  • 🌟 Success stories? Share how this package improved the workflow
  • 💬 Feedback? We welcome suggestions and comments

🔧 Development

New code contributions, follow this process:

  1. Fork: Fork the repo on GitHub (using the webpage UI).
  2. Clone: Clone the forked project (git clone https://github.com/yourname/repo-name.git).
  3. Navigate: Navigate to the cloned project (cd repo-name)
  4. Branch: Create a feature branch (git checkout -b feature/xxx).
  5. Code: Implement the changes with comprehensive tests
  6. Testing: (Golang project) Ensure tests pass (go test ./...) and follow Go code style conventions
  7. Documentation: Update documentation to support client-facing changes
  8. Stage: Stage changes (git add .)
  9. Commit: Commit changes (git commit -m "Add feature xxx") ensuring backward compatible code
  10. Push: Push to the branch (git push origin feature/xxx).
  11. PR: Open a merge request on GitHub (on the GitHub webpage) with detailed description.

Please ensure tests pass and include relevant documentation updates.


🌟 Support

Welcome to contribute to this project via submitting merge requests and reporting issues.

Project Support:

  • Give GitHub stars if this project helps you
  • 🤝 Share with teammates and (golang) programming friends
  • 📝 Write tech blogs about development tools and workflows - we provide content writing support
  • 🌟 Join the ecosystem - committed to supporting open source and the (golang) development scene

Have Fun Coding with this package! 🎉🎉🎉


📈 GitHub Stars

starring

About

`gormcngen`: Provides a Columns() Function to Retrieve Column Names for GORM Models

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •