Compare commits
2 Commits
c95fd7e5da
...
0827cdc551
Author | SHA1 | Date | |
---|---|---|---|
0827cdc551 | |||
7c50f2b1a1 |
|
@ -34,7 +34,7 @@ func PostComments(args ...any) ([]uint64, error) {
|
||||||
r, err := model.Finds[models.Comments](ctx, model.Conditions(
|
r, err := model.Finds[models.Comments](ctx, model.Conditions(
|
||||||
model.Where(model.SqlBuilder{
|
model.Where(model.SqlBuilder{
|
||||||
{"comment_approved", "1"},
|
{"comment_approved", "1"},
|
||||||
{"comment_post_ID", "=", number.ToString(postId), "int"},
|
{"comment_post_ID", "=", number.IntToString(postId), "int"},
|
||||||
}),
|
}),
|
||||||
model.Fields("comment_ID"),
|
model.Fields("comment_ID"),
|
||||||
model.Order(model.SqlBuilder{
|
model.Order(model.SqlBuilder{
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fthvgb1/wp-go/app/pkg/models"
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models/relation"
|
||||||
"github.com/fthvgb1/wp-go/app/wpconfig"
|
"github.com/fthvgb1/wp-go/app/wpconfig"
|
||||||
|
"github.com/fthvgb1/wp-go/helper"
|
||||||
"github.com/fthvgb1/wp-go/helper/slice"
|
"github.com/fthvgb1/wp-go/helper/slice"
|
||||||
"github.com/fthvgb1/wp-go/model"
|
"github.com/fthvgb1/wp-go/model"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -17,7 +19,7 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
||||||
ctx := a[0].(context.Context)
|
ctx := a[0].(context.Context)
|
||||||
m = make(map[uint64]models.Posts)
|
m = make(map[uint64]models.Posts)
|
||||||
ids := a[1].([]uint64)
|
ids := a[1].([]uint64)
|
||||||
rawPosts, err := model.Finds[models.Posts](ctx, model.Conditions(
|
q := model.Conditions(
|
||||||
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
model.Where(model.SqlBuilder{{"Id", "in", ""}}),
|
||||||
model.Join(model.SqlBuilder{
|
model.Join(model.SqlBuilder{
|
||||||
{"a", "left join", "wp_term_relationships b", "a.Id=b.object_id"},
|
{"a", "left join", "wp_term_relationships b", "a.Id=b.object_id"},
|
||||||
|
@ -26,7 +28,11 @@ func GetPostsByIds(a ...any) (m map[uint64]models.Posts, err error) {
|
||||||
}),
|
}),
|
||||||
model.Fields("a.*,ifnull(d.name,'') category_name,ifnull(c.term_id,0) terms_id,ifnull(taxonomy,'') `taxonomy`"),
|
model.Fields("a.*,ifnull(d.name,'') category_name,ifnull(c.term_id,0) terms_id,ifnull(taxonomy,'') `taxonomy`"),
|
||||||
model.In(slice.ToAnySlice(ids)),
|
model.In(slice.ToAnySlice(ids)),
|
||||||
))
|
)
|
||||||
|
if helper.GetContextVal(ctx, "getPostAuthor", false) {
|
||||||
|
q.RelationFn = append(q.RelationFn, model.AddRelationFn(true, false, helper.GetContextVal[*model.QueryCondition](ctx, "postAuthorQueryCondition", nil), relation.PostsWithAuthor))
|
||||||
|
}
|
||||||
|
rawPosts, err := model.Finds[models.Posts](ctx, q)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return m, err
|
return m, err
|
||||||
|
|
23
app/pkg/models/relation/posts.go
Normal file
23
app/pkg/models/relation/posts.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package relation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fthvgb1/wp-go/app/pkg/models"
|
||||||
|
"github.com/fthvgb1/wp-go/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hasUser = model.RelationHasOne(func(m *models.Posts) uint64 {
|
||||||
|
return m.PostAuthor
|
||||||
|
}, func(p *models.Users) uint64 {
|
||||||
|
return p.Id
|
||||||
|
}, func(m *models.Posts, p *models.Users) {
|
||||||
|
m.Author = p
|
||||||
|
}, model.Relationship{
|
||||||
|
RelationType: model.HasOne,
|
||||||
|
Table: "wp_users user",
|
||||||
|
ForeignKey: "ID",
|
||||||
|
Local: "post_author",
|
||||||
|
})
|
||||||
|
|
||||||
|
func PostsWithAuthor() (func(any) []any, func(any, any), any, any, model.Relationship) {
|
||||||
|
return hasUser()
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ type Posts struct {
|
||||||
IsSticky bool
|
IsSticky bool
|
||||||
Thumbnail PostThumbnail
|
Thumbnail PostThumbnail
|
||||||
AttachmentMetadata WpAttachmentMetadata
|
AttachmentMetadata WpAttachmentMetadata
|
||||||
|
Author *Users
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostThumbnail struct {
|
type PostThumbnail struct {
|
||||||
|
|
|
@ -50,20 +50,20 @@ func (h *Handle) BodyClass() string {
|
||||||
if cate.Slug[0] != '%' {
|
if cate.Slug[0] != '%' {
|
||||||
class = append(class, str.Join("category-", cate.Slug))
|
class = append(class, str.Join("category-", cate.Slug))
|
||||||
}
|
}
|
||||||
class = append(class, str.Join("category-", number.ToString(cate.Terms.TermId)))
|
class = append(class, str.Join("category-", number.IntToString(cate.Terms.TermId)))
|
||||||
|
|
||||||
case constraints.Author:
|
case constraints.Author:
|
||||||
class = append(class, "archive", "author")
|
class = append(class, "archive", "author")
|
||||||
author := h.Index.Param.Author
|
author := h.Index.Param.Author
|
||||||
user, _ := cache.GetUserByName(h.C, author)
|
user, _ := cache.GetUserByName(h.C, author)
|
||||||
class = append(class, str.Join("author-", number.ToString(user.Id)))
|
class = append(class, str.Join("author-", number.IntToString(user.Id)))
|
||||||
if user.UserLogin[0] != '%' {
|
if user.UserLogin[0] != '%' {
|
||||||
class = append(class, str.Join("author-", user.UserLogin))
|
class = append(class, str.Join("author-", user.UserLogin))
|
||||||
}
|
}
|
||||||
|
|
||||||
case constraints.Detail:
|
case constraints.Detail:
|
||||||
class = append(class, "post-template-default", "single", "single-post")
|
class = append(class, "post-template-default", "single", "single-post")
|
||||||
class = append(class, str.Join("postid-", number.ToString(h.Detail.Post.Id)))
|
class = append(class, str.Join("postid-", number.IntToString(h.Detail.Post.Id)))
|
||||||
if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
|
if len(h.themeMods.ThemeSupport.PostFormats) > 0 {
|
||||||
class = append(class, "single-format-standard")
|
class = append(class, "single-format-standard")
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ func pagination[T Model](db dbQuery, ctx context.Context, q *QueryCondition, pag
|
||||||
err = er
|
err = er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
qx.From = str.Join("( ", sq, " ) ", "table", number.ToString(rand.Int()))
|
qx.From = str.Join("( ", sq, " ) ", "table", number.IntToString(rand.Int()))
|
||||||
qx = QueryCondition{
|
qx = QueryCondition{
|
||||||
From: qx.From,
|
From: qx.From,
|
||||||
In: qx.In,
|
In: qx.In,
|
||||||
|
|
|
@ -36,7 +36,7 @@ func finds[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r []T,
|
||||||
err = db.Select(ctx, &r, sq, args...)
|
err = db.Select(ctx, &r, sq, args...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = parseRelation(true, db, ctx, &r, q)
|
err = ParseRelation(true, db, ctx, &r, q)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,14 +317,14 @@ func gets[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r T, err
|
||||||
err = db.Get(ctx, &r, s, args...)
|
err = db.Get(ctx, &r, s, args...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = parseRelation(false, db, ctx, &r, q)
|
err = ParseRelation(false, db, ctx, &r, q)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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(isMultiple, db, ctx, r, q)
|
before, after := Relation(isMultiple, db, ctx, r, q)
|
||||||
for _, f := range fn {
|
for _, fn := range before {
|
||||||
f()
|
fn()
|
||||||
}
|
}
|
||||||
s, args, err := BuildQuerySql(q)
|
s, args, err := BuildQuerySql(q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -339,8 +339,8 @@ func parseRelation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *Q
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range fns {
|
for _, fn := range after {
|
||||||
err = f()
|
err = fn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,20 @@ func setTable[T Model](q *QueryCondition) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
HasOne = "hasOne"
|
||||||
|
HasMany = "hasMany"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationship join table
|
||||||
|
//
|
||||||
|
// # RelationType HasOne| HasMany
|
||||||
|
//
|
||||||
|
// eg: hasOne, post has a user. ForeignKey is user's id , Local is post's userId field
|
||||||
|
//
|
||||||
|
// eg: hasMany, post has many comments,ForeignKey is comment's postId field, Local is post's id field
|
||||||
|
//
|
||||||
|
// On is additional join on conditions
|
||||||
type Relationship struct {
|
type Relationship struct {
|
||||||
RelationType string
|
RelationType string
|
||||||
Table string
|
Table string
|
||||||
|
@ -24,14 +38,14 @@ type Relationship struct {
|
||||||
On string
|
On string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Relation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
|
func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
|
||||||
var fn []func()
|
var beforeFn []func()
|
||||||
var fns []func() error
|
var afterFn []func() error
|
||||||
for _, f := range q.RelationFn {
|
for _, f := range q.RelationFn {
|
||||||
getVal, isJoin, qq, ff := f()
|
getVal, isJoin, qq, relationship := f()
|
||||||
idFn, assignment, rr, rrs, ship := ff()
|
idFn, assignmentFn, rr, rrs, ship := relationship()
|
||||||
if isJoin {
|
if isJoin {
|
||||||
fn = append(fn, func() {
|
beforeFn = append(beforeFn, func() {
|
||||||
tables := strings.Split(ship.Table, " ")
|
tables := strings.Split(ship.Table, " ")
|
||||||
from := strings.Split(q.From, " ")
|
from := strings.Split(q.From, " ")
|
||||||
on := ""
|
on := ""
|
||||||
|
@ -46,44 +60,45 @@ func Relation(isMultiple bool, db dbQuery, ctx context.Context, r any, q *QueryC
|
||||||
if !getVal {
|
if !getVal {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fns = append(fns, func() error {
|
afterFn = append(afterFn, func() error {
|
||||||
ids := idFn(r)
|
ids := idFn(r)
|
||||||
if len(ids) < 1 {
|
if len(ids) < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
{
|
if qq == nil {
|
||||||
if qq == nil {
|
qq = &QueryCondition{
|
||||||
qq = &QueryCondition{
|
Fields: "*",
|
||||||
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)
|
var w any = qq.Where
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if w == nil {
|
||||||
return err
|
w = SqlBuilder{}
|
||||||
}
|
}
|
||||||
assignment(r, helper.Or(isMultiple, rrs, rr))
|
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(isPlural || ship.RelationType == HasMany, db, ctx, helper.Or(isPlural, rrs, rr), qq)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assignmentFn(r, helper.Or(isPlural, rrs, rr))
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return fn, fns
|
return beforeFn, afterFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetWithID[T, V any](fn func(*T) V) func(any) []any {
|
func GetWithID[T, V any](fn func(*T) V) func(any) []any {
|
||||||
|
@ -98,62 +113,82 @@ func GetWithID[T, V any](fn func(*T) V) func(any) []any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetHasOne[T, V any, K comparable](fn func(*T, *V), idFn func(*T) K, iddFn func(*V) K) func(any, any) {
|
// SetHasOne mIdFn is main , pIdFn is part
|
||||||
return func(m, v any) {
|
//
|
||||||
|
// eg: post has a user. mIdFn is post's userId, iddFn is user's id
|
||||||
|
func SetHasOne[T, V any, K comparable](assignmentFn func(*T, *V), mIdFn func(*T) K, pIdFn func(*V) K) func(any, any) {
|
||||||
|
return func(m, p any) {
|
||||||
one, ok := m.(*T)
|
one, ok := m.(*T)
|
||||||
if ok {
|
if ok {
|
||||||
fn(one, v.(*V))
|
assignmentFn(one, p.(*V))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r := m.(*[]T)
|
mSlice := m.(*[]T)
|
||||||
vv := v.(*[]V)
|
pSLice := p.(*[]V)
|
||||||
mm := slice.SimpleToMap(*vv, func(v V) K {
|
mm := slice.SimpleToMap(*pSLice, func(v V) K {
|
||||||
return iddFn(&v)
|
return pIdFn(&v)
|
||||||
})
|
})
|
||||||
for i := 0; i < len(*r); i++ {
|
for i := 0; i < len(*mSlice); i++ {
|
||||||
val := &(*r)[i]
|
m := &(*mSlice)[i]
|
||||||
id := idFn(val)
|
id := mIdFn(m)
|
||||||
v, ok := mm[id]
|
p, ok := mm[id]
|
||||||
if ok {
|
if ok {
|
||||||
fn(val, &v)
|
assignmentFn(m, &p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetHasMany[T, V any, K comparable](fn func(*T, *[]V), idFn func(*T) K, iddFn func(*V) K) func(any, any) {
|
// SetHasMany
|
||||||
return func(m, v any) {
|
// eg: post has many comments,pIdFn is comment's postId, mIdFn is post's id
|
||||||
|
func SetHasMany[T, V any, K comparable](assignmentFn func(*T, *[]V), pIdFn func(*T) K, mIdFn func(*V) K) func(any, any) {
|
||||||
|
return func(m, p any) {
|
||||||
one, ok := m.(*T)
|
one, ok := m.(*T)
|
||||||
if ok {
|
if ok {
|
||||||
fn(one, v.(*[]V))
|
assignmentFn(one, p.(*[]V))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r := m.(*[]T)
|
r := m.(*[]T)
|
||||||
vv := v.(*[]V)
|
vv := p.(*[]V)
|
||||||
mm := slice.GroupBy(*vv, func(t V) (K, V) {
|
mm := slice.GroupBy(*vv, func(t V) (K, V) {
|
||||||
return iddFn(&t), t
|
return mIdFn(&t), t
|
||||||
})
|
})
|
||||||
for i := 0; i < len(*r); i++ {
|
for i := 0; i < len(*r); i++ {
|
||||||
val := &(*r)[i]
|
m := &(*r)[i]
|
||||||
id := idFn(val)
|
id := pIdFn(m)
|
||||||
v, ok := mm[id]
|
p, ok := mm[id]
|
||||||
if ok {
|
if ok {
|
||||||
fn(val, &v)
|
assignmentFn(m, &p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelationHasOne
|
||||||
|
// eg: post has a user. fId is post's userId, pId is user's id
|
||||||
func RelationHasOne[M, P any, I constraints.Integer | uint64](fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn {
|
func RelationHasOne[M, P any, I constraints.Integer | uint64](fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn {
|
||||||
|
idFn := GetWithID(fId)
|
||||||
|
setFn := SetHasOne(setVal, fId, pId)
|
||||||
return func() (func(any) []any, func(any, any), any, any, Relationship) {
|
return func() (func(any) []any, func(any, any), any, any, Relationship) {
|
||||||
var s P
|
var s P
|
||||||
var ss []P
|
var ss []P
|
||||||
return GetWithID(fId), SetHasOne(setVal, fId, pId), &s, &ss, r
|
return idFn, setFn, &s, &ss, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelationHasMany
|
||||||
|
// eg: post has many comments,mId is comment's postId, pId is post's id
|
||||||
func RelationHasMany[M, P any, I constraints.Integer | uint64](mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn {
|
func RelationHasMany[M, P any, I constraints.Integer | uint64](mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn {
|
||||||
|
idFn := GetWithID(mId)
|
||||||
|
setFn := SetHasMany(setVal, mId, pId)
|
||||||
return func() (func(any) []any, func(any, any), any, any, Relationship) {
|
return func() (func(any) []any, func(any, any), any, any, Relationship) {
|
||||||
var ss []P
|
var ss []P
|
||||||
return GetWithID(mId), SetHasMany(setVal, mId, pId), &ss, &ss, r
|
return idFn, setFn, &ss, &ss, r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRelationFn(getVal, join bool, q *QueryCondition, r RelationFn) func() (bool, bool, *QueryCondition, RelationFn) {
|
||||||
|
return func() (bool, bool, *QueryCondition, RelationFn) {
|
||||||
|
return getVal, join, q, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,6 @@ func userId(u *user) uint64 {
|
||||||
return u.Id
|
return u.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
func metaId(m *models.PostMeta) uint64 {
|
|
||||||
return m.MetaId
|
|
||||||
}
|
|
||||||
func metasPostId(m *models.PostMeta) uint64 {
|
func metasPostId(m *models.PostMeta) uint64 {
|
||||||
return m.PostId
|
return m.PostId
|
||||||
}
|
}
|
||||||
|
@ -39,7 +36,7 @@ func PostAuthor() (func(any) []any, func(any, any), any, any, Relationship) {
|
||||||
}),
|
}),
|
||||||
&u, &uu,
|
&u, &uu,
|
||||||
Relationship{
|
Relationship{
|
||||||
RelationType: "hasOne",
|
RelationType: HasOne,
|
||||||
Table: "wp_users user",
|
Table: "wp_users user",
|
||||||
ForeignKey: "ID",
|
ForeignKey: "ID",
|
||||||
Local: "post_author",
|
Local: "post_author",
|
||||||
|
@ -56,7 +53,7 @@ func PostMetas() (func(any) []any, func(any, any), any, any, Relationship) {
|
||||||
}, func(m *models.PostMeta) uint64 {
|
}, func(m *models.PostMeta) uint64 {
|
||||||
return m.PostId
|
return m.PostId
|
||||||
}), &u, &u, Relationship{
|
}), &u, &u, Relationship{
|
||||||
RelationType: "hasMany",
|
RelationType: HasMany,
|
||||||
Table: "wp_postmeta meta",
|
Table: "wp_postmeta meta",
|
||||||
ForeignKey: "post_id",
|
ForeignKey: "post_id",
|
||||||
Local: "ID",
|
Local: "ID",
|
||||||
|
@ -75,7 +72,7 @@ func Meta2() RelationFn {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostAuthor2() RelationFn {
|
func PostAuthor2() RelationFn {
|
||||||
return RelationHasOne[post, user](postAuthorId, userId, func(p *post, u *user) {
|
return RelationHasOne(postAuthorId, userId, func(p *post, u *user) {
|
||||||
p.User = u
|
p.User = u
|
||||||
}, Relationship{
|
}, Relationship{
|
||||||
RelationType: "hasOne",
|
RelationType: "hasOne",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user