Create associated content with authenticated user with Phoenix LiveView

Following on from my previous topic (How to display different UI if cookie exists or not in Phoenix LiveView) I found you could use put_session in order to access the user’s ID in the mount/3 function.

I’m now wanting to expand on this I want to create an associated piece of content with that authenticated user. To expand on this a little further this is what my mount/3 function looks like…

  @impl true
  def mount(_params, %{"authenticated_user" => authenticated_user_id}, socket) do
    {
      :ok,
      socket
        |> assign(:clients, list_clients(authenticated_user_id))
    }
  end

My form template component thing looks like this,


<%= if @live_action in [:new, :edit] do %>
  <%= live_modal @socket, AppWeb.ClientLive.FormComponent,
    id: @client.id || :new,
    title: @page_title,
    action: @live_action,
    client: @client,
    return_to: Routes.client_index_path(@socket, :index) %>
<% end %>

and my handle_event/3 function looks something like this

  def handle_event("save", %{"client" => client_params}, socket) do
    save_client(socket, socket.assigns.action, client_params)
  end

  defp save_client(socket, :new, client_params) do
    case Clients.create_client(client_params) do
      {:ok, _client} ->
        {:noreply,
         socket
         |> put_flash(:info, "Client created successfully")
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end

What would be the most effective way in order to create this new item associated with the signed in user? A couple of ideas I’ve already attempted are:

  • In handle_params/3 attempting to assign :client with a pre-existing association e.g. %Client{user: ...} but this didn’t work.
  • Passing authenticated_user_id as a parameter to the live_modal template, although I could get this working with enough force it just doesn’t seem right.

Really appreciate the help in advance :slight_smile: Please let me know if you would like more information.

Your second option is probably the easiest, not sure there is another straight forward way, it is a bit explicit is all - which is generally considered a good thing.

Appreciate the response :slight_smile:

I get the positives with it being more explicit and in general I’d prefer that, however this feels like quite a common usecase (wanting to create something associated with a user). I don’t think this extra layer would exist would it with the existing model? (without LiveViews) - I might be wrong here as I don’t use phoenix/ecto to much.

Seperate but related question: If I am passing the authenticated user ID not as part of the changeset, what would be the nicest way to add that to the changeset later?

  defp save_client(socket, :new, client_params) do
    # _authenticated_user_id = socket.assigns[:authenticated_user_id]}
    case Clients.create_client(client_params) do
      {:ok, _client} ->
        {:noreply,
         socket
         |> put_flash(:info, "Client created successfully")
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end

Here I’d like to get the authenticated_user_id merge it with client_params but I’m not quite sure if that is the conventional way or not.

You could make it a hidden field on the client and submit with the rest of the user supplied fields

1 Like

This could work, however I think this would open a security issue unless I write more code to mitigate against a user just putting a random user string inside this hidden field before they click save. To mitigate from the issue I would still need to get the authenticated_user_id form the cookie and compare it against the one in the changeset.

I think I’d prefer (if there isn’t another option) to merge a new struct containing the user_id before calling save, somewhat annoying but I can live with it. Anyway I really do appreciate the help :slight_smile:, for me though I think this is where I would consider Phoenix LiveView just isn’t ready for me which totally makes sense since its ‘0.15.1’ at the time of writing.

1 Like

That’s how I would do it: get the user_id from the socket. It would be the same in the traditional controller endpoint scenario (just from the conn instead of the socket). In the end, it’s sourced from the cookie session in either case.

Have your Clients.create_client take the user_id as a 2nd parameter, or even create a separate Clients.create_client_with_user_id(params, user_id).

1 Like