Search code examples
elixirphoenix-frameworkecto

A list of maps as argument


I created a schema that looks as follow:

schema "countries" do

    field :code, :string
    field :en, :string
    field :de, :string
    field :it, :string
    field :fr, :string

    timestamps

  end

Now a I want to create changeset function that would accept a list of map. How can I define it? Is this right(I would say it is wrong):

def changeset_all(model, []) do

Then how can I validate if the passed parameter contains maps or not?

The second question is, how can I loop through the list of maps, that after I would get a changeset of all pass values? I would do like this:

def changeset_all(model, [params]) do

  for param <- [params] do
      model
      |> cast(param, [:code, :en, :de, :it, :fr])
      |> validate_required([:code, :en])
      |> validate_length(:code, max: 3)
      |> unique_constraint(:code)
    end
  end

I hope my questions are clear.


Solution

  • def changeset_all(model, [params]) do will only accept a list of 1 element. To accept any lists, you can do:

    def changeset_all(model, params) when is_list(params) do
    

    To create a list of changesets, just do for param <- params. The final code should look like:

    def changeset_all(model, params) when is_list(params) do
      for param <- params do
        model
        |> cast(param, [:code, :en, :de, :it, :fr])
        |> validate_required([:code, :en])
        |> validate_length(:code, max: 3)
        |> unique_constraint(:code)
      end
    end
    

    Then how can I validate if the passed parameter contains maps or not?

    You don't really need to do this as if you pass an invalid parameter, cast will handle throwing an error. If you want to do this, you can loop through the list and use if to check if it's a map:

    for param <- params
      if !is_map(param) do
        raise "#{param} is not a map"
      end
    end
    
    for param <- params do
      # changeset stuff
    end
    

    I would recommend creating a changeset/2 function separately and then calling it using Enum.map in changeset_all/2:

    def changeset(model, params) do
      model
      |> cast(params, [:code, :en, :de, :it, :fr])
      |> validate_required([:code, :en])
      |> validate_length(:code, max: 3)
      |> unique_constraint(:code)
    end
    
    def changeset_all(model, list) do
      list |> Enum.map(fn params -> changeset(model, params) end)
    end