README.md

# Flop

![CI](https://github.com/woylie/flop/workflows/CI/badge.svg) [![Hex](https://img.shields.io/hexpm/v/flop)](https://hex.pm/packages/flop)

Flop is an Elixir library for making filtering, ordering and pagination with
Ecto a bit easier.

**This library is in early development.**

## Features

- ordering by multiple fields in multiple directions
- offset/limit based pagination
- page number/page size based pagination
- filtering by multiple conditions with diverse operators on multiple fields
- parameter validation
- configurable filterable and sortable fields

## To Do

See https://github.com/woylie/flop/projects/1

## Installation

Add `flop` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:flop, "~> 0.2.0"}
  ]
end
```

## Usage

### Define sortable and filterable fields

If you want the order by and filter fields to be validated, configure the
sortable and filterable fields in your Ecto schema like this:

```elixir
defmodule MyApp.Pet do
  use Ecto.Schema

  @derive {Flop.Schema,
           filterable: [:name, :species], sortable: [:name, :age, :species]}

  schema "pets" do
    field :name, :string
    field :age, :integer
    field :species, :string
    field :social_security_number, :string
  end
end
```

### Querying

The most important functions are `Flop.query/2` and `Flop.validate/2`. You can
use them like this:

```elixir
defmodule MyApp.Pets do
  import Ecto.Query, warn: false

  alias Ecto.Changeset
  alias Flop
  alias MyApp.{Pet, Repo}

  @spec list_pets(Flop.t()) :: {:ok, Pet.t()} | {:error, Changeset.t()}
  def list_pets(flop \\ %Flop{}) do
    with {:ok, flop} <- Flop.validate(flop, for: Pet) do
      pets =
        Pet
        |> Flop.query(flop)
        |> Repo.all()

      {:ok, pets}
    end
  end
end
```

If you didn't derive Flop.Schema as described above and don't care to do so,
just call the validate function without the second parameter:
`Flop.validate(flop)`.

### Phoenix

If you are using Phoenix, you might prefer to validate the Flop parameters in
your controller instead.

```elixir
defmodule MyAppWeb.PetController do
  use MyAppWeb, :controller

  alias Flop
  alias MyApp.Pets
  alias MyApp.Pets.Pet

  action_fallback MyAppWeb.FallbackController

  def index(conn, params) do
    with {:ok, flop} <- Flop.validate(params, for: Pet) do
      pets = Pets.list_pets(flop)
      render(conn, "index.html", pets: pets)
    end
  end
end

defmodule MyApp.Pets do
  import Ecto.Query, warn: false

  alias Flop
  alias MyApp.Pets.Pet
  alias MyApp.Repo

  @spec list_pets(Flop.t()) :: [Pet.t()]
  def list_pets(flop \\ %Flop{}) do
    Pet
    |> Flop.query(flop)
    |> Repo.all()
  end
end
```