Does the Phoenix Framework employ any types of callback filter such as those found in Rails? I know it is possible to validate changesets, but I am looking for ways to implement actions such as before_create
, before_save
and after_commit
.
2 Answers
Ecto does: https://hexdocs.pm/ecto/#!Ecto.Model.Callbacks.html
They are sightly different from the Rails ones: they receive and must return changesets and must be used for data consistency (don't use them to send e-mails and what not).
From Ecto 2.0, callbacks have been completely removed.
So how to handle callbacks now?. Here are two ways
For before_
callbacks you can use Changeset itself. One of the reasons callbacks where removed was because many developers relied on callbacks in many cases where changesets would suffice. So simply apply the required function to your changeset,
def changeset(post, params \\ :empty) do
post
|> cast(params, @required_params, @optional_params)
|> validate_length(:title, min: 3)
|> validate_length(:metadata, min: 3)
|> implement_a_before_callback
end
def implement_a_before_callback(changeset)
#Apply required actions and return Changeset
end
Another way, is to group multiple repo operations together using Ecto.Multi. From the docs
Ecto.Multi makes it possible to pack operations that should be performed together (in a single database transaction) and gives a way to introspect the queued operations without actually performing them. Each operation is given a name that is unique and will identify its result or will help to identify the place of failure in case it occurs. So whenever you want a group of data related operations to happen at once you could use
Multi
, bothbefore_
andafter_
callbacks can be substituted here.
An example would be
# In defmodule Service
def password_reset(account, params) do
Multi.new
|> Multi.update(:account, Account.password_reset_changeset(account, params))
|> Multi.insert(:log, Log.password_reset_changeset(account, params))
|> Multi.delete_all(:sessions, assoc(account, :sessions))
end
Run it using
result = Repo.transaction(Service.password_reset(account, params))
You have to remember that you must execute data related queries and not do other tasks like send an email. For that you can simply pattern match on the result and perform appropriate action. Lets sat you wanted to send a mail if the transaction was successful and display some error message if not
case result do
{:ok, %{account: account, log: log, sessions: sessions}} ->
# Operation was successful, perform actions like sending a mail
{:error, failed_operation, failed_value, changes_so_far} ->
# One of the operations failed. Raise error message
end
Source: