Skip to content

Commit 598e0c2

Browse files
added section on working with lifecycle callbacks
removed reference to internet trolls and spiderman's Uncle ben dying (lol) changed opening paragraph to be more concise forgot backticks replaced the 2 instances of "his" with "the" and "that" which are so cleverly gender-neutral that they don't stick out as being gender-neutral
1 parent 0d84d94 commit 598e0c2

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

H_ecto_models.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,60 @@ end
597597
```
598598
Because we declared a relationship in `HelloPhoenix.User`, `%User{}` will now contain a videos property which starts out as an unloaded relationship. In order to properly display it in `render`, we'll need to tell Ecto to preload the field. Note that `Repo.preload/2` is smart enough to work on just one model or collection of them.
599599

600+
### Working with Callback Hooks
601+
602+
Continuing with our video sharing app example, many web applications require moderation for user submitted content. Our Video model has an `approved_at` field to signify that a moderator has approved the video. The question is, what happens if the user edits the video? Ideally, we'd like to be able to "null out" a video's `approved_at` field when this happens. Fortunately, Ecto provides us with a handful of life-cycle callbacks to achieve this.
603+
604+
We will instruct Ecto to use the `before_update` hook in `web/models/video.ex` like so:
605+
606+
```elixir
607+
defmodule HelloPhoenix.Video do
608+
. . .
609+
before_update :reset_approved_at
610+
def reset_approved_at(changeset) do
611+
changeset
612+
|> Ecto.Changeset.put_change(:approved_at, nil)
613+
end
614+
end
615+
```
616+
Now, when `Repo.update` is called for the `Video` model, the `approved_at` field will automatically be reset to nil. This should keep us safe from the ravages of an often hostile modern Internet!
617+
618+
`Ecto.Model.Callbacks` actually ships with with a lot more than the `before_update` callback, in addition, it comes with:
619+
620+
- `before_delete`: called before the adapter deletes our record from the database; if perhaps we have a foreign_key constraint from another table, we would use this hook to clean it up.
621+
- `after_delete`: called after the adapter deletes our record. If we wished to archive our record, we'd do it in this hook.
622+
- `before_update`: called before the adapter updates our record. We used it above to reset the likes counter.
623+
- `after_update`: called after the adapter updates our record. If we wanted to update our user that the model has changed, now would do that here.
624+
- `before_insert`: called before the adapter first creates a new record. We would use it in cases when we wanted to infer default values to certain fields from the partial changeset we receive from the user.
625+
- `after_insert`: called after the adapter creates our record. We could use this to notify the user a new has been created under that user's account.
626+
- `after_load`: called after the model is loaded from the database. We might use this to load in other values based upon what we found in our record.
627+
628+
Note: there is no `before_load` hook.
629+
630+
In all cases, a hook expects to be passed an atom that is the name of the function it is to call if that function is defined in the current module:
631+
632+
```elixir
633+
before_insert :name_of_function_to_call
634+
def name_of_function_to_call(changeset) do
635+
# do stuff with the changeset
636+
# and return the modified changeset
637+
end
638+
```
639+
If our callback function lives elsewhere, hooks can take the name of the module as the first argument:
640+
```elixir
641+
before_insert HelloPhoenix.AnotherPlace, :name_of_function_to_call
642+
```
643+
644+
Our callback function must always take a changeset as its first argument, and return a changeset. In our callbacks, we can expect to often be calling `Ecto.Changeset` functions like:
645+
646+
- `put_change(changeset, key, value)`
647+
- `get_field(changeset, key, defaultValue//nil)`
648+
- `add_error(changeset, key, error)`
649+
650+
For the full reference, visit the docs at [Ecto Changeset documentation](http://hexdocs.pm/ecto/Ecto.Changeset.html).
651+
652+
Ecto callbacks give can be very powerful, but we need to take care when using them. On one hand, they can add behaviors in a way which may not be apparent in the execution flow because callbacks can be defined far from the action in our model. On the other hand, it might seem tempting to put a lot of behavior in a single callback, which can make the model difficult to test and debug.
653+
600654
### Integrating Ecto into an Existing Application
601655

602656
Adding Ecto to a pre-existing Phoenix application is easy. Once we include Ecto and Postgrex as dependencies, there are mix tasks to help us.

0 commit comments

Comments
 (0)