ThomasReggi
ThomasReggi

Reputation: 59495

Transform a changeset property before it goes into the database

I added a textarea to an Elixir form, and I set the value to a string and have got the string into a database metadata column that is of a jsonb type. The string in the textarea I wish to split by new-line and store an array of strings rather then just one big string.

I have since changed the type as follows.

field :names, {:array, :string} 

However I am unsure how to use Ecto to intercept this data and split it before it enters the database.

I have looked at:

It seems that I can add a validation function to the schema, this is my attempt.

def changeset(schema, params \\ %{}) do
    schema
    |> cast(params, @required, @optional)
    |> validate_name()
end

defp validate_names(changeset) do
    # fetch_field(changeset, :names)
    # Map.put(changeset, :names, String.split(:names, "\r\n"))
end

How do I change the value of one property in an Elixir object?

Update:

person_metadata.ex

defmodule DB.PersonMetadata do
  use DB.Schema

  embedded_schema do
    field :names, :string
  end

  @required ~w()
  @optional ~w(names)

  def changeset(schema, params \\ %{}) do
    schema
    |> cast(params, @required, @optional)
    |> validate_names()
  end

  defp validate_names(changeset) do
    case get_field(changeset, :names) do
      # Don't do anything if names don't exist
      nil ->
        changeset

      # Update names if they do exist
      names ->
        new_names = String.split(names, "\n")
        put_change(changeset, :names, new_names)
    end
  end

  def types, do: @types
end

Upvotes: 0

Views: 2387

Answers (2)

Sheharyar
Sheharyar

Reputation: 75820

Try this:

defp validate_names(changeset) do
  case get_change(changeset, :names) do
    # Update name if it's a string
    names when is_binary(names) ->
      new_names = String.split(names, "\r\n")
      put_change(changeset, :names, new_names)

    # Don't do anything if nil or already an array
    _other ->
      changeset
  end
end

Upvotes: 4

lab419
lab419

Reputation: 1258

Seems the whole custom function can now (since when?) be replaced by Ecto.Changeset.update_change/3

 |> update_change(:names, fn
     nil -> nil # or preferably []?
     names -> String.split(names, ~r{\r?\n})
    end)

or as a matter of style

 |> update_change(:names, &my_name_splitter/1)

Upvotes: 4

Related Questions