Packages

phoenix_kit

1.7.165
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 phoenix_kit utils reorder.ex
Raw

lib/phoenix_kit/utils/reorder.ex

defmodule PhoenixKit.Utils.Reorder do
@moduledoc """
Two-phase index rewrite for drag-to-reorder list views.
Given a list of UUIDs in their new display order and an Ecto schema,
rewrites a position field on the matching rows to `1..N` matching
the order of the input list. The write runs in two passes inside a
transaction — first to negative indices, then to positive — so a
unique index on the position column (should one ever be added)
wouldn't trip mid-update.
Consumers that need pre-write validation (scope checks, permission
guards) or post-write side effects (activity logging, PubSub
broadcasts) should wrap this helper rather than fold the logic in
here. `PhoenixKitProjects.reorder_projects/2` is the reference
consumer doing exactly that — this module owns only the index-rewrite
primitive.
Non-UUID entries in the payload are silently filtered (a stale or
malformed drop event can't poison the rewrite). Duplicates dedup
last-write-wins via `Enum.uniq/1`.
## Example
defmodule MyApp.Endpoints do
alias PhoenixKit.Utils.Reorder
def reorder_endpoints(ordered_ids) do
Reorder.reorder(MyApp.Endpoint, ordered_ids, :sort_order, repo: repo())
end
end
"""
import Ecto.Query
alias PhoenixKit.Utils.UUID
@default_max_uuids 500
@type result :: {:ok, non_neg_integer()} | {:error, :too_many_uuids}
@doc """
Rewrites `field` on the rows whose `uuid` appears in `ordered_ids`,
setting it to each UUID's 1-based position in the list.
Returns `{:ok, count}` where `count` is the number of rows actually
updated in the positive-write phase (matches `Repo.update_all`'s
count semantics — UUIDs in the payload that don't resolve to real
rows aren't counted). Returns `{:error, :too_many_uuids}` when the
dedup'd payload exceeds the configured cap. An empty / fully-filtered
payload returns `{:ok, 0}`.
## Options
- `:repo` — the Ecto repo to use. Defaults to
`PhoenixKit.RepoHelper.repo/0` so it picks up the host app's repo.
- `:max_uuids` — payload cap, checked after dedup. Default `500`.
Guards against runaway drop events from a misbehaving client.
"""
@spec reorder(module(), [String.t()], atom(), keyword()) :: result()
def reorder(schema, ordered_ids, field, opts \\ [])
when is_atom(schema) and is_list(ordered_ids) and is_atom(field) do
max = Keyword.get(opts, :max_uuids, @default_max_uuids)
repo = Keyword.get(opts, :repo, PhoenixKit.RepoHelper.repo())
case dedupe_uuids(ordered_ids) do
[] ->
{:ok, 0}
uuids when length(uuids) > max ->
{:error, :too_many_uuids}
uuids ->
{:ok, count} =
repo.transaction(fn ->
pairs = Enum.with_index(uuids, 1)
_ = write_phase(repo, schema, pairs, field, -1)
write_phase(repo, schema, pairs, field, 1)
end)
{:ok, count}
end
end
defp write_phase(repo, schema, pairs, field, sign) do
Enum.reduce(pairs, 0, fn {uuid, idx}, total ->
{n, _} =
from(r in schema, where: r.uuid == ^uuid)
|> repo.update_all(set: [{field, sign * idx}])
total + n
end)
end
defp dedupe_uuids(ids) do
ids
|> Enum.filter(&UUID.valid?/1)
|> Enum.uniq()
end
end