CRUD
This service it gives you ability to build query and apply different query parameters to the query that should be used in listing pages
Register the service into your main.go
file:
registry.ServiceProviderCrud(),
Access the service:
service.DI().Crud()
Search vs Filter
Search is used on strings while filtering can be used on wide range of data types. One important note to remember is that if your column is searchable it can't be filterable.
defining columns
First you need to define what columns you're going to have and which of them will be Searchable, Sortable or Filterable(user for enum values). Using this configuration you also define the supported filters that can be applied.
The second step is in your controller(handler) to call few methods from that service that will build the right query for you based on the request. Crud service supports mysql query builder and redis-search query builder.
Example of the way you can use it:
//defining columns.
func columns() []crud.Column {
return []crud.Column{
{
Key: "ID",
Type: crud.NumberType,
Label: "ID",
Searchable: false,
Sortable: true,
Visible: true,
Filterable: true,
FilterValidMap: nil,
},
{
Key: "Name",
Type: crud.StringType,
Label: "Name",
Searchable: true,
Sortable: false,
Visible: true,
Filterable: false,
FilterValidMap: nil,
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//listing request
type ListRequest struct {
Page *int `json:"Page"`
PageSize *int `json:"PageSize"`
Filter map[string]interface{} `json:"Filter"`
Search map[string]interface{} `json:"Search"`
SearchOr map[string]interface{} `json:"SearchOR"`
Sort map[string]interface{} `json:"Sort"`
}
2
3
4
5
6
7
8
9
and at the end your method where you return the response:
cols := columns()
crudService := service.DI().CrudService()
searchParams := crudService.ExtractListParams(cols, userListRequest)
query := crudService.GenerateListRedisSearchQuery(searchParams)
ormService := ioc.GetOrmEngineFromContext(ctx)
var userEntities []*entity.UserEntity
ormService.RedisSearch(&userEntities, query, beeorm.NewPager(searchParams.Page, searchParams.PageSize))
userEntityList := make([]*model.User, len(userEntities))
for i, userEntity := range userEntities {
userEntityList[i] = populate.UserAdmin(userEntity)
}
return &model.UserList{
Rows: userEntityList,
Total: len(userEntityList),
Columns: cols,
}, nil
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Use Select fields with dependency
Imagine you have these fields with such options, on is the country and for the city filter to work user must select the country first and then select the proper city that exists in the list of that country otherwise this filter will be ignored.
Take london as an example, if we select United States
as country and London
as city, then city filter will be ignored, so make sure this is also handled in your front-end.
Note: If you don't set FilterDependencyField
or DataMapStringStringKeyStringValue
this filter will be ignored!
func columns() []crud.Column {
return []crud.Column{
{
Key: "Country",
FilterType: hitrixCrud.SelectTypeStringString,
Label: "Country",
Searchable: true,
Sortable: false,
Visible: true,
TranslationDataEnabled: true,
DataStringKeyStringValue: []*hitrixCrud.StringKeyStringValue{
{Key: "bg", Label: "Bulgaria"},
{Key: "us", Label: "United States"},
{Key: "uk", Label: "United Kingdom"},
},
},
{
Key: "City",
FilterType: hitrixCrud.SelectTypeStringString,
Label: "City",
Searchable: true,
Sortable: false,
Visible: true,
FilterDependencyField: "Country",
DataMapStringStringKeyStringValue: map[string][]*hitrixCrud.StringKeyStringValue{
"bg": {
{Key: "sofia", Label: "Sofia"},
{Key: "plovdiv", Label: "Plovdiv"},
},
"us": {
{Key: "new_york", Label: "New York"},
{Key: "los_angeles", Label: "Los Angeles"},
},
"uk": {
{Key: "london", Label: "London"},
},
},
},
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Use CRUD with our export service
You can mix our crud service with our exporter service to add a quick and painless exporting system to your project
In order to do that you can follow these steps
1 - Making your currently list view function private and creating another function for passing to gin router
for example here we renamed "List" to "list" and we created a new "ListRequest" function and we're passing it to gin router instead of "List"
func Columns() []hitrixCrud.Column {
return []hitrixCrud.Column{
{
Key: "ID",
Label: "ID",
Searchable: false,
Sortable: true,
Visible: true,
},
{
Key: "Name",
FilterType: hitrixCrud.InputTypeString,
Label: "Name",
Searchable: true,
Sortable: false,
Visible: true,
},
}
}
type ListRow struct {
ID uint64
Name string
}
func ListRequest(ctx context.Context, request *hitrixCrud.ListRequest) (*city.ResponseDTOList, error) {
// You should pass this function to the gin router
return list(service.DI().OrmEngineForContext(ctx), request)
}
func list(ormService *beeorm.Engine, request *hitrixCrud.ListRequest) (*city.ResponseDTOList, error) {
// this is the function that handles the getting and making the payload fot the crud that is going to be used
// for both list (endpoint) and exporting
cols := Columns()
crudService := service.DI().Crud()
searchParams := crudService.ExtractListParams(cols, request)
query := crudService.GenerateListRedisSearchQuery(searchParams)
var cityEntities []*entity.CityEntity
// your logic for getting entities from database with the query generated from crud
rows := make([]ListRow, len(cityEntities))
// you logic for creating rows for your crud
return &ResponseDTOList{
Rows: rows,
Total: int(total),
Columns: cols,
PageContext: getPageContext(ormService),
}, nil
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2 - Creating a new handler for exporting the data obtained from "list" function
func ListExport(ormService *beeorm.Engine, request *hitrixCrud.ListRequest, _ uint64, _ map[string]string) (error, []string, [][]interface{}) {
// This function handles the data for exporting
exportColumns := make([]string, 0) // excel or csv Columns for passing to our exporter service
allExportData := make([][]interface{}, 0) // data for passing to our exporter service
pager := beeorm.NewPager(1, 1000)
for {
// loop for getting all the data out of the list function, bypassing the pagination
request.Page = &pager.CurrentPage
request.PageSize = &pager.PageSize
res, err := list(ormService, request)
if err != nil {
return err, nil, nil
}
// GetExporterDataCrud converts any given rows which in example is []city.ListRow to
// exporter service columns and data as types of []string, [][]interface, exactly the data you need for passing to our
// exporting system
columns, exportData := hitrixCrud.GetExporterDataCrud(Columns(), res.Rows)
for _, exportDataRow := range exportData {
allExportData = append(allExportData, exportDataRow)
}
exportColumns = columns
if len(res.Rows) < pager.PageSize {
break
}
pager.IncrementPage()
}
return nil, exportColumns, allExportData
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
3 - Creating the config and passing it to the service
we pass the ListExport
function that we created above to the config
const (
CityExportID = "cities"
)
var RegisteredExportConfigs = []crud.ExportConfig{
{
Handler: cityView.ListExport,
ID: CityExportID,
AllowedExtraArgs: nil,
Resource: "city",
Permissions: []string{"read"},
},
}
RegisterDIGlobalService(
...
hitrixRegistry.ServiceProviderCrud(RegisteredExportConfigs),
...
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
4 - Getting export ready data from crud
Imagine we have these two cities in our database
[
{
"ID": 1,
"Name": "Sofia"
},
{
"ID": 2,
"Name": "New York"
}
]
2
3
4
5
6
7
8
9
10
We use the crud method to get config/handler and export our data
handler, exists := crudService.GetExportHandler(CityExportID) // getting the handler
err, columns, data := handler(
ormService,
&hitrixCrud.ListRequest{},
1, // user requesting the export
nil,
)
print(columns) // would print []string{"ID", "Name"}
print(data) // would print [][]interface{}{[]interface{"1", "Sofia"}, []interface{"2", "New York"}}
2
3
4
5
6
7
8
9
10
11
12
and you can easily pass this data to our exporter service, for example:
excelBytes, err := exporterService.XLSXExportToByte("cities", columns, data)
and if you like to check the permissions you can get the whole config like this
config, exists := crudService.GetExportConfig(CityExportID) // getting the config