Problem

Du hast mehrere Formular mit Feldern, bei denen du Leerzeichen am Anfang und Ende vermeiden möchtest. Beispielsweise Name oder E-Mail-Adresse.

Lösung

Im Normal würdest du sicherlich in einem Changeset eine Funktion schreiben, um genau dies zu tun. Der oben beschriebene Fall ist aber fast der Normalfall und nicht die Ausnahme. Deshalb zeige ich dir hier eine Alternative.

Für Ecto hast du die Möglichkeit, einen eigen Ecto-Type zu Schreiben. Das bedeutet, du kannst folgende 3 Aktionen beeinflussen:

  • cast: Wie werden Paramter verarbeitet, die von außen kommen. Diese Funktion benutzt, wenn cast in einem Changeset aufgerufen wird.
  • dump: Die Funktion wird vor dem Schreiben in die Datenbank aufgerufen.
  • load: Die Funktion wird beim Laden aus der Datenbank aufgerufen.

Einen Ecto-Type zu schreiben ist extrem einfach. Dafür gibt es ein Behaviour @behaviour Ecto.Type mit den oben beschriebenen Funktionen

defmodule Ecto.TrimmedString do
  @behaviour Ecto.Type
  def type, do: :string

  @doc """
  iex> cast("  John Doe   ")
  {:ok, "John Doe"}

  iex> cast("John    Doe")
  {:ok, "John Doe"}

  iex> cast(1)
  :error
  """
  def cast(str) when is_binary(str) do
    clean_string =
      str
      |> String.trim()
      |> String.replace(~r/\s+/, " ")

    {:ok, clean_string}
  end

  def cast(_) do
    :error
  end

  def dump(str) do
    cast(str)
  end

  def load(str) when is_binary(str) do
    {:ok, str}
  end
end

Über def type, do: :string geben wir den Ausgangstyp aus dem Schema an. Dies könnten auch :integer, :map, etc. sein.

Die Rückgabe werte von cast, dump und load sind ein Erfolg-Tuple {:ok, ...} oder ein :error-Atom.

Code

Der zugehörige Aufruf der Doctests sieht folgendermaßen aus:

defmodule Ecto.TrimmedStringTest do
  use ExUnit.Case
  import Ecto.TrimmedString
  doctest Ecto.TrimmedString
end