View Source Parameter.Schema (Parameter v0.14.1)
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
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.
use Parameter.Schema
When you
use Parameter.Schema
, theSchema
module will register module attributes and inject functions that are necessary forParameter.load/3
,Parameter.validate/3
andParameter.dump/3
to fetch the schema on runtime.
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
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
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
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}}
Summary
Functions
See Parameter.Schema.Compiler.compile_schema!/1
.
Functions
See Parameter.Schema.Compiler.compile_schema!/1
.