1 Star 1 Fork 1

c./goframe框架

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gdb_model_with.go 10.83 KB
一键复制 编辑 原始数据 按行查看 历史
admin 提交于 9个月前 . 2024-11-7
// 版权归GoFrame作者(https://goframe.org)所有。保留所有权利。
//
// 本源代码形式受MIT许可证条款约束。
// 如果未随本文件一同分发MIT许可证副本,
// 您可以在https://github.com/gogf/gf处获取。
// md5:a9832f33b234e3f3
package db类
import (
"database/sql"
"reflect"
gcode "gitee.com/go_888/goframe/errors/gcode"
gerror "gitee.com/go_888/goframe/errors/gerror"
"gitee.com/go_888/goframe/internal/utils"
"gitee.com/go_888/goframe/os/gstructs"
gstr "gitee.com/go_888/goframe/text/gstr"
gutil "gitee.com/go_888/goframe/util/gutil"
)
// X关联对象 创建并返回一个基于给定对象元数据的 ORM 模型。它还为给定的 `object` 启用模型关联操作功能。
// 可以多次调用此函数,以向模型中添加一个或多个对象,并启用它们的模式关联操作功能。
// 例如,如果给出的结构体定义如下:
//
// ```
//
// type User struct {
// gmeta.Meta `orm:"table:user"`
// Id int `json:"id"`
// Name string `json:"name"`
// UserDetail *UserDetail `orm:"with:uid=id"`
// UserScores []*UserScores `orm:"with:uid=id"`
// }
//
// ```
//
// 我们可以通过以下方式在 `UserDetail` 和 `UserScores` 属性上启用模型关联操作:
//
// ```
// db.X关联对象(User{}.UserDetail).X关联对象(User{}.UserScores).Scan(xxx)
// ```
//
// 或者:
//
// ```
// db.X关联对象(UserDetail{}).X关联对象(UserScores{}).Scan(xxx)
// ```
//
// 或者:
//
// ```
// db.X关联对象(UserDetail{}, UserScores{}).Scan(xxx)
// ```
// md5:c9498702475d54a9
func (m *X结构_Model) X关联对象(关联结构 ...interface{}) *X结构_Model {
model := m.getModel()
for _, object := range 关联结构 {
if m.tables == "" {
m.tablesInit = m.db.X取Core对象().X底层添加前缀字符和引用字符(
getTableNameFromOrmTag(object),
)
m.tables = m.tablesInit
return model
}
model.withArray = append(model.withArray, object)
}
return model
}
// X关联全部对象 启用对结构体中带有 "with" 标签的所有对象进行模型关联操作。 md5:83d3591315f0add0
func (m *X结构_Model) X关联全部对象() *X结构_Model {
model := m.getModel()
model.withAll = true
return model
}
// doWithScanStruct 处理单个结构体的模型关联操作功能。 md5:64dcc9bfd0382aa8
func (m *X结构_Model) doWithScanStruct(pointer interface{}) error {
var (
err error
allowedTypeStrArray = make([]string, 0)
)
currentStructFieldMap, err := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
if err != nil {
return err
}
// 它会检查with数组,并自动调用ScanList来完成关联查询。 md5:cb83f16b7131ad65
if !m.withAll {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := gstructs.StructType(withItem)
if err != nil {
return err
}
var (
fieldTypeStr = gstr.X删所有空与字符(field.Type().String(), "*[]")
withItemReflectValueTypeStr = gstr.X删所有空与字符(withItemReflectValueType.String(), "*[]")
)
// 如果字段类型在指定的"with"类型数组中,它会执行选择操作。 md5:b425357c98d952c8
if gstr.X顺序比较(fieldTypeStr, withItemReflectValueTypeStr) == 0 {
allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr)
}
}
}
}
for _, field := range currentStructFieldMap {
var (
fieldTypeStr = gstr.X删所有空与字符(field.Type().String(), "*[]")
parsedTagOutput = m.parseWithTagInFieldStruct(field)
)
if parsedTagOutput.With == "" {
continue
}
// 它仅处理带有"type"属性的"with"类型结构体,因此会忽略其他类型的结构体。 md5:c1f385406b699f00
if !m.withAll && !gstr.X数组是否存在(allowedTypeStrArray, fieldTypeStr) {
continue
}
array := gstr.X分割并忽略空值(parsedTagOutput.With, "=")
if len(array) == 1 {
// 它还支持仅使用一个列名
// 如果两个表使用相同的列名进行关联。
// md5:c924339d8b4eddbc
array = append(array, parsedTagOutput.With)
}
var (
model *X结构_Model
fieldKeys []string
relatedSourceName = array[0]
relatedTargetName = array[1]
relatedTargetValue interface{}
)
// 从`pointer`中找到相关的属性值。 md5:b2da611599aed2d2
for attributeName, attributeValue := range currentStructFieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
relatedTargetValue = attributeValue.Value.Interface()
break
}
}
if relatedTargetValue == nil {
return gerror.X创建错误码并格式化(
gcode.X变量_CodeInvalidParameter,
`在 with 标签 "%s" 中找不到名称 "%s" 的目标相关值,属性为 "%s.%s"`,
relatedTargetName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
)
}
bindToReflectValue := field.Value
if bindToReflectValue.Kind() != reflect.Ptr && bindToReflectValue.CanAddr() {
bindToReflectValue = bindToReflectValue.Addr()
}
// 它会自动从当前属性结构体/切片中获取字段名。 md5:09af2856a6801ffd
if structType, err := gstructs.StructType(field.Value); err != nil {
return err
} else {
fieldKeys = structType.FieldKeys()
}
// 递归实现并带有特性检查。 md5:9ddeb46ca8a2b86d
model = m.db.X关联对象(field.Value).Hook(m.hookHandler)
if m.withAll {
model = model.X关联全部对象()
} else {
model = model.X关联对象(m.withArray...)
}
if parsedTagOutput.Where != "" {
model = model.X条件(parsedTagOutput.Where)
}
if parsedTagOutput.Order != "" {
model = model.X排序(parsedTagOutput.Order)
}
// With cache feature.
if m.cacheEnabled && m.cacheOption.X名称 == "" {
model = model.X缓存(m.cacheOption)
}
err = model.X字段保留过滤(fieldKeys).
X条件(relatedSourceName, relatedTargetValue).
X查询结构或结构数组(bindToReflectValue)
// 它在该特性中忽略sql.ErrNoRows错误。 md5:4b82d692c0646927
if err != nil && err != sql.ErrNoRows {
return err
}
}
return nil
}
// doWithScanStructs 处理结构切片的模型关联操作功能。
// 参见 doWithScanStruct。
// md5:6219b8feabf0e7d9
func (m *X结构_Model) doWithScanStructs(pointer interface{}) error {
if v, ok := pointer.(reflect.Value); ok {
pointer = v.Interface()
}
var (
err error
allowedTypeStrArray = make([]string, 0)
)
currentStructFieldMap, err := gstructs.FieldMap(gstructs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
if err != nil {
return err
}
// 它会检查with数组,并自动调用ScanList来完成关联查询。 md5:cb83f16b7131ad65
if !m.withAll {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := gstructs.StructType(withItem)
if err != nil {
return err
}
var (
fieldTypeStr = gstr.X删所有空与字符(field.Type().String(), "*[]")
withItemReflectValueTypeStr = gstr.X删所有空与字符(withItemReflectValueType.String(), "*[]")
)
// 如果字段类型在指定的数组类型中,它将执行选择操作。 md5:afefe105662c6d79
if gstr.X顺序比较(fieldTypeStr, withItemReflectValueTypeStr) == 0 {
allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr)
}
}
}
}
for fieldName, field := range currentStructFieldMap {
var (
fieldTypeStr = gstr.X删所有空与字符(field.Type().String(), "*[]")
parsedTagOutput = m.parseWithTagInFieldStruct(field)
)
if parsedTagOutput.With == "" {
continue
}
if !m.withAll && !gstr.X数组是否存在(allowedTypeStrArray, fieldTypeStr) {
continue
}
array := gstr.X分割并忽略空值(parsedTagOutput.With, "=")
if len(array) == 1 {
// 它支持仅使用一个列名的情况,
// 当两个表通过相同的列名关联时。
// md5:18222f22ecbee1ef
array = append(array, parsedTagOutput.With)
}
var (
model *X结构_Model
fieldKeys []string
relatedSourceName = array[0]
relatedTargetName = array[1]
relatedTargetValue interface{}
)
// 从`pointer`中查找相关属性的值切片。 md5:e729db1e29dfb929
for attributeName := range currentStructFieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
relatedTargetValue = X取结构数组或Map数组值并去重(pointer, attributeName)
break
}
}
if relatedTargetValue == nil {
return gerror.X创建错误码并格式化(
gcode.X变量_CodeInvalidParameter,
`在 with 标签 "%s" 中找不到属性名称 "%s" 的相关值`,
relatedTargetName, parsedTagOutput.With,
)
}
// 如果相关值为空,它什么也不做,只是返回。 md5:e4acb6a4c5d73f8f
if gutil.X是否为空(relatedTargetValue) {
return nil
}
// 它会自动从当前属性结构体/切片中获取字段名。 md5:09af2856a6801ffd
if structType, err := gstructs.StructType(field.Value); err != nil {
return err
} else {
fieldKeys = structType.FieldKeys()
}
// 递归实现并带有特性检查。 md5:9ddeb46ca8a2b86d
model = m.db.X关联对象(field.Value).Hook(m.hookHandler)
if m.withAll {
model = model.X关联全部对象()
} else {
model = model.X关联对象(m.withArray...)
}
if parsedTagOutput.Where != "" {
model = model.X条件(parsedTagOutput.Where)
}
if parsedTagOutput.Order != "" {
model = model.X排序(parsedTagOutput.Order)
}
// With cache feature.
if m.cacheEnabled && m.cacheOption.X名称 == "" {
model = model.X缓存(m.cacheOption)
}
err = model.X字段保留过滤(fieldKeys).
X条件(relatedSourceName, relatedTargetValue).
X查询结构或结构数组并关联(pointer, fieldName, parsedTagOutput.With)
// 它在该特性中忽略sql.ErrNoRows错误。 md5:4b82d692c0646927
if err != nil && err != sql.ErrNoRows {
return err
}
}
return nil
}
type parseWithTagInFieldStructOutput struct {
With string
Where string
Order string
}
func (m *X结构_Model) parseWithTagInFieldStruct(field gstructs.X结构_Field) (output parseWithTagInFieldStructOutput) {
var (
ormTag = field.Tag(X常量_OrmTagForStruct)
data = make(map[string]string)
array []string
key string
)
for _, v := range gstr.X分割并忽略空值(ormTag, " ") {
array = gstr.X分割(v, ":")
if len(array) == 2 {
key = array[0]
data[key] = gstr.X删首尾空与字符(array[1])
} else {
data[key] += " " + gstr.X删首尾空与字符(v)
}
}
for k, v := range data {
data[k] = gstr.X删尾空与字符(v, ",")
}
output.With = data[X常量_OrmTagForWith]
output.Where = data[X常量_OrmTagForWithWhere]
output.Order = data[X常量_OrmTagForWithOrder]
return
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/go_888/goframe.git
git@gitee.com:go_888/goframe.git
go_888
goframe
goframe框架
782a3f7170cf

搜索帮助