I’m trying to build a signup form which is able to create multiple models. Not only needs it to create a User
but also creates an Organization
.
I’m trying to find the best way to handle this. I’m following some of the advice from this talk: Controller Control: Designing Domains for Web Applications - Gary Rennie, and this post: Beyond 10,000 Lines by keeping my controllers Repo free and moving most of the logic to services. My goals are to place as much of the logic in services which can be reused in other places as well. Next to that I’m also keeping my schema’s really small, and only describe the tables in the database. This in preparation already for the next version of Phoenix
, which I have understood is going in that direction.
Now I have two base services already in place, one for creating a User
and one for creating an Organization
.
To accomplish what I want with this page I have created a separate service that uses Ecto.Multi
to call the other two services like this
defmodule Erry.Signups.Signup do
def create(email, password, organization) do
Ecto.Multi.new()
|> Ecto.Multi.run(:user, fn _ ->
Erry.Users.Register.call(email, password)
end)
|> Ecto.Multi.run(:organization, fn %{user: user} ->
Erry.Organizations.Builder.call(organization, user)
end)
|> Erry.Repo.transaction
|> handle_new_signup
end
defp handle_new_signup({:ok, results}) do
user = results.user
{:ok, user}
end
defp handle_new_signup({:error, _, changeset, _}) do
{:error, changeset}
end
end
And from my controller I call
case Erry.Signups.Signup.create(email, password, organization) do
{:ok, _user} -> redirect(conn, home_path(conn, :index))
{:error, changeset} -> render(conn, "new.html", changeset: changeset)
end
Now each service uses different schema’s which can have validations which can fail. But when using Eco.Multi
I will only get the changeset of the failing call. This changeset though has not all the information given by the user using the form, so it can’t be used again to populate the form again with errors being shown. I have thought about adding a separate schema for the signup flow like this:
defmodule Erry.Signup do
use Erry.Web, :model
embedded_schema do
field :email, :string
field :password, :string
field :organization_name, :string
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:email, :password, :organization_name])
end
end
But I’m not really sure if that is the best way to go or how to integrate it with the rest.
Regards,
Marcel