has one/many complete
This commit is contained in:
parent
4242d850ff
commit
c95fd7e5da
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
128
model/relation_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user