wp-go/model/relation.go
2023-05-21 01:38:19 +08:00

160 lines
3.6 KiB
Go

package model
import (
"context"
"database/sql"
"fmt"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice"
"golang.org/x/exp/constraints"
"strings"
)
func setTable[T Model](q *QueryCondition) {
if q.From == "" {
q.From = Table[T]()
}
}
type Relationship struct {
RelationType string
Table string
ForeignKey string
Local string
On string
}
func Relation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
var fn []func()
var fns []func() error
for _, f := range q.RelationFn {
getVal, isJoin, qq, ff := f()
idFn, assignment, rr, rrs, ship := ff()
if isJoin {
fn = append(fn, func() {
tables := strings.Split(ship.Table, " ")
from := strings.Split(q.From, " ")
on := ""
if ship.On != "" {
on = fmt.Sprintf("and %s", on)
}
qq := helper.GetContextVal(ctx, "ancestorsQueryCondition", q)
qq.Join = append(qq.Join, []string{
"left join", ship.Table, fmt.Sprintf("%s.%s=%s.%s %s", tables[len(tables)-1], ship.ForeignKey, from[len(from)-1], ship.Local, on)})
})
}
if !getVal {
continue
}
fns = append(fns, func() error {
ids := idFn(r)
if len(ids) < 1 {
return nil
}
var err error
{
if qq == nil {
qq = &QueryCondition{
Fields: "*",
}
}
var w any = qq.Where
if w == nil {
w = SqlBuilder{}
}
ww, ok := w.(SqlBuilder)
if ok {
ww = append(ww, SqlBuilder{{
ship.ForeignKey, "in", "",
}}...)
qq.In = [][]any{ids}
qq.Where = ww
}
if qq.From == "" {
qq.From = ship.Table
}
}
err = parseRelation(isMultiple || ship.RelationType == "hasMany", db, ctx, helper.Or(isMultiple, rrs, rr), qq)
if err != nil && err != sql.ErrNoRows {
return err
}
assignment(r, helper.Or(isMultiple, rrs, rr))
return err
})
}
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
}
}