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
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
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
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)
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)
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
Slam dunk, done, baby! For now, I'm super stoked!
Top comments (1)
If your
Blog.create_post/1
is always creating post belongs to a user, I prefer to define it like this:or
This will improve the readability a bit. And you don't need to merge params.