Repository: gogf/gf

Issue:
os/orm: The "With" static association feature not supported multiple conditional ORDER with version 2.9.0
### Go version

1.20

### GoFrame version

2.9.0

### Can this bug be reproduced with the latest release?

Option Yes

### What did you do?

<img width="1039" alt="Image" src="https://github.com/user-attachments/assets/8ecef5e3-ea12-4899-9ec9-1a22cd1d7866" />

### What did you see happen?

The order condition parse error and have an error in SQL syntax
`is_route_menu desc  rank asc  id asc`

<img width="1898" alt="Image" src="https://github.com/user-attachments/assets/7b93fe23-6a18-497b-897a-508389b9470d" />

### What did you expect to see?

is_route_menu desc ,rank asc ,id asc

Discussion:
@fainc: Another project with gf version v2.7.1 supports this feature, it may be a side effect of the upgrade changes.
Or is this the intended design? 

Solution patch:
diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go
index fcd36aeec..8e7e333dd 100644
--- a/database/gdb/gdb_model_with.go
+++ b/database/gdb/gdb_model_with.go
@@ -333,7 +333,12 @@ func (m *Model) parseWithTagInFieldStruct(field gstructs.Field) (output parseWit
 			key = array[0]
 			data[key] = gstr.Trim(array[1])
 		} else {
-			data[key] += " " + gstr.Trim(v)
+			if key == OrmTagForWithOrder {
+				// supporting multiple order fields
+				data[key] += "," + gstr.Trim(v)
+			} else {
+				data[key] += " " + gstr.Trim(v)
+			}
 		}
 	}
 	output.With = data[OrmTagForWith]
diff --git a/util/gconv/internal/converter/converter_scan.go b/util/gconv/internal/converter/converter_scan.go
index 9630ffb2a..a3dff798f 100644
--- a/util/gconv/internal/converter/converter_scan.go
+++ b/util/gconv/internal/converter/converter_scan.go
@@ -159,6 +159,7 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ...ScanOption) (er
 			dstElemType = dstPointerReflectValueElem.Type().Elem()
 			dstElemKind = dstElemType.Kind()
 		)
+
 		// The slice element might be a pointer type
 		if dstElemKind == reflect.Ptr {
 			dstElemType = dstElemType.Elem()
@@ -209,9 +210,12 @@ func (c *Converter) Scan(srcValue any, dstPointer any, option ...ScanOption) (er
 					}
 					newSlice.Index(i).SetBool(v)
 				default:
-					return c.Scan(
+					err = c.Scan(
 						srcElem, newSlice.Index(i).Addr().Interface(), option...,
 					)
+					if err != nil && !scanOption.ContinueOnError {
+						return err
+					}
 				}
 			}
 			dstPointerReflectValueElem.Set(newSlice)



Test patch:
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 64a491991..30390b4cb 100644
--- a/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go
+++ b/contrib/drivers/mysql/mysql_z_unit_feature_with_test.go
@@ -1888,3 +1888,111 @@ PRIMARY KEY (user_id)
 		t.AssertNil(user3.UserDetail)
 	})
 }
+
+func Test_Table_Relation_WithAll_Order(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, order:user_id asc,address desc, 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/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go
index eddfc9bbc..86f2d2aca 100644
--- a/util/gconv/gconv_z_unit_scan_test.go
+++ b/util/gconv/gconv_z_unit_scan_test.go
@@ -11,6 +11,7 @@ import (
 	"testing"
 
 	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/encoding/gjson"
 	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/os/gtime"
@@ -367,10 +368,10 @@ func TestScan(t *testing.T) {
 			t.Assert(test["Name"], scanExpects.structSub.Place)
 			t.Assert(test["Place"], scanExpects.structSub.Name)
 
-			//t.Logf("%#v", test)
+			// t.Logf("%#v", test)
 			err = gconv.Scan(test, &scanExpects.structSubPtr, mapParameter)
 			t.AssertNil(err)
-			//t.Logf("%#v", scanExpects.structSubPtr)
+			// t.Logf("%#v", scanExpects.structSubPtr)
 			t.Assert(test["Name"], scanExpects.structSubPtr.Place)
 			t.Assert(test["Place"], scanExpects.structSubPtr.Name)
 		}
@@ -421,3 +422,22 @@ func TestScanEmptyStringToCustomType(t *testing.T) {
 		t.Assert(len(req.Types), 0)
 	})
 }
+
+func TestScanDeepSlice(t *testing.T) {
+	gtest.C(t, func(t *gtest.T) {
+		var (
+			req   [][]int
+			req2  [][][]int
+			data1 = gjson.New("[[1,2,3],[4,5,6]]")
+			data2 = gjson.New("[[[1,2,3]],[[4,5,6]]]")
+		)
+		err := data1.Scan(&req)
+		t.AssertNil(err)
+		err = gconv.Scan(data1.String(), &req)
+		t.AssertNil(err)
+		err = data2.Scan(&req2)
+		t.AssertNil(err)
+		t.Assert(len(req), 2)
+		t.Assert(len(req2), 2)
+	})
+}