diff --git a/.example/frame/mvc/app/model/article/article_model.go b/.example/frame/mvc/app/model/article/article_model.go index b0ab9dea9..4b9234220 100644 --- a/.example/frame/mvc/app/model/article/article_model.go +++ b/.example/frame/mvc/app/model/article/article_model.go @@ -53,6 +53,12 @@ func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { return Model.Value(fieldsAndWhere...) } +// FindCount retrieves and returns the record number by a primary key or where conditions by +// Model.Where. Also see FindOne. +func FindCount(where ...interface{}) (int, error) { + return Model.Count(gdb.GetPrimaryKeyCondition(Primary, where...)...) +} + // Insert is alias of Model.Insert. func Insert(data ...interface{}) (result sql.Result, err error) { return Model.Insert(data...) diff --git a/.example/other/test.go b/.example/other/test.go index 92339853b..cf048df50 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,10 +1,10 @@ package main import ( - "github.com/gogf/gf/.example/frame/mvc/app/model/article" - "github.com/gogf/gf/frame/g" + "fmt" + "github.com/gogf/gf/os/gtime" ) func main() { - g.Dump(article.FindAll(g.Slice{2, 3})) + fmt.Println(gtime.Timestamp()) } diff --git a/database/gdb/gdb_base.go b/database/gdb/gdb_base.go index e3413ca5e..b734bff0b 100644 --- a/database/gdb/gdb_base.go +++ b/database/gdb/gdb_base.go @@ -93,9 +93,9 @@ func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result query, args = formatQuery(query, args) query = bs.db.handleSqlBeforeExec(query) if bs.db.getDebug() { - mTime1 := gtime.Millisecond() + mTime1 := gtime.TimestampMilli() result, err = link.Exec(query, args...) - mTime2 := gtime.Millisecond() + mTime2 := gtime.TimestampMilli() s := &Sql{ Sql: query, Args: args, diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 56cf0a8dd..b36d59042 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -112,7 +112,14 @@ func (tx *TX) From(tables string) *Model { return tx.Table(tables) } -// TX sets the transaction for current operation. +// DB sets/changes the db object for current operation. +func (m *Model) DB(db DB) *Model { + model := m.getModel() + model.db = db + return model +} + +// TX sets/changes the transaction for current operation. func (m *Model) TX(tx *TX) *Model { model := m.getModel() model.tx = tx diff --git a/database/gdb/gdb_structure.go b/database/gdb/gdb_structure.go index 9a30948e8..78cadeeea 100644 --- a/database/gdb/gdb_structure.go +++ b/database/gdb/gdb_structure.go @@ -20,7 +20,8 @@ import ( "github.com/gogf/gf/util/gconv" ) -// convertValue converts field value from database type to golang variable type. +// convertValue automatically checks and converts field value from database type +// to golang variable type. func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} { t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType) t = strings.ToLower(t) @@ -97,9 +98,9 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} } } -// 将map的数据按照fields进行过滤,只保留与表字段同名的数据 +// filterFields removes all key-value pairs which are not the field of given table. func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} { - // It must use data copy here to avoid changing the origin data map. + // It must use data copy here to avoid its changing the origin data map. newDataMap := make(map[string]interface{}, len(data)) if fields, err := bs.db.TableFields(table); err == nil { for k, v := range data { @@ -111,7 +112,7 @@ func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[st return newDataMap } -// 返回当前数据库所有的数据表名称 +// Tables returns the table name array of current schema. func (bs *dbBase) Tables() (tables []string, err error) { result := (Result)(nil) result, err = bs.GetAll(`SHOW TABLES`) @@ -126,9 +127,12 @@ func (bs *dbBase) Tables() (tables []string, err error) { return } -// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值为字段数据结构. +// TableFields retrieves and returns the fields of given table. +// Note that it returns a map containing the field name and its corresponding fields. +// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields. +// +// It's using cache feature to enhance the performance, which is never expired util the process restarts. func (bs *dbBase) TableFields(table string) (fields map[string]*TableField, err error) { - // 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署) v := bs.cache.GetOrSetFunc("table_fields_"+table, func() interface{} { result := (Result)(nil) result, err = bs.GetAll(fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, bs.db.quoteWord(table))) diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index f24458914..9ef5b5692 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -9,23 +9,24 @@ package gdb import ( "database/sql" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/encoding/gparser" ) -// 将记录结果转换为JSON字符串 +// Json converts to JSON format content. func (r Record) Json() string { content, _ := gparser.VarToJson(r.Map()) - return string(content) + return gconv.UnsafeBytesToStr(content) } -// 将记录结果转换为XML字符串 +// Xml converts to XML format content. func (r Record) Xml(rootTag ...string) string { content, _ := gparser.VarToXml(r.Map(), rootTag...) - return string(content) + return gconv.UnsafeBytesToStr(content) } -// 将Record转换为Map类型 +// Map converts to map[string]interface{}. func (r Record) Map() Map { m := make(map[string]interface{}) for k, v := range r { @@ -34,12 +35,13 @@ func (r Record) Map() Map { return m } -// 将Record转换为常用的gmap.StrAnyMap类型 +// GMap converts to a gmap. func (r Record) GMap() *gmap.StrAnyMap { return gmap.NewStrAnyMapFrom(r.Map()) } -// 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针 +// Struct converts to a struct. +// Note that the parameter should be type of *struct/**struct. func (r Record) Struct(pointer interface{}) error { if r == nil { return sql.ErrNoRows diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 41d852012..b10d00b83 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -14,19 +14,19 @@ import ( "github.com/gogf/gf/encoding/gparser" ) -// 将结果集转换为JSON字符串 +// Json converts to JSON format content. func (r Result) Json() string { content, _ := gparser.VarToJson(r.List()) return string(content) } -// 将结果集转换为XML字符串 +// Xml converts to XML format content. func (r Result) Xml(rootTag ...string) string { content, _ := gparser.VarToXml(r.List(), rootTag...) return string(content) } -// 将结果集转换为List类型返回,便于json处理 +// List converts to a List. func (r Result) List() List { l := make(List, len(r)) for k, v := range r { @@ -35,7 +35,7 @@ func (r Result) List() List { return l } -// 将结果列表按照指定的字段值做map[string]Map +// MapKeyStr converts to a map[string]Map of which key is specified by . func (r Result) MapKeyStr(key string) map[string]Map { m := make(map[string]Map) for _, item := range r { @@ -46,7 +46,7 @@ func (r Result) MapKeyStr(key string) map[string]Map { return m } -// 将结果列表按照指定的字段值做map[int]Map +// MapKeyInt converts to a map[int]Map of which key is specified by . func (r Result) MapKeyInt(key string) map[int]Map { m := make(map[int]Map) for _, item := range r { @@ -57,7 +57,7 @@ func (r Result) MapKeyInt(key string) map[int]Map { return m } -// 将结果列表按照指定的字段值做map[uint]Map +// MapKeyUint converts to a map[uint]Map of which key is specified by . func (r Result) MapKeyUint(key string) map[uint]Map { m := make(map[uint]Map) for _, item := range r { @@ -68,7 +68,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map { return m } -// 将结果列表按照指定的字段值做map[string]Record +// RecordKeyInt converts to a map[int]Record of which key is specified by . func (r Result) RecordKeyStr(key string) map[string]Record { m := make(map[string]Record) for _, item := range r { @@ -79,7 +79,7 @@ func (r Result) RecordKeyStr(key string) map[string]Record { return m } -// 将结果列表按照指定的字段值做map[int]Record +// RecordKeyInt converts to a map[int]Record of which key is specified by . func (r Result) RecordKeyInt(key string) map[int]Record { m := make(map[int]Record) for _, item := range r { @@ -90,7 +90,7 @@ func (r Result) RecordKeyInt(key string) map[int]Record { return m } -// 将结果列表按照指定的字段值做map[uint]Record +// RecordKeyUint converts to a map[uint]Record of which key is specified by . func (r Result) RecordKeyUint(key string) map[uint]Record { m := make(map[uint]Record) for _, item := range r { @@ -101,7 +101,8 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { return m } -// 将结果列表转换为指定对象的slice。 +// Structs converts to struct slice. +// Note that the parameter should be type of *[]struct/*[]*struct. func (r Result) Structs(pointer interface{}) (err error) { l := len(r) if l == 0 { diff --git a/frame/gins/gins.go b/frame/gins/gins.go index 01be3b1e2..2eba3fdd3 100644 --- a/frame/gins/gins.go +++ b/frame/gins/gins.go @@ -168,6 +168,7 @@ func Database(name ...string) gdb.DB { } instanceKey := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_DATABASE, group) db := instances.GetOrSetFuncLock(instanceKey, func() interface{} { + // Configuration already exists. if gdb.GetConfig(group) != nil { db, err := gdb.Instance(group) if err != nil { diff --git a/frame/gmvc/view.go b/frame/gmvc/view.go index 06a9a6595..f28f68fce 100644 --- a/frame/gmvc/view.go +++ b/frame/gmvc/view.go @@ -7,6 +7,7 @@ package gmvc import ( + "github.com/gogf/gf/frame/gins" "sync" "github.com/gogf/gf/util/gmode" @@ -26,7 +27,7 @@ type View struct { // 创建一个MVC请求中使用的视图对象 func NewView(w *ghttp.Response) *View { return &View{ - view: gview.New(), + view: gins.View(), data: make(gview.Params), response: w, } diff --git a/net/ghttp/ghttp_unit_middleware_test.go b/net/ghttp/ghttp_unit_middleware_test.go index e1fc2be1f..d97c1931a 100644 --- a/net/ghttp/ghttp_unit_middleware_test.go +++ b/net/ghttp/ghttp_unit_middleware_test.go @@ -615,3 +615,58 @@ func Test_Middleware_CORSAndAuth(t *testing.T) { gtest.Assert(client.GetContent("/api.v2/user/list", "token=123456"), "list") }) } + +func MiddlewareScope1(r *ghttp.Request) { + r.Response.Write("a") + r.Middleware.Next() + r.Response.Write("b") +} + +func MiddlewareScope2(r *ghttp.Request) { + r.Response.Write("c") + r.Middleware.Next() + r.Response.Write("d") +} + +func MiddlewareScope3(r *ghttp.Request) { + r.Response.Write("e") + r.Middleware.Next() + r.Response.Write("f") +} + +func Test_Middleware_Scope(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(MiddlewareScope1) + group.ALL("/scope1", func(r *ghttp.Request) { + r.Response.Write("1") + }) + group.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(MiddlewareScope2) + group.ALL("/scope2", func(r *ghttp.Request) { + r.Response.Write("2") + }) + }) + group.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(MiddlewareScope3) + group.ALL("/scope3", func(r *ghttp.Request) { + r.Response.Write("3") + }) + }) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + time.Sleep(100 * time.Millisecond) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + gtest.Assert(client.GetContent("/"), "Not Found") + gtest.Assert(client.GetContent("/scope1"), "a1b") + gtest.Assert(client.GetContent("/scope2"), "ac2db") + gtest.Assert(client.GetContent("/scope3"), "ae3fb") + }) +} diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index a046d6204..0860aaa3a 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -96,24 +96,48 @@ func SetTimeZone(zone string) error { return err } -// Nanosecond returns the timestamp in nanoseconds. -func Nanosecond() int64 { - return time.Now().UnixNano() +// Timestamp returns the timestamp in seconds. +func Timestamp() int64 { + return Now().Timestamp() } -// Microsecond returns the timestamp in microseconds. -func Microsecond() int64 { - return time.Now().UnixNano() / 1e3 +// TimestampMilli returns the timestamp in milliseconds. +func TimestampMilli() int64 { + return Now().TimestampMilli() } -// Millisecond returns the timestamp in milliseconds. -func Millisecond() int64 { - return time.Now().UnixNano() / 1e6 +// TimestampMicro returns the timestamp in microseconds. +func TimestampMicro() int64 { + return Now().TimestampMicro() +} + +// TimestampNano returns the timestamp in nanoseconds. +func TimestampNano() int64 { + return Now().TimestampNano() } // Second returns the timestamp in seconds. +// Deprecated, use Timestamp instead. func Second() int64 { - return time.Now().Unix() + return Timestamp() +} + +// Millisecond returns the timestamp in milliseconds. +// Deprecated, use TimestampMilli instead. +func Millisecond() int64 { + return TimestampMilli() +} + +// Microsecond returns the timestamp in microseconds. +// Deprecated, use TimestampMicro instead. +func Microsecond() int64 { + return TimestampMicro() +} + +// Nanosecond returns the timestamp in nanoseconds. +// Deprecated, use TimestampNano instead. +func Nanosecond() int64 { + return TimestampNano() } // Date returns current date in string like "2006-01-02". diff --git a/os/gtime/gtime_format.go b/os/gtime/gtime_format.go index 1d2b46e46..571a76708 100644 --- a/os/gtime/gtime_format.go +++ b/os/gtime/gtime_format.go @@ -126,7 +126,12 @@ func (t *Time) Format(format string) string { return buffer.String() } -// FormatTo formats and returns a new Time object with given custom . +// FormatNew formats and returns a new Time object with given custom . +func (t *Time) FormatNew(format string) *Time { + return NewFromStr(t.Format(format)) +} + +// FormatTo formats with given custom . func (t *Time) FormatTo(format string) *Time { t.Time = NewFromStr(t.Format(format)).Time return t @@ -137,7 +142,12 @@ func (t *Time) Layout(layout string) string { return t.Time.Format(layout) } -// Layout formats the time with stdlib layout and returns the new Time object. +// LayoutNew formats the time with stdlib layout and returns the new Time object. +func (t *Time) LayoutNew(layout string) *Time { + return NewFromStr(t.Layout(layout)) +} + +// LayoutTo formats with stdlib layout. func (t *Time) LayoutTo(layout string) *Time { t.Time = NewFromStr(t.Layout(layout)).Time return t diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index a04fb222a..d382fb4f5 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -78,24 +78,48 @@ func NewFromTimeStamp(timestamp int64) *Time { } } -// Second returns the timestamp in seconds. -func (t *Time) Second() int64 { +// Timestamp returns the timestamp in seconds. +func (t *Time) Timestamp() int64 { return t.UnixNano() / 1e9 } -// Nanosecond returns the timestamp in nanoseconds. -func (t *Time) Nanosecond() int64 { - return t.UnixNano() +// TimestampMilli returns the timestamp in milliseconds. +func (t *Time) TimestampMilli() int64 { + return t.UnixNano() / 1e6 } -// Microsecond returns the timestamp in microseconds. -func (t *Time) Microsecond() int64 { +// TimestampMicro returns the timestamp in microseconds. +func (t *Time) TimestampMicro() int64 { return t.UnixNano() / 1e3 } -// Millisecond returns the timestamp in milliseconds. -func (t *Time) Millisecond() int64 { - return t.UnixNano() / 1e6 +// TimestampNano returns the timestamp in nanoseconds. +func (t *Time) TimestampNano() int64 { + return t.UnixNano() +} + +// Second returns the second offset within the minute specified by t, +// in the range [0, 59]. +func (t *Time) Second() int { + return t.Time.Second() +} + +// Millisecond returns the millisecond offset within the second specified by t, +// in the range [0, 999]. +func (t *Time) Millisecond() int { + return t.Time.Nanosecond() / 1e6 +} + +// Microsecond returns the microsecond offset within the second specified by t, +// in the range [0, 999999]. +func (t *Time) Microsecond() int { + return t.Time.Nanosecond() / 1e3 +} + +// Nanosecond returns the nanosecond offset within the second specified by t, +// in the range [0, 999999999]. +func (t *Time) Nanosecond() int { + return t.Time.Nanosecond() } // String returns current time object as string. @@ -196,6 +220,25 @@ func (t *Time) Truncate(d time.Duration) *Time { return t } +// Equal reports whether t and u represent the same time instant. +// Two times can be equal even if they are in different locations. +// For example, 6:00 +0200 CEST and 4:00 UTC are Equal. +// See the documentation on the Time type for the pitfalls of using == with +// Time values; most code should use Equal instead. +func (t *Time) Equal(u *Time) bool { + return t.Time.Equal(u.Time) +} + +// Before reports whether the time instant t is before u. +func (t *Time) Before(u *Time) bool { + return t.Time.Before(u.Time) +} + +// After reports whether the time instant t is after u. +func (t *Time) After(u *Time) bool { + return t.Time.After(u.Time) +} + // Sub returns the duration t-u. If the result exceeds the maximum (or minimum) // value that can be stored in a Duration, the maximum (or minimum) duration // will be returned. diff --git a/os/gtime/gtime_z_unit_time_test.go b/os/gtime/gtime_z_unit_time_test.go index 071844c46..01b86e491 100644 --- a/os/gtime/gtime_z_unit_time_test.go +++ b/os/gtime/gtime_z_unit_time_test.go @@ -73,28 +73,28 @@ func Test_NewFromTimeStamp(t *testing.T) { func Test_Time_Second(t *testing.T) { gtest.Case(t, func() { timeTemp := gtime.Now() - gtest.Assert(timeTemp.Second(), timeTemp.Time.Unix()) + gtest.Assert(timeTemp.Second(), timeTemp.Time.Second()) }) } func Test_Time_Nanosecond(t *testing.T) { gtest.Case(t, func() { timeTemp := gtime.Now() - gtest.Assert(timeTemp.Nanosecond(), timeTemp.Time.UnixNano()) + gtest.Assert(timeTemp.Nanosecond(), timeTemp.Time.Nanosecond()) }) } func Test_Time_Microsecond(t *testing.T) { gtest.Case(t, func() { timeTemp := gtime.Now() - gtest.Assert(timeTemp.Microsecond(), timeTemp.Time.UnixNano()/1e3) + gtest.Assert(timeTemp.Microsecond(), timeTemp.Time.Nanosecond()/1e3) }) } func Test_Time_Millisecond(t *testing.T) { gtest.Case(t, func() { timeTemp := gtime.Now() - gtest.Assert(timeTemp.Millisecond(), timeTemp.Time.UnixNano()/1e6) + gtest.Assert(timeTemp.Millisecond(), timeTemp.Time.Nanosecond()/1e6) }) } diff --git a/os/gview/gview_doparse.go b/os/gview/gview_doparse.go index a067b37fe..bee5cb703 100644 --- a/os/gview/gview_doparse.go +++ b/os/gview/gview_doparse.go @@ -15,6 +15,7 @@ import ( "github.com/gogf/gf/os/gfcache" "github.com/gogf/gf/os/gfsnotify" "github.com/gogf/gf/os/gmlock" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "strconv" "strings" @@ -135,11 +136,10 @@ func (view *View) Parse(file string, params ...Params) (result string, err error if err := tpl.Execute(buffer, variables); err != nil { return "", err } - return view.i18nTranslate(buffer.String(), variables), nil // TODO any graceful plan to replace ""? - //result = gstr.Replace(buffer.String(), "", "") - //result = view.i18nTranslate(result, variables) - //return result, nil + result = gstr.Replace(buffer.String(), "", "") + result = view.i18nTranslate(result, variables) + return result, nil } // ParseDefault parses the default template file with params. @@ -201,11 +201,10 @@ func (view *View) ParseContent(content string, params ...Params) (string, error) if err := tpl.Execute(buffer, variables); err != nil { return "", err } - return view.i18nTranslate(buffer.String(), variables), nil // TODO any graceful plan to replace ""? - //result := gstr.Replace(buffer.String(), "", "") - //result = view.i18nTranslate(result, variables) - //return result, nil + result := gstr.Replace(buffer.String(), "", "") + result = view.i18nTranslate(result, variables) + return result, nil } // getTemplate returns the template object associated with given template file .