package controllers import ( "math" "strconv" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // PaginationHelper provides pagination utilities type PaginationHelper struct{} // PaginationParams represents pagination parameters type PaginationParams struct { Page int `json:"page"` PageSize int `json:"page_size"` Offset int `json:"-"` } // PaginationMeta represents pagination metadata type PaginationMeta struct { Page int `json:"page"` PageSize int `json:"page_size"` Total int64 `json:"total"` TotalPages int `json:"total_pages"` HasNext bool `json:"has_next"` HasPrev bool `json:"has_prev"` } // NewPaginationHelper creates a new PaginationHelper instance func NewPaginationHelper() *PaginationHelper { return &PaginationHelper{} } // GetPaginationParams extracts pagination parameters from the request // Defaults: page=1, pageSize=20 func (ph *PaginationHelper) GetPaginationParams(c *gin.Context) PaginationParams { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) // Validate and constrain values if page < 1 { page = 1 } if pageSize < 1 { pageSize = 20 } if pageSize > 100 { pageSize = 100 // Max page size to prevent abuse } offset := (page - 1) * pageSize return PaginationParams{ Page: page, PageSize: pageSize, Offset: offset, } } // BuildPaginationMeta creates pagination metadata func (ph *PaginationHelper) BuildPaginationMeta(params PaginationParams, total int64) PaginationMeta { totalPages := int(math.Ceil(float64(total) / float64(params.PageSize))) return PaginationMeta{ Page: params.Page, PageSize: params.PageSize, Total: total, TotalPages: totalPages, HasNext: params.Page < totalPages, HasPrev: params.Page > 1, } } // Paginate applies pagination to a GORM query and returns the paginated results with metadata func (ph *PaginationHelper) Paginate(c *gin.Context, db *gorm.DB, dest interface{}) (PaginationMeta, error) { params := ph.GetPaginationParams(c) // Count total records var total int64 if err := db.Count(&total).Error; err != nil { return PaginationMeta{}, err } // Apply pagination if err := db.Offset(params.Offset).Limit(params.PageSize).Find(dest).Error; err != nil { return PaginationMeta{}, err } meta := ph.BuildPaginationMeta(params, total) return meta, nil } // PaginateWithPreload applies pagination with preloading associations func (ph *PaginationHelper) PaginateWithPreload(c *gin.Context, db *gorm.DB, dest interface{}, preloads ...string) (PaginationMeta, error) { params := ph.GetPaginationParams(c) // Count total records (without preload) var total int64 if err := db.Count(&total).Error; err != nil { return PaginationMeta{}, err } // Apply preloads query := db for _, preload := range preloads { query = query.Preload(preload) } // Apply pagination if err := query.Offset(params.Offset).Limit(params.PageSize).Find(dest).Error; err != nil { return PaginationMeta{}, err } meta := ph.BuildPaginationMeta(params, total) return meta, nil } // Global PaginationHelper instance for convenience var Paginator = NewPaginationHelper()