hasone hasmany 待完善

This commit is contained in:
xing 2023-05-17 22:22:31 +08:00
parent a6ee333232
commit 044e55a399
5 changed files with 184 additions and 60 deletions

View File

@ -1,16 +1,17 @@
package model
type QueryCondition struct {
Where ParseWhere
From string
Fields string
Group string
Order SqlBuilder
Join SqlBuilder
Having SqlBuilder
Limit int
Offset int
In [][]any
Where ParseWhere
From string
Fields string
Group string
Order SqlBuilder
Join SqlBuilder
Having SqlBuilder
Limit int
Offset int
In [][]any
Relation map[string]*QueryCondition
}
func Conditions(fns ...Condition) QueryCondition {
@ -23,6 +24,16 @@ func Conditions(fns ...Condition) QueryCondition {
}
return r
}
func WithConditions(fns ...Condition) *QueryCondition {
r := QueryCondition{}
for _, fn := range fns {
fn(&r)
}
if r.Fields == "" {
r.Fields = "*"
}
return &r
}
type Condition func(c *QueryCondition)
@ -84,3 +95,12 @@ func In(in ...[]any) Condition {
c.In = append(c.In, in...)
}
}
func With(tableTag string, q *QueryCondition) Condition {
return func(c *QueryCondition) {
if c.Relation == nil {
c.Relation = map[string]*QueryCondition{}
}
c.Relation[tableTag] = q
}
}

View File

@ -39,7 +39,10 @@ func pagination[T Model](db dbQuery, ctx context.Context, q QueryCondition, page
}
if q.Group != "" {
qx.Fields = q.Fields
sq, in, er := BuildQuerySql[T](qx)
if qx.From == "" {
qx.From = Table[T]()
}
sq, in, er := BuildQuerySql(qx)
qx.In = [][]any{in}
if er != nil {
err = er
@ -91,24 +94,18 @@ func FindOneById[T Model, I constraints.Integer](ctx context.Context, id I) (T,
return gets[T](globalBb, ctx, QueryCondition{
Fields: "*",
Where: SqlBuilder{
{PrimaryKey[T](), "=", number.ToString(id), "int"},
{PrimaryKey[T](), "=", number.IntToString(id), "int"},
},
})
}
func FirstOne[T Model](ctx context.Context, where ParseWhere, fields string, order SqlBuilder, in ...[]any) (r T, err error) {
s, args, err := BuildQuerySql[T](QueryCondition{
Where: where,
Fields: fields,
Order: order,
In: in,
Limit: 1,
})
if err != nil {
return
}
err = globalBb.Get(ctx, &r, s, args...)
return
func FirstOne[T Model](ctx context.Context, where ParseWhere, fields string, order SqlBuilder, in ...[]any) (T, error) {
return gets[T](globalBb, ctx, Conditions(
Where(where),
Fields(fields),
Order(order),
In(in...),
))
}
func LastOne[T Model](ctx context.Context, where ParseWhere, fields string, in ...[]any) (T, error) {
@ -122,10 +119,11 @@ func LastOne[T Model](ctx context.Context, where ParseWhere, fields string, in .
}
func SimpleFind[T Model](ctx context.Context, where ParseWhere, fields string, in ...[]any) (r []T, err error) {
s, args, err := BuildQuerySql[T](QueryCondition{
s, args, err := BuildQuerySql(QueryCondition{
Where: where,
Fields: fields,
In: in,
From: Table[T](),
})
if err != nil {
return
@ -155,8 +153,9 @@ func Find[T Model](ctx context.Context, where ParseWhere, fields, group string,
Having: having,
Limit: limit,
In: in,
From: Table[T](),
}
s, args, err := BuildQuerySql[T](q)
s, args, err := BuildQuerySql(q)
if err != nil {
return
}

View File

@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/safety"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
@ -13,29 +14,31 @@ import (
)
type post struct {
Id uint64 `gorm:"column:ID" db:"ID" json:"ID" form:"ID"`
PostAuthor uint64 `gorm:"column:post_author" db:"post_author" json:"post_author" form:"post_author"`
PostDate time.Time `gorm:"column:post_date" db:"post_date" json:"post_date" form:"post_date"`
PostDateGmt time.Time `gorm:"column:post_date_gmt" db:"post_date_gmt" json:"post_date_gmt" form:"post_date_gmt"`
PostContent string `gorm:"column:post_content" db:"post_content" json:"post_content" form:"post_content"`
PostTitle string `gorm:"column:post_title" db:"post_title" json:"post_title" form:"post_title"`
PostExcerpt string `gorm:"column:post_excerpt" db:"post_excerpt" json:"post_excerpt" form:"post_excerpt"`
PostStatus string `gorm:"column:post_status" db:"post_status" json:"post_status" form:"post_status"`
CommentStatus string `gorm:"column:comment_status" db:"comment_status" json:"comment_status" form:"comment_status"`
PingStatus string `gorm:"column:ping_status" db:"ping_status" json:"ping_status" form:"ping_status"`
PostPassword string `gorm:"column:post_password" db:"post_password" json:"post_password" form:"post_password"`
PostName string `gorm:"column:post_name" db:"post_name" json:"post_name" form:"post_name"`
ToPing string `gorm:"column:to_ping" db:"to_ping" json:"to_ping" form:"to_ping"`
Pinged string `gorm:"column:pinged" db:"pinged" json:"pinged" form:"pinged"`
PostModified time.Time `gorm:"column:post_modified" db:"post_modified" json:"post_modified" form:"post_modified"`
PostModifiedGmt time.Time `gorm:"column:post_modified_gmt" db:"post_modified_gmt" json:"post_modified_gmt" form:"post_modified_gmt"`
PostContentFiltered string `gorm:"column:post_content_filtered" db:"post_content_filtered" json:"post_content_filtered" form:"post_content_filtered"`
PostParent uint64 `gorm:"column:post_parent" db:"post_parent" json:"post_parent" form:"post_parent"`
Guid string `gorm:"column:guid" db:"guid" json:"guid" form:"guid"`
MenuOrder int `gorm:"column:menu_order" db:"menu_order" json:"menu_order" form:"menu_order"`
PostType string `gorm:"column:post_type" db:"post_type" json:"post_type" form:"post_type"`
PostMimeType string `gorm:"column:post_mime_type" db:"post_mime_type" json:"post_mime_type" form:"post_mime_type"`
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
Id uint64 `gorm:"column:ID" db:"ID" json:"ID" form:"ID"`
PostAuthor uint64 `gorm:"column:post_author" db:"post_author" json:"post_author" form:"post_author"`
PostDate time.Time `gorm:"column:post_date" db:"post_date" json:"post_date" form:"post_date"`
PostDateGmt time.Time `gorm:"column:post_date_gmt" db:"post_date_gmt" json:"post_date_gmt" form:"post_date_gmt"`
PostContent string `gorm:"column:post_content" db:"post_content" json:"post_content" form:"post_content"`
PostTitle string `gorm:"column:post_title" db:"post_title" json:"post_title" form:"post_title"`
PostExcerpt string `gorm:"column:post_excerpt" db:"post_excerpt" json:"post_excerpt" form:"post_excerpt"`
PostStatus string `gorm:"column:post_status" db:"post_status" json:"post_status" form:"post_status"`
CommentStatus string `gorm:"column:comment_status" db:"comment_status" json:"comment_status" form:"comment_status"`
PingStatus string `gorm:"column:ping_status" db:"ping_status" json:"ping_status" form:"ping_status"`
PostPassword string `gorm:"column:post_password" db:"post_password" json:"post_password" form:"post_password"`
PostName string `gorm:"column:post_name" db:"post_name" json:"post_name" form:"post_name"`
ToPing string `gorm:"column:to_ping" db:"to_ping" json:"to_ping" form:"to_ping"`
Pinged string `gorm:"column:pinged" db:"pinged" json:"pinged" form:"pinged"`
PostModified time.Time `gorm:"column:post_modified" db:"post_modified" json:"post_modified" form:"post_modified"`
PostModifiedGmt time.Time `gorm:"column:post_modified_gmt" db:"post_modified_gmt" json:"post_modified_gmt" form:"post_modified_gmt"`
PostContentFiltered string `gorm:"column:post_content_filtered" db:"post_content_filtered" json:"post_content_filtered" form:"post_content_filtered"`
PostParent uint64 `gorm:"column:post_parent" db:"post_parent" json:"post_parent" form:"post_parent"`
Guid string `gorm:"column:guid" db:"guid" json:"guid" form:"guid"`
MenuOrder int `gorm:"column:menu_order" db:"menu_order" json:"menu_order" form:"menu_order"`
PostType string `gorm:"column:post_type" db:"post_type" json:"post_type" form:"post_type"`
PostMimeType string `gorm:"column:post_mime_type" db:"post_mime_type" json:"post_mime_type" form:"post_mime_type"`
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
User *user `table:"wp_users user" foreignKey:"ID" local:"post_author" relation:"hasOne"`
PostMeta *[]models.PostMeta `table:"wp_postmeta meta" foreignKey:"post_id" local:"ID" relation:"hasMany"`
}
type user struct {
@ -362,6 +365,18 @@ func TestFirstOne(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := FirstOne[post](ctx, tt.args.where, tt.args.fields, tt.args.order, tt.args.in...)
gott, err := Gets[post](ctx, Conditions(
Where(SqlBuilder{{"post_status", "publish"}}),
Order([][]string{{"ID", "desc"}}),
With("user", WithConditions(
Fields("ID,user_login,user_pass"),
Where(SqlBuilder{
{"user.ID", ">", "0", "int"},
}),
)),
With("meta", nil),
))
_ = gott
if (err != nil) != tt.wantErr {
t.Errorf("FirstOne() error = %v, wantErr %v", err, tt.wantErr)
return

View File

@ -26,11 +26,13 @@ func FindFromDB[T Model](db dbQuery, ctx context.Context, q QueryCondition) (r [
}
func finds[T Model](db dbQuery, ctx context.Context, q QueryCondition) (r []T, err error) {
sq, args, err := BuildQuerySql[T](q)
setTable[T](&q)
sq, args, err := BuildQuerySql(q)
if err != nil {
return
}
err = db.Select(ctx, &r, sq, args...)
return
}
@ -179,7 +181,8 @@ func GetFieldFromDB[T Model](db dbQuery, ctx context.Context, field string, q Qu
}
func getToStringMap[T Model](db dbQuery, ctx context.Context, q QueryCondition) (r map[string]string, err error) {
rawSql, in, err := BuildQuerySql[T](q)
setTable[T](&q)
rawSql, in, err := BuildQuerySql(q)
if err != nil {
return nil, err
}
@ -193,7 +196,8 @@ func GetToStringMap[T Model](ctx context.Context, q QueryCondition) (r map[strin
}
func findToStringMap[T Model](db dbQuery, ctx context.Context, q QueryCondition) (r []map[string]string, err error) {
rawSql, in, err := BuildQuerySql[T](q)
setTable[T](&q)
rawSql, in, err := BuildQuerySql(q)
if err != nil {
return nil, err
}
@ -217,7 +221,7 @@ func GetToStringMapFromDB[T Model](db dbQuery, ctx context.Context, q QueryCondi
return
}
func BuildQuerySql[T Model](q QueryCondition) (r string, args []any, err error) {
func BuildQuerySql(q QueryCondition) (r string, args []any, err error) {
w := ""
if q.Where != nil {
w, args, err = q.Where.ParseWhere(&q.In)
@ -251,10 +255,7 @@ func BuildQuerySql[T Model](q QueryCondition) (r string, args []any, err error)
}
tp := "select %s from %s %s %s %s %s %s %s"
l := ""
table := Table[T]()
if q.From != "" {
table = q.From
}
table := q.From
if q.Limit > 0 {
l = fmt.Sprintf(" limit %d", q.Limit)
}
@ -266,7 +267,8 @@ func BuildQuerySql[T Model](q QueryCondition) (r string, args []any, err error)
}
func findScanner[T Model](db dbQuery, ctx context.Context, fn func(T), q QueryCondition) (err error) {
s, args, err := BuildQuerySql[T](q)
setTable[T](&q)
s, args, err := BuildQuerySql(q)
if err != nil {
return
}
@ -295,10 +297,17 @@ func GetsFromDB[T Model](db dbQuery, ctx context.Context, q QueryCondition) (T,
}
func gets[T Model](db dbQuery, ctx context.Context, q QueryCondition) (r T, err error) {
s, args, err := BuildQuerySql[T](q)
setTable[T](&q)
s, args, err := BuildQuerySql(q)
if err != nil {
return
}
err = db.Get(ctx, &r, s, args...)
if err != nil {
return
}
if len(q.Relation) > 0 {
err = Relation[T](db, ctx, &r, &q)
}
return
}

81
model/relation.go Normal file
View File

@ -0,0 +1,81 @@
package model
import (
"context"
"fmt"
"reflect"
"strings"
)
func setTable[T Model](q *QueryCondition) {
if q.From == "" {
q.From = Table[T]()
}
}
func Relation[T Model](db dbQuery, ctx context.Context, r *T, q *QueryCondition) (err error) {
var rr T
t := reflect.TypeOf(rr)
v := reflect.ValueOf(r).Elem()
for tableTag, relation := range q.Relation {
if tableTag == "" {
continue
}
for i := 0; i < t.NumField(); i++ {
tag := t.Field(i).Tag
table, ok := tag.Lookup("table")
if !ok || table == "" {
continue
}
tables := strings.Split(table, " ")
if tables[len(tables)-1] != tableTag {
continue
}
if relation == nil {
relation = &QueryCondition{
Fields: "*",
}
}
relation.From = table
id := ""
j := 0
for ; j < t.NumField(); j++ {
vvv, ok := t.Field(j).Tag.Lookup("db")
if ok && vvv == tag.Get("local") {
id = fmt.Sprintf("%v", v.Field(j).Interface())
break
}
}
{
var w any = relation.Where
if w == nil {
w = SqlBuilder{}
}
ww, ok := w.(SqlBuilder)
if ok {
ww = append(ww, SqlBuilder{{
tag.Get("foreignKey"), "=", id, "int",
}}...)
relation.Where = ww
}
}
sq, args, er := BuildQuerySql(*relation)
if er != nil {
err = er
return
}
vv := reflect.New(v.Field(i).Type().Elem()).Interface()
switch tag.Get("relation") {
case "hasOne":
err = db.Get(ctx, vv, sq, args...)
case "hasMany":
err = db.Select(ctx, vv, sq, args...)
}
if err != nil {
return
}
v.Field(i).Set(reflect.ValueOf(vv))
}
}
return
}