feat: member user relation
This commit is contained in:
parent
b0614aae22
commit
bec7e705e8
4 changed files with 296 additions and 1 deletions
|
|
@ -13,7 +13,11 @@ defmodule Mv.Membership.Member do
|
|||
|
||||
create :create_member do
|
||||
primary? true
|
||||
# Properties can be created along with member
|
||||
argument :properties, {:array, :map}
|
||||
# Allow user to be passed as argument for relationship management
|
||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||
argument :user, :map, allow_nil?: true
|
||||
|
||||
accept [
|
||||
:first_name,
|
||||
|
|
@ -32,12 +36,29 @@ defmodule Mv.Membership.Member do
|
|||
]
|
||||
|
||||
change manage_relationship(:properties, type: :create)
|
||||
|
||||
# Manage the user relationship during member creation
|
||||
change manage_relationship(:user, :user,
|
||||
# Look up existing user and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if user doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# Error if user is already linked to another member (prevents "stealing")
|
||||
on_match: :error,
|
||||
# If no user provided, that's fine (optional relationship)
|
||||
on_missing: :ignore
|
||||
)
|
||||
end
|
||||
|
||||
update :update_member do
|
||||
primary? true
|
||||
# Required because custom validation function cannot be done atomically
|
||||
require_atomic? false
|
||||
# Properties can be updated or created along with member
|
||||
argument :properties, {:array, :map}
|
||||
# Allow user to be passed as argument for relationship management
|
||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||
argument :user, :map, allow_nil?: true
|
||||
|
||||
accept [
|
||||
:first_name,
|
||||
|
|
@ -56,6 +77,18 @@ defmodule Mv.Membership.Member do
|
|||
]
|
||||
|
||||
change manage_relationship(:properties, on_match: :update, on_no_match: :create)
|
||||
|
||||
# Manage the user relationship during member update
|
||||
change manage_relationship(:user, :user,
|
||||
# Look up existing user and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if user doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# Error if user is already linked to another member (prevents "stealing")
|
||||
on_match: :error,
|
||||
# If no user provided, remove existing relationship (allows user removal)
|
||||
on_missing: :unrelate
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -67,6 +100,40 @@ defmodule Mv.Membership.Member do
|
|||
validate present(:last_name)
|
||||
validate present(:email)
|
||||
|
||||
# Prevent linking to a user that already has a member
|
||||
# This validation prevents "stealing" users from other members by checking
|
||||
# if the target user is already linked to a different member
|
||||
# This is necessary because manage_relationship's on_match: :error only checks
|
||||
# if the user is already linked to THIS specific member, not ANY member
|
||||
validate fn changeset, _context ->
|
||||
user_arg = Ash.Changeset.get_argument(changeset, :user)
|
||||
|
||||
if user_arg && user_arg[:id] do
|
||||
user_id = user_arg[:id]
|
||||
current_member_id = changeset.data.id
|
||||
|
||||
# Check the current state of the user in the database
|
||||
case Ash.get(Mv.Accounts.User, user_id) do
|
||||
# User is free to be linked
|
||||
{:ok, %{member_id: nil}} ->
|
||||
:ok
|
||||
|
||||
# User already linked to this member (update scenario)
|
||||
{:ok, %{member_id: ^current_member_id}} ->
|
||||
:ok
|
||||
|
||||
{:ok, %{member_id: _other_member_id}} ->
|
||||
# User is linked to a different member - prevent "stealing"
|
||||
{:error, field: :user, message: "User is already linked to another member"}
|
||||
|
||||
{:error, _} ->
|
||||
{:error, field: :user, message: "User not found"}
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
# Birth date not in the future
|
||||
validate compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0),
|
||||
where: [present(:birth_date)],
|
||||
|
|
@ -170,5 +237,9 @@ defmodule Mv.Membership.Member do
|
|||
|
||||
relationships do
|
||||
has_many :properties, Mv.Membership.Property
|
||||
# 1:1 relationship - Member can optionally have one User
|
||||
# This references the User's member_id attribute
|
||||
# The relationship is optional (allow_nil? true by default)
|
||||
has_one :user, Mv.Accounts.User
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue