lib/views/error_view.ex
Error View personalizada
defmodule RocketpayWeb.ErrorView do
use RocketpayWeb, :view
import Ecto.Changeset, only: [traverse_errors: 2]
alias Ecto.Changeset
# If you want to customize a particular status code
# for a certain format, you may uncomment below.
# def render("500.json", _assigns) do
# %{errors: %{detail: "Internal Server Error"}}
# end
# By default, Phoenix returns the status message from
# the template name. For example, "404.json" becomes
# "Not Found".
def template_not_found(template, _assigns) do
%{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
end
def render("400.json", %{result: %Changeset{} = changeset}) do
%{message: translate_errors(changeset)}
end
def render("400.json", %{result: message}) do
%{message: message}
end
defp translate_errors(changeset) do
traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
end
end
Changeset é uma função dentro de schemas que serve para fazer validações. EX:
defmodule Rocketpay.Account do
use Ecto.Schema
import Ecto.Changeset
alias Rocketpay.User
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@required_params [:balance, :user_id]
schema "accounts" do
field :balance, :decimal
belongs_to :user, User
timestamps()
end
def changeset(struct \\\\ %__MODULE__{}, params) do
struct
|> cast(params, @required_params)
|> validate_required(@required_params)
|> check_constraint(:balance, name: :balance_must_be_positive_or_zero)
end
end
# Fallback de erros gerais pasta controller
defmodule RocketpayWeb.FallbackController do
use RocketpayWeb, :controller
def call(conn, {:error, result}) do
conn
|> put_status(:bad_request)
|> put_view(RocketpayWeb.ErrorView)
|> render("400.json", result: result)
end
end
# No controller action_fallback RocketpayWeb.FallbackController
defmodule RocketpayWeb.UsersController do
use RocketpayWeb, :controller
alias Rocketpay.User
action_fallback RocketpayWeb.FallbackController
def create(conn, params) do
with {:ok, %User{} = user} <- Rocketpay.create_user(params) do
conn
|> put_status(:created)
|> render("create.json", user: user)
end
end
end
Constraint
create constraint(:accounts, :balance_must_be_positive_or_zero)
Relacionamento entre um schema e outro
schema "accounts" do
field :balance, :decimal
# belongs_to user (pertence a user)
belongs_to :user, User
timestamps()
end
User tem uma conta
defmodule Rocketpay.User do
use Ecto.Schema
import Ecto.Changeset
alias Ecto.Changeset
alias Rocketpay.Account
@primary_key {:id, :binary_id, autogenerate: true}
@required_params [:name, :age, :email, :password, :nickname]
schema "users" do
field :name, :string
field :age, :integer
field :email, :string
field :password, :string, virtual: true
field :password_hash, :string
field :nickname, :string
# has_one tem uma conta
has_one :account, Account
timestamps()
end
def changeset(params) do
%__MODULE__{}
|> cast(params, @required_params)
|> validate_required(@required_params)
|> validate_length(:password, min: 6)
|> validate_number(:age, greater_than_or_equal_to: 18)
|> validate_format(:email, ~r/@/)
|> unique_constraint([:email])
|> unique_constraint([:nickname])
|> put_password_hash()
end
defp put_password_hash(%Changeset{valid?: true, changes: %{password: password}} = changeset) do
change(changeset, Pbkdf2.add_hash(password))
end
defp put_password_hash(changeset), do: changeset
end