DEV Community

loading...

Validando associações com Ecto: put_assoc vs cast_assoc

Marcos Vinicius O. Silveira
Software Engineer interested on Elixir and Phoenix.
・2 min read

Recentemente fazendo algumas refatorações de código, identifiquei um problema onde a validação de alguns changesets não estavam funcionando, e olhando a implementação tudo estava aparentemente correto:

def create_user(params) do
  User
  |> User.changeset(params)
  |> put_assoc(:personal_info, params.personal_info)
  |> Repo.create()
end
Enter fullscreen mode Exit fullscreen mode

Debugando o código, vi que o problema era que ao executar o create_user, o changeset do personal_info não era executado. Então fui ver na documentação, e vi que a put_assoc exige que os parâmetros tenham sido validados pelo changeset antes da chamada da função, então o que eu precisava fazer era o seguinte:

def create_user(params) do
  User
  |> User.changeset(params)
  |> put_assoc(:personal_info, build_personal_info(params))
  |> Repo.create()
end

defp build_personal_info(%{
  personal_info: personal_info
}) do
 PersonalInfo
 |> PersonalInfo.changeset(personal_info)
end
Enter fullscreen mode Exit fullscreen mode

E dessa forma a validação passou a ser feita da maneira esperada. Porém, eu adicionei mais uma função privada no meu contexto. Então, eu fui atrás de uma forma melhor de resolver esse problema. A solução foi utilizar a cast_assoc diretamente no changeset do User, pois ela faz a relação dos dados e trata as informações com o changeset. E como os parâmetros já possuíam a chave personal_info, eu não precisei informar qual parâmetro
deveria ser validado, pois a função já faz isso:

  defmodule MyApp.Accounts.User do
    ...

    schema "users" do
      ...
      # Atente a necessidade da relação estar declarada no schema
      has_one(:personal_info, PersonalInfo, on_delete: delete_all)
    end

    def changeset(user, attrs)
      user
      |> cast(attrs, @optional_fields ++ @required_fields)
      |> validate_required(@required_fields)
      |> cast_assoc(:personal_info, required: true)
  end
Enter fullscreen mode Exit fullscreen mode

Dessa forma, eu não precisei fazer nenhuma chamada no Contexto, nem criar uma nova função para validar os dados de PersonalInfo ao salvar dados de uma nova pessoa usuária.

Discussion (0)