Is PRG a valid technique in Phoenix?

Yes of course. I fooled myself thinking that maybe Phoenix could be doing some “magic” behind the scenes. But it makes total sense that the “conn” struct is different because it’s a different request.

I’m gonna study other options of what can be done, but I think this could be encapsulated in a helper? Something like a redirect_with/3, to put the errors on the session and then retrieve on the other side (maybe a plug?).

Update: So, I’ve read a little bit about Plug and this is what I came up with (It’s probably a naive implementation, but right now I can get full Post Redirect Get):

defmodule MyAppWeb.Plug.TempData do
  import Plug.Conn
  use MyAppWeb, :controller

  @session_key "temp_data"
  @assigns_key :loaded

  def init(opts), do: opts

  def call(%Plug.Conn{private: %{:plug_session => %{@session_key => temp}}} = conn, _opts) do
    conn
    |> assign(@assigns_key, temp)
    |> delete_session(@session_key)
  end

  def call(conn, _opts), do: conn

  def load_redirected(conn, default) do
    conn.assigns[@assigns_key] || default
  end

  def redirect_with(conn, object, opts) do
    conn
    |> fetch_session()
    |> put_session(@session_key, object)
    |> redirect(opts)
  end
end

Then it can be used like:

import MyAppWeb.Plug.TempData, only: [redirect_with: 3, load_redirected: 2]

alias MyAppWeb.Plug.TempData

plug TempData

def new(conn, _params) do
    changeset = load_redirected(conn, Context.change_resource(%Resource{}))
    render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"resource" => resource_params}) do
    case Context.create_resource(resource_params) do
      {:ok, resource} ->
        conn
        |> put_flash(:info, "Resource created successfully.")
        |> redirect(to: Routes.resource_path(conn, :show, resource))

      {:error, %Ecto.Changeset{} = changeset} ->
        redirect_with(conn, changeset, to: Routes.resource_path(conn, :new))
    end
  end
1 Like