gconv_scan.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gconv
  7. import (
  8. "database/sql"
  9. "reflect"
  10. "github.com/gogf/gf/v2/errors/gcode"
  11. "github.com/gogf/gf/v2/errors/gerror"
  12. "github.com/gogf/gf/v2/internal/utils"
  13. "github.com/gogf/gf/v2/os/gstructs"
  14. )
  15. // Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
  16. // with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
  17. //
  18. // It calls function `doMapToMap` internally if `pointer` is type of *map for converting.
  19. // It calls function `doMapToMaps` internally if `pointer` is type of *[]map/*[]*map for converting.
  20. // It calls function `doStruct` internally if `pointer` is type of *struct/**struct for converting.
  21. // It calls function `doStructs` internally if `pointer` is type of *[]struct/*[]*struct for converting.
  22. func Scan(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
  23. var (
  24. pointerType reflect.Type
  25. pointerKind reflect.Kind
  26. pointerValue reflect.Value
  27. )
  28. if v, ok := pointer.(reflect.Value); ok {
  29. pointerValue = v
  30. pointerType = v.Type()
  31. } else {
  32. pointerValue = reflect.ValueOf(pointer)
  33. pointerType = reflect.TypeOf(pointer) // Do not use pointerValue.Type() as pointerValue might be zero.
  34. }
  35. if pointerType == nil {
  36. return gerror.NewCode(gcode.CodeInvalidParameter, "parameter pointer should not be nil")
  37. }
  38. pointerKind = pointerType.Kind()
  39. if pointerKind != reflect.Ptr {
  40. if pointerValue.CanAddr() {
  41. pointerValue = pointerValue.Addr()
  42. pointerType = pointerValue.Type()
  43. pointerKind = pointerType.Kind()
  44. } else {
  45. return gerror.NewCodef(
  46. gcode.CodeInvalidParameter,
  47. "params should be type of pointer, but got type: %v",
  48. pointerType,
  49. )
  50. }
  51. }
  52. // Direct assignment checks!
  53. var (
  54. paramsType reflect.Type
  55. paramsValue reflect.Value
  56. )
  57. if v, ok := params.(reflect.Value); ok {
  58. paramsValue = v
  59. paramsType = paramsValue.Type()
  60. } else {
  61. paramsValue = reflect.ValueOf(params)
  62. paramsType = reflect.TypeOf(params) // Do not use paramsValue.Type() as paramsValue might be zero.
  63. }
  64. // If `params` and `pointer` are the same type, the do directly assignment.
  65. // For performance enhancement purpose.
  66. var (
  67. pointerValueElem = pointerValue.Elem()
  68. )
  69. if pointerValueElem.CanSet() && paramsType == pointerValueElem.Type() {
  70. pointerValueElem.Set(paramsValue)
  71. return nil
  72. }
  73. // Converting.
  74. var (
  75. pointerElem = pointerType.Elem()
  76. pointerElemKind = pointerElem.Kind()
  77. keyToAttributeNameMapping map[string]string
  78. )
  79. if len(paramKeyToAttrMap) > 0 {
  80. keyToAttributeNameMapping = paramKeyToAttrMap[0]
  81. }
  82. switch pointerElemKind {
  83. case reflect.Map:
  84. return doMapToMap(params, pointer, paramKeyToAttrMap...)
  85. case reflect.Array, reflect.Slice:
  86. var (
  87. sliceElem = pointerElem.Elem()
  88. sliceElemKind = sliceElem.Kind()
  89. )
  90. for sliceElemKind == reflect.Ptr {
  91. sliceElem = sliceElem.Elem()
  92. sliceElemKind = sliceElem.Kind()
  93. }
  94. if sliceElemKind == reflect.Map {
  95. return doMapToMaps(params, pointer, paramKeyToAttrMap...)
  96. }
  97. return doStructs(params, pointer, keyToAttributeNameMapping, "")
  98. default:
  99. return doStruct(params, pointer, keyToAttributeNameMapping, "")
  100. }
  101. }
  102. // ScanList converts `structSlice` to struct slice which contains other complex struct attributes.
  103. // Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
  104. //
  105. // Usage example 1: Normal attribute struct relation:
  106. //
  107. // type EntityUser struct {
  108. // Uid int
  109. // Name string
  110. // }
  111. //
  112. // type EntityUserDetail struct {
  113. // Uid int
  114. // Address string
  115. // }
  116. //
  117. // type EntityUserScores struct {
  118. // Id int
  119. // Uid int
  120. // Score int
  121. // Course string
  122. // }
  123. //
  124. // type Entity struct {
  125. // User *EntityUser
  126. // UserDetail *EntityUserDetail
  127. // UserScores []*EntityUserScores
  128. // }
  129. //
  130. // var users []*Entity
  131. // var userRecords = EntityUser{Uid: 1, Name:"john"}
  132. // var detailRecords = EntityUser{Uid: 1, Address: "chengdu"}
  133. // var scoresRecords = EntityUser{Id: 1, Uid: 1, Score: 100, Course: "math"}
  134. // ScanList(userRecords, &users, "User")
  135. // ScanList(userRecords, &users, "User", "uid")
  136. // ScanList(detailRecords, &users, "UserDetail", "User", "uid:Uid")
  137. // ScanList(scoresRecords, &users, "UserScores", "User", "uid:Uid")
  138. // ScanList(scoresRecords, &users, "UserScores", "User", "uid")
  139. //
  140. // Usage example 2: Embedded attribute struct relation:
  141. //
  142. // type EntityUser struct {
  143. // Uid int
  144. // Name string
  145. // }
  146. //
  147. // type EntityUserDetail struct {
  148. // Uid int
  149. // Address string
  150. // }
  151. //
  152. // type EntityUserScores struct {
  153. // Id int
  154. // Uid int
  155. // Score int
  156. // }
  157. //
  158. // type Entity struct {
  159. // EntityUser
  160. // UserDetail EntityUserDetail
  161. // UserScores []EntityUserScores
  162. // }
  163. //
  164. // var userRecords = EntityUser{Uid: 1, Name:"john"}
  165. // var detailRecords = EntityUser{Uid: 1, Address: "chengdu"}
  166. // var scoresRecords = EntityUser{Id: 1, Uid: 1, Score: 100, Course: "math"}
  167. // ScanList(userRecords, &users)
  168. // ScanList(detailRecords, &users, "UserDetail", "uid")
  169. // ScanList(scoresRecords, &users, "UserScores", "uid")
  170. //
  171. // The parameters "User/UserDetail/UserScores" in the example codes specify the target attribute struct
  172. // that current result will be bound to.
  173. //
  174. // The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
  175. // struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute
  176. // name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with
  177. // given `relation` parameter.
  178. //
  179. // See the example or unit testing cases for clear understanding for this function.
  180. func ScanList(structSlice interface{}, structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) {
  181. var (
  182. relationAttrName string
  183. relationFields string
  184. )
  185. switch len(relationAttrNameAndFields) {
  186. case 2:
  187. relationAttrName = relationAttrNameAndFields[0]
  188. relationFields = relationAttrNameAndFields[1]
  189. case 1:
  190. relationFields = relationAttrNameAndFields[0]
  191. }
  192. return doScanList(structSlice, structSlicePointer, bindToAttrName, relationAttrName, relationFields)
  193. }
  194. // doScanList converts `structSlice` to struct slice which contains other complex struct attributes recursively.
  195. // Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
  196. func doScanList(
  197. structSlice interface{}, structSlicePointer interface{}, bindToAttrName, relationAttrName, relationFields string,
  198. ) (err error) {
  199. var (
  200. maps = Maps(structSlice)
  201. )
  202. if len(maps) == 0 {
  203. return nil
  204. }
  205. // Necessary checks for parameters.
  206. if bindToAttrName == "" {
  207. return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
  208. }
  209. if relationAttrName == "." {
  210. relationAttrName = ""
  211. }
  212. var (
  213. reflectValue = reflect.ValueOf(structSlicePointer)
  214. reflectKind = reflectValue.Kind()
  215. )
  216. if reflectKind == reflect.Interface {
  217. reflectValue = reflectValue.Elem()
  218. reflectKind = reflectValue.Kind()
  219. }
  220. if reflectKind != reflect.Ptr {
  221. return gerror.NewCodef(
  222. gcode.CodeInvalidParameter,
  223. "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
  224. reflectKind,
  225. )
  226. }
  227. reflectValue = reflectValue.Elem()
  228. reflectKind = reflectValue.Kind()
  229. if reflectKind != reflect.Slice && reflectKind != reflect.Array {
  230. return gerror.NewCodef(
  231. gcode.CodeInvalidParameter,
  232. "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
  233. reflectKind,
  234. )
  235. }
  236. length := len(maps)
  237. if length == 0 {
  238. // The pointed slice is not empty.
  239. if reflectValue.Len() > 0 {
  240. // It here checks if it has struct item, which is already initialized.
  241. // It then returns error to warn the developer its empty and no conversion.
  242. if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
  243. return sql.ErrNoRows
  244. }
  245. }
  246. // Do nothing for empty struct slice.
  247. return nil
  248. }
  249. var (
  250. arrayValue reflect.Value // Like: []*Entity
  251. arrayItemType reflect.Type // Like: *Entity
  252. reflectType = reflect.TypeOf(structSlicePointer)
  253. )
  254. if reflectValue.Len() > 0 {
  255. arrayValue = reflectValue
  256. } else {
  257. arrayValue = reflect.MakeSlice(reflectType.Elem(), length, length)
  258. }
  259. // Slice element item.
  260. arrayItemType = arrayValue.Index(0).Type()
  261. // Relation variables.
  262. var (
  263. relationDataMap map[string]interface{}
  264. relationFromFieldName string // Eg: relationKV: id:uid -> id
  265. relationBindToFieldName string // Eg: relationKV: id:uid -> uid
  266. )
  267. if len(relationFields) > 0 {
  268. // The relation key string of table field name and attribute name
  269. // can be joined with char '=' or ':'.
  270. array := utils.SplitAndTrim(relationFields, "=")
  271. if len(array) == 1 {
  272. // Compatible with old splitting char ':'.
  273. array = utils.SplitAndTrim(relationFields, ":")
  274. }
  275. if len(array) == 1 {
  276. // The relation names are the same.
  277. array = []string{relationFields, relationFields}
  278. }
  279. if len(array) == 2 {
  280. // Defined table field to relation attribute name.
  281. // Like:
  282. // uid:Uid
  283. // uid:UserId
  284. relationFromFieldName = array[0]
  285. relationBindToFieldName = array[1]
  286. if key, _ := utils.MapPossibleItemByKey(maps[0], relationFromFieldName); key == "" {
  287. return gerror.NewCodef(
  288. gcode.CodeInvalidParameter,
  289. `cannot find possible related table field name "%s" from given relation fields "%s"`,
  290. relationFromFieldName,
  291. relationFields,
  292. )
  293. } else {
  294. relationFromFieldName = key
  295. }
  296. } else {
  297. return gerror.NewCode(
  298. gcode.CodeInvalidParameter,
  299. `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`,
  300. )
  301. }
  302. if relationFromFieldName != "" {
  303. // Note that the value might be type of slice.
  304. relationDataMap = utils.ListToMapByKey(maps, relationFromFieldName)
  305. }
  306. if len(relationDataMap) == 0 {
  307. return gerror.NewCodef(
  308. gcode.CodeInvalidParameter,
  309. `cannot find the relation data map, maybe invalid relation fields given "%v"`,
  310. relationFields,
  311. )
  312. }
  313. }
  314. // Bind to target attribute.
  315. var (
  316. ok bool
  317. bindToAttrValue reflect.Value
  318. bindToAttrKind reflect.Kind
  319. bindToAttrType reflect.Type
  320. bindToAttrField reflect.StructField
  321. )
  322. if arrayItemType.Kind() == reflect.Ptr {
  323. if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok {
  324. return gerror.NewCodef(
  325. gcode.CodeInvalidParameter,
  326. `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
  327. bindToAttrName,
  328. )
  329. }
  330. } else {
  331. if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok {
  332. return gerror.NewCodef(
  333. gcode.CodeInvalidParameter,
  334. `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
  335. bindToAttrName,
  336. )
  337. }
  338. }
  339. bindToAttrType = bindToAttrField.Type
  340. bindToAttrKind = bindToAttrType.Kind()
  341. // Bind to relation conditions.
  342. var (
  343. relationFromAttrValue reflect.Value
  344. relationFromAttrField reflect.Value
  345. relationBindToFieldNameChecked bool
  346. )
  347. for i := 0; i < arrayValue.Len(); i++ {
  348. arrayElemValue := arrayValue.Index(i)
  349. // The FieldByName should be called on non-pointer reflect.Value.
  350. if arrayElemValue.Kind() == reflect.Ptr {
  351. // Like: []*Entity
  352. arrayElemValue = arrayElemValue.Elem()
  353. if !arrayElemValue.IsValid() {
  354. // The element is nil, then create one and set it to the slice.
  355. // The "reflect.New(itemType.Elem())" creates a new element and returns the address of it.
  356. // For example:
  357. // reflect.New(itemType.Elem()) => *Entity
  358. // reflect.New(itemType.Elem()).Elem() => Entity
  359. arrayElemValue = reflect.New(arrayItemType.Elem()).Elem()
  360. arrayValue.Index(i).Set(arrayElemValue.Addr())
  361. }
  362. } else {
  363. // Like: []Entity
  364. }
  365. bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName)
  366. if relationAttrName != "" {
  367. // Attribute value of current slice element.
  368. relationFromAttrValue = arrayElemValue.FieldByName(relationAttrName)
  369. if relationFromAttrValue.Kind() == reflect.Ptr {
  370. relationFromAttrValue = relationFromAttrValue.Elem()
  371. }
  372. } else {
  373. // Current slice element.
  374. relationFromAttrValue = arrayElemValue
  375. }
  376. if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
  377. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  378. }
  379. // Check and find possible bind to attribute name.
  380. if relationFields != "" && !relationBindToFieldNameChecked {
  381. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  382. if !relationFromAttrField.IsValid() {
  383. var (
  384. fieldMap, _ = gstructs.FieldMap(gstructs.FieldMapInput{
  385. Pointer: relationFromAttrValue,
  386. RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
  387. })
  388. )
  389. if key, _ := utils.MapPossibleItemByKey(Map(fieldMap), relationBindToFieldName); key == "" {
  390. return gerror.NewCodef(
  391. gcode.CodeInvalidParameter,
  392. `cannot find possible related attribute name "%s" from given relation fields "%s"`,
  393. relationBindToFieldName,
  394. relationFields,
  395. )
  396. } else {
  397. relationBindToFieldName = key
  398. }
  399. }
  400. relationBindToFieldNameChecked = true
  401. }
  402. switch bindToAttrKind {
  403. case reflect.Array, reflect.Slice:
  404. if len(relationDataMap) > 0 {
  405. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  406. if relationFromAttrField.IsValid() {
  407. // results := make(Result, 0)
  408. results := make([]interface{}, 0)
  409. for _, v := range SliceAny(relationDataMap[String(relationFromAttrField.Interface())]) {
  410. item := v
  411. results = append(results, item)
  412. }
  413. if err = Structs(results, bindToAttrValue.Addr()); err != nil {
  414. return err
  415. }
  416. } else {
  417. // Maybe the attribute does not exist yet.
  418. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  419. }
  420. } else {
  421. return gerror.NewCodef(
  422. gcode.CodeInvalidParameter,
  423. `relationKey should not be empty as field "%s" is slice`,
  424. bindToAttrName,
  425. )
  426. }
  427. case reflect.Ptr:
  428. var element reflect.Value
  429. if bindToAttrValue.IsNil() {
  430. element = reflect.New(bindToAttrType.Elem()).Elem()
  431. } else {
  432. element = bindToAttrValue.Elem()
  433. }
  434. if len(relationDataMap) > 0 {
  435. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  436. if relationFromAttrField.IsValid() {
  437. v := relationDataMap[String(relationFromAttrField.Interface())]
  438. if v == nil {
  439. // There's no relational data.
  440. continue
  441. }
  442. if utils.IsSlice(v) {
  443. if err = Struct(SliceAny(v)[0], element); err != nil {
  444. return err
  445. }
  446. } else {
  447. if err = Struct(v, element); err != nil {
  448. return err
  449. }
  450. }
  451. } else {
  452. // Maybe the attribute does not exist yet.
  453. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  454. }
  455. } else {
  456. if i >= len(maps) {
  457. // There's no relational data.
  458. continue
  459. }
  460. v := maps[i]
  461. if v == nil {
  462. // There's no relational data.
  463. continue
  464. }
  465. if err = Struct(v, element); err != nil {
  466. return err
  467. }
  468. }
  469. bindToAttrValue.Set(element.Addr())
  470. case reflect.Struct:
  471. if len(relationDataMap) > 0 {
  472. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  473. if relationFromAttrField.IsValid() {
  474. relationDataItem := relationDataMap[String(relationFromAttrField.Interface())]
  475. if relationDataItem == nil {
  476. // There's no relational data.
  477. continue
  478. }
  479. if utils.IsSlice(relationDataItem) {
  480. if err = Struct(SliceAny(relationDataItem)[0], bindToAttrValue); err != nil {
  481. return err
  482. }
  483. } else {
  484. if err = Struct(relationDataItem, bindToAttrValue); err != nil {
  485. return err
  486. }
  487. }
  488. } else {
  489. // Maybe the attribute does not exist yet.
  490. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  491. }
  492. } else {
  493. if i >= len(maps) {
  494. // There's no relational data.
  495. continue
  496. }
  497. relationDataItem := maps[i]
  498. if relationDataItem == nil {
  499. // There's no relational data.
  500. continue
  501. }
  502. if err = Struct(relationDataItem, bindToAttrValue); err != nil {
  503. return err
  504. }
  505. }
  506. default:
  507. return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
  508. }
  509. }
  510. reflect.ValueOf(structSlicePointer).Elem().Set(arrayValue)
  511. return nil
  512. }