I’m writing my first non-toy app in Phoenix. I’m starting grok schemas, changesets, and ecto in general, but I have a ways to go.
So I have an Organization
schema living inside an Organizations
context. When an Organization
is created, I want to capture the user that created it and the user who owns it. On create, these two are always going to be the same user.
Is there an idiom to follow here? I had a few iterations:
The one I have landed on (for now) involves setting a virtual creator
field and setting the users in the changeset. It looks like this (note, I have omitted most boilerplate):
# context
defmodule MyApp.Organizations do
def create_organization(attrs) do
%Organization{}
|> Organization.create_changeset(attrs)
|> Repo.insert()
end
end
# schema
defmodule MyApp.Organization do
schema "organizations" do
field :name, :string
belongs_to :created_by, MyApp.Accounts.User
belongs_to :owned_by, MyApp.Accounts.User
field :creator, :map, virtual: true
timestamps()
end
def create_changeset(organization, attrs) do
organization
|> cast(attrs, [:name, :creator])
|> validate_required([:name, :creator])
|> put_users()
end
defp put_users(changeset) do
creator = get_change(changeset, :creator)
changeset
|> put_change(:owned_by, creator)
|> put_change(:created_by, creator)
end
end
This next bit is an earlier iteration where I set the users in the context.
# context
defmodule MyApp.Organizations do
def create_organization(user, attrs) do
attrs =
attrs
|> Map.put(created_by: user)
|> Map.put(owned_by: user)
%Organization{}
|> Organization.create_changeset(attrs)
|> Repo.insert()
end
end
# schema
defmodule MyApp.Organization do
schema "organizations" do
field :name, :string
belongs_to :created_by, MyApp.Accounts.User
belongs_to :owned_by, MyApp.Accounts.User
timestamps()
end
def create_changeset(organization, attrs) do
organization
|> cast(attrs, [:name, :created_by, :owned_by])
|> validate_required([:name, :created_by, :owned_by])
end
end
So the only thing I like about the second one is that you Organizations.create_organization/2
takes a user
as a required param which immediately calls out that a user is required to create and org. Of course, the final iteration also ensures a user exists, it’s just not immediately apparently by just looking at the context (is this important? Maybe not… I should be looking at the schemas anyway).
I think the motivation behind this question is that in Rails, we would do something like current_user.organizations.create(params)
and the fact that the org is owned by a user is but only in the callsite. I like how requiring a user param in my Phoenix version makes that relationship clear in the signature but I’m not married to it. I’m really just wondering what is idiomatic.
Otherwise, I’d love to just get general feedback on ways to improve in general.
Thank you!