-
Notifications
You must be signed in to change notification settings - Fork 0
/
pagination.go
124 lines (106 loc) · 2.46 KB
/
pagination.go
1
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package database
import (
"math"
"github.com/enorith/http/contracts"
"gorm.io/gorm"
)
type PageParams interface {
Page() int
PerPage() int
}
type PageMeta struct {
Total int64 `json:"total"`
PerPage int `json:"per_page"`
Page int `json:"page"`
LastPage int `json:"last_page"`
From int `json:"from"`
To int `json:"to"`
}
type PageResult[T interface{}] struct {
Meta PageMeta `json:"meta"`
Data []T `json:"data"`
Extra map[string]interface{} `json:"extra,omitempty"`
}
func (pr *PageResult[T]) With(key string, data interface{}) *PageResult[T] {
if pr.Extra == nil {
pr.Extra = make(map[string]interface{})
}
pr.Extra[key] = data
return pr
}
func (pr *PageResult[T]) WithOut(key string) *PageResult[T] {
if pr.Extra == nil {
pr.Extra = make(map[string]interface{})
}
delete(pr.Extra, key)
return pr
}
type Paginator[T interface{}] struct {
Params PageParams
}
type PaginateOptions struct {
AggTableScope func(tx *gorm.DB) *gorm.DB
}
func (p Paginator[T]) Paginate(tx *gorm.DB, opts ...PaginateOptions) (*PageResult[T], error) {
var (
meta PageMeta
targets []T
)
opt := PaginateOptions{
AggTableScope: func(tx *gorm.DB) *gorm.DB {
return tx
},
}
if len(opts) > 0 {
if opts[0].AggTableScope != nil {
opt.AggTableScope = opts[0].AggTableScope
}
}
page := p.Params.Page()
perPage := p.Params.PerPage()
if page < 1 {
page = 1
}
if perPage < 1 {
perPage = DefaultPageSize
}
meta.Page = page
meta.PerPage = perPage
meta.From = perPage*(page-1) + 1
newTx := tx.Session(&gorm.Session{
NewDB: true,
})
tx.Statement.Dest = targets
aggTable := tx.Session(&gorm.Session{}).Scopes(opt.AggTableScope)
e := newTx.Table("(?) aggragate", aggTable).Count(&meta.Total).Error
if e != nil {
return nil, e
}
db := tx.Limit(int(perPage)).Offset(int(perPage * (page - 1))).Find(&targets)
e = db.Error
if e != nil {
return nil, e
}
meta.LastPage = int(math.Ceil(float64(meta.Total) / float64(perPage)))
meta.To = meta.From + int(db.RowsAffected-1)
return &PageResult[T]{
Data: targets,
Meta: meta,
}, nil
}
func NewPaginator[T interface{}](params PageParams) Paginator[T] {
return Paginator[T]{
Params: params,
}
}
type RequestPageParams struct {
request contracts.RequestContract
}
func (r RequestPageParams) Page() int {
p, _ := r.request.GetInt(PageKey)
return p
}
func (r RequestPageParams) PerPage() int {
p, _ := r.request.GetInt(PageSizeKey)
return p
}