DEV Community

Tim Kamanin ๐Ÿš€
Tim Kamanin ๐Ÿš€

Posted on

How to update post params in Elixir Phoenix

Started playing with the Phoenix Framework and I have no idea what I'm doing, but it's nice to have that beginner feeling after 14 years in PHP, Python and JavaScript.

So here I am, trying to create a simple blog with Phoenix.

And one of the first stumbling blocks for me was:

How on earth, I'm going to add current user id to post parameters?

So I have the following posts schema:

defmodule Timphx.Blog.Post do
  use Ecto.Schema
  import Ecto.Changeset

  schema "posts" do
    field :body, :string
    field :published, :boolean, default: false
    field :title, :string
    belongs_to :author, Timphx.Accounts.User

    timestamps()
  end

  @doc false
  def changeset(post, attrs) do
    post
    |> cast(attrs, [:title, :body, :published, :author_id])
    |> validate_required([:title, :body, :published, :author_id])
  end
end
Enter fullscreen mode Exit fullscreen mode

And Phoenix generated for me the following PostController:

defmodule TimphxWeb.PostController do
  use TimphxWeb, :controller

  alias Timphx.Blog
  alias Timphx.Blog.Post

def create(conn, %{"post" => post_params}) do
    case Blog.create_post(post_params) do
      {:ok, post} ->
        conn
        |> put_flash(:info, "Post created successfully.")
        |> redirect(to: Routes.post_path(conn, :show, post))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
end
Enter fullscreen mode Exit fullscreen mode

And now, within the create function, I want to add current user to post_params. How can I do this?

Turns out post_params is a map and it's immutable, so you can't do something naive like:

post_params.author_id = conn.assigns.current_user.id
Enter fullscreen mode Exit fullscreen mode

Oh yes, btw, conn.assigns.current_user.id is how you get the current user id.

I had to dig into Elixir docs (which I see for the first time) and found explanation of Maps. There I read about the Map.put method that allows you to create a new map from a map and a new key value:

Map.put(%{:a => 1, 2 => :b}, :c, 3)
Enter fullscreen mode Exit fullscreen mode

Eureka! Now I knew how to update post_params and add current user there:

post_params = Map.put(post_params, "author_id", conn.assigns.current_user.id)
Enter fullscreen mode Exit fullscreen mode

So my final code for the create function looks like:

def create(conn, %{"post" => post_params}) do
    post_params = Map.put(post_params, "author_id", conn.assigns.current_user.id)
    case Blog.create_post(post_params) do
      {:ok, post} ->
        conn
        |> put_flash(:info, "Post created successfully.")
        |> redirect(to: Routes.post_path(conn, :show, post))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end
Enter fullscreen mode Exit fullscreen mode

Slam dunk, done, baby! For now, I'm super stoked!

Top comments (1)

Collapse
 
c4710n profile image
Zeke Dou • Edited

If your Blog.create_post/1 is always creating post belongs to a user, I prefer to define it like this:

def create_post(user_id, post) do
   # ...
end
Enter fullscreen mode Exit fullscreen mode

or

def create_post(%User{} = user, post) do
  # ...
end
Enter fullscreen mode Exit fullscreen mode

This will improve the readability a bit. And you don't need to merge params.