Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 19, 2019 01:10 pm GMT

Phoenix without Contexts

When you learn about the Phoenix framework you will come across contexts. While contexts are great, they are not always needed. Your app might only have a couple of schemas or, like in our case, it's just easier and faster (to develop the app) to use something else instead of Phoenix contexts. The main issue we at Novistore had was, that it took too long to figure out how to structure our contexts.

In the below code I'm going to show a way to dynamically build MyApp.<Schema>.find/1, MyApp.<Schema>.create/1 and MyApp.<Schema>.update/2 for all your schemas.

Schema

We use a separate Schema module to define some helper functions on our schemas so we can use it in a more simpler way.

First we create a straightforward schema that we're going to use in our schemas:

defmodule MyApp.Schema do  @moduledoc """  Define commonly used imports for all `Ecto.Schema`s here.  """  defmacro __using__(_) do    quote do      use Ecto.Schema      import Ecto.Changeset    end  endend

User schema

Next up, a simple user schema:

defmodule MyApp.User do  use MyApp.Schema  schema "users" do    field :name, :string    timestamps()  endend

Once our schema is done we would like to have functions like MyApp.User.find/1

Example

iex> MyApp.User.find(1)%User{id: 1, name: "Jane"}

Let's add .find/1 to our schema:

defmodule MyApp.Schema do  @moduledoc """  Define commonly used imports for all `Ecto.Schema`s here.  """  defmacro __using__(_) do    quote do      use Ecto.Schema      import Ecto.Changeset      alias MyApp.Repo      @type id :: non_neg_integer      @spec find(id) :: {:ok, __MODULE__.t()} | {:error, {__MODULE__, :not_found}}      def find(id) do        case Repo.get(__MODULE__, id) do          nil -> {:error, {__MODULE__, :not_found}}          struct -> {:ok, struct}        end      end    end  endend

The next function is create/1.

Example

iex> User.insert(%{name: "Jane"}){:ok, %User{id: 1, name: "Jane"}}

Let's add .create/1 to our schema:

defmodule MyApp.Schema do  @moduledoc """  Define commonly used imports for all `Ecto.Schema`s here.  """  defmacro __using__(_) do    quote do      use Ecto.Schema      import Ecto.Changeset      alias MyApp.Repo      @type id :: non_neg_integer      @spec find(id) :: {:ok, __MODULE__.t()} | {:error, {__MODULE__, :not_found}}      def find(id) do        case Repo.get(__MODULE__, id) do          nil -> {:error, {__MODULE__, :not_found}}          struct -> {:ok, struct}        end      end      @spec insert(map, keyword) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}      def insert(params, opts \\ []) when is_map(params) do        changeset = changeset(params)        Repo.insert(changeset, opts)      end    end  endend

Our User schema needs a .changeset/1 so let's add one:

defmodule MyApp.User do  use MyApp.Schema  schema "users" do    field :name, :string    timestamps()  end  @required [:name]  optional = []  @fields optional ++ @required  @spec changeset(t, map) :: Ecto.Changeset.t()  def changeset(struct \\ %__MODULE__{}, params) when is_map(params) do    struct    |> cast(params, @fields)    |> validate_required(@required)  endend

The last one that we need is .update/2

defmodule MyApp.Schema do  @moduledoc """  Define commonly used imports for all `Ecto.Schema`s here.  """  defmacro __using__(_) do    quote do      use Ecto.Schema      import Ecto.Changeset      alias MyApp.Repo      @type id :: non_neg_integer      @spec find(id) :: {:ok, __MODULE__.t()} | {:error, {__MODULE__, :not_found}}      def find(id) do        case Repo.get(__MODULE__, id) do          nil -> {:error, {__MODULE__, :not_found}}          struct -> {:ok, struct}        end      end      @spec insert(map, keyword) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}      def insert(params, opts \\ []) when is_map(params) do        changeset = changeset(params)        Repo.insert(changeset, opts)      end      @spec update(struct, map, keyword) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}      def update(%{__struct__: __MODULE__} = struct, params, opts \\ []) when is_map(params) do        changeset = changeset(struct, params)        Repo.update(changeset, opts)      end    end  endend

This way, every schema that uses use MyApp.Schema, has all the functions defined that we need to retrieve, create and update our schemas.

Bonus

You can also expand your schema module to import commonly used changeset functions like .validate_email/3 or add queries helpers like .filter_by/2:

defmodule MyApp.Schema do  @moduledoc """  Define commonly used imports for all `Ecto.Schema`s here.  """  defmacro __using__(_) do    quote do      use Ecto.Schema      import Ecto.Changeset      import MyApp.Schema      alias MyApp.Repo      @type id :: non_neg_integer      @spec find(id) :: {:ok, __MODULE__.t()} | {:error, {__MODULE__, :not_found}}      def find(id) do        case Repo.get(__MODULE__, id) do          nil -> {:error, {__MODULE__, :not_found}}          struct -> {:ok, struct}        end      end      @spec insert(map, keyword) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}      def insert(params, opts \\ []) when is_map(params) do        changeset = changeset(params)        Repo.insert(changeset, opts)      end      @spec update(struct, map, keyword) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}      def update(%{__struct__: __MODULE__} = struct, params, opts \\ []) when is_map(params) do        changeset = changeset(struct, params)        Repo.update(changeset, opts)      end      @spec filter_by(Ecto.Queryable.t(), keyword) :: Ecto.Queryable.t()      def filter_by(queryable \\ __MODULE__, clauses) do        Enum.reduce(clauses, queryable, fn {k, v}, query ->          from q in query, where: field(q, ^k) == ^v        end)      end    end  end  import Ecto.Changeset  @spec validate_email(Ecto.Changeset.t(), atom) :: Ecto.Changeset.t()  def validate_email(%{valid?: true} = changeset, key) do    case fetch_change(changeset, key) do      {:ok, _email} -> validate_format(changeset, key, ~r@)      :error -> changeset    end  endend

Original Link: https://dev.to/novistore/phoenix-without-contexts-4d33

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To