Reputation: 2674
Using Ecto v2.2.6, Phoenix 1.3
I have a blog app. When a user makes a Post, it inserts into the Post
table, then it get the resulting id of that post and inserts that into a Newsfeeditem
table. Ideally, I would like for this to happen as a transaction.
(I am using Absinthe graphql, so my return for the insert must be of the form {:ok, post}
)
I have a working function that looks like this:
def create_post_add_newsfeed(%{
title: title,
content: content,
user_id: user_id
}) do
case Repo.insert!(%Post{title: title, content: content, user_id: user_id}) do
post ->
case Repo.insert!(%Newsfeeditem{type: "user_creates_post", user_id: user_id, post_id: post.id}) do
newsfeeditem ->
{:ok, post}
_ ->
{:error, "Post not recorded in newsfeed"}
end
_ ->
{:error, "Post not inserted"}
end
end
This code is not a transaction, and it reeks of callback stink. Ecto.Multi seems like a more appropriate tool to use here, but I do not know how to get the result of the Post
insert so that I can insert it into Newsfeed
.
I would like to do something like this
def create_post_add_newsfeed(%{
title: title,
content: content,
user_id: user_id
}) do
multi =
Multi.new
|> Multi.insert(:post, %Post{title: title, content: content, user_id: user_id})
|> # Some intermediate step where I get the 'post' from the line above
|> Multi.insert(:newsfeeditem, %Newsfeeditem{type: "user_creates_post", user_id: users_id, post_id: post.id})
case Repo.transaction(multi) do
{:ok, %{post: post}} ->
{:ok, post}
{:error, _} ->
{:error, "Error"}
end
end
Any idea how to pull that off?
Upvotes: 1
Views: 672
Reputation: 222040
You can use Multi.run/3
for this. The function passed to Multi.run/3
will receive the changes so far as the argument. You can extract the inserted post
from that and issue a Repo.insert
for the Newsfeeditem
inside Multi.run
. The function should return {:ok, _}
or {:error, _}
which is exactly what Repo.insert
returns so you don't need to do anything more inside the function.
Multi.new
|> Multi.insert(:post, %Post{title: title, content: content, user_id: user_id})
|> Multi.run(:newsfeeditem, fn %{post: post} ->
Repo.insert(%Newsfeeditem{type: "user_creates_post", user_id: users_id, post_id: post.id})
end)
Upvotes: 2