View Source Parameter.Schema (Parameter v0.12.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
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
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
.