I have this function that has an Ecto.Multi operation where I include a Multi.insert if the function argument (a map) has a certain field in it.
The way I achieved it was this:
def create_thing(attrs \\ %{}) do
Multi.new()
|> Multi.insert(:first, Thing.changeset(%Thing{}, attrs))
|> has_special_field(attrs)
|> Repo.transaction()
end
def has_special_field(multi, attrs) do
if Map.has_key?(attrs, :special_field) do
multi
|> Multi.insert(:second,
fn %{ first: %Thing{id: thing_id}} ->
AnotherThing.changeset(
%AnotherThing{}, %{thing_id: thing_id, special_field: attrs.special_field}
)
end)
else
multi
end
end
It works, but I can’t help it looks more complicated/ugly than it needs to be.
How would you guys do that?
Be aware about the naming of the functions. has_special_field sounds like a predicate but hides a multi action. It can lead to misreading of the code. The name should explicit this multi. For instance, maybe_insert_multi or insert_multi_if_has_special_field
BTW, the elixir convention is to add a question mark at the end of the predicate name.
But here, it’s more about general best practices in the naming, nothing specifically tied to elixir. The name has to reflect what the function is doing.
Yes, I was thinking of Map.has_key?, though it wouldn’t clash because of lack of question mark and the fact it’s a private function (though I didn’t write it as such here), I really like this convention!
Thanks for the clarification, I do think your naming is more descriptive of what the function does!
You can run arbitrary code and logic using Multi.run/3/5 this is useful if for some reason you want the multi operation key to be part of the resulting multi and to check it in other multi operations down the line, e.g. with the has_special_field function
Didn’t need that now but now that you mentioned I feel like it’s just a matter of time I’ll need that! That’s a really good thing to be aware of, thank you!