Вообще говоря, вы хотите, чтобы ваши приложения MVC имели "тонкие контроллеры" - эта рекомендация применима для нескольких языков. В некоторых языках / средах это очень важно, потому что тестирование контроллеров может быть довольно сложным. Хотя тестирование контроллеров относительно просто на функциональном языке, таком как Elixir, и хорошо структурированной среде, такой как Phoenix, вы все равно должны поддерживать свои контроллеры как можно более компактными. Проще говоря, контроллеры должны подключить сервисный модуль (часто называемый «контекст» в Фениксе) с представлением. Обычно в них не так уж много логи c.
Термины «схемы» могут быть запутанными - потратьте немного времени, чтобы понять, что разные базы данных используют разные термины для обозначения (более или менее) ) одинаковые компоненты. Например, «база данных» в MySQL называется «схемой» в Oracle или Postgres. В Ecto "схема" аналогична "модели" ORM во многих других средах: схема Ecto представляет в коде "форму" определенной таблицы базы данных c (поэтому, возможно, именно поэтому они используют один и тот же термин).
Однако в вашем случае что-то вроде вычисляемого поля пароля ha sh должно находиться в том же модуле, что и схема Ecto. (Да, Ecto делает несколько макрочасов c, когда определяет форму таблицы в коде, но это все еще модуль, и это рекомендуемое место для размещения changeset
функций, которые имеют дело с проверкой и изменением данных, прежде чем они попадут в база данных).
Если вам когда-нибудь станет интересно, где некоторые функции должны go, спросите себя, куда бы вы поместили код, если пользовательский ввод исходил из CLI, а не из Интернета. Если бы у вас была задача смешивания CLI, которая создала пользователя с паролем, вы бы использовали веб-контроллер? Нет, вы бы не стали: контроллер - это просто go -потерь, который соединяет веб-запрос / ответ с базовой моделью / схемой. Вы не захотите продублировать код, который рассчитал пароль ha sh только потому, что вы хотели создать его для скрипта CLI, чтобы у вас было логическое место для его размещения: в схеме.
Наборы изменений Ecto позволяют изменять данные на пути к базе данных. Вы можете услышать, что это называется «мутацией» в некоторых системах или «вычисляемым полем».
Вот пример, который вычисляет поле «create_at» (да, вы можете сделать это в базе данных, но это полезный пример того, как сделать вычисляемое поле в коде):
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:username,:email])
|> validate_required([:username,:email])
|> add_created_at()
|> unique_constraint(:username)
|> unique_constraint(:email)
end
defp add_created_at(changeset) do
case changeset do
%Ecto.Changeset{
valid?: true,
changes: _user
} ->
put_change(
changeset,
:created_at,
DateTime.utc_now
|> DateTime.truncate(:second)
)
_ ->
changeset
end
end
А вот пример расчета пароля ha sh для хранения в вашей базе данных - с использованием пакета Argon2
. Это также добавляет поле, которое идентифицирует, какой алгоритм использовался, чтобы иметь sh пароль (в виде строки, полезной для справки):
def changeset(user, attrs) do
user
|> cast(attrs, [:username, :password])
|> validate_required([:username, :password])
|> validate_length(:password, min: 8, max: 100)
|> unique_constraint(:username)
|> put_password_hash()
end
defp put_password_hash(changeset) do
case changeset do
%Ecto.Changeset{
valid?: true,
changes: %{
password: plain_text
}
} ->
put_change(changeset, :password_hash, Argon2.hash_pwd_salt(plain_text))
|> put_change(:algorithm, "argon2")
_ ->
changeset
end
end
Надеюсь, что поможет.