Hello there, dear community!
I have been dealing the last days with a function that save multiple records at once using Ecto.Multi
. At the moment on my code I have 2 functions that successfully works (yeey ) using
Multi
too.
The logic of this function is to take the Attributes comming after submiting a form. The aim of this form is to allow users to Sign-up for a Sports Club: This form could send:
A - The data to insert 1 new user. (Already working ).
B - The data to insert 2 new users, a minor user + the “Legal guardian” (Already working ).
C - (The one I am currently working on) That will insert X amount of users (group of users like 2 parents and 2 children for instance).
Starting from function A.
def create_membership(attrs, club_id, plan_ids) do
# This will store the new user changeset
membership_cs = Membership.create_changeset(attrs, plan_ids)
multi = Multi.insert(Multi.new(), :insert_membership, membership_cs)
# list of events to record on the activity logs table.
~w(register_membership accept_articles join_club)a
|> Enum.reduce(multi, fn event, multi ->
Multi.run(multi, {:insert_activity_log, event}, fn repo, %{insert_membership: membership} ->
membership = Repo.preload(membership, :club)
Users.update_user(membership.user, %{membership_id: membership.id})
log_params = %{event: event, actor: :membership, data: %{club_name: membership.club.name}}
%ActivityLog{}
|> ActivityLog.create_changeset(membership.club, membership, log_params)
|> repo.insert()
end)
end)
|> Repo.transaction()
end
Function B.
This will record 2 new users, a minor user and an adult user. The logic the for the sport clubs is also to save the users on a family group.
def create_membership_with_group(attrs, club_id, plan_ids) do
membership_cs = Membership.create_changeset_minor(attrs, plan_ids)
membership_guardian = Membership.create_changeset(attrs, []) #not passing plan_ids since the guardian is not an active member of the club, only pays for the kid.
multi =
Multi.insert(Multi.new(), :insert_membership, membership_cs)
|> Multi.run(:insert_guardian, fn repo, %{insert_membership: membership} ->
Users.update_user(membership.user, %{membership_id: membership.id})
membership_number = generate_membership_number(membership.membership_number)
repo.insert(
Ecto.Changeset.put_change(
membership_guardian,
:membership_number,
membership_number
)
)
end)
# After the new 2 memberships are created, we create a new group, and we insert both memberships.
|> Multi.run(:create_group, fn repo, %{insert_guardian: guardian} ->
repo.insert(Group.create_changeset(attrs, guardian))
end)
|> Multi.run(:update_group, fn repo, %{insert_membership: membership, create_group: group} ->
repo.update(Group.add_memberships_changeset([membership], group))
end)
# List of events to record on the activity-logs table.
~w(register_membership accept_articles join_club)a
|> Enum.reduce(multi, fn event, multi ->
Multi.run(multi, {:insert_activity_log, event}, fn repo, %{insert_membership: membership} ->
membership = Repo.preload(membership, :club)
Users.update_user(membership.user, %{membership_id: membership.id})
log_params = %{event: event, actor: :membership, data: %{club_name: membership.club.name}}
%ActivityLog{}
|> ActivityLog.create_changeset(membership.club, membership, log_params)
|> repo.insert()
end)
end)
|> Repo.transaction()
end
Function C.
This will record X new users (minimum 2 as the example above, but it could be up to 12 new users), The logic of the membership group is to have between 1 Parent and 1 children (But in this case the parent is an active members of the club, so it has plans), and 2 Parents and 10 children. The logic the for the sport clubs here is also to save all the new memberships into a family group, as Funciton B.
My attempt:
def create_membership_with_family_group(attrs, club_id, plan_ids) do
# This creates the changeset for the Payer of the Group (User on the form)
membership_cs = Membership.create_changeset(attrs, plan_ids)
# This Returns a list of Changesets for the rest of the members of the group (without the payer).
membership_fm = []
membership_fm = for _group_member <- attrs["group_members"] do
group_member = Membership.create_changeset_family_group_members(attrs, [])
membership_fm ++ group_member
end
# The boths variables have the required data, which I checked with the inspector.
# Here is where my code starts to sink.
# Since I have now X amount of memberships, I should do a loop over the list of memberships_fm.
# On my logic I should insert the first changeset, membership_cs, and then loop over the rest and insert them too. I am trying to follow the logic from Function B but I am confused on the loop part.
multi_payer = Multi.insert(Multi.new(), :insert_membership, membership_cs)
multi =
membership_fm
|> Enum.reduce(multi_payer, fn group_member, multi_payer ->
Multi.insert(Multi.new(), :insert_membership, membership_cs)
|> Multi.run(:insert_family_group, fn repo, %{insert_membership: membership} ->
Users.update_user(membership.user, %{membership_id: membership.id})
membership_number = generate_membership_number(membership.membership_number)
# Here where I think I should start to loop over membership_fm.
repo.insert(
Ecto.Changeset.put_change(
group_member,
:membership_number,
membership_number
)
)
end
end)
|> Multi.run(:create_group, fn repo, %{insert_membership: payer_group} ->
repo.insert(Group.create_changeset(attrs, payer_group))
end)
|> Multi.run(:update_group, fn repo, %{insert_family_group: memberships, create_group: group} ->
# Here I insert the list of memberships into the group already created with the payer.
repo.update(Group.add_memberships_changeset([memberships], group))
end)
end)
# List of events to record on the activity-logs table.
~w(register_membership accept_articles join_club)a
|> Enum.reduce(multi, fn event, multi ->
Multi.run(multi, {:insert_activity_log, event}, fn repo, %{insert_membership: membership} ->
membership = Repo.preload(membership, :club)
Users.update_user(membership.user, %{membership_id: membership.id})
log_params = %{event: event, actor: :membership, data: %{club_name: membership.club.name}}
%ActivityLog{}
|> ActivityLog.create_changeset(membership.club, membership, log_params)
|> repo.insert()
end)
end)
|> Repo.transaction()
end
I hope it’s enough information, if needed I can post more on the thread. I will truly appreciate your time to read and give me a feedback.
Thanks a lot!