diff --git a/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go b/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go index 1b38d2276e..b32af2fb1e 100644 --- a/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go @@ -10,6 +10,8 @@ import ( "fmt" "testing" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/test/gtest" @@ -1988,3 +1990,111 @@ PRIMARY KEY (id) t.Assert(user.UserScores[4].Score, 5) }) } + +func Test_Table_Relation_WithAll_Unscoped(t *testing.T) { + var ( + tableUser = "user101" + tableUserDetail = "user_detail101" + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +user_id int(10) unsigned NOT NULL, +address varchar(45) NOT NULL, +deleted_at datetime default NULL , +PRIMARY KEY (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail101"` + UserID int `json:"user_id"` + Address string `json:"address"` + DeletedAt *gtime.Time `json:"deleted_at"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:user101"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id"` + } + type UserWithDeletedDetail struct { + gmeta.Meta `orm:"table:user101"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id, unscoped:true"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + // Delete detail where i = 3 + if i == 3 { + _, err = db.Delete(ctx, tableUserDetail, g.Map{ + "user_id": i, + }) + } + gtest.AssertNil(err) + } + gtest.C(t, func(t *gtest.T) { + var user0 User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user0) + t.AssertNil(err) + t.Assert(user0.ID, 4) + t.AssertNE(user0.UserDetail, nil) + t.AssertNil(user0.UserDetail.DeletedAt) + t.Assert(user0.UserDetail.UserID, 4) + t.Assert(user0.UserDetail.Address, `address_4`) + + var user1 User + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user1) + t.AssertNil(err) + t.Assert(user1.ID, 3) + t.AssertNil(user1.UserDetail) + + var user2 UserWithDeletedDetail + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user2) + t.AssertNil(err) + t.Assert(user2.ID, 3) + t.AssertNE(user2.UserDetail, nil) + t.AssertNE(user2.UserDetail.DeletedAt, nil) + t.Assert(user2.UserDetail.UserID, 3) + t.Assert(user2.UserDetail.Address, `address_3`) + + // Unscoped outside test + var user3 User + err = db.Model(tableUser).Unscoped().WithAll().Where("id", 3).Scan(&user3) + t.AssertNil(err) + t.Assert(user3.ID, 3) + t.AssertNil(user3.UserDetail) + }) +} diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 00a00462a2..99ff19933b 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -58,12 +58,13 @@ type iTableName interface { } const ( - OrmTagForStruct = "orm" - OrmTagForTable = "table" - OrmTagForWith = "with" - OrmTagForWithWhere = "where" - OrmTagForWithOrder = "order" - OrmTagForDo = "do" + OrmTagForStruct = "orm" + OrmTagForTable = "table" + OrmTagForWith = "with" + OrmTagForWithWhere = "where" + OrmTagForWithOrder = "order" + OrmTagForWithUnscoped = "unscoped" + OrmTagForDo = "do" ) var ( diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 4d80d919ee..e259d11868 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -162,6 +162,9 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if parsedTagOutput.Order != "" { model = model.Order(parsedTagOutput.Order) } + if parsedTagOutput.Unscoped == "true" { + model = model.Unscoped() + } // With cache feature. if m.cacheEnabled && m.cacheOption.Name == "" { model = model.Cache(m.cacheOption) @@ -277,6 +280,9 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { if parsedTagOutput.Order != "" { model = model.Order(parsedTagOutput.Order) } + if parsedTagOutput.Unscoped == "true" { + model = model.Unscoped() + } // With cache feature. if m.cacheEnabled && m.cacheOption.Name == "" { model = model.Cache(m.cacheOption) @@ -293,9 +299,10 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } type parseWithTagInFieldStructOutput struct { - With string - Where string - Order string + With string + Where string + Order string + Unscoped string } func (m *Model) parseWithTagInFieldStruct(field gstructs.Field) (output parseWithTagInFieldStructOutput) { @@ -320,5 +327,6 @@ func (m *Model) parseWithTagInFieldStruct(field gstructs.Field) (output parseWit output.With = data[OrmTagForWith] output.Where = data[OrmTagForWithWhere] output.Order = data[OrmTagForWithOrder] + output.Unscoped = data[OrmTagForWithUnscoped] return }