Packages
phoenix_kit
1.7.81
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/connections/connection.ex
defmodule PhoenixKit.Modules.Connections.Connection do
@moduledoc """
Schema for two-way mutual connection relationships.
Represents a bidirectional relationship that requires acceptance from both parties.
Similar to LinkedIn connections or Facebook friend requests.
## Status Flow
- `pending` - Request sent, awaiting response
- `accepted` - Both parties have agreed to connect
- `rejected` - Recipient declined the request
## Fields
- `requester_uuid` - UUID of the user who initiated the connection request
- `recipient_uuid` - UUID of the user who received the request
- `status` - Current status of the connection
- `requested_at` - When the request was sent
- `responded_at` - When the recipient responded (nil if pending)
## Examples
# Pending connection request
%Connection{
uuid: "018e3c4a-9f6b-7890-abcd-ef1234567890",
requester_uuid: "019abc12-3456-7890-abcd-ef1234567890",
recipient_uuid: "019abc12-9876-5432-abcd-ef1234567890",
status: "pending",
requested_at: ~N[2025-01-15 10:30:00],
responded_at: nil
}
# Accepted connection
%Connection{
uuid: "018e3c4a-9f6b-7890-abcd-ef1234567890",
requester_uuid: "019abc12-3456-7890-abcd-ef1234567890",
recipient_uuid: "019abc12-9876-5432-abcd-ef1234567890",
status: "accepted",
requested_at: ~N[2025-01-15 10:30:00],
responded_at: ~N[2025-01-15 11:00:00]
}
## Business Rules
- Cannot connect with yourself
- Cannot connect if blocked (either direction)
- If A requests B while B has pending request to A -> auto-accept both
- Only one active connection per user pair
"""
use Ecto.Schema
import Ecto.Changeset
alias PhoenixKit.Utils.Date, as: UtilsDate
@primary_key {:uuid, UUIDv7, autogenerate: true}
@statuses ["pending", "accepted", "rejected"]
@type status :: String.t()
@type t :: %__MODULE__{
uuid: UUIDv7.t() | nil,
requester_uuid: UUIDv7.t(),
recipient_uuid: UUIDv7.t(),
status: status(),
requested_at: DateTime.t(),
responded_at: DateTime.t() | nil,
requester: PhoenixKit.Users.Auth.User.t() | Ecto.Association.NotLoaded.t(),
recipient: PhoenixKit.Users.Auth.User.t() | Ecto.Association.NotLoaded.t(),
inserted_at: DateTime.t() | nil,
updated_at: DateTime.t() | nil
}
schema "phoenix_kit_user_connections" do
belongs_to :requester, PhoenixKit.Users.Auth.User,
foreign_key: :requester_uuid,
references: :uuid,
type: UUIDv7
belongs_to :recipient, PhoenixKit.Users.Auth.User,
foreign_key: :recipient_uuid,
references: :uuid,
type: UUIDv7
field :status, :string, default: "pending"
field :requested_at, :utc_datetime
field :responded_at, :utc_datetime
timestamps(type: :utc_datetime)
end
@doc """
Returns the list of valid statuses.
"""
def statuses, do: @statuses
@doc """
Changeset for creating a new connection request.
## Required Fields
- `requester_uuid` - UUID of the user sending the request
- `recipient_uuid` - UUID of the user receiving the request
## Validation Rules
- Both user UUIDs are required
- Cannot request connection with yourself
- Status must be valid
"""
def changeset(connection, attrs) do
connection
|> cast(attrs, [
:requester_uuid,
:recipient_uuid,
:status,
:requested_at,
:responded_at
])
|> validate_required([:requester_uuid, :recipient_uuid])
|> validate_inclusion(:status, @statuses)
|> validate_not_self_connection()
|> put_requested_at()
|> foreign_key_constraint(:requester_uuid)
|> foreign_key_constraint(:recipient_uuid)
end
@doc """
Changeset for updating connection status (accept/reject).
"""
def status_changeset(connection, attrs) do
connection
|> cast(attrs, [:status, :responded_at])
|> validate_required([:status])
|> validate_inclusion(:status, @statuses)
|> put_responded_at()
end
defp validate_not_self_connection(changeset) do
requester_uuid = get_field(changeset, :requester_uuid)
recipient_uuid = get_field(changeset, :recipient_uuid)
if requester_uuid && recipient_uuid && requester_uuid == recipient_uuid do
add_error(changeset, :recipient_uuid, "cannot connect with yourself")
else
changeset
end
end
defp put_requested_at(changeset) do
if get_field(changeset, :requested_at) do
changeset
else
put_change(
changeset,
:requested_at,
UtilsDate.utc_now()
)
end
end
defp put_responded_at(changeset) do
status = get_change(changeset, :status)
if status in ["accepted", "rejected"] && !get_field(changeset, :responded_at) do
put_change(
changeset,
:responded_at,
UtilsDate.utc_now()
)
else
changeset
end
end
@doc """
Returns whether this connection is pending.
"""
def pending?(%__MODULE__{status: "pending"}), do: true
def pending?(_), do: false
@doc """
Returns whether this connection is accepted.
"""
def accepted?(%__MODULE__{status: "accepted"}), do: true
def accepted?(_), do: false
@doc """
Returns whether this connection is rejected.
"""
def rejected?(%__MODULE__{status: "rejected"}), do: true
def rejected?(_), do: false
end