Packages

phoenix_kit

1.7.30
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
phoenix_kit lib modules connections connection.ex
Raw

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_id` - User who initiated the connection request
- `recipient_id` - 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{
id: "018e3c4a-9f6b-7890-abcd-ef1234567890",
requester_id: 1,
recipient_id: 2,
status: "pending",
requested_at: ~N[2025-01-15 10:30:00],
responded_at: nil
}
# Accepted connection
%Connection{
id: "018e3c4a-9f6b-7890-abcd-ef1234567890",
requester_id: 1,
recipient_id: 2,
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
@primary_key {:id, UUIDv7, autogenerate: true}
@statuses ["pending", "accepted", "rejected"]
@type status :: String.t()
@type t :: %__MODULE__{
id: UUIDv7.t() | nil,
requester_id: integer(),
recipient_id: integer(),
status: status(),
requested_at: NaiveDateTime.t(),
responded_at: NaiveDateTime.t() | nil,
requester: PhoenixKit.Users.Auth.User.t() | Ecto.Association.NotLoaded.t(),
recipient: PhoenixKit.Users.Auth.User.t() | Ecto.Association.NotLoaded.t(),
inserted_at: NaiveDateTime.t() | nil,
updated_at: NaiveDateTime.t() | nil
}
schema "phoenix_kit_user_connections" do
belongs_to :requester, PhoenixKit.Users.Auth.User, type: :integer
belongs_to :recipient, PhoenixKit.Users.Auth.User, type: :integer
field :status, :string, default: "pending"
field :requested_at, :naive_datetime
field :responded_at, :naive_datetime
timestamps(type: :naive_datetime)
end
@doc """
Returns the list of valid statuses.
"""
def statuses, do: @statuses
@doc """
Changeset for creating a new connection request.
## Required Fields
- `requester_id` - The user sending the request
- `recipient_id` - The user receiving the request
## Validation Rules
- Both user IDs are required
- Cannot request connection with yourself
- Status must be valid
"""
def changeset(connection, attrs) do
connection
|> cast(attrs, [:requester_id, :recipient_id, :status, :requested_at, :responded_at])
|> validate_required([:requester_id, :recipient_id])
|> validate_inclusion(:status, @statuses)
|> validate_not_self_connection()
|> put_requested_at()
|> foreign_key_constraint(:requester_id)
|> foreign_key_constraint(:recipient_id)
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_id = get_field(changeset, :requester_id)
recipient_id = get_field(changeset, :recipient_id)
if requester_id && recipient_id && requester_id == recipient_id do
add_error(changeset, :recipient_id, "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,
NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
)
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,
NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
)
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