github.com/kotovmak/go-admin@v1.1.1/plugins/admin/modules/table/default.go (about) 1 package table 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "html/template" 8 "io/ioutil" 9 "net/http" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/kotovmak/go-admin/modules/config" 15 16 "github.com/kotovmak/go-admin/modules/db" 17 "github.com/kotovmak/go-admin/modules/db/dialect" 18 errs "github.com/kotovmak/go-admin/modules/errors" 19 "github.com/kotovmak/go-admin/modules/language" 20 "github.com/kotovmak/go-admin/modules/logger" 21 "github.com/kotovmak/go-admin/plugins/admin/modules" 22 "github.com/kotovmak/go-admin/plugins/admin/modules/constant" 23 "github.com/kotovmak/go-admin/plugins/admin/modules/form" 24 "github.com/kotovmak/go-admin/plugins/admin/modules/paginator" 25 "github.com/kotovmak/go-admin/plugins/admin/modules/parameter" 26 "github.com/kotovmak/go-admin/template/types" 27 ) 28 29 // DefaultTable is an implementation of table.Table 30 type DefaultTable struct { 31 *BaseTable 32 connectionDriver string 33 connectionDriverMode string 34 connection string 35 sourceURL string 36 getDataFun GetDataFun 37 38 dbObj db.Connection 39 } 40 41 type GetDataFun func(params parameter.Parameters) ([]map[string]interface{}, int) 42 43 func NewDefaultTable(cfgs ...Config) Table { 44 45 var cfg Config 46 47 if len(cfgs) > 0 && cfgs[0].PrimaryKey.Name != "" { 48 cfg = cfgs[0] 49 } else { 50 cfg = DefaultConfig() 51 } 52 53 return &DefaultTable{ 54 BaseTable: &BaseTable{ 55 Info: types.NewInfoPanel(cfg.PrimaryKey.Name), 56 Form: types.NewFormPanel(), 57 NewForm: types.NewFormPanel(), 58 Detail: types.NewInfoPanel(cfg.PrimaryKey.Name), 59 CanAdd: cfg.CanAdd, 60 Editable: cfg.Editable, 61 Deletable: cfg.Deletable, 62 Exportable: cfg.Exportable, 63 PrimaryKey: cfg.PrimaryKey, 64 OnlyNewForm: cfg.OnlyNewForm, 65 OnlyUpdateForm: cfg.OnlyUpdateForm, 66 OnlyDetail: cfg.OnlyDetail, 67 OnlyInfo: cfg.OnlyInfo, 68 }, 69 connectionDriver: cfg.Driver, 70 connectionDriverMode: cfg.DriverMode, 71 connection: cfg.Connection, 72 sourceURL: cfg.SourceURL, 73 getDataFun: cfg.GetDataFun, 74 } 75 } 76 77 // Copy copy a new table.Table from origin DefaultTable 78 func (tb *DefaultTable) Copy() Table { 79 return &DefaultTable{ 80 BaseTable: &BaseTable{ 81 Form: types.NewFormPanel().SetTable(tb.Form.Table). 82 SetDescription(tb.Form.Description). 83 SetTitle(tb.Form.Title), 84 NewForm: types.NewFormPanel().SetTable(tb.Form.Table). 85 SetDescription(tb.Form.Description). 86 SetTitle(tb.Form.Title), 87 Info: types.NewInfoPanel(tb.PrimaryKey.Name).SetTable(tb.Info.Table). 88 SetDescription(tb.Info.Description). 89 SetTitle(tb.Info.Title). 90 SetGetDataFn(tb.Info.GetDataFn), 91 Detail: types.NewInfoPanel(tb.PrimaryKey.Name).SetTable(tb.Detail.Table). 92 SetDescription(tb.Detail.Description). 93 SetTitle(tb.Detail.Title). 94 SetGetDataFn(tb.Detail.GetDataFn), 95 CanAdd: tb.CanAdd, 96 Editable: tb.Editable, 97 Deletable: tb.Deletable, 98 Exportable: tb.Exportable, 99 PrimaryKey: tb.PrimaryKey, 100 }, 101 connectionDriver: tb.connectionDriver, 102 connectionDriverMode: tb.connectionDriverMode, 103 connection: tb.connection, 104 sourceURL: tb.sourceURL, 105 getDataFun: tb.getDataFun, 106 } 107 } 108 109 // GetData query the data set. 110 func (tb *DefaultTable) GetData(params parameter.Parameters) (PanelInfo, error) { 111 112 var ( 113 data []map[string]interface{} 114 size int 115 beginTime = time.Now() 116 ) 117 118 if tb.Info.UpdateParametersFns != nil { 119 for _, fn := range tb.Info.UpdateParametersFns { 120 fn(¶ms) 121 } 122 } 123 124 if tb.Info.QueryFilterFn != nil { 125 var ids []string 126 var stopQuery bool 127 128 if tb.getDataFun == nil && tb.Info.GetDataFn == nil { 129 ids, stopQuery = tb.Info.QueryFilterFn(params, tb.db()) 130 } else { 131 ids, stopQuery = tb.Info.QueryFilterFn(params, nil) 132 } 133 134 if stopQuery { 135 return tb.GetDataWithIds(params.WithPKs(ids...)) 136 } 137 } 138 139 if tb.getDataFun != nil { 140 data, size = tb.getDataFun(params) 141 } else if tb.sourceURL != "" { 142 data, size = tb.getDataFromURL(params) 143 } else if tb.Info.GetDataFn != nil { 144 data, size = tb.Info.GetDataFn(params) 145 } else if params.IsAll() { 146 return tb.getAllDataFromDatabase(params) 147 } else { 148 return tb.getDataFromDatabase(params) 149 } 150 151 infoList := make(types.InfoList, 0) 152 153 for i := 0; i < len(data); i++ { 154 infoList = append(infoList, tb.getTempModelData(data[i], params, []string{})) 155 } 156 157 thead, _, _, _, _, filterForm := tb.getTheadAndFilterForm(params, []string{}) 158 159 endTime := time.Now() 160 161 extraInfo := "" 162 163 if !tb.Info.IsHideQueryInfo { 164 extraInfo = fmt.Sprintf("<b>" + language.Get("query time") + ": </b>" + 165 fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000)) 166 } 167 168 return PanelInfo{ 169 Thead: thead, 170 InfoList: infoList, 171 Paginator: paginator.Get(paginator.Config{ 172 Size: size, 173 Param: params, 174 PageSizeList: tb.Info.GetPageSizeList(), 175 }).SetExtraInfo(template.HTML(extraInfo)), 176 Title: tb.Info.Title, 177 FilterFormData: filterForm, 178 Description: tb.Info.Description, 179 }, nil 180 } 181 182 type GetDataFromURLRes struct { 183 Data []map[string]interface{} 184 Size int 185 } 186 187 func (tb *DefaultTable) getDataFromURL(params parameter.Parameters) ([]map[string]interface{}, int) { 188 189 u := "" 190 if strings.Contains(tb.sourceURL, "?") { 191 u = tb.sourceURL + "&" + params.Join() 192 } else { 193 u = tb.sourceURL + "?" + params.Join() 194 } 195 res, err := http.Get(u + "&pk=" + strings.Join(params.PKs(), ",")) 196 197 if err != nil { 198 return []map[string]interface{}{}, 0 199 } 200 201 defer func() { 202 _ = res.Body.Close() 203 }() 204 205 body, err := ioutil.ReadAll(res.Body) 206 207 if err != nil { 208 return []map[string]interface{}{}, 0 209 } 210 211 var data GetDataFromURLRes 212 213 err = json.Unmarshal(body, &data) 214 215 if err != nil { 216 return []map[string]interface{}{}, 0 217 } 218 219 return data.Data, data.Size 220 } 221 222 // GetDataWithIds query the data set. 223 func (tb *DefaultTable) GetDataWithIds(params parameter.Parameters) (PanelInfo, error) { 224 225 var ( 226 data []map[string]interface{} 227 size int 228 beginTime = time.Now() 229 ) 230 231 if tb.getDataFun != nil { 232 data, size = tb.getDataFun(params) 233 } else if tb.sourceURL != "" { 234 data, size = tb.getDataFromURL(params) 235 } else if tb.Info.GetDataFn != nil { 236 data, size = tb.Info.GetDataFn(params) 237 } else { 238 return tb.getDataFromDatabase(params) 239 } 240 241 infoList := make([]map[string]types.InfoItem, 0) 242 243 for i := 0; i < len(data); i++ { 244 infoList = append(infoList, tb.getTempModelData(data[i], params, []string{})) 245 } 246 247 thead, _, _, _, _, filterForm := tb.getTheadAndFilterForm(params, []string{}) 248 249 endTime := time.Now() 250 251 return PanelInfo{ 252 Thead: thead, 253 InfoList: infoList, 254 Paginator: paginator.Get(paginator.Config{ 255 Size: size, 256 Param: params, 257 PageSizeList: tb.Info.GetPageSizeList(), 258 }). 259 SetExtraInfo(template.HTML(fmt.Sprintf("<b>" + language.Get("query time") + ": </b>" + 260 fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000)))), 261 Title: tb.Info.Title, 262 FilterFormData: filterForm, 263 Description: tb.Info.Description, 264 }, nil 265 } 266 267 func (tb *DefaultTable) getTempModelData(res map[string]interface{}, params parameter.Parameters, columns Columns) map[string]types.InfoItem { 268 269 var tempModelData = map[string]types.InfoItem{ 270 "__goadmin_edit_params": {}, 271 "__goadmin_delete_params": {}, 272 "__goadmin_detail_params": {}, 273 } 274 headField := "" 275 editParams := "" 276 deleteParams := "" 277 detailParams := "" 278 279 primaryKeyValue := db.GetValueFromDatabaseType(tb.PrimaryKey.Type, res[tb.PrimaryKey.Name], len(columns) == 0) 280 281 for _, field := range tb.Info.FieldList { 282 283 headField = field.Field 284 285 if field.Joins.Valid() { 286 headField = field.Joins.Last().GetTableName() + parameter.FilterParamJoinInfix + field.Field 287 } 288 289 if field.Hide { 290 continue 291 } 292 if !modules.InArrayWithoutEmpty(params.Columns, headField) { 293 continue 294 } 295 296 typeName := field.TypeName 297 298 if field.Joins.Valid() { 299 typeName = db.Varchar 300 } 301 302 combineValue := db.GetValueFromDatabaseType(typeName, res[headField], len(columns) == 0).String() 303 304 // TODO: ToDisplay some same logic execute repeatedly, it can be improved. 305 var value interface{} 306 if len(columns) == 0 || modules.InArray(columns, headField) || field.Joins.Valid() { 307 value = field.ToDisplay(types.FieldModel{ 308 ID: primaryKeyValue.String(), 309 Value: combineValue, 310 Row: res, 311 }) 312 } else { 313 value = field.ToDisplay(types.FieldModel{ 314 ID: primaryKeyValue.String(), 315 Value: "", 316 Row: res, 317 }) 318 } 319 var valueStr string 320 var ok bool 321 if valueStr, ok = value.(string); ok { 322 tempModelData[headField] = types.InfoItem{ 323 Content: template.HTML(valueStr), 324 Value: combineValue, 325 } 326 } else { 327 valueStr = string(value.(template.HTML)) 328 tempModelData[headField] = types.InfoItem{ 329 Content: value.(template.HTML), 330 Value: combineValue, 331 } 332 } 333 334 if field.IsEditParam { 335 editParams += "__goadmin_edit_" + field.Field + "=" + valueStr + "&" 336 } 337 if field.IsDeleteParam { 338 deleteParams += "__goadmin_delete_" + field.Field + "=" + valueStr + "&" 339 } 340 if field.IsDetailParam { 341 detailParams += "__goadmin_detail_" + field.Field + "=" + valueStr + "&" 342 } 343 } 344 345 if editParams != "" { 346 tempModelData["__goadmin_edit_params"] = types.InfoItem{Content: template.HTML("&" + editParams[:len(editParams)-1])} 347 } 348 if deleteParams != "" { 349 tempModelData["__goadmin_delete_params"] = types.InfoItem{Content: template.HTML("&" + deleteParams[:len(deleteParams)-1])} 350 } 351 if detailParams != "" { 352 tempModelData["__goadmin_detail_params"] = types.InfoItem{Content: template.HTML("&" + detailParams[:len(detailParams)-1])} 353 } 354 355 primaryKeyField := tb.Info.FieldList.GetFieldByFieldName(tb.PrimaryKey.Name) 356 value := primaryKeyField.ToDisplay(types.FieldModel{ 357 ID: primaryKeyValue.String(), 358 Value: primaryKeyValue.String(), 359 Row: res, 360 }) 361 if valueStr, ok := value.(string); ok { 362 tempModelData[tb.PrimaryKey.Name] = types.InfoItem{ 363 Content: template.HTML(valueStr), 364 Value: primaryKeyValue.String(), 365 } 366 } else { 367 tempModelData[tb.PrimaryKey.Name] = types.InfoItem{ 368 Content: value.(template.HTML), 369 Value: primaryKeyValue.String(), 370 } 371 } 372 373 return tempModelData 374 } 375 376 func (tb *DefaultTable) getAllDataFromDatabase(params parameter.Parameters) (PanelInfo, error) { 377 var ( 378 connection = tb.db() 379 queryStatement = "select %s from %s %s %s %s order by " + modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), "%s") + " %s" 380 ) 381 382 columns, _ := tb.getColumns(tb.Info.Table) 383 384 thead, fields, joins := tb.Info.FieldList.GetThead(types.TableInfo{ 385 Table: tb.Info.Table, 386 Delimiter: connection.GetDelimiter(), 387 Delimiter2: connection.GetDelimiter2(), 388 Driver: tb.connectionDriver, 389 PrimaryKey: tb.PrimaryKey.Name, 390 }, params, columns) 391 392 fields += tb.Info.Table + "." + modules.FilterField(tb.PrimaryKey.Name, connection.GetDelimiter(), connection.GetDelimiter2()) 393 394 groupBy := "" 395 if joins != "" { 396 groupBy = " GROUP BY " + tb.Info.Table + "." + modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), tb.PrimaryKey.Name) 397 } 398 399 var ( 400 wheres = "" 401 whereArgs = make([]interface{}, 0) 402 existKeys = make([]string, 0) 403 ) 404 405 wheres, whereArgs, existKeys = params.Statement(wheres, tb.Info.Table, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, columns, existKeys, 406 tb.Info.FieldList.GetFieldFilterProcessValue) 407 wheres, whereArgs = tb.Info.Wheres.Statement(wheres, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, existKeys, columns) 408 wheres, whereArgs = tb.Info.WhereRaws.Statement(wheres, whereArgs) 409 410 if wheres != "" { 411 wheres = " where " + wheres 412 } 413 414 if !modules.InArray(columns, params.SortField) { 415 params.SortField = tb.PrimaryKey.Name 416 } 417 418 queryCmd := fmt.Sprintf(queryStatement, fields, tb.Info.Table, joins, wheres, groupBy, params.SortField, params.SortType) 419 420 logger.LogSQL(queryCmd, []interface{}{}) 421 422 res, err := connection.QueryWithConnection(tb.connection, queryCmd, whereArgs...) 423 424 if err != nil { 425 return PanelInfo{}, err 426 } 427 428 infoList := make([]map[string]types.InfoItem, 0) 429 430 for i := 0; i < len(res); i++ { 431 infoList = append(infoList, tb.getTempModelData(res[i], params, columns)) 432 } 433 434 return PanelInfo{ 435 InfoList: infoList, 436 Thead: thead, 437 Title: tb.Info.Title, 438 Description: tb.Info.Description, 439 }, nil 440 } 441 442 // TODO: refactor 443 func (tb *DefaultTable) getDataFromDatabase(params parameter.Parameters) (PanelInfo, error) { 444 445 var ( 446 connection = tb.db() 447 delimiter = connection.GetDelimiter() 448 delimiter2 = connection.GetDelimiter2() 449 placeholder = modules.Delimiter(delimiter, delimiter2, "%s") 450 queryStatement string 451 countStatement string 452 ids = params.PKs() 453 table = modules.Delimiter(delimiter, delimiter2, tb.Info.Table) 454 pk = table + "." + modules.Delimiter(delimiter, delimiter2, tb.PrimaryKey.Name) 455 ) 456 457 beginTime := time.Now() 458 459 if len(ids) > 0 { 460 countExtra := "" 461 if connection.Name() == db.DriverMssql { 462 countExtra = "as [size]" 463 } 464 // %s means: fields, table, join table, pk values, group by, order by field, order by type 465 queryStatement = "select %s from " + placeholder + " %s where " + pk + " in (%s) %s ORDER BY %s." + placeholder + " %s" 466 // %s means: table, join table, pk values 467 countStatement = "select count(*) " + countExtra + " from " + placeholder + " %s where " + pk + " in (%s)" 468 } else { 469 if connection.Name() == db.DriverMssql { 470 // %s means: order by field, order by type, fields, table, join table, wheres, group by 471 queryStatement = "SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s." + placeholder + " %s) as ROWNUMBER_, %s from " + 472 placeholder + "%s %s %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > ? AND TMP_.ROWNUMBER_ <= ?" 473 // %s means: table, join table, wheres 474 countStatement = "select count(*) as [size] from (select count(*) as [size] from " + placeholder + " %s %s %s) src" 475 } else { 476 // %s means: fields, table, join table, wheres, group by, order by field, order by type 477 queryStatement = "select %s from " + placeholder + "%s %s %s order by " + placeholder + "." + placeholder + " %s LIMIT ? OFFSET ?" 478 // %s means: table, join table, wheres 479 countStatement = "select count(*) from (select " + pk + " from " + placeholder + " %s %s %s) src" 480 } 481 } 482 483 columns, _ := tb.getColumns(tb.Info.Table) 484 485 thead, fields, joinFields, joins, joinTables, filterForm := tb.getTheadAndFilterForm(params, columns) 486 487 fields += pk 488 489 allFields := fields 490 groupFields := fields 491 492 if joinFields != "" { 493 allFields += "," + joinFields[:len(joinFields)-1] 494 if connection.Name() == db.DriverMssql { 495 for _, field := range tb.Info.FieldList { 496 if field.TypeName == db.Text || field.TypeName == db.Longtext { 497 f := modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), field.Field) 498 headField := table + "." + f 499 allFields = strings.ReplaceAll(allFields, headField, "CAST("+headField+" AS NVARCHAR(MAX)) as "+f) 500 groupFields = strings.ReplaceAll(groupFields, headField, "CAST("+headField+" AS NVARCHAR(MAX))") 501 } 502 } 503 } 504 } 505 506 if !modules.InArray(columns, params.SortField) { 507 params.SortField = tb.PrimaryKey.Name 508 } 509 510 var ( 511 wheres = "" 512 whereArgs = make([]interface{}, 0) 513 args = make([]interface{}, 0) 514 existKeys = make([]string, 0) 515 ) 516 517 if len(ids) > 0 { 518 for _, value := range ids { 519 if value != "" { 520 wheres += "?," 521 args = append(args, value) 522 } 523 } 524 wheres = wheres[:len(wheres)-1] 525 } else { 526 527 // parameter 528 wheres, whereArgs, existKeys = params.Statement(wheres, tb.Info.Table, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, columns, existKeys, 529 tb.Info.FieldList.GetFieldFilterProcessValue) 530 // pre query 531 wheres, whereArgs = tb.Info.Wheres.Statement(wheres, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, existKeys, columns) 532 wheres, whereArgs = tb.Info.WhereRaws.Statement(wheres, whereArgs) 533 534 if wheres != "" { 535 wheres = " where " + wheres 536 } 537 538 if connection.Name() == db.DriverMssql { 539 args = append(whereArgs, (params.PageInt-1)*params.PageSizeInt, params.PageInt*params.PageSizeInt) 540 } else { 541 args = append(whereArgs, params.PageSizeInt, (params.PageInt-1)*params.PageSizeInt) 542 } 543 } 544 545 groupBy := "" 546 if len(joinTables) > 0 { 547 if connection.Name() == db.DriverMssql { 548 groupBy = " GROUP BY " + groupFields 549 } else { 550 groupBy = " GROUP BY " + pk 551 } 552 } 553 554 queryCmd := "" 555 if connection.Name() == db.DriverMssql && len(ids) == 0 { 556 queryCmd = fmt.Sprintf(queryStatement, tb.Info.Table, params.SortField, params.SortType, 557 allFields, tb.Info.Table, joins, wheres, groupBy) 558 } else { 559 queryCmd = fmt.Sprintf(queryStatement, allFields, tb.Info.Table, joins, wheres, groupBy, 560 tb.Info.Table, params.SortField, params.SortType) 561 } 562 563 logger.LogSQL(queryCmd, args) 564 565 res, err := connection.QueryWithConnection(tb.connection, queryCmd, args...) 566 567 if err != nil { 568 return PanelInfo{}, err 569 } 570 571 infoList := make([]map[string]types.InfoItem, 0) 572 573 for i := 0; i < len(res); i++ { 574 infoList = append(infoList, tb.getTempModelData(res[i], params, columns)) 575 } 576 577 // TODO: use the dialect 578 var size int 579 580 if len(ids) == 0 { 581 countCmd := fmt.Sprintf(countStatement, tb.Info.Table, joins, wheres, groupBy) 582 583 total, err := connection.QueryWithConnection(tb.connection, countCmd, whereArgs...) 584 585 if err != nil { 586 return PanelInfo{}, err 587 } 588 589 logger.LogSQL(countCmd, nil) 590 591 if tb.connectionDriver == "postgresql" { 592 if tb.connectionDriverMode == "h2" { 593 size = int(total[0]["count(*)"].(int64)) 594 } else if config.GetDatabases().GetDefault().DriverMode == "h2" { 595 size = int(total[0]["count(*)"].(int64)) 596 } else { 597 size = int(total[0]["count"].(int64)) 598 } 599 } else if tb.connectionDriver == db.DriverMssql { 600 size = int(total[0]["size"].(int64)) 601 } else { 602 size = int(total[0]["count(*)"].(int64)) 603 } 604 } 605 606 endTime := time.Now() 607 608 return PanelInfo{ 609 Thead: thead, 610 InfoList: infoList, 611 Paginator: tb.GetPaginator(size, params, 612 template.HTML(fmt.Sprintf("<b>"+language.Get("query time")+": </b>"+ 613 fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000)))), 614 Title: tb.Info.Title, 615 FilterFormData: filterForm, 616 Description: tb.Info.Description, 617 }, nil 618 } 619 620 func getDataRes(list []map[string]interface{}, _ int) map[string]interface{} { 621 if len(list) > 0 { 622 return list[0] 623 } 624 return nil 625 } 626 627 // GetDataWithId query the single row of data. 628 func (tb *DefaultTable) GetDataWithId(param parameter.Parameters) (FormInfo, error) { 629 630 var ( 631 res map[string]interface{} 632 columns Columns 633 id = param.PK() 634 ) 635 636 if tb.getDataFun != nil { 637 res = getDataRes(tb.getDataFun(param)) 638 } else if tb.sourceURL != "" { 639 res = getDataRes(tb.getDataFromURL(param)) 640 } else if tb.Detail.GetDataFn != nil { 641 res = getDataRes(tb.Detail.GetDataFn(param)) 642 } else if tb.Info.GetDataFn != nil { 643 res = getDataRes(tb.Info.GetDataFn(param)) 644 } else { 645 646 columns, _ = tb.getColumns(tb.Form.Table) 647 648 var ( 649 fields, joinFields, joins, groupBy = "", "", "", "" 650 651 err error 652 joinTables = make([]string, 0) 653 args = []interface{}{id} 654 connection = tb.db() 655 delimiter = connection.GetDelimiter() 656 delimiter2 = connection.GetDelimiter2() 657 tableName = modules.Delimiter(delimiter, delimiter2, tb.GetForm().Table) 658 pk = tableName + "." + modules.Delimiter(delimiter, delimiter2, tb.PrimaryKey.Name) 659 queryStatement = "select %s from %s %s where " + pk + " = ? %s " 660 ) 661 662 for i := 0; i < len(tb.Form.FieldList); i++ { 663 664 if tb.Form.FieldList[i].Field != pk && modules.InArray(columns, tb.Form.FieldList[i].Field) && 665 !tb.Form.FieldList[i].Joins.Valid() { 666 fields += tableName + "." + modules.FilterField(tb.Form.FieldList[i].Field, delimiter, delimiter2) + "," 667 } 668 669 if tb.Form.FieldList[i].Joins.Valid() { 670 headField := tb.Form.FieldList[i].Joins.Last().GetTableName() + parameter.FilterParamJoinInfix + tb.Form.FieldList[i].Field 671 joinFields += db.GetAggregationExpression(connection.Name(), tb.Form.FieldList[i].Joins.Last().GetTableName(delimiter, delimiter2)+"."+ 672 modules.FilterField(tb.Form.FieldList[i].Field, delimiter, delimiter2), headField, types.JoinFieldValueDelimiter) + "," 673 for _, join := range tb.Form.FieldList[i].Joins { 674 if !modules.InArray(joinTables, join.GetTableName(delimiter, delimiter2)) { 675 joinTables = append(joinTables, join.GetTableName(delimiter, delimiter2)) 676 if join.BaseTable == "" { 677 join.BaseTable = tableName 678 } 679 joins += " left join " + modules.FilterField(join.Table, delimiter, delimiter2) + " " + join.TableAlias + " on " + 680 join.GetTableName(delimiter, delimiter2) + "." + modules.FilterField(join.JoinField, delimiter, delimiter2) + " = " + 681 join.BaseTable + "." + modules.FilterField(join.Field, delimiter, delimiter2) 682 } 683 } 684 } 685 } 686 687 fields += pk 688 groupFields := fields 689 690 if joinFields != "" { 691 fields += "," + joinFields[:len(joinFields)-1] 692 if connection.Name() == db.DriverMssql { 693 for i := 0; i < len(tb.Form.FieldList); i++ { 694 if tb.Form.FieldList[i].TypeName == db.Text || tb.Form.FieldList[i].TypeName == db.Longtext { 695 f := modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), tb.Form.FieldList[i].Field) 696 headField := tb.Info.Table + "." + f 697 fields = strings.ReplaceAll(fields, headField, "CAST("+headField+" AS NVARCHAR(MAX)) as "+f) 698 groupFields = strings.ReplaceAll(groupFields, headField, "CAST("+headField+" AS NVARCHAR(MAX))") 699 } 700 } 701 } 702 } 703 704 if len(joinTables) > 0 { 705 if connection.Name() == db.DriverMssql { 706 groupBy = " GROUP BY " + groupFields 707 } else { 708 groupBy = " GROUP BY " + pk 709 } 710 } 711 712 queryCmd := fmt.Sprintf(queryStatement, fields, tableName, joins, groupBy) 713 714 logger.LogSQL(queryCmd, args) 715 716 result, err := connection.QueryWithConnection(tb.connection, queryCmd, args...) 717 718 if err != nil { 719 return FormInfo{Title: tb.Form.Title, Description: tb.Form.Description}, err 720 } 721 722 if len(result) == 0 { 723 return FormInfo{Title: tb.Form.Title, Description: tb.Form.Description}, errors.New(errs.WrongID) 724 } 725 726 res = result[0] 727 } 728 729 var ( 730 groupFormList = make([]types.FormFields, 0) 731 groupHeaders = make([]string, 0) 732 ) 733 734 if len(tb.Form.TabGroups) > 0 { 735 groupFormList, groupHeaders = tb.Form.GroupFieldWithValue(tb.PrimaryKey.Name, id, columns, res, tb.sqlObjOrNil) 736 return FormInfo{ 737 FieldList: tb.Form.FieldList, 738 GroupFieldList: groupFormList, 739 GroupFieldHeaders: groupHeaders, 740 Title: tb.Form.Title, 741 Description: tb.Form.Description, 742 }, nil 743 } 744 745 var fieldList = tb.Form.FieldsWithValue(tb.PrimaryKey.Name, id, columns, res, tb.sqlObjOrNil) 746 747 return FormInfo{ 748 FieldList: fieldList, 749 GroupFieldList: groupFormList, 750 GroupFieldHeaders: groupHeaders, 751 Title: tb.Form.Title, 752 Description: tb.Form.Description, 753 }, nil 754 } 755 756 // UpdateData update data. 757 func (tb *DefaultTable) UpdateData(dataList form.Values) error { 758 759 dataList.Add(form.PostTypeKey, "0") 760 761 var ( 762 errMsg = "" 763 err error 764 ) 765 766 if tb.Form.PostHook != nil { 767 defer func() { 768 dataList.Add(form.PostTypeKey, "0") 769 dataList.Add(form.PostResultKey, errMsg) 770 go func() { 771 defer func() { 772 if err := recover(); err != nil { 773 logger.Error(err) 774 } 775 }() 776 777 err := tb.Form.PostHook(dataList) 778 if err != nil { 779 logger.Error(err) 780 } 781 }() 782 }() 783 } 784 785 if tb.Form.Validator != nil { 786 if err := tb.Form.Validator(dataList); err != nil { 787 errMsg = "post error: " + err.Error() 788 return err 789 } 790 } 791 792 if tb.Form.PreProcessFn != nil { 793 dataList = tb.Form.PreProcessFn(dataList) 794 } 795 796 if tb.Form.UpdateFn != nil { 797 dataList.Delete(form.PostTypeKey) 798 err = tb.Form.UpdateFn(tb.PreProcessValue(dataList, types.PostTypeUpdate)) 799 if err != nil { 800 errMsg = "post error: " + err.Error() 801 } 802 return err 803 } 804 805 if len(dataList) == 0 { 806 return nil 807 } 808 809 _, err = tb.sql().Table(tb.Form.Table). 810 Where(tb.PrimaryKey.Name, "=", dataList.Get(tb.PrimaryKey.Name)). 811 Update(tb.getInjectValueFromFormValue(dataList, types.PostTypeUpdate)) 812 813 // NOTE: some errors should be ignored. 814 if db.CheckError(err, db.UPDATE) { 815 if err != nil { 816 errMsg = "post error: " + err.Error() 817 } 818 return err 819 } 820 821 return nil 822 } 823 824 // InsertData insert data. 825 func (tb *DefaultTable) InsertData(dataList form.Values) error { 826 827 dataList.Add(form.PostTypeKey, "1") 828 829 var ( 830 id = int64(0) 831 err error 832 errMsg = "" 833 f = tb.GetActualNewForm() 834 ) 835 836 if f.PostHook != nil { 837 defer func() { 838 dataList.Add(form.PostTypeKey, "1") 839 dataList.Add(tb.GetPrimaryKey().Name, strconv.Itoa(int(id))) 840 dataList.Add(form.PostResultKey, errMsg) 841 842 go func() { 843 defer func() { 844 if err := recover(); err != nil { 845 logger.Error(err) 846 } 847 }() 848 849 err := f.PostHook(dataList) 850 if err != nil { 851 logger.Error(err) 852 } 853 }() 854 }() 855 } 856 857 if f.Validator != nil { 858 if err := f.Validator(dataList); err != nil { 859 errMsg = "post error: " + err.Error() 860 return err 861 } 862 } 863 864 if f.PreProcessFn != nil { 865 dataList = f.PreProcessFn(dataList) 866 } 867 868 if f.InsertFn != nil { 869 dataList.Delete(form.PostTypeKey) 870 err = f.InsertFn(tb.PreProcessValue(dataList, types.PostTypeCreate)) 871 if err != nil { 872 errMsg = "post error: " + err.Error() 873 } 874 return err 875 } 876 877 if len(dataList) == 0 { 878 return nil 879 } 880 881 id, err = tb.sql().Table(f.Table).Insert(tb.getInjectValueFromFormValue(dataList, types.PostTypeCreate)) 882 883 // NOTE: some errors should be ignored. 884 if db.CheckError(err, db.INSERT) { 885 errMsg = "post error: " + err.Error() 886 return err 887 } 888 889 return nil 890 } 891 892 func (tb *DefaultTable) getInjectValueFromFormValue(dataList form.Values, typ types.PostType) dialect.H { 893 894 var ( 895 value = make(dialect.H) 896 exceptString = make([]string, 0) 897 columns, auto = tb.getColumns(tb.Form.Table) 898 899 fun types.PostFieldFilterFn 900 ) 901 902 // If a key is a auto increment primary key, it can`t be insert or update. 903 if auto { 904 exceptString = []string{tb.PrimaryKey.Name, form.PreviousKey, form.MethodKey, form.TokenKey, 905 constant.IframeKey, constant.IframeIDKey} 906 } else { 907 exceptString = []string{form.PreviousKey, form.MethodKey, form.TokenKey, 908 constant.IframeKey, constant.IframeIDKey} 909 } 910 911 if !dataList.IsSingleUpdatePost() { 912 for i := 0; i < len(tb.Form.FieldList); i++ { 913 if tb.Form.FieldList[i].FormType.IsMultiSelect() { 914 if _, ok := dataList[tb.Form.FieldList[i].Field+"[]"]; !ok { 915 dataList[tb.Form.FieldList[i].Field+"[]"] = []string{""} 916 } 917 } 918 } 919 } 920 921 dataList = dataList.RemoveRemark() 922 923 for k, v := range dataList { 924 k = strings.ReplaceAll(k, "[]", "") 925 if !modules.InArray(exceptString, k) { 926 if modules.InArray(columns, k) { 927 field := tb.Form.FieldList.FindByFieldName(k) 928 delimiter := "," 929 if field != nil { 930 fun = field.PostFilterFn 931 delimiter = modules.SetDefault(field.DefaultOptionDelimiter, ",") 932 } 933 vv := modules.RemoveBlankFromArray(v) 934 if fun != nil { 935 value[k] = fun(types.PostFieldModel{ 936 ID: dataList.Get(tb.PrimaryKey.Name), 937 Value: vv, 938 Row: dataList.ToMap(), 939 PostType: typ, 940 }) 941 } else { 942 if len(vv) > 1 { 943 value[k] = strings.Join(vv, delimiter) 944 } else if len(vv) > 0 { 945 value[k] = vv[0] 946 } else { 947 value[k] = "" 948 } 949 } 950 } else { 951 field := tb.Form.FieldList.FindByFieldName(k) 952 if field != nil && field.PostFilterFn != nil { 953 field.PostFilterFn(types.PostFieldModel{ 954 ID: dataList.Get(tb.PrimaryKey.Name), 955 Value: modules.RemoveBlankFromArray(v), 956 Row: dataList.ToMap(), 957 PostType: typ, 958 }) 959 } 960 } 961 } 962 } 963 return value 964 } 965 966 func (tb *DefaultTable) PreProcessValue(dataList form.Values, typ types.PostType) form.Values { 967 968 exceptString := []string{form.PreviousKey, form.MethodKey, form.TokenKey, 969 constant.IframeKey, constant.IframeIDKey} 970 dataList = dataList.RemoveRemark() 971 var fun types.PostFieldFilterFn 972 973 for k, v := range dataList { 974 k = strings.ReplaceAll(k, "[]", "") 975 if !modules.InArray(exceptString, k) { 976 field := tb.Form.FieldList.FindByFieldName(k) 977 if field != nil { 978 fun = field.PostFilterFn 979 } 980 vv := modules.RemoveBlankFromArray(v) 981 if fun != nil { 982 dataList.Add(k, fmt.Sprintf("%s", fun(types.PostFieldModel{ 983 ID: dataList.Get(tb.PrimaryKey.Name), 984 Value: vv, 985 Row: dataList.ToMap(), 986 PostType: typ, 987 }))) 988 } 989 } 990 } 991 return dataList 992 } 993 994 // DeleteData delete data. 995 func (tb *DefaultTable) DeleteData(id string) error { 996 997 var ( 998 idArr = strings.Split(id, ",") 999 err error 1000 ) 1001 1002 if tb.Info.DeleteHook != nil { 1003 defer func() { 1004 go func() { 1005 defer func() { 1006 if recoverErr := recover(); recoverErr != nil { 1007 logger.Error(recoverErr) 1008 } 1009 }() 1010 1011 if hookErr := tb.Info.DeleteHook(idArr); hookErr != nil { 1012 logger.Error(hookErr) 1013 } 1014 }() 1015 }() 1016 } 1017 1018 if tb.Info.DeleteHookWithRes != nil { 1019 defer func() { 1020 go func() { 1021 defer func() { 1022 if recoverErr := recover(); recoverErr != nil { 1023 logger.Error(recoverErr) 1024 } 1025 }() 1026 1027 if hookErr := tb.Info.DeleteHookWithRes(idArr, err); hookErr != nil { 1028 logger.Error(hookErr) 1029 } 1030 }() 1031 }() 1032 } 1033 1034 if tb.Info.PreDeleteFn != nil { 1035 if err = tb.Info.PreDeleteFn(idArr); err != nil { 1036 return err 1037 } 1038 } 1039 1040 if tb.Info.DeleteFn != nil { 1041 err = tb.Info.DeleteFn(idArr) 1042 return err 1043 } 1044 1045 if len(idArr) == 0 || tb.Info.Table == "" { 1046 err = errors.New("delete error: wrong parameter") 1047 return err 1048 } 1049 1050 err = tb.delete(tb.Info.Table, tb.PrimaryKey.Name, idArr) 1051 return err 1052 } 1053 1054 func (tb *DefaultTable) GetNewFormInfo() FormInfo { 1055 1056 f := tb.GetActualNewForm() 1057 1058 if len(f.TabGroups) == 0 { 1059 return FormInfo{FieldList: f.FieldsWithDefaultValue(tb.sqlObjOrNil)} 1060 } 1061 1062 newForm, headers := f.GroupField(tb.sqlObjOrNil) 1063 1064 return FormInfo{GroupFieldList: newForm, GroupFieldHeaders: headers} 1065 } 1066 1067 // *************************************** 1068 // helper function for database operation 1069 // *************************************** 1070 1071 func (tb *DefaultTable) delete(table, key string, values []string) error { 1072 1073 var vals = make([]interface{}, len(values)) 1074 for i, v := range values { 1075 vals[i] = v 1076 } 1077 1078 return tb.sql().Table(table). 1079 WhereIn(key, vals). 1080 Delete() 1081 } 1082 1083 func (tb *DefaultTable) getTheadAndFilterForm(params parameter.Parameters, columns Columns) (types.Thead, 1084 string, string, string, []string, []types.FormField) { 1085 1086 return tb.Info.FieldList.GetTheadAndFilterForm(types.TableInfo{ 1087 Table: tb.Info.Table, 1088 Delimiter: tb.delimiter(), 1089 Delimiter2: tb.delimiter2(), 1090 Driver: tb.connectionDriver, 1091 PrimaryKey: tb.PrimaryKey.Name, 1092 }, params, columns, tb.sqlObjOrNil) 1093 } 1094 1095 // db is a helper function return raw db connection. 1096 func (tb *DefaultTable) db() db.Connection { 1097 if tb.dbObj == nil { 1098 tb.dbObj = db.GetConnectionFromService(services.Get(tb.connectionDriver)) 1099 } 1100 return tb.dbObj 1101 } 1102 1103 func (tb *DefaultTable) delimiter() string { 1104 if tb.getDataFromDB() { 1105 return tb.db().GetDelimiter() 1106 } 1107 return "" 1108 } 1109 1110 func (tb *DefaultTable) delimiter2() string { 1111 if tb.getDataFromDB() { 1112 return tb.db().GetDelimiter2() 1113 } 1114 return "" 1115 } 1116 1117 func (tb *DefaultTable) getDataFromDB() bool { 1118 return tb.sourceURL == "" && tb.getDataFun == nil && tb.Info.GetDataFn == nil && tb.Detail.GetDataFn == nil 1119 } 1120 1121 // sql is a helper function return db sql. 1122 func (tb *DefaultTable) sql() *db.SQL { 1123 return db.WithDriverAndConnection(tb.connection, tb.db()) 1124 } 1125 1126 // sqlObjOrNil is a helper function return db sql obj or nil. 1127 func (tb *DefaultTable) sqlObjOrNil() *db.SQL { 1128 if tb.connectionDriver != "" && tb.getDataFromDB() { 1129 return db.WithDriverAndConnection(tb.connection, tb.db()) 1130 } 1131 return nil 1132 } 1133 1134 type Columns []string 1135 1136 func (tb *DefaultTable) getColumns(table string) (Columns, bool) { 1137 1138 columnsModel, _ := tb.sql().Table(table).ShowColumns() 1139 1140 columns := make(Columns, len(columnsModel)) 1141 switch tb.connectionDriver { 1142 case db.DriverPostgresql: 1143 auto := false 1144 for key, model := range columnsModel { 1145 columns[key] = model["column_name"].(string) 1146 if columns[key] == tb.PrimaryKey.Name { 1147 if v, ok := model["column_default"].(string); ok { 1148 if strings.Contains(v, "nextval") { 1149 auto = true 1150 } 1151 } 1152 } 1153 } 1154 return columns, auto 1155 case db.DriverMysql: 1156 auto := false 1157 for key, model := range columnsModel { 1158 columns[key] = model["Field"].(string) 1159 if columns[key] == tb.PrimaryKey.Name { 1160 if v, ok := model["Extra"].(string); ok { 1161 if v == "auto_increment" { 1162 auto = true 1163 } 1164 } 1165 } 1166 } 1167 return columns, auto 1168 case db.DriverSqlite: 1169 for key, model := range columnsModel { 1170 columns[key] = string(model["name"].(string)) 1171 } 1172 1173 num, _ := tb.sql().Table("sqlite_sequence"). 1174 Where("name", "=", tb.GetForm().Table).Count() 1175 1176 return columns, num > 0 1177 case db.DriverMssql: 1178 for key, model := range columnsModel { 1179 columns[key] = string(model["column_name"].(string)) 1180 } 1181 return columns, true 1182 default: 1183 panic("wrong driver") 1184 } 1185 }