Current section
Files
Jump to
Current section
Files
lib/uquery/paginator.ex
defmodule UQuery.Paginator do
@moduledoc """
Documentation for `UQuery Paginator`.
Paginates a query and returns items and metadata.
"""
@doc """
Add to your Repo file the "paginate" method following the example below:
```
defmodule MyApp.Repo do
use Ecto.Repo,
otp_app: :my_app,
adapter: Ecto.Adapters.Postgres
...
def paginate(query, page, per_page) do
UQuery.Paginator.paginate(__MODULE__, query, page, per_page)
end
...
end
```
Now call the method above in your models:
```
defmodule MyApp.Projects do
...
import Ecto.Query, warn: false
alias MyApp.Repo
alias MyApp.Projects.Project
...
def list_projects(page, per_page) do
from(p in Project, select: p)
|> Repo.paginate(page, per_page)
end
...
end
```
"""
import Ecto.Query, warn: false
def paginate(repo, %Ecto.Query{} = query, page, per_page) when is_integer(page) and is_integer(per_page) do
items = query_items(repo, query, page, per_page)
count = UQuery.Counter.count(repo, query)
last = get_last_page(count, per_page)
first = get_first_page(last)
prev = get_prev_page(page, first)
next = get_next_page(page, last)
serie = build_serie(page, last, prev, next)
{
items,
%{
count: count,
page: page,
per_page: per_page,
first: first,
last: last,
prev: prev,
next: next,
serie: serie
}
}
end
def query_items(repo, %Ecto.Query{} = query, page, per_page) when is_integer(page) and is_integer(per_page) do
limit = limit_per_page(per_page)
offset = (page - 1) * limit
repo.all(from query, limit: ^limit, offset: ^offset)
end
defp limit_per_page(per_page) do
if per_page > 50 do
50
else
per_page
end
end
defp get_first_page(total) do
if total > 0 do
1
else
nil
end
end
defp get_last_page(total, per_page) do
pages = calculate_number_of_pages(total, per_page)
if pages == 0 do
nil
else
pages
end
end
defp calculate_number_of_pages(total, per_page) do
if per_page == 0 do
0
else
div(total, per_page) + increment_if_remaining_great_than_one(total, per_page)
end
end
defp increment_if_remaining_great_than_one(total, per_page) do
if rem(total, per_page) > 0 do
1
else
0
end
end
defp get_next_page(page, last) do
if is_nil(last) || page >= last do
nil
else
page + 1
end
end
defp get_prev_page(page, first) do
if is_nil(first) || page <= 1 do
nil
else
page - 1
end
end
defp build_serie(page, last, prev, next) do
series = []
if is_nil(prev) && is_nil(next) do
series
else
series = [page]
while_push(series, 0, page, last)
end
end
defp while_push(series, acc, page, last) do
acc = acc + 1
if (Enum.count(series) > 6) || (acc > last) do
ellipsis_serie(series, last)
else
series
|> increment_serie(acc, page, last)
|> while_push(acc, page, last)
end
end
defp increment_serie(series, acc, page, last) do
series = if acc < page do
[page - acc | series]
else
series
end
if (page + acc) <= last do
Enum.concat(series, [page + acc])
else
series
end
end
defp ellipsis_serie(series, last) do
series = if Enum.at(series, 0) >= 3 do
Enum.concat([1, "..."], series)
else
series
end
if last - Enum.at(series, Enum.count(series) - 1) >= 3 do
Enum.concat(series, ["...", last])
else
series
end
end
end