Elixir Ecto 变更集或验证

Elixir Ecto Changeset OR Validation

我正在尝试对电子邮件或 phone 号码执行变更集验证,并且我在此处 从@Dogbert 找到了一个漂亮的 OR 变更集函数 - 但我无法获得我的 OR 验证流程正常工作。

有人介意快速了解一下为什么电子邮件或 phone 验证总是返回 nil 变更集吗?

  @doc false
  def changeset(%User{} = user, attrs) do
    user
    |> cast(attrs, [:email, :phone])
    |> validate_required_inclusion([:email, :phone])
    |> validate_required_inclusion_format([:email, :phone])
  end

  defp validate_required_inclusion(changeset, fields) do
    if Enum.any?(fields, &present?(changeset, &1)) do
      changeset
    else
      # Add the error to the first field only since Ecto requires a field name for each error.
      add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
    end
  end

  defp present?(changeset, field) do
    value = get_field(changeset, field)
    value && value != ""
  end

  ## TODO - this doesnt work
  defp validate_required_inclusion_format(changeset, fields) do
    if Enum.member?(fields, :email) do
      value = get_field(changeset, :email)
      if value && value != "" do
        IO.inspect(value, label: "email found: ")
        changeset
        |> email_changeset()
      end
    end
    if Enum.member?(fields, :phone) do
      value = get_field(changeset, :phone)
      if value && value != "" do
        IO.inspect(value, label: "phone found: ")
        changeset
        |> phone_changeset()
      end
    end
    changeset
  end

  defp email_changeset(changeset) do
    changeset
    |> validate_required([:email])
    |> validate_format(:email, ~r/.+@.+/)
    |> unique_constraint(:email)
  end

  defp phone_changeset(changeset) do
    changeset
    |> validate_required([:phone])
    |> valid_phone(:phone)
    |> unique_constraint(:phone)
  end

  defp valid_phone(changeset, field) do
    phone = get_field(changeset, field)
    IO.inspect(phone, label: "phone: ")
    {:ok, phone_number} = ExPhoneNumber.parse(phone, "US")
    IO.inspect(phone_number, label: "ExPhoneNumber: ")
    if ExPhoneNumber.is_valid_number?(phone_number) do
      changeset
    else
      changeset
      |> add_error(field, "Invalid Phone Number")
    end
  end

提前致谢!

您没有 return 在 validate_required_inclusion_format 中正确地修改修改后的变更集。在 Elixir 中,块中的最后一个值是它的 return 值。在 if 语句中,其 true 和 false 分支的最后一个值是其 return 值。如果您没有 else 分支且条件为假,则 return 值为 nil.

这是解决问题的最简单方法:将两个顶级 if 和回退 changeset return 与 ||:

连接起来
defp validate_required_inclusion_format(changeset, fields) do
  if Enum.member?(fields, :email) do
    value = get_field(changeset, :email)
    if value && value != "" do
      IO.inspect(value, label: "email found: ")
      changeset
      |> email_changeset()
    end
  end || # <- note this
  if Enum.member?(fields, :phone) do
    value = get_field(changeset, :phone)
    if value && value != "" do
      IO.inspect(value, label: "phone found: ")
      changeset
      |> phone_changeset()
    end
  end || # <- and this
  changeset
end

现在,如果第一个或第二个 if 条件不满足,您将得到一个 nil 并且将评估第三个 if。如果第三个或第四个也不满足,则最终回退 changeset 将 returned.


注意:此函数的命名具有误导性。与您在我之前的回答中使用的功能不同,您在这里根本没有使用 fields 。你最好不要将 fields 传递给此函数并调用它类似于 add_email_or_phone_changeset,例如

if value = get_field(changeset, :email) do
   ...
end ||
if value = get_field(changeset, :phone) do
   ...
end || ...