has one/many complete

This commit is contained in:
xing 2023-05-21 01:38:19 +08:00
parent 4242d850ff
commit c95fd7e5da
5 changed files with 218 additions and 101 deletions

View File

@ -13,9 +13,11 @@ type QueryCondition struct {
Limit int Limit int
Offset int Offset int
In [][]any In [][]any
RelationFn []func() (bool, bool, *QueryCondition, func() (func(any) []any, func(any, any) error, any, Relationship)) RelationFn []func() (bool, bool, *QueryCondition, RelationFn)
} }
type RelationFn func() (func(any) []any, func(any, any), any, any, Relationship)
func Conditions(fns ...Condition) *QueryCondition { func Conditions(fns ...Condition) *QueryCondition {
r := &QueryCondition{} r := &QueryCondition{}
for _, fn := range fns { for _, fn := range fns {
@ -94,9 +96,9 @@ func WithCtx(ctx *context.Context) Condition {
} }
} }
func WithFn(getVal, isJoin bool, q *QueryCondition, fn func() (func(any) []any, func(any, any) error, any, Relationship)) Condition { func WithFn(getVal, isJoin bool, q *QueryCondition, fn func() (func(any) []any, func(any, any), any, any, Relationship)) Condition {
return func(c *QueryCondition) { return func(c *QueryCondition) {
c.RelationFn = append(c.RelationFn, func() (bool, bool, *QueryCondition, func() (func(any) []any, func(any, any) error, any, Relationship)) { c.RelationFn = append(c.RelationFn, func() (bool, bool, *QueryCondition, RelationFn) {
return getVal, isJoin, q, fn return getVal, isJoin, q, fn
}) })
} }

View File

@ -3,7 +3,6 @@ package model
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
@ -42,49 +41,6 @@ type post struct {
PostMeta *[]models.PostMeta PostMeta *[]models.PostMeta
} }
func PostAuthor() (func(any) []any, func(posts, users any) error, any, Relationship) {
var u user
return func(a any) []any {
return []any{a.(*post).PostAuthor}
}, func(posts, users any) error {
u := users.(*user)
postss, ok := posts.(*post)
if !ok {
postsss, ok := posts.(*[]post)
if !ok {
return errors.New("无法识别post的类型")
}
for i := 0; i < len(*postsss); i++ {
(*postsss)[i].User = u
}
}
postss.User = u
return nil
}, &u, Relationship{
RelationType: "hasOne",
Table: "wp_users user",
ForeignKey: "ID",
Local: "post_author",
}
}
func PostMetas() (func(any) []any, func(posts, users any) error, any, Relationship) {
var u []models.PostMeta
return func(a any) []any {
return []any{a.(*post).Id}
}, func(posts, meta any) error {
postss := posts.(*post)
u := meta.(*[]models.PostMeta)
postss.PostMeta = u
return nil
}, &u, Relationship{
RelationType: "hasMany",
Table: "wp_postmeta meta",
ForeignKey: "post_id",
Local: "ID",
}
}
type TermRelationships struct { type TermRelationships struct {
ObjectID uint64 `db:"object_id"` ObjectID uint64 `db:"object_id"`
TermTaxonomyId uint64 `db:"term_taxonomy_id"` TermTaxonomyId uint64 `db:"term_taxonomy_id"`
@ -379,48 +335,6 @@ func TestFindOneById(t *testing.T) {
} }
} }
func TestGets2(t *testing.T) {
t.Run("hasOne", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"posts.id = 190"}}),
WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"),
), PostAuthor),
Fields("posts.*"),
From("wp_posts posts"),
WithFn(false, true, nil, PostMetas),
)
got, err := Gets[post](ctx, q)
_ = got
if err != nil {
t.Errorf("err:%v", err)
}
}
})
t.Run("hasOne", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"posts.id", "in", ""}}),
In([]any{190, 2978}),
WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"),
), PostAuthor),
Fields("posts.*"),
From("wp_posts posts"),
WithFn(false, false, nil, PostMetas),
)
got, err := Finds[post](ctx, q)
_ = got
if err != nil {
t.Errorf("err:%v", err)
}
}
})
}
func TestFirstOne(t *testing.T) { func TestFirstOne(t *testing.T) {
type args struct { type args struct {
where ParseWhere where ParseWhere

View File

@ -322,7 +322,7 @@ func gets[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r T, err
} }
func parseRelation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) (err error) { func parseRelation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) (err error) {
fn, fns := Relation(db, ctx, r, q) fn, fns := Relation(isMultiple, db, ctx, r, q)
for _, f := range fn { for _, f := range fn {
f() f()
} }

View File

@ -5,6 +5,8 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice"
"golang.org/x/exp/constraints"
"strings" "strings"
) )
@ -22,12 +24,12 @@ type Relationship struct {
On string On string
} }
func Relation(db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) { func Relation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
var fn []func() var fn []func()
var fns []func() error var fns []func() error
for _, f := range q.RelationFn { for _, f := range q.RelationFn {
getVal, isJoin, qq, ff := f() getVal, isJoin, qq, ff := f()
idFn, assignment, rr, ship := ff() idFn, assignment, rr, rrs, ship := ff()
if isJoin { if isJoin {
fn = append(fn, func() { fn = append(fn, func() {
tables := strings.Split(ship.Table, " ") tables := strings.Split(ship.Table, " ")
@ -45,6 +47,10 @@ func Relation(db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func
continue continue
} }
fns = append(fns, func() error { fns = append(fns, func() error {
ids := idFn(r)
if len(ids) < 1 {
return nil
}
var err error var err error
{ {
if qq == nil { if qq == nil {
@ -61,26 +67,93 @@ func Relation(db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func
ww = append(ww, SqlBuilder{{ ww = append(ww, SqlBuilder{{
ship.ForeignKey, "in", "", ship.ForeignKey, "in", "",
}}...) }}...)
qq.In = [][]any{idFn(r)} qq.In = [][]any{ids}
qq.Where = ww qq.Where = ww
} }
if qq.From == "" { if qq.From == "" {
qq.From = ship.Table qq.From = ship.Table
} }
} }
// todo finds的情况 err = parseRelation(isMultiple || ship.RelationType == "hasMany", db, ctx, helper.Or(isMultiple, rrs, rr), qq)
switch ship.RelationType {
case "hasOne":
err = parseRelation(false, db, ctx, rr, qq)
case "hasMany":
err = parseRelation(true, db, ctx, rr, qq)
}
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return err return err
} }
err = assignment(r, rr) assignment(r, helper.Or(isMultiple, rrs, rr))
return err return err
}) })
} }
return fn, fns return fn, fns
} }
func GetWithID[T, V any](fn func(*T) V) func(any) []any {
return func(a any) []any {
one, ok := a.(*T)
if ok {
return []any{fn(one)}
}
return slice.ToAnySlice(slice.Unique(slice.Map(*a.(*[]T), func(t T) any {
return fn(&t)
})))
}
}
func SetHasOne[T, V any, K comparable](fn func(*T, *V), idFn func(*T) K, iddFn func(*V) K) func(any, any) {
return func(m, v any) {
one, ok := m.(*T)
if ok {
fn(one, v.(*V))
return
}
r := m.(*[]T)
vv := v.(*[]V)
mm := slice.SimpleToMap(*vv, func(v V) K {
return iddFn(&v)
})
for i := 0; i < len(*r); i++ {
val := &(*r)[i]
id := idFn(val)
v, ok := mm[id]
if ok {
fn(val, &v)
}
}
}
}
func SetHasMany[T, V any, K comparable](fn func(*T, *[]V), idFn func(*T) K, iddFn func(*V) K) func(any, any) {
return func(m, v any) {
one, ok := m.(*T)
if ok {
fn(one, v.(*[]V))
return
}
r := m.(*[]T)
vv := v.(*[]V)
mm := slice.GroupBy(*vv, func(t V) (K, V) {
return iddFn(&t), t
})
for i := 0; i < len(*r); i++ {
val := &(*r)[i]
id := idFn(val)
v, ok := mm[id]
if ok {
fn(val, &v)
}
}
}
}
func RelationHasOne[M, P any, I constraints.Integer | uint64](fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn {
return func() (func(any) []any, func(any, any), any, any, Relationship) {
var s P
var ss []P
return GetWithID(fId), SetHasOne(setVal, fId, pId), &s, &ss, r
}
}
func RelationHasMany[M, P any, I constraints.Integer | uint64](mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn {
return func() (func(any) []any, func(any, any), any, any, Relationship) {
var ss []P
return GetWithID(mId), SetHasMany(setVal, mId, pId), &ss, &ss, r
}
}

128
model/relation_test.go Normal file
View File

@ -0,0 +1,128 @@
package model
import (
"github.com/fthvgb1/wp-go/app/pkg/models"
"testing"
)
func postAuthorId(p *post) uint64 {
return p.PostAuthor
}
func postId(p *post) uint64 {
return p.Id
}
func userId(u *user) uint64 {
return u.Id
}
func metaId(m *models.PostMeta) uint64 {
return m.MetaId
}
func metasPostId(m *models.PostMeta) uint64 {
return m.PostId
}
func PostAuthor() (func(any) []any, func(any, any), any, any, Relationship) {
var u user
var uu []user
return GetWithID[post](func(t *post) uint64 {
return t.PostAuthor
}),
SetHasOne(func(p *post, u *user) {
p.User = u
}, func(t *post) uint64 {
return t.PostAuthor
}, func(u *user) uint64 {
return u.Id
}),
&u, &uu,
Relationship{
RelationType: "hasOne",
Table: "wp_users user",
ForeignKey: "ID",
Local: "post_author",
}
}
func PostMetas() (func(any) []any, func(any, any), any, any, Relationship) {
var u []models.PostMeta
return GetWithID(func(t *post) any {
return t.Id
}), SetHasMany(func(t *post, v *[]models.PostMeta) {
t.PostMeta = v
}, func(t *post) uint64 {
return t.Id
}, func(m *models.PostMeta) uint64 {
return m.PostId
}), &u, &u, Relationship{
RelationType: "hasMany",
Table: "wp_postmeta meta",
ForeignKey: "post_id",
Local: "ID",
}
}
func Meta2() RelationFn {
return RelationHasMany(postId, metasPostId, func(m *post, i *[]models.PostMeta) {
m.PostMeta = i
}, Relationship{
RelationType: "hasMany",
Table: "wp_postmeta meta",
ForeignKey: "post_id",
Local: "ID",
})
}
func PostAuthor2() RelationFn {
return RelationHasOne[post, user](postAuthorId, userId, func(p *post, u *user) {
p.User = u
}, Relationship{
RelationType: "hasOne",
Table: "wp_users user",
ForeignKey: "ID",
Local: "post_author",
})
}
func TestGets2(t *testing.T) {
t.Run("one", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"posts.id = 190"}}),
WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"),
), PostAuthor2()),
Fields("posts.*"),
From("wp_posts posts"),
WithFn(true, true, nil, Meta2()),
)
got, err := Gets[post](ctx, q)
_ = got
if err != nil {
t.Errorf("err:%v", err)
}
}
})
t.Run("many", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"posts.id", "in", ""}}),
In([]any{190, 3022}),
WithCtx(&ctx),
WithFn(true, false, Conditions(
Fields("ID,user_login,user_pass"),
), PostAuthor2()),
Fields("posts.*"),
From("wp_posts posts"),
WithFn(true, false, nil, Meta2()),
)
got, err := Finds[post](ctx, q)
_ = got
if err != nil {
t.Errorf("err:%v", err)
}
}
})
}