Skip to content

Commit

Permalink
feat: 表关联
Browse files Browse the repository at this point in the history
  • Loading branch information
zodial committed Oct 26, 2022
1 parent 16e81e1 commit 94fde7f
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 48 deletions.
24 changes: 24 additions & 0 deletions config/database/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
* 该目录存放表关联配置文件,
* 创建以database.yaml的connection名对应的```connection名.json```文件
* json结构:

```json
{
"表名": [
...Relationship结构
]
}
```
* Relationship Go的结构说明:
```go
type Relationship struct {
Type string `json:"type"` //关联类型:belongs_to、has_one、has_many、many2many
Table string `json:"table"` //关联表名
Alias string `json:"alias"` //别名(可不声明,默认用表名)
ForeignKey string `json:"foreign_key"` //外键(可不声明,默认为'id'或'表名_id')
ReferenceKey string `json:"reference_key"` //引用键(可不声明,默认为'id'或'表名_id')
RelationshipTable string `json:"relationship_table"` //当many2many时,连接表名
JoinForeignKey string `json:"join_foreign_key"` //当many2many时,本表在连接表的外键
JoinTargetKey string `json:"join_target_key"` //当many2many时,关联表在连接表的外键
}
```
85 changes: 63 additions & 22 deletions console/commands/orm/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package orm
import (
"database/sql"
_ "embed"
"encoding/json"
"fmt"
"github.com/go-home-admin/home/bootstrap/services"
"github.com/go-home-admin/toolset/parser"
Expand All @@ -22,6 +23,17 @@ func IsExist(f string) bool {

type Conf map[interface{}]interface{}

type Relationship struct {
Type string `json:"type"` //关联类型:belongs_to、has_one、has_many、many2many
Table string `json:"table"` //关联表名
Alias string `json:"alias"` //别名(可不声明,默认用表名)
ForeignKey string `json:"foreign_key"` //外键(可不声明,默认为'id'或'表名_id')
ReferenceKey string `json:"reference_key"` //引用键(可不声明,默认为'id'或'表名_id')
RelationshipTable string `json:"relationship_table"` //当many2many时,连接表名
JoinForeignKey string `json:"join_foreign_key"` //当many2many时,本表在连接表的外键
JoinTargetKey string `json:"join_target_key"` //当many2many时,关联表在连接表的外键
}

func GenMysql(name string, conf Conf, out string) {
if !IsExist(out) {
os.MkdirAll(out, 0766)
Expand All @@ -30,6 +42,16 @@ func GenMysql(name string, conf Conf, out string) {
db := NewDb(conf)
tableColumns := db.tableColumns()

root, _ := os.Getwd()
file, err := os.ReadFile(root + "/config/database/" + name + ".json")
var relationship map[string][]*Relationship
if err == nil {
err = json.Unmarshal(file, &relationship)
if err != nil {
panic("表关系JSON文件配置出错:" + err.Error())
}
}

// 计算import
imports := getImports(tableColumns)
for table, columns := range tableColumns {
Expand All @@ -38,7 +60,7 @@ func GenMysql(name string, conf Conf, out string) {

str := "package " + name
str += "\nimport (" + imports[table] + "\n)"
str += "\n" + genOrmStruct(table, columns, conf)
str += "\n" + genOrmStruct(table, columns, conf, relationship[table])

baseFunStr := baseMysqlFuncStr
for old, newStr := range map[string]string{
Expand Down Expand Up @@ -222,10 +244,12 @@ func getImports(tableColumns map[string][]tableColumn) map[string]string {
for table, columns := range tableColumns {
// 初始引入
tm := map[string]string{
"strings": "",
"gorm.io/gorm": "gorm",
"github.com/go-home-admin/home/bootstrap/providers": "providers",
"github.com/sirupsen/logrus": "logrus",
"database/sql": "sql",
"github.com/go-home-admin/home/app": "home",
}
for _, column := range columns {
index := strings.Index(column.GoType, ".")
Expand All @@ -241,7 +265,7 @@ func getImports(tableColumns map[string][]tableColumn) map[string]string {
return got
}

func genOrmStruct(table string, columns []tableColumn, conf Conf) string {
func genOrmStruct(table string, columns []tableColumn, conf Conf, relationships []*Relationship) string {
TableName := parser.StringToHump(table)

hasField := make(map[string]bool)
Expand All @@ -255,27 +279,44 @@ func genOrmStruct(table string, columns []tableColumn, conf Conf) string {
fieldName := parser.StringToHump(column.COLUMN_NAME)
str += fmt.Sprintf("\n\t%v %v%v`%v` // %v", fieldName, p, column.GoType, genGormTag(column), strings.ReplaceAll(column.COLUMN_COMMENT, "\n", " "))
}
// 表依赖
if helper, ok := conf["helper"]; ok {
helperConf := helper.(map[interface{}]interface{})
tableConfig, ok := helperConf[table].([]interface{})
if ok {
for _, c := range tableConfig {
cf := c.(map[interface{}]interface{})
with := cf["with"]
tbName := parser.StringToHump(cf["table"].(string))
switch with {
case "belongs_to":
str += fmt.Sprintf("\n\t%v *%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "has_one":
str += fmt.Sprintf("\n\t%v *%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "has_many":
str += fmt.Sprintf("\n\t%v []%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "many2many":
str += fmt.Sprintf("\n\t%v []%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
default:
panic("with: belongs_to,has_one,has_many,many2many")
// 表关系
if len(relationships) > 0 {
for _, relationship := range relationships {
switch relationship.Type {
case "belongs_to", "has_one", "has_many", "many2many":
default:
panic("with: belongs_to,has_one,has_many,many2many")
}
tbName := "*" + parser.StringToHump(relationship.Table)
if relationship.Type == "has_many" || relationship.Type == "many2many" {
tbName = "[]" + tbName
}
fieldName := parser.StringToHump(relationship.Table)
if relationship.Alias != "" {
fieldName = parser.StringToHump(relationship.Alias)
}
str += fmt.Sprintf("\n\t%v %v", fieldName, tbName)
if relationship.ForeignKey != "" || relationship.ReferenceKey != "" || relationship.Type == "many2many" {
str += " `gorm:\""
if relationship.Type == "many2many" {
if relationship.RelationshipTable == "" {
panic("表" + relationship.Table + "的many2many必须声明连接表")
}
str += "many2many:" + relationship.RelationshipTable + ";"
if relationship.JoinForeignKey != "" {
str += "joinForeignKey:" + relationship.JoinForeignKey + ";"
}
if relationship.JoinTargetKey != "" {
str += "joinReferences:" + relationship.JoinTargetKey + ";"
}
}
if relationship.ForeignKey != "" {
str += "foreignKey:" + relationship.ForeignKey + ";"
}
if relationship.ReferenceKey != "" {
str += "references:" + relationship.ReferenceKey + ";"
}
str += "\"`"
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions console/commands/orm/mysql.go.text
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,22 @@ func (orm *OrmMysqlTableName) Or(fuc func(orm *OrmMysqlTableName)) *OrmMysqlTabl
orm.db.Or(ormOr.db)
return orm
}

// Preload preload associations with given conditions
// db.Preload("Orders|orders", "state NOT IN (?)", "cancelled").Find(&users)
func (orm *OrmMysqlTableName) Preload(query string, args ...interface{}) *OrmMysqlTableName {
orm.db.Preload(home.StringToHump(query), args...)
return orm
}

// Joins specify Joins conditions
// db.Joins("Account|account").Find(&user)
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
// db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
func (orm *OrmMysqlTableName) Joins(query string, args ...interface{}) *OrmMysqlTableName {
if !strings.Contains(query, " ") {
query = home.StringToHump(query)
}
orm.db.Joins(query, args...)
return orm
}
75 changes: 53 additions & 22 deletions console/commands/pgorm/pgsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package pgorm
import (
"database/sql"
_ "embed"
"encoding/json"
"fmt"
"github.com/go-home-admin/home/bootstrap/services"
"github.com/go-home-admin/toolset/console/commands/orm"
"github.com/go-home-admin/toolset/parser"
_ "github.com/lib/pq"
"log"
Expand All @@ -30,6 +32,16 @@ func GenSql(name string, conf Conf, out string) {
db := NewDb(conf)
tableColumns := db.tableColumns()

root, _ := os.Getwd()
file, err := os.ReadFile(root + "/config/database/" + name + ".json")
var relationship map[string][]*orm.Relationship
if err == nil {
err = json.Unmarshal(file, &relationship)
if err != nil {
panic("表关系JSON文件配置出错:" + err.Error())
}
}

// 计算import
imports := getImports(tableColumns)
for table, columns := range tableColumns {
Expand All @@ -38,7 +50,7 @@ func GenSql(name string, conf Conf, out string) {

str := "package " + name
str += "\nimport (" + imports[table] + "\n)"
str += "\n" + genOrmStruct(table, columns, conf)
str += "\n" + genOrmStruct(table, columns, conf, relationship[table])

baseFunStr := baseMysqlFuncStr
for old, newStr := range map[string]string{
Expand Down Expand Up @@ -223,10 +235,12 @@ func getImports(tableColumns map[string][]tableColumn) map[string]string {
for table, columns := range tableColumns {
// 初始引入
tm := map[string]string{
"strings": "",
"gorm.io/gorm": "gorm",
"github.com/go-home-admin/home/bootstrap/providers": "providers",
"github.com/sirupsen/logrus": "logrus",
"database/sql": "sql",
"github.com/go-home-admin/home/app": "home",
}
for _, column := range columns {
index := strings.Index(column.GoType, ".")
Expand All @@ -242,7 +256,7 @@ func getImports(tableColumns map[string][]tableColumn) map[string]string {
return got
}

func genOrmStruct(table string, columns []tableColumn, conf Conf) string {
func genOrmStruct(table string, columns []tableColumn, conf Conf, relationships []*orm.Relationship) string {
TableName := parser.StringToHump(table)

hasField := make(map[string]bool)
Expand All @@ -256,27 +270,44 @@ func genOrmStruct(table string, columns []tableColumn, conf Conf) string {
fieldName := parser.StringToHump(column.ColumnName)
str += fmt.Sprintf("\n\t%v %v%v`%v` // %v", fieldName, p, column.GoType, genGormTag(column), strings.ReplaceAll(column.Comment, "\n", " "))
}
// 表依赖
if helper, ok := conf["helper"]; ok {
helperConf := helper.(map[interface{}]interface{})
tableConfig, ok := helperConf[table].([]interface{})
if ok {
for _, c := range tableConfig {
cf := c.(map[interface{}]interface{})
with := cf["with"]
tbName := parser.StringToHump(cf["table"].(string))
switch with {
case "belongs_to":
str += fmt.Sprintf("\n\t%v *%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "has_one":
str += fmt.Sprintf("\n\t%v *%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "has_many":
str += fmt.Sprintf("\n\t%v []%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
case "many2many":
str += fmt.Sprintf("\n\t%v []%v `gorm:\"%v\"`", parser.StringToHump(cf["alias"].(string)), tbName, cf["gorm"])
default:
panic("with: belongs_to,has_one,has_many,many2many")
// 表关系
if len(relationships) > 0 {
for _, relationship := range relationships {
switch relationship.Type {
case "belongs_to", "has_one", "has_many", "many2many":
default:
panic("with: belongs_to,has_one,has_many,many2many")
}
tbName := "*" + parser.StringToHump(relationship.Table)
if relationship.Type == "has_many" || relationship.Type == "many2many" {
tbName = "[]" + tbName
}
fieldName := parser.StringToHump(relationship.Table)
if relationship.Alias != "" {
fieldName = parser.StringToHump(relationship.Alias)
}
str += fmt.Sprintf("\n\t%v %v", fieldName, tbName)
if relationship.ForeignKey != "" || relationship.ReferenceKey != "" || relationship.Type == "many2many" {
str += " `gorm:\""
if relationship.Type == "many2many" {
if relationship.RelationshipTable == "" {
panic("表" + relationship.Table + "的many2many必须声明连接表")
}
str += "many2many:" + relationship.RelationshipTable + ";"
if relationship.JoinForeignKey != "" {
str += "joinForeignKey:" + relationship.JoinForeignKey + ";"
}
if relationship.JoinTargetKey != "" {
str += "joinReferences:" + relationship.JoinTargetKey + ";"
}
}
if relationship.ForeignKey != "" {
str += "foreignKey:" + relationship.ForeignKey + ";"
}
if relationship.ReferenceKey != "" {
str += "references:" + relationship.ReferenceKey + ";"
}
str += "\"`"
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions console/commands/pgorm/pgsql.go.text
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,22 @@ func (orm *Orm{orm_table_name}) Or(fuc func(orm *Orm{orm_table_name})) *Orm{orm_
orm.db.Or(ormOr.db)
return orm
}

// Preload preload associations with given conditions
// db.Preload("Orders|orders", "state NOT IN (?)", "cancelled").Find(&users)
func (orm *OrmMysqlTableName) Preload(query string, args ...interface{}) *OrmMysqlTableName {
orm.db.Preload(home.StringToHump(query), args...)
return orm
}

// Joins specify Joins conditions
// db.Joins("Account|account").Find(&user)
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
// db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
func (orm *OrmMysqlTableName) Joins(query string, args ...interface{}) *OrmMysqlTableName {
if !strings.Contains(query, " ") {
query = home.StringToHump(query)
}
orm.db.Joins(query, args...)
return orm
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ go 1.16

require (
github.com/ctfang/command v1.0.1
github.com/go-home-admin/home v0.3.6
github.com/go-home-admin/home v0.4.14
github.com/go-sql-driver/mysql v1.6.0
github.com/joho/godotenv v1.4.0
github.com/lib/pq v1.10.6
gopkg.in/yaml.v2 v2.4.0
gorm.io/gorm v1.23.5 // indirect
gorm.io/gorm v1.23.5
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/gin-gonic/gin v1.8.0 h1:4WFH5yycBMA3za5Hnl425yd9ymdw1XPm4666oab+hv4=
github.com/gin-gonic/gin v1.8.0/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-home-admin/home v0.3.6 h1:tk56T1DgdqTWcuOdT07N/SLojrAW5BhqSC02kdlNiVI=
github.com/go-home-admin/home v0.3.6/go.mod h1:51swp6MVnaZVT1ZfZlSWMGGTT7zuqI7NMd0mX6EEenw=
github.com/go-home-admin/home v0.4.14 h1:Ta2D4Wys1pSH78lYNJP+PF8XwOkLy+IH75KkaWGDJ0I=
github.com/go-home-admin/home v0.4.14/go.mod h1:51swp6MVnaZVT1ZfZlSWMGGTT7zuqI7NMd0mX6EEenw=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
Expand Down

0 comments on commit 94fde7f

Please sign in to comment.