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 IHP.Hasql.FromRow (FromRowHasql)
import IHP.Hasql.Encoders (ToSnippetParams(..))
import Network.Wai (Request)
import Database.PostgreSQL.Simple (Query(..), Only(Only), (:.)(..))
paginate :: forall controller table queryBuilderProvider joinRegister .
(?context::ControllerContext
, ?modelContext :: ModelContext
, ?theAction :: controller
, ?request :: Request
, 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, ?request::Request, 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, ?request::Request, 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
, ?request :: Request
, 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, ?request::Request, KnownSymbol table,
HasQueryBuilder queryBuilderProvider joinRegister) =>
Options
-> queryBuilderProvider table
-> IO (queryBuilderProvider table, Pagination)
paginateWithOptions Options
options queryBuilderProvider table
query = do
count <- queryBuilderProvider table
query
queryBuilderProvider table
-> (queryBuilderProvider table -> IO Int) -> IO Int
forall a b. a -> (a -> b) -> b
|> 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 = (?request::Request) => Options -> Int
Options -> Int
pageSize' Options
options
pagination = Pagination
{ currentPage :: Int
currentPage = Int
(?request::Request) => 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
query
queryBuilderProvider table
-> (queryBuilderProvider table -> queryBuilderProvider table)
-> queryBuilderProvider table
forall a b. a -> (a -> b) -> b
|> 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 a b. a -> (a -> b) -> b
|> 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
(?request::Request) => Int
page)
pure
( results
, pagination
)
filterList :: forall name table model queryBuilderProvider joinRegister .
(?context::ControllerContext
, ?request :: Request
, 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, ?request::Request, 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.
(?request::Request, 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, DefaultParamEncoder 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
:: ( FromRowHasql model
, ToSnippetParams parameters
, ToSnippetParams (parameters :. Only Int :. Only Int)
, ?context :: ControllerContext
, ?modelContext :: ModelContext
, ?request :: Request
)
=> Query -> parameters -> IO ([model], Pagination)
paginatedSqlQuery :: forall model parameters.
(FromRowHasql model, ToSnippetParams parameters,
ToSnippetParams (parameters :. (Only Int :. Only Int)),
?context::ControllerContext, ?modelContext::ModelContext,
?request::Request) =>
Query -> parameters -> IO ([model], Pagination)
paginatedSqlQuery = Options -> Query -> parameters -> IO ([model], Pagination)
forall model parameters.
(FromRowHasql model, ToSnippetParams parameters,
ToSnippetParams (parameters :. (Only Int :. Only Int)),
?context::ControllerContext, ?modelContext::ModelContext,
?request::Request) =>
Options -> Query -> parameters -> IO ([model], Pagination)
paginatedSqlQueryWithOptions Options
defaultPaginationOptions
paginatedSqlQueryWithOptions
:: ( FromRowHasql model
, ToSnippetParams parameters
, ToSnippetParams (parameters :. Only Int :. Only Int)
, ?context :: ControllerContext
, ?modelContext :: ModelContext
, ?request :: Request
)
=> Options -> Query -> parameters -> IO ([model], Pagination)
paginatedSqlQueryWithOptions :: forall model parameters.
(FromRowHasql model, ToSnippetParams parameters,
ToSnippetParams (parameters :. (Only Int :. Only Int)),
?context::ControllerContext, ?modelContext::ModelContext,
?request::Request) =>
Options -> Query -> parameters -> IO ([model], Pagination)
paginatedSqlQueryWithOptions Options
options Query
sql parameters
placeholders = do
count :: Int <- Query -> parameters -> IO Int
forall q value.
(?modelContext::ModelContext, ToSnippetParams q,
HasqlDecodeColumn value) =>
Query -> q -> IO value
sqlQueryScalar (Query
"SELECT count(subquery.*) FROM (" Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
sql Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
") as subquery") parameters
placeholders
let pageSize = (?request::Request) => Options -> Int
Options -> Int
pageSize' Options
options
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
(?request::Request) => Int
page
, window :: Int
window = Options -> Int
windowSize Options
options
}
results :: [model] <- sqlQuery
("SELECT subquery.* FROM (" <> sql <> ") as subquery LIMIT ? OFFSET ?")
(placeholders :. Only pageSize :. Only (offset' pageSize page))
pure (results, pagination)
pageSize' :: (?request :: Request) => Options -> Int
pageSize' :: (?request::Request) => 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.
(?request::Request, ParamReader a) =>
a -> ByteString -> a
paramOrDefault @Int (Options -> Int
maxItems Options
options) ByteString
"maxItems") Int
200
page :: (?request :: Request) => Int
page :: (?request::Request) => 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.
(?request::Request, 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