gconv_scan.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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{}, mapping ...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(mapping) > 0 {
  80. keyToAttributeNameMapping = mapping[0]
  81. }
  82. switch pointerElemKind {
  83. case reflect.Map:
  84. return doMapToMap(params, pointer, mapping...)
  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, mapping...)
  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. // ScanList(records, &users, "User")
  132. // ScanList(records, &users, "User", "uid")
  133. // ScanList(records, &users, "UserDetail", "User", "uid:Uid")
  134. // ScanList(records, &users, "UserScores", "User", "uid:Uid")
  135. // ScanList(records, &users, "UserScores", "User", "uid")
  136. //
  137. // Usage example 2: Embedded attribute struct relation:
  138. //
  139. // type EntityUser struct {
  140. // Uid int
  141. // Name string
  142. // }
  143. //
  144. // type EntityUserDetail struct {
  145. // Uid int
  146. // Address string
  147. // }
  148. //
  149. // type EntityUserScores struct {
  150. // Id int
  151. // Uid int
  152. // Score int
  153. // }
  154. //
  155. // type Entity struct {
  156. // EntityUser
  157. // UserDetail EntityUserDetail
  158. // UserScores []EntityUserScores
  159. // }
  160. //
  161. // var users []*Entity
  162. // ScanList(records, &users)
  163. // ScanList(records, &users, "UserDetail", "uid")
  164. // ScanList(records, &users, "UserScores", "uid")
  165. //
  166. // The parameters "User/UserDetail/UserScores" in the example codes specify the target attribute struct
  167. // that current result will be bound to.
  168. //
  169. // The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
  170. // struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute
  171. // name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with
  172. // given `relation` parameter.
  173. //
  174. // See the example or unit testing cases for clear understanding for this function.
  175. func ScanList(structSlice interface{}, structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) {
  176. var (
  177. relationAttrName string
  178. relationFields string
  179. )
  180. switch len(relationAttrNameAndFields) {
  181. case 2:
  182. relationAttrName = relationAttrNameAndFields[0]
  183. relationFields = relationAttrNameAndFields[1]
  184. case 1:
  185. relationFields = relationAttrNameAndFields[0]
  186. }
  187. return doScanList(structSlice, structSlicePointer, bindToAttrName, relationAttrName, relationFields)
  188. }
  189. // doScanList converts `structSlice` to struct slice which contains other complex struct attributes recursively.
  190. // Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
  191. func doScanList(
  192. structSlice interface{}, structSlicePointer interface{}, bindToAttrName, relationAttrName, relationFields string,
  193. ) (err error) {
  194. var (
  195. maps = Maps(structSlice)
  196. )
  197. if len(maps) == 0 {
  198. return nil
  199. }
  200. // Necessary checks for parameters.
  201. if bindToAttrName == "" {
  202. return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
  203. }
  204. if relationAttrName == "." {
  205. relationAttrName = ""
  206. }
  207. var (
  208. reflectValue = reflect.ValueOf(structSlicePointer)
  209. reflectKind = reflectValue.Kind()
  210. )
  211. if reflectKind == reflect.Interface {
  212. reflectValue = reflectValue.Elem()
  213. reflectKind = reflectValue.Kind()
  214. }
  215. if reflectKind != reflect.Ptr {
  216. return gerror.NewCodef(
  217. gcode.CodeInvalidParameter,
  218. "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
  219. reflectKind,
  220. )
  221. }
  222. reflectValue = reflectValue.Elem()
  223. reflectKind = reflectValue.Kind()
  224. if reflectKind != reflect.Slice && reflectKind != reflect.Array {
  225. return gerror.NewCodef(
  226. gcode.CodeInvalidParameter,
  227. "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
  228. reflectKind,
  229. )
  230. }
  231. length := len(maps)
  232. if length == 0 {
  233. // The pointed slice is not empty.
  234. if reflectValue.Len() > 0 {
  235. // It here checks if it has struct item, which is already initialized.
  236. // It then returns error to warn the developer its empty and no conversion.
  237. if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
  238. return sql.ErrNoRows
  239. }
  240. }
  241. // Do nothing for empty struct slice.
  242. return nil
  243. }
  244. var (
  245. arrayValue reflect.Value // Like: []*Entity
  246. arrayItemType reflect.Type // Like: *Entity
  247. reflectType = reflect.TypeOf(structSlicePointer)
  248. )
  249. if reflectValue.Len() > 0 {
  250. arrayValue = reflectValue
  251. } else {
  252. arrayValue = reflect.MakeSlice(reflectType.Elem(), length, length)
  253. }
  254. // Slice element item.
  255. arrayItemType = arrayValue.Index(0).Type()
  256. // Relation variables.
  257. var (
  258. relationDataMap map[string]interface{}
  259. relationFromFieldName string // Eg: relationKV: id:uid -> id
  260. relationBindToFieldName string // Eg: relationKV: id:uid -> uid
  261. )
  262. if len(relationFields) > 0 {
  263. // The relation key string of table filed name and attribute name
  264. // can be joined with char '=' or ':'.
  265. array := utils.SplitAndTrim(relationFields, "=")
  266. if len(array) == 1 {
  267. // Compatible with old splitting char ':'.
  268. array = utils.SplitAndTrim(relationFields, ":")
  269. }
  270. if len(array) == 1 {
  271. // The relation names are the same.
  272. array = []string{relationFields, relationFields}
  273. }
  274. if len(array) == 2 {
  275. // Defined table field to relation attribute name.
  276. // Like:
  277. // uid:Uid
  278. // uid:UserId
  279. relationFromFieldName = array[0]
  280. relationBindToFieldName = array[1]
  281. if key, _ := utils.MapPossibleItemByKey(maps[0], relationFromFieldName); key == "" {
  282. return gerror.NewCodef(
  283. gcode.CodeInvalidParameter,
  284. `cannot find possible related table field name "%s" from given relation fields "%s"`,
  285. relationFromFieldName,
  286. relationFields,
  287. )
  288. } else {
  289. relationFromFieldName = key
  290. }
  291. } else {
  292. return gerror.NewCode(
  293. gcode.CodeInvalidParameter,
  294. `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`,
  295. )
  296. }
  297. if relationFromFieldName != "" {
  298. // Note that the value might be type of slice.
  299. relationDataMap = utils.ListToMapByKey(maps, relationFromFieldName)
  300. }
  301. if len(relationDataMap) == 0 {
  302. return gerror.NewCodef(
  303. gcode.CodeInvalidParameter,
  304. `cannot find the relation data map, maybe invalid relation fields given "%v"`,
  305. relationFields,
  306. )
  307. }
  308. }
  309. // Bind to target attribute.
  310. var (
  311. ok bool
  312. bindToAttrValue reflect.Value
  313. bindToAttrKind reflect.Kind
  314. bindToAttrType reflect.Type
  315. bindToAttrField reflect.StructField
  316. )
  317. if arrayItemType.Kind() == reflect.Ptr {
  318. if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok {
  319. return gerror.NewCodef(
  320. gcode.CodeInvalidParameter,
  321. `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
  322. bindToAttrName,
  323. )
  324. }
  325. } else {
  326. if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok {
  327. return gerror.NewCodef(
  328. gcode.CodeInvalidParameter,
  329. `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
  330. bindToAttrName,
  331. )
  332. }
  333. }
  334. bindToAttrType = bindToAttrField.Type
  335. bindToAttrKind = bindToAttrType.Kind()
  336. // Bind to relation conditions.
  337. var (
  338. relationFromAttrValue reflect.Value
  339. relationFromAttrField reflect.Value
  340. relationBindToFieldNameChecked bool
  341. )
  342. for i := 0; i < arrayValue.Len(); i++ {
  343. arrayElemValue := arrayValue.Index(i)
  344. // The FieldByName should be called on non-pointer reflect.Value.
  345. if arrayElemValue.Kind() == reflect.Ptr {
  346. // Like: []*Entity
  347. arrayElemValue = arrayElemValue.Elem()
  348. if !arrayElemValue.IsValid() {
  349. // The element is nil, then create one and set it to the slice.
  350. // The "reflect.New(itemType.Elem())" creates a new element and returns the address of it.
  351. // For example:
  352. // reflect.New(itemType.Elem()) => *Entity
  353. // reflect.New(itemType.Elem()).Elem() => Entity
  354. arrayElemValue = reflect.New(arrayItemType.Elem()).Elem()
  355. arrayValue.Index(i).Set(arrayElemValue.Addr())
  356. }
  357. } else {
  358. // Like: []Entity
  359. }
  360. bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName)
  361. if relationAttrName != "" {
  362. // Attribute value of current slice element.
  363. relationFromAttrValue = arrayElemValue.FieldByName(relationAttrName)
  364. if relationFromAttrValue.Kind() == reflect.Ptr {
  365. relationFromAttrValue = relationFromAttrValue.Elem()
  366. }
  367. } else {
  368. // Current slice element.
  369. relationFromAttrValue = arrayElemValue
  370. }
  371. if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
  372. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  373. }
  374. // Check and find possible bind to attribute name.
  375. if relationFields != "" && !relationBindToFieldNameChecked {
  376. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  377. if !relationFromAttrField.IsValid() {
  378. var (
  379. filedMap, _ = gstructs.FieldMap(gstructs.FieldMapInput{
  380. Pointer: relationFromAttrValue,
  381. RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
  382. })
  383. )
  384. if key, _ := utils.MapPossibleItemByKey(Map(filedMap), relationBindToFieldName); key == "" {
  385. return gerror.NewCodef(
  386. gcode.CodeInvalidParameter,
  387. `cannot find possible related attribute name "%s" from given relation fields "%s"`,
  388. relationBindToFieldName,
  389. relationFields,
  390. )
  391. } else {
  392. relationBindToFieldName = key
  393. }
  394. }
  395. relationBindToFieldNameChecked = true
  396. }
  397. switch bindToAttrKind {
  398. case reflect.Array, reflect.Slice:
  399. if len(relationDataMap) > 0 {
  400. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  401. if relationFromAttrField.IsValid() {
  402. // results := make(Result, 0)
  403. results := make([]interface{}, 0)
  404. for _, v := range SliceAny(relationDataMap[String(relationFromAttrField.Interface())]) {
  405. item := v
  406. results = append(results, item)
  407. }
  408. if err = Structs(results, bindToAttrValue.Addr()); err != nil {
  409. return err
  410. }
  411. } else {
  412. // Maybe the attribute does not exist yet.
  413. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  414. }
  415. } else {
  416. return gerror.NewCodef(
  417. gcode.CodeInvalidParameter,
  418. `relationKey should not be empty as field "%s" is slice`,
  419. bindToAttrName,
  420. )
  421. }
  422. case reflect.Ptr:
  423. var element reflect.Value
  424. if bindToAttrValue.IsNil() {
  425. element = reflect.New(bindToAttrType.Elem()).Elem()
  426. } else {
  427. element = bindToAttrValue.Elem()
  428. }
  429. if len(relationDataMap) > 0 {
  430. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  431. if relationFromAttrField.IsValid() {
  432. v := relationDataMap[String(relationFromAttrField.Interface())]
  433. if v == nil {
  434. // There's no relational data.
  435. continue
  436. }
  437. if utils.IsSlice(v) {
  438. if err = Struct(SliceAny(v)[0], element); err != nil {
  439. return err
  440. }
  441. } else {
  442. if err = Struct(v, element); err != nil {
  443. return err
  444. }
  445. }
  446. } else {
  447. // Maybe the attribute does not exist yet.
  448. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  449. }
  450. } else {
  451. if i >= len(maps) {
  452. // There's no relational data.
  453. continue
  454. }
  455. v := maps[i]
  456. if v == nil {
  457. // There's no relational data.
  458. continue
  459. }
  460. if err = Struct(v, element); err != nil {
  461. return err
  462. }
  463. }
  464. bindToAttrValue.Set(element.Addr())
  465. case reflect.Struct:
  466. if len(relationDataMap) > 0 {
  467. relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
  468. if relationFromAttrField.IsValid() {
  469. relationDataItem := relationDataMap[String(relationFromAttrField.Interface())]
  470. if relationDataItem == nil {
  471. // There's no relational data.
  472. continue
  473. }
  474. if utils.IsSlice(relationDataItem) {
  475. if err = Struct(SliceAny(relationDataItem)[0], bindToAttrValue); err != nil {
  476. return err
  477. }
  478. } else {
  479. if err = Struct(relationDataItem, bindToAttrValue); err != nil {
  480. return err
  481. }
  482. }
  483. } else {
  484. // Maybe the attribute does not exist yet.
  485. return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
  486. }
  487. } else {
  488. if i >= len(maps) {
  489. // There's no relational data.
  490. continue
  491. }
  492. relationDataItem := maps[i]
  493. if relationDataItem == nil {
  494. // There's no relational data.
  495. continue
  496. }
  497. if err = Struct(relationDataItem, bindToAttrValue); err != nil {
  498. return err
  499. }
  500. }
  501. default:
  502. return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
  503. }
  504. }
  505. reflect.ValueOf(structSlicePointer).Elem().Set(arrayValue)
  506. return nil
  507. }