diff --git a/.example/other/test.go b/.example/other/test.go index 34747c58e..d110a510c 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -2,22 +2,9 @@ package main import ( "fmt" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" ) func main() { - s := `user u LEFT JOIN user_detail ud ON(ud.id=u.id) LEFT JOIN user_stats us ON(us.id=u.id)` - split := " JOIN " - as := "my-as" - if gstr.Contains(s, split) { - // For join table. - array := gstr.Split(s, split) - array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1]) - s = gstr.Join(array, split) - } else { - // For base table. - s = gstr.TrimRight(s) + " AS " + as - } - fmt.Println(s) + a := []interface{}{1} + fmt.Println(a[1]) } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index de5eb5caa..e212ca68b 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -28,6 +28,7 @@ type Model struct { whereHolder []*whereHolder // Condition strings for where operation. groupBy string // Used for "group by" statement. orderBy string // Used for "order by" statement. + having []interface{} // Used for "having..." statement. start int // Used for "select ... start, limit ..." statement. limit int // Used for "select ... start, limit ..." statement. option int // Option for extra operation features. diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index cee015c08..4034b1d31 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -32,6 +32,17 @@ func (m *Model) Where(where interface{}, args ...interface{}) *Model { return model } +// Having sets the having statement for the model. +// The parameters of this function usage are as the same as function Where. +// See Where. +func (m *Model) Having(having interface{}, args ...interface{}) *Model { + model := m.getModel() + model.having = []interface{}{ + having, args, + } + return model +} + // WherePri does the same logic as Model.Where except that if the parameter // is a single condition like int/string/float/slice, it treats the condition as the primary // key value. That is, if primary key is "id" and given parameter as "123", the diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index b5e75e7f2..efdf2fa68 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -12,6 +12,7 @@ import ( "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gconv" "time" ) @@ -159,7 +160,9 @@ func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExt switch v.operator { case gWHERE_HOLDER_WHERE: if conditionWhere == "" { - newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0) + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + ) if len(newWhere) > 0 { conditionWhere = newWhere conditionArgs = newArgs @@ -169,7 +172,9 @@ func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExt fallthrough case gWHERE_HOLDER_AND: - newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0) + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + ) if len(newWhere) > 0 { if conditionWhere[0] == '(' { conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere) @@ -180,7 +185,9 @@ func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExt } case gWHERE_HOLDER_OR: - newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0) + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + ) if len(newWhere) > 0 { if conditionWhere[0] == '(' { conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere) @@ -201,6 +208,15 @@ func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExt if m.orderBy != "" { conditionExtra += " ORDER BY " + m.orderBy } + if len(m.having) > 0 { + havingStr, havingArgs := formatWhere( + m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0, + ) + if len(havingStr) > 0 { + conditionExtra += " HAVING " + havingStr + conditionArgs = append(conditionArgs, havingArgs...) + } + } if m.limit != 0 { if m.start >= 0 { conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit) diff --git a/database/gdb/gdb_unit_z_mysql_model_test.go b/database/gdb/gdb_unit_z_mysql_model_test.go index 086cc7696..1281d984d 100644 --- a/database/gdb/gdb_unit_z_mysql_model_test.go +++ b/database/gdb/gdb_unit_z_mysql_model_test.go @@ -2200,3 +2200,29 @@ func Test_Model_Cache(t *testing.T) { t.Assert(one["passport"], "user_200") }) } + +func Test_Model_Having(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(table).Where("id > 1").Having("id > 8").All() + t.Assert(err, nil) + t.Assert(len(all), 2) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(table).Where("id > 1").Having("id > ?", 8).All() + t.Assert(err, nil) + t.Assert(len(all), 2) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(table).Where("id > ?", 1).Having("id > ?", 8).All() + t.Assert(err, nil) + t.Assert(len(all), 2) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(table).Where("id > ?", 1).Having("id", 8).All() + t.Assert(err, nil) + t.Assert(len(all), 1) + }) +}