Whats the difference between these Phoenix files?

I have these two files in my to-do list, one of them todo_live.ex I know is a way to create functions and tell the data what to do. I am not sure what the other is for.

1st file:

#todos.ex
defmodule LiveViewTodos.Todos do
  @moduledoc """
  The Todos context.
  """

  import Ecto.Query, warn: false
  alias LiveViewTodos.Repo

  alias LiveViewTodos.Todos.Todo

  @topic inspect(__MODULE__)

  def subscribe do
    Phoenix.PubSub.subscribe(LiveViewTodos.PubSub, @topic)
  end

  defp broadcast_change({:ok, data} = result, event) do
    Phoenix.PubSub.broadcast(LiveViewTodos.PubSub, @topic, {__MODULE__, event, data})
  end

  defp broadcast_change(result, _event) do
    result
  end

  @doc """
  Returns the list of todos.

  ## Examples

      iex> list_todos()
      [%Todo{}, ...]

  """
  def list_todos do
   Repo.all(Todo)
  end

  @doc """
  Gets a single todo.

  Raises `Ecto.NoResultsError` if the Todo does not exist.

  ## Examples

      iex> get_todo!(123)
      %Todo{}

      iex> get_todo!(456)
      ** (Ecto.NoResultsError)

  """
  def get_todo!(id), do: Repo.get!(Todo, id)

  @doc """
  Creates a todo.

  ## Examples

      iex> create_todo(%{field: value})
      {:ok, %Todo{}}

      iex> create_todo(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_todo(attrs \\ %{}) do
    %Todo{}
    |> Todo.changeset(attrs)
    |> Repo.insert()
    |> broadcast_change([:todo, :created])
  end
#|> means take this and inject it before 
 # broadcast_change([:todo, :created], Repo.insert(Todo.changeset(%Todo{}, attrs)))


  @doc """
  Updates a todo.

  ## Examples

      iex> update_todo(todo, %{field: new_value})
      {:ok, %Todo{}}

      iex> update_todo(todo, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_todo(%Todo{} = todo, attrs) do
    todo
    |> Todo.changeset(attrs)
    |> Repo.update()
    |> broadcast_change([:todo, :updated])
  end

  @doc """
  Deletes a todo.

  ## Examples

      iex> delete_todo(todo)
      {:ok, %Todo{}}

      iex> delete_todo(todo)
      {:error, %Ecto.Changeset{}}

  """
  def delete_todo(%Todo{} = todo) do
    todo
    |> Repo.delete()
    |> broadcast_change([:todo, :deleted])
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking todo changes.

  ## Examples

      iex> change_todo(todo)
      %Ecto.Changeset{data: %Todo{}}

  """
  def change_todo(%Todo{} = todo, attrs \\ %{}) do
    Todo.changeset(todo, attrs)
  end

  def remove_all do
   %Todo{}
    |> Repo.delete_all()
    |> broadcast_change([:todo, :deleted])
  end
end

2nd File


#todo_live.ex
defmodule LiveViewTodosWeb.TodoLive do
  use LiveViewTodosWeb, :live_view
  alias LiveViewTodos.Todos

  def mount(_params, _session, socket) do
    Todos.subscribe()
    {:ok, assign(socket, todos: Todos.list_todos())}
  end

  def handle_event("add", %{"todo" => todo}, socket) do
    Todos.create_todo(todo)
    {:noreply, socket}
  end

  def handle_event("toggle_done", %{"id" => id}, socket) do
    todo = Todos.get_todo!(id)
    Todos.update_todo(todo, %{done: !todo.done})
    {:noreply, socket}
  end

  def handle_event("clear", _params, socket) do
    # Todos.remove_all()
    socket = assign(socket, :todos, [])
    {:noreply, socket}
  end

  def handle_event("delete", %{"id" => id}, socket) do
    todo = Todos.get_todo!(id)

    case Todos.delete_todo(todo) do
      {:ok, deleted_todo} ->
        socket =
          update(socket, :todos, fn todos ->
            Enum.reject(todos, &(&1.id == deleted_todo.id))
          end)

        {:noreply, put_flash(socket, :info, "Todo '#{deleted_todo.id}' deleted")}

      {:error, _} ->
        {:noreply, put_flash(socket, :error, "An error occured while deleting a todo")}
    end
  end

  def handle_info({Todos, [:todo | _], _}, socket) do
    {:noreply, fetch(socket)}
  end

  defp fetch(socket) do
    assign(socket, todos: Todos.list_todos())
  end
end

whats the difference between the two, and going forward would I need both to create a blog post?

1 Like

First one is a Phoenix Context and the second one is a Phoenix Liveview

Phoenix Contexts are used to encapsulate and abstract business logic. So instead of doing manually something like Todo.changeset(%Todo{}, attrs) you create a context method Todos.create_todo(attrs) where you abstract this part. Context also can encapsulate multiple subdomains.

Phoenix Liveview is the part where you write your “frontend” code. So essentially you split your frontend in views and components, and those are using contexts to manipulate your data.

So essentially a common flow would be, LIveview -> Context -> Schema -> DB

To answer your question, yes you will need both. Unless you want to use normal views where you replace Liveview with that. Both are used to write your frontend code.

3 Likes

Pretty much any first Phoenix/LiveView tutorial would answer that question.

The official Phoenix guides are a good start.,

Admittedly recommending first learning resources for LiveView is a bit tricky. LiveView is a moving target, and many resources aren’t up-to-date, so might be more confusing than helpful. The official docs aren’t quite as good as are the ‘dead view’ ones, but if you’re already a reasonably experienced programmer this might be an OK start.

Otherwise, if you enjoy learning from videos I can recommend the free portion of the Pragmatic Studio course: Phoenix LiveView Free Course | The Pragmatic Studio (I don’t know though if they’ve updated it for the new heex template format).

Perhaps others here might recommend some other basic LiveView tutorial resources.

1 Like

I did learn from a member here that before the Leex format was used. Now per the new update, it is the heex file. This Phoenix LiveView Free Course | The Pragmatic Studio has constantly been recommended, I will do this first.

Thank you