DEV Community

Tim Kamanin πŸš€
Tim Kamanin πŸš€

Posted on

5 1

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!

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

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.

Sentry image

See why 4M developers consider Sentry, β€œnot bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay