Search code examples
elixirphoenix-frameworkecto

How to use Repo module in my model file


In my Tag model code

schema "tags" do
  field :name, :string
  field :parent, :integer # parent tag id
  timestamps
end

def add_error_when_not_exists_tag_id(changeset, params) do
  tags = Repo.all(Tag)
  is_exists_tag_id = Enum.reduce(tags, fn(x, acc) -> acc || (x.id === params.parent) end)
  if is_exists_tag_id, do: changeset, else: add_error(changeset, :parent, "not exists parent!")
end

Above code caused a error below.

(UndefinedFunctionError) undefined function: Repo.all/1 (module Repo is not available)

Can I fix the error ?

The Tag model is nested tag model.

Tag can has parent tag.


Finaly code is below. This is work fine.

In model

def add_error_when_not_exists_tag_id(changeset, params, tags) do
  is_exists_tag_id = Enum.reduce(tags, false, fn(x, acc) -> acc || (Integer.to_string(x.id) === params["parent"]) end)
  if is_exists_tag_id, do: changeset, else: add_error(changeset, :parent, "The tag is not exists.")
end

In controller

def create(conn, %{"tag" => tag_params}) do
  changeset = Tag.changeset(%Tag{}, tag_params)
  |> Tag.add_error_when_not_exists_tag_id(tag_params, Repo.all(Tag))
  //
  // ...

Solution

  • You can't use the Repo variable as it isn't available in this module. You will need to alias it:

    alias MyApp.Repo
    

    In controllers this is handled for you in web.ex which gets called in your module with:

    use MyApp.Web, :controller
    

    However, I strongly suggest that you avoid using Repo in your model. Your models are meant to be pure, this means that they should not have side effects. Calling a function in your model should always have the same output for a particular input (idempotence).

    In this example you could change the implementation of the function to:

    def add_error_when_not_exists_tag_id(changeset, params, tags) do
      is_exists_tag_id = Enum.reduce(tags, fn(x, acc) -> acc || (x.id === params.parent) end)
      if is_exists_tag_id, do: changeset, else: add_error(changeset, :parent, "not exists parent!")
    end
    

    You can call Repo.all in your controller and pass the tags through to the function.

    If you have more complex behaviour, consider creating a TagService module which uses the function as well as calling the Repo.all