mirror of
https://gitee.com/johng/gf
synced 2026-07-06 13:42:46 +08:00
improve WhereBuilder for package gdb
This commit is contained in:
@ -136,6 +136,9 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
extraArgs: extraArgs,
|
||||
}
|
||||
m.whereBuilder = m.Builder()
|
||||
// Assign the safe attribute of WhereBuilder to nil,
|
||||
// to make it use the safe attribute of its bound model.
|
||||
m.whereBuilder.safe = nil
|
||||
if defaultModelSafe {
|
||||
m.safe = true
|
||||
}
|
||||
@ -262,7 +265,7 @@ func (m *Model) Clone() *Model {
|
||||
// Basic attributes copy.
|
||||
*newModel = *m
|
||||
// WhereBuilder copy, note the attribute pointer.
|
||||
newModel.whereBuilder = m.whereBuilder.Clone()
|
||||
newModel.whereBuilder = m.whereBuilder.clone()
|
||||
newModel.whereBuilder.model = newModel
|
||||
// Shallow copy slice attributes.
|
||||
if n := len(m.extraArgs); n > 0 {
|
||||
|
||||
@ -8,12 +8,11 @@ package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type WhereBuilder struct {
|
||||
model *Model // Bound to parent model.
|
||||
safe *bool // If nil, it uses the safe attribute of its model.
|
||||
model *Model // A WhereBuilder should be bound to certain Model.
|
||||
whereHolder []WhereHolder // Condition strings for where operation.
|
||||
}
|
||||
|
||||
@ -27,7 +26,10 @@ type WhereHolder struct {
|
||||
}
|
||||
|
||||
func (m *Model) Builder() *WhereBuilder {
|
||||
// The WhereBuilder is safe in default when it is created using Builder().
|
||||
var isSafe = true
|
||||
b := &WhereBuilder{
|
||||
safe: &isSafe,
|
||||
model: m,
|
||||
whereHolder: make([]WhereHolder, 0),
|
||||
}
|
||||
@ -37,15 +39,22 @@ func (m *Model) Builder() *WhereBuilder {
|
||||
// getBuilder creates and returns a cloned WhereBuilder of current WhereBuilder if `safe` is true,
|
||||
// or else it returns the current WhereBuilder.
|
||||
func (b *WhereBuilder) getBuilder() *WhereBuilder {
|
||||
if !b.model.safe {
|
||||
var isSafe bool
|
||||
if b.safe != nil {
|
||||
isSafe = *b.safe
|
||||
} else {
|
||||
isSafe = b.model.safe
|
||||
}
|
||||
if !isSafe {
|
||||
return b
|
||||
} else {
|
||||
return b.Clone()
|
||||
return b.clone()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *WhereBuilder) Clone() *WhereBuilder {
|
||||
func (b *WhereBuilder) clone() *WhereBuilder {
|
||||
newBuilder := b.model.Builder()
|
||||
newBuilder.safe = b.safe
|
||||
newBuilder.whereHolder = make([]WhereHolder, len(b.whereHolder))
|
||||
copy(newBuilder.whereHolder, b.whereHolder)
|
||||
return newBuilder
|
||||
@ -63,24 +72,7 @@ func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface
|
||||
holder.Prefix = autoPrefix
|
||||
}
|
||||
switch holder.Operator {
|
||||
case whereHolderOperatorWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{
|
||||
WhereHolder: holder,
|
||||
OmitNil: b.model.option&optionOmitNilWhere > 0,
|
||||
OmitEmpty: b.model.option&optionOmitEmptyWhere > 0,
|
||||
Schema: b.model.schema,
|
||||
Table: tableForMappingAndFiltering,
|
||||
})
|
||||
if len(newWhere) > 0 {
|
||||
conditionWhere = newWhere
|
||||
conditionArgs = newArgs
|
||||
}
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case whereHolderOperatorAnd:
|
||||
case whereHolderOperatorWhere, whereHolderOperatorAnd:
|
||||
newWhere, newArgs := formatWhereHolder(ctx, b.model.db, formatWhereHolderInput{
|
||||
WhereHolder: holder,
|
||||
OmitNil: b.model.option&optionOmitNilWhere > 0,
|
||||
@ -120,24 +112,5 @@ func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface
|
||||
}
|
||||
}
|
||||
}
|
||||
// Soft deletion.
|
||||
softDeletingCondition := b.model.getConditionForSoftDeleting()
|
||||
if b.model.rawSql != "" && conditionWhere != "" {
|
||||
if gstr.ContainsI(b.model.rawSql, " WHERE ") {
|
||||
conditionWhere = " AND " + conditionWhere
|
||||
} else {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
} else if !b.model.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition)
|
||||
} else {
|
||||
conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition)
|
||||
}
|
||||
} else {
|
||||
if conditionWhere != "" {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -15,20 +15,20 @@ import (
|
||||
// doWhereType sets the condition statement for the model. The parameter `where` can be type of
|
||||
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
|
||||
// multiple conditions will be joined into where statement using "AND".
|
||||
func (b *WhereBuilder) doWhereType(t string, where interface{}, args ...interface{}) *WhereBuilder {
|
||||
func (b *WhereBuilder) doWhereType(whereType string, where interface{}, args ...interface{}) *WhereBuilder {
|
||||
builder := b.getBuilder()
|
||||
if builder.whereHolder == nil {
|
||||
builder.whereHolder = make([]WhereHolder, 0)
|
||||
}
|
||||
if t == "" {
|
||||
if whereType == "" {
|
||||
if len(args) == 0 {
|
||||
t = whereHolderTypeNoArgs
|
||||
whereType = whereHolderTypeNoArgs
|
||||
} else {
|
||||
t = whereHolderTypeDefault
|
||||
whereType = whereHolderTypeDefault
|
||||
}
|
||||
}
|
||||
builder.whereHolder = append(builder.whereHolder, WhereHolder{
|
||||
Type: t,
|
||||
Type: whereType,
|
||||
Operator: whereHolderOperatorWhere,
|
||||
Where: where,
|
||||
Args: args,
|
||||
@ -59,6 +59,20 @@ func (b *WhereBuilder) doWherefType(t string, format string, args ...interface{}
|
||||
// Where("age IN(?,?)", 18, 50)
|
||||
// Where(User{ Id : 1, UserName : "john"}).
|
||||
func (b *WhereBuilder) Where(where interface{}, args ...interface{}) *WhereBuilder {
|
||||
var builder *WhereBuilder
|
||||
switch v := where.(type) {
|
||||
case WhereBuilder:
|
||||
builder = &v
|
||||
case *WhereBuilder:
|
||||
builder = v
|
||||
}
|
||||
if builder != nil {
|
||||
conditionWhere, conditionArgs := builder.Build()
|
||||
if len(b.whereHolder) == 0 {
|
||||
conditionWhere = "(" + conditionWhere + ")"
|
||||
}
|
||||
return b.doWhereType(``, conditionWhere, conditionArgs...)
|
||||
}
|
||||
return b.doWhereType(``, where, args...)
|
||||
}
|
||||
|
||||
|
||||
@ -616,6 +616,24 @@ func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStateme
|
||||
}
|
||||
// WHERE
|
||||
conditionWhere, conditionArgs = m.whereBuilder.Build()
|
||||
softDeletingCondition := m.getConditionForSoftDeleting()
|
||||
if m.rawSql != "" && conditionWhere != "" {
|
||||
if gstr.ContainsI(m.rawSql, " WHERE ") {
|
||||
conditionWhere = " AND " + conditionWhere
|
||||
} else {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
} else if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition)
|
||||
} else {
|
||||
conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition)
|
||||
}
|
||||
} else {
|
||||
if conditionWhere != "" {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
}
|
||||
// HAVING.
|
||||
if len(m.having) > 0 {
|
||||
havingHolder := WhereHolder{
|
||||
|
||||
46
database/gdb/gdb_z_mysql_feature_model_builder_test.go
Normal file
46
database/gdb/gdb_z_mysql_feature_model_builder_test.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Model_Builder(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table)
|
||||
b := m.Builder()
|
||||
|
||||
all, err := m.Where(
|
||||
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
|
||||
).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 6)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table)
|
||||
b := m.Builder()
|
||||
|
||||
all, err := m.Where(
|
||||
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
|
||||
).Where(
|
||||
b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}),
|
||||
).Where(
|
||||
b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}),
|
||||
).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
})
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user