module IHP.Pagination.ControllerFunctions
( paginate,
paginateWithOptions,
filterList,
defaultPaginationOptions,
paginatedSqlQuery,
paginatedSqlQueryWithOptions,
) where
import IHP.Prelude
import IHP.Controller.Context
import IHP.Controller.Param ( paramOrDefault, paramOrNothing )
import IHP.Pagination.Types
( Options(..), Pagination(..) )
import IHP.QueryBuilder
( HasQueryBuilder, filterWhereILike, limit, offset )
import IHP.Fetch (fetchCount)
import IHP.ModelSupport (GetModelByTableName, sqlQuery, sqlQueryScalar, Table)
import Database.PostgreSQL.Simple.ToField (toField, Action)
import Database.PostgreSQL.Simple.Types (Query(Query))
paginate :: forall controller table queryBuilderProvider joinRegister .
(?context::ControllerContext
, ?modelContext :: ModelContext
, ?theAction :: controller
, KnownSymbol table
, HasQueryBuilder queryBuilderProvider joinRegister) =>
queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginate :: forall {k} controller (table :: Symbol)
(queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(?context::ControllerContext, ?modelContext::ModelContext,
?theAction::controller, KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister) =>
queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginate = Options
-> queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
forall {k} controller (table :: Symbol)
(queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(?context::ControllerContext, ?modelContext::ModelContext,
?theAction::controller, KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister) =>
Options
-> queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginateWithOptions Options
defaultPaginationOptions
paginateWithOptions :: forall controller table queryBuilderProvider joinRegister .
(?context::ControllerContext
, ?modelContext :: ModelContext
, ?theAction :: controller
, KnownSymbol table
, HasQueryBuilder queryBuilderProvider joinRegister) =>
Options
-> queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginateWithOptions :: forall {k} controller (table :: Symbol)
(queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(?context::ControllerContext, ?modelContext::ModelContext,
?theAction::controller, KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister) =>
Options
-> queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginateWithOptions Options
options queryBuilderProvider table
query = do
Int
count <- queryBuilderProvider table
query
queryBuilderProvider table
-> (queryBuilderProvider table -> IO Int) -> IO Int
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> queryBuilderProvider table -> IO Int
forall {k} (table :: Symbol) (queryBuilderProvider :: Symbol -> *)
(joinRegister :: k).
(?modelContext::ModelContext, KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister) =>
queryBuilderProvider table -> IO Int
fetchCount
let pageSize :: Int
pageSize = (?context::ControllerContext) => Options -> Int
Options -> Int
pageSize' Options
options
pagination :: Pagination
pagination = Pagination
{ currentPage :: Int
currentPage = Int
(?context::ControllerContext) => Int
page
, totalItems :: Int
totalItems = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
count
, pageSize :: Int
pageSize = Int
pageSize
, window :: Int
window = Options -> Int
windowSize Options
options
}
let results :: queryBuilderProvider table
results = queryBuilderProvider table
query
queryBuilderProvider table
-> (queryBuilderProvider table -> queryBuilderProvider table)
-> queryBuilderProvider table
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Int -> queryBuilderProvider table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
(joinRegister :: k) (model :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
Int -> queryBuilderProvider model -> queryBuilderProvider model
limit Int
pageSize
queryBuilderProvider table
-> (queryBuilderProvider table -> queryBuilderProvider table)
-> queryBuilderProvider table
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Int -> queryBuilderProvider table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
(joinRegister :: k) (model :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
Int -> queryBuilderProvider model -> queryBuilderProvider model
offset (Int -> Int -> Int
offset' Int
pageSize Int
(?context::ControllerContext) => Int
page)
(queryBuilderProvider table, Pagination)
-> IO (queryBuilderProvider table, Pagination)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
( queryBuilderProvider table
results
, Pagination
pagination
)
filterList :: forall name table model queryBuilderProvider joinRegister .
(?context::ControllerContext
, KnownSymbol name
, HasField name model Text
, model ~ GetModelByTableName table
, KnownSymbol table
, HasQueryBuilder queryBuilderProvider joinRegister
, Table model
) =>
Proxy name
-> queryBuilderProvider table
-> queryBuilderProvider table
filterList :: forall {k} (name :: Symbol) (table :: Symbol) model
(queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(?context::ControllerContext, KnownSymbol name,
HasField name model Text, model ~ GetModelByTableName table,
KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
filterList Proxy name
field =
case forall paramType.
(?context::ControllerContext, ParamReader (Maybe paramType)) =>
ByteString -> Maybe paramType
paramOrNothing @Text ByteString
"filter" of
Just Text
uf -> (Proxy name, Text)
-> queryBuilderProvider table -> queryBuilderProvider table
forall {k} (name :: Symbol) (table :: Symbol) model value
(queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, ToField value,
HasField name model value, model ~ GetModelByTableName table,
HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
(Proxy name, value)
-> queryBuilderProvider table -> queryBuilderProvider table
filterWhereILike (Proxy name
field, Text
"%" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
uf Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"%")
Maybe Text
Nothing -> queryBuilderProvider table -> queryBuilderProvider table
forall a. a -> a
forall {k} (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id
defaultPaginationOptions :: Options
=
Options
{ maxItems :: Int
maxItems = Int
50
, windowSize :: Int
windowSize = Int
5
}
paginatedSqlQuery
:: forall model
. ( FromRow model
, ?context :: ControllerContext
, ?modelContext :: ModelContext
)
=> ByteString -> [Action] -> IO ([model], Pagination)
paginatedSqlQuery :: forall model.
(FromRow model, ?context::ControllerContext,
?modelContext::ModelContext) =>
ByteString -> [Action] -> IO ([model], Pagination)
paginatedSqlQuery = Options -> ByteString -> [Action] -> IO ([model], Pagination)
forall model.
(FromRow model, ?context::ControllerContext,
?modelContext::ModelContext) =>
Options -> ByteString -> [Action] -> IO ([model], Pagination)
paginatedSqlQueryWithOptions Options
defaultPaginationOptions
paginatedSqlQueryWithOptions
:: forall model
. ( FromRow model
, ?context :: ControllerContext
, ?modelContext :: ModelContext
)
=> Options -> ByteString -> [Action] -> IO ([model], Pagination)
paginatedSqlQueryWithOptions :: forall model.
(FromRow model, ?context::ControllerContext,
?modelContext::ModelContext) =>
Options -> ByteString -> [Action] -> IO ([model], Pagination)
paginatedSqlQueryWithOptions Options
options ByteString
sql [Action]
placeholders = do
Int
count :: Int <- Query -> [Action] -> IO Int
forall q value.
(?modelContext::ModelContext, ToRow q, FromField value) =>
Query -> q -> IO value
sqlQueryScalar (ByteString -> Query
Query (ByteString -> Query) -> ByteString -> Query
forall a b. (a -> b) -> a -> b
$ ByteString
"SELECT count(subquery.*) FROM (" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
sql ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
") as subquery") [Action]
placeholders
let pageSize :: Int
pageSize = (?context::ControllerContext) => Options -> Int
Options -> Int
pageSize' Options
options
pagination :: Pagination
pagination = Pagination
{ pageSize :: Int
pageSize = Int
pageSize
, totalItems :: Int
totalItems = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
count
, currentPage :: Int
currentPage = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
(?context::ControllerContext) => Int
page
, window :: Int
window = Options -> Int
windowSize Options
options
}
[model]
results :: [model] <- Query -> [Action] -> IO [model]
forall q r.
(?modelContext::ModelContext, ToRow q, FromRow r) =>
Query -> q -> IO [r]
sqlQuery
(ByteString -> Query
Query (ByteString -> Query) -> ByteString -> Query
forall a b. (a -> b) -> a -> b
$ ByteString
"SELECT subquery.* FROM (" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
sql ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
") as subquery LIMIT ? OFFSET ?")
([Action]
placeholders [Action] -> [Action] -> [Action]
forall a. Semigroup a => a -> a -> a
++ (Int -> Action) -> [Int] -> [Action]
forall a b. (a -> b) -> [a] -> [b]
map Int -> Action
forall a. ToField a => a -> Action
toField [Int
pageSize, Int -> Int -> Int
offset' Int
pageSize Int
(?context::ControllerContext) => Int
page])
([model], Pagination) -> IO ([model], Pagination)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([model]
results, Pagination
pagination)
pageSize' :: (?context::ControllerContext) => Options -> Int
pageSize' :: (?context::ControllerContext) => Options -> Int
pageSize' Options
options = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ forall a.
(?context::ControllerContext, ParamReader a) =>
a -> ByteString -> a
paramOrDefault @Int (Options -> Int
maxItems Options
options) ByteString
"maxItems") Int
200
page :: (?context::ControllerContext) => Int
page :: (?context::ControllerContext) => Int
page = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ forall a.
(?context::ControllerContext, ParamReader a) =>
a -> ByteString -> a
paramOrDefault @Int Int
1 ByteString
"page"
offset' :: Int -> Int -> Int
offset' :: Int -> Int -> Int
offset' Int
pageSize Int
page = (Int
page Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
pageSize