ash/documentation/topics/validations.md
2023-09-29 10:11:14 -04:00

100 lines
2.4 KiB
Markdown

# Validations
## Builtin Validations
Checkout the documentation for `Ash.Resource.Validation.Builtins` to see the builtin validations.
Some examples of usage of builtin validations
```elixir
validate match(:email, ~r/@/)
validate compare(:age, greater_than_or_equal_to: 18) do
message "must be over 18 to sign up"
end
validate present(:last_name) do
where [present(:first_name), present(:middle_name)]
message "must also be supplied if setting first name and middle_name"
end
```
## Custom Validations
```elixir
defmodule MyApp.Validations.IsPrime do
# transform and validate opts
def init(opts) do
if is_atom(opts[:attribute]) do
{:ok, opts}
else
{:error, "attribute must be an atom!"}
end
end
def validate(changeset, opts) do
value = Ash.Changeset.get_attribute(changeset, opts[:attribute])
# this is a function I made up for example
if is_nil(value) || Math.is_prime?(value) do
:ok
else
# The returned error will be passed into `Ash.Error.to_ash_error/3`
{:error, field: opts[:attribute], message: "must be prime"}
end
end
end
```
This could then be used in a resource via:
```elixir
validate {MyApp.Validations.IsPrime, attribute: :foo}
```
## Where
The `where` can be used to perform changes/validations conditionally. This functions by running the validation, and if the validation returns an error, we discard the error and skip the operation. This means that even custom validations can be used in conditions.
For example:
```elixir
validate present(:other_number) do
where [{MyApp.Validations.IsPrime, attribute: :foo}]
end
```
## Action vs Global Validations
You can place a validation in any create, update, or destroy action. For example:
```elixir
actions do
create :create do
validate compare(:age, greater_than_or_equal_to: 18)
end
end
```
Or you can use the global validations block to validate on all actions of a given type. Where statements can be used in either. Use `on` to determine the types of actions the validation runs on. By default, it only runs on create an update actions
```elixir
validations do
validate present([:foo, :bar], at_least: 1) do
on [:create, :update]
where present(:baz)
end
end
```
## Action-Specific Validation
You can also put a validation directly in an action, like so:
```elixir
actions do
create do
...
validate present([:foo, :bar], at_least: 1)
end
end
```