我刚开始吃Phoenix,我对一件事不太了解,我搜索了一下,发现模式是数据库结构,模型具有较高的层次并可以处理逻辑,但是在phoenix中,我们只有模式,所以例如,如果我要进行密码哈希处理,则应在用户模式中进行?还是应该将其放在控制器中?
答案 0 :(得分:1)
通常来说,您希望MVC应用程序具有“薄控制器”-这是一种建议,适用于多种语言。在某些语言/框架中,这很关键,因为测试控制器可能非常困难。尽管在功能语言(如Elixir)和结构良好的框架(如Phoenix)中测试控制器相对简单,但您仍应保持控制器尽可能精简。简而言之,控制器应将服务模块(在Phoenix中通常称为“上下文”)与视图连接。通常,其中根本没有很多逻辑。
“模式”可能是一个令人困惑的术语-花点时间了解不同的数据库使用不同的术语来指代(或多或少)相同的组件。例如。 MySQL中的“数据库”在Oracle或Postgres中称为“模式”。在Ecto中,“模式”类似于许多其他框架中的ORM“模型”:Ecto模式在代码中表示特定数据库表的“形状”(所以也许这就是为什么他们使用相同的术语)。
但是,在您的情况下,诸如密码哈希的计算字段之类的内容应与Ecto模式位于同一模块中。 (是的,Ecto在代码中定义表的形状时会执行一些宏操作,但它仍然是一个模块,建议在将changeset
函数用于处理数据的验证和变异之前,将其放置在该位置。数据库)。
如果您想知道应该在哪里使用某些功能,请问一下自己,如果用户输入来自CLI而不是来自网络,则应将代码放在哪里。如果您有一个使用密码创建用户的CLI混合任务,您会使用Web控制器吗?不,您不会:控制器只是将Web请求/响应与基础模型/架构连接的中介。您不想只因为要为CLI脚本创建密码哈希而重复计算密码哈希的代码,这样就可以在逻辑上放置逻辑哈希:在架构中。
Ecto变更集允许您在进入数据库的过程中修改数据。您可能会在某些框架中将其称为“变异”或“计算字段”。
下面是一个计算“ created_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
这是一个使用Argon2
包计算要存储在数据库中的密码哈希的示例。这还会添加一个字段,该字段标识用于对密码进行哈希处理的算法(作为字符串,对参考很有用):
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
希望有帮助。