View Source Parameter.Schema (Parameter v0.12.0)

The first step for building a schema for your data is to create a schema definition to model the external data. This can be achieved by using the Parameter.Schema macro.

schema

Schema

The example below mimics an User model that have one main_address and a list of phones.

defmodule User do
  use Parameter.Schema

  param do
    field :first_name, :string, key: "firstName", required: true
    field :last_name, :string, key: "lastName", required: true, default: ""
    has_one :main_address, Address, key: "mainAddress", required: true
    has_many :phones, Phone
  end
end

defmodule Address do
  use Parameter.Schema

  param do
    field :city, :string, required: true
    field :street, :string
    field :number, :integer
  end
end

defmodule Phone do
  use Parameter.Schema

  param do
    field :country, :string
    field :number, :integer
  end
end

Parameter offers other ways for creating a schema such as nesting the has_one and has_many fields. This require module name as the second parameter using do at the end:

defmodule User do
  use Parameter.Schema

  param do
    field :first_name, :string, key: "firstName", required: true
    field :last_name, :string, key: "lastName", required: true, default: ""

    has_one :main_address, Address, key: "mainAddress", required: true do
      field :city, :string, required: true
      field :street, :string
      field :number, :integer
    end

    has_many :phones, Phone do
      field :country, :string
      field :number, :integer
    end
  end
end

Another possibility is avoiding creating files for a schema at all. This can be done by importing Parameter.Schema and using the param/2 macro. This is useful for adding params in Phoenix controllers. For example:

defmodule MyProjectWeb.UserController do
  use MyProjectWeb, :controller
  import Parameter.Schema

  alias MyProject.Users

  param UserParams do
    field :first_name, :string, required: true
    field :last_name, :string, required: true
  end

  def create(conn, params) do
    with {:ok, user_params} <- Parameter.load(__MODULE__.UserParams, params),
        {:ok, user} <- Users.create_user(user_params) do
      render(conn, "user.json", %{user: user})
    end
  end
end

It's recommended to use this approach when the schema will only be used in a single module.

runtime-schemas

Runtime Schemas

It's also possible to create schemas via runtime without relying on any macros. The API is almost the same comparing to the macro's examples:

schema = %{
  first_name: [key: "firstName", type: :string, required: true],
  address: [type: {:map, %{street: [type: :string, required: true]}}],
  phones: [type: {:array, %{country: [type: :string, required: true]}}]
} |> Parameter.Schema.compile!()

Parameter.load(schema, %{"firstName" => "John"})
{:ok, %{first_name: "John"}}

The same API can also be evaluated on compile time by using module attributes:

defmodule UserParams do
  alias Parameter.Schema

  @schema %{
    first_name: [key: "firstName", type: :string, required: true],
    address: [required: true, type: {:map, %{street: [type: :string, required: true]}}],
    phones: [type: {:array, %{country: [type: :string, required: true]}}]
  } |> Schema.compile!()

  def load(params) do
    Parameter.load(@schema, params)
  end
end

This makes it easy to dynamically create schemas or just avoid using any macros.

required-fields

Required fields

By default, Parameter.Schema considers all fields to be optional when validating the schema. This behaviour can be changed by passing the module attribute @fields_required true on the module where the schema is declared.

example

Example

defmodule MyApp.UserSchema do
  use Parameter.Schema

  @fields_required true

  param do
    field :name, :string
    field :age, :integer
  end
end

Parameter.load(MyApp.UserSchema, %{})
{:error, %{age: "is required", name: "is required"}}

custom-field-loading-and-dumping

Custom field loading and dumping

The load and dump behavior can be customized per field by implementing on_load or on_dump functions in the field definition. This can be useful if the field needs to be fetched or even validate in a different way than the defaults implemented by Parameter. Both functions should return {:ok, value} or {:error, reason} tuple.

For example, imagine that there is a parameter called full_name in your schema that you want to customize on how it will be parsed:

defmodule MyApp.UserSchema do
  use Parameter.Schema

  param do
    field :first_name, :string
    field :last_name, :string
    field :full_name, :string, on_load: &__MODULE__.load_full_name/2
  end

  def load_full_name(value, params) do
    # if `full_name` is not `nil` it just return the `full_name`
    if value do
      {:ok, value}
    else
      # Otherwise it will join the `first_name` and `last_name` params
      {:ok, params["first_name"] <> " " <> params["last_name"]}
    end
  end
end

Now when loading, the full_name field will be handled by the load_full_name/2 function:

Parameter.load(MyApp.UserSchema, %{first_name: "John", last_name: "Doe", full_name: nil})
{:ok, %{first_name:  "John", full_name: "John Doe", last_name: "Doe"}}

The same behavior is possible when dumping the schema parameters by using on_dump/2 function:

schema = %{
  level: [type: :integer, on_dump: fn value, _input -> {:ok, value || 0}  end]
} |> Parameter.Schema.compile!()

Parameter.dump(schema, %{level: nil})
{:ok, %{"level" => 0}}

Link to this section Summary

Functions

See Parameter.Schema.Compiler.compile_schema!/1.

Link to this section Functions

See Parameter.Schema.Compiler.compile_schema!/1.