Files
MyClub/internal/controllers/query_helper.go
T
Tomas Dvorak 9ccca365b3 dev day #65
2025-10-19 18:09:28 +02:00

238 lines
6.1 KiB
Go

package controllers
import (
"fmt"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// QueryHelper provides query parameter parsing and filtering utilities
type QueryHelper struct{}
// SortParams represents sorting parameters
type SortParams struct {
Field string
Order string // "asc" or "desc"
}
// FilterParams represents generic filter parameters
type FilterParams map[string]interface{}
// NewQueryHelper creates a new QueryHelper instance
func NewQueryHelper() *QueryHelper {
return &QueryHelper{}
}
// GetSortParams extracts sort parameters from the request
// Expects: ?sort=field:order (e.g., ?sort=created_at:desc)
func (qh *QueryHelper) GetSortParams(c *gin.Context, defaultField, defaultOrder string) SortParams {
sortQuery := c.Query("sort")
if sortQuery == "" {
return SortParams{
Field: defaultField,
Order: defaultOrder,
}
}
parts := strings.Split(sortQuery, ":")
field := parts[0]
order := defaultOrder
if len(parts) > 1 {
order = strings.ToLower(parts[1])
if order != "asc" && order != "desc" {
order = defaultOrder
}
}
return SortParams{
Field: field,
Order: order,
}
}
// ApplySort applies sorting to a GORM query
func (qh *QueryHelper) ApplySort(db *gorm.DB, params SortParams) *gorm.DB {
if params.Field != "" {
return db.Order(params.Field + " " + params.Order)
}
return db
}
// ApplySortFromContext extracts sort params from context and applies them
func (qh *QueryHelper) ApplySortFromContext(c *gin.Context, db *gorm.DB, defaultField, defaultOrder string) *gorm.DB {
params := qh.GetSortParams(c, defaultField, defaultOrder)
return qh.ApplySort(db, params)
}
// GetSearchQuery extracts search query from request
// Expects: ?search=term or ?q=term
func (qh *QueryHelper) GetSearchQuery(c *gin.Context) string {
search := c.Query("search")
if search == "" {
search = c.Query("q")
}
return strings.TrimSpace(search)
}
// ApplySearch applies search to specified fields using LIKE
func (qh *QueryHelper) ApplySearch(db *gorm.DB, searchTerm string, fields ...string) *gorm.DB {
if searchTerm == "" || len(fields) == 0 {
return db
}
searchPattern := "%" + searchTerm + "%"
query := db
// Build OR conditions for each field
for i, field := range fields {
if i == 0 {
query = query.Where(field+" LIKE ?", searchPattern)
} else {
query = query.Or(field+" LIKE ?", searchPattern)
}
}
return query
}
// ApplySearchFromContext extracts search query and applies it
func (qh *QueryHelper) ApplySearchFromContext(c *gin.Context, db *gorm.DB, fields ...string) *gorm.DB {
searchTerm := qh.GetSearchQuery(c)
return qh.ApplySearch(db, searchTerm, fields...)
}
// GetBoolQuery extracts a boolean query parameter
func (qh *QueryHelper) GetBoolQuery(c *gin.Context, key string) *bool {
value := c.Query(key)
if value == "" {
return nil
}
boolValue := strings.ToLower(value) == "true" || value == "1"
return &boolValue
}
// ApplyBoolFilter applies a boolean filter if the parameter exists
func (qh *QueryHelper) ApplyBoolFilter(db *gorm.DB, c *gin.Context, paramKey, dbField string) *gorm.DB {
value := qh.GetBoolQuery(c, paramKey)
if value != nil {
return db.Where(dbField+" = ?", *value)
}
return db
}
// GetIDsFromQuery extracts comma-separated IDs from query parameter
// Expects: ?ids=1,2,3,4
func (qh *QueryHelper) GetIDsFromQuery(c *gin.Context, key string) []uint {
idsStr := c.Query(key)
if idsStr == "" {
return nil
}
parts := strings.Split(idsStr, ",")
ids := make([]uint, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
// Parse as uint
var id uint
if _, err := fmt.Sscanf(part, "%d", &id); err == nil {
ids = append(ids, id)
}
}
return ids
}
// ApplyIDsFilter applies IN filter for IDs
func (qh *QueryHelper) ApplyIDsFilter(db *gorm.DB, c *gin.Context, paramKey, dbField string) *gorm.DB {
ids := qh.GetIDsFromQuery(c, paramKey)
if len(ids) > 0 {
return db.Where(dbField+" IN ?", ids)
}
return db
}
// GetDateRange extracts date range from query parameters
// Expects: ?from=2024-01-01&to=2024-12-31
func (qh *QueryHelper) GetDateRange(c *gin.Context) (from, to string) {
from = c.Query("from")
to = c.Query("to")
return
}
// ApplyDateRangeFilter applies date range filter
func (qh *QueryHelper) ApplyDateRangeFilter(db *gorm.DB, c *gin.Context, dbField string) *gorm.DB {
from, to := qh.GetDateRange(c)
if from != "" {
db = db.Where(dbField+" >= ?", from)
}
if to != "" {
db = db.Where(dbField+" <= ?", to)
}
return db
}
// BuildQueryChain combines multiple query operations
func (qh *QueryHelper) BuildQueryChain(c *gin.Context, db *gorm.DB) *QueryChainBuilder {
return &QueryChainBuilder{
ctx: c,
query: db,
qh: qh,
}
}
// QueryChainBuilder provides a fluent interface for building queries
type QueryChainBuilder struct {
ctx *gin.Context
query *gorm.DB
qh *QueryHelper
}
// WithSort adds sorting
func (qcb *QueryChainBuilder) WithSort(defaultField, defaultOrder string) *QueryChainBuilder {
qcb.query = qcb.qh.ApplySortFromContext(qcb.ctx, qcb.query, defaultField, defaultOrder)
return qcb
}
// WithSearch adds search across fields
func (qcb *QueryChainBuilder) WithSearch(fields ...string) *QueryChainBuilder {
qcb.query = qcb.qh.ApplySearchFromContext(qcb.ctx, qcb.query, fields...)
return qcb
}
// WithBoolFilter adds a boolean filter
func (qcb *QueryChainBuilder) WithBoolFilter(paramKey, dbField string) *QueryChainBuilder {
qcb.query = qcb.qh.ApplyBoolFilter(qcb.query, qcb.ctx, paramKey, dbField)
return qcb
}
// WithIDsFilter adds an IDs filter
func (qcb *QueryChainBuilder) WithIDsFilter(paramKey, dbField string) *QueryChainBuilder {
qcb.query = qcb.qh.ApplyIDsFilter(qcb.query, qcb.ctx, paramKey, dbField)
return qcb
}
// WithDateRange adds date range filter
func (qcb *QueryChainBuilder) WithDateRange(dbField string) *QueryChainBuilder {
qcb.query = qcb.qh.ApplyDateRangeFilter(qcb.query, qcb.ctx, dbField)
return qcb
}
// Build returns the final query
func (qcb *QueryChainBuilder) Build() *gorm.DB {
return qcb.query
}
// Global QueryHelper instance for convenience
var QueryParser = NewQueryHelper()