Packages

phoenix_kit

1.7.22
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 entities entities.ex
Raw

lib/modules/entities/entities.ex

defmodule PhoenixKit.Modules.Entities do
@moduledoc """
Dynamic entity system for PhoenixKit - WordPress ACF equivalent.
This module provides both the Ecto schema definition and business logic for
managing custom content types (entities) with flexible field schemas.
## Schema Fields
- `name`: Unique identifier for the entity (e.g., "brand", "product")
- `display_name`: Human-readable singular name shown in UI (e.g., "Brand")
- `display_name_plural`: Human-readable plural name (e.g., "Brands")
- `description`: Description of what this entity represents
- `icon`: Icon identifier for UI display (hero icons)
- `status`: Workflow status string - one of "draft", "published", or "archived"
- `fields_definition`: JSONB array of field definitions
- `settings`: JSONB map of entity-specific settings
- `created_by`: User ID of the admin who created the entity
- `date_created`: When the entity was created
- `date_updated`: When the entity was last modified
## Field Definition Structure
Each field in `fields_definition` is a map with:
- `type`: Field type (text, textarea, number, boolean, date, select, etc.)
- `key`: Unique field identifier (snake_case)
- `label`: Display label for the field
- `required`: Whether the field is required
- `default`: Default value
- `validation`: Map of validation rules
- `options`: Array of options (for select, radio, checkbox types)
## Core Functions
### Entity Management
- `list_entities/0` - Get all entities
- `list_active_entities/0` - Get only active entities
- `get_entity!/1` - Get an entity by ID (raises if not found)
- `get_entity_by_name/1` - Get an entity by its name
- `create_entity/1` - Create a new entity
- `update_entity/2` - Update an existing entity
- `delete_entity/1` - Delete an entity (and all its data)
- `change_entity/2` - Get changeset for forms
### System Settings
- `enabled?/0` - Check if entities system is enabled
- `enable_system/0` - Enable the entities system
- `disable_system/0` - Disable the entities system
- `get_config/0` - Get current system configuration
- `get_max_per_user/0` - Get max entities per user limit
- `validate_user_entity_limit/1` - Check if user can create more entities
## Usage Examples
# Check if system is enabled
if PhoenixKit.Modules.Entities.enabled?() do
# System is active
end
# Create a brand entity
# Note: fields_definition requires string keys, not atom keys
{:ok, entity} = PhoenixKit.Modules.Entities.create_entity(%{
name: "brand",
display_name: "Brand",
display_name_plural: "Brands",
description: "Brand content type for company profiles",
icon: "hero-building-office",
created_by: admin_user.id,
fields_definition: [
%{"type" => "text", "key" => "name", "label" => "Name", "required" => true},
%{"type" => "textarea", "key" => "tagline", "label" => "Tagline"},
%{"type" => "rich_text", "key" => "description", "label" => "Description", "required" => true},
%{"type" => "select", "key" => "industry", "label" => "Industry",
"options" => ["Technology", "Manufacturing", "Retail"]},
%{"type" => "date", "key" => "founded_date", "label" => "Founded Date"},
%{"type" => "boolean", "key" => "featured", "label" => "Featured Brand"}
]
})
# Get entity by name
entity = PhoenixKit.Modules.Entities.get_entity_by_name("brand")
# List all active entities
entities = PhoenixKit.Modules.Entities.list_active_entities()
"""
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query, warn: false
alias PhoenixKit.Modules.Entities.EntityData
alias PhoenixKit.Modules.Entities.Events
alias PhoenixKit.Modules.Entities.Mirror.Exporter
alias PhoenixKit.Modules.Entities.Mirror.Storage
alias PhoenixKit.Settings
alias PhoenixKit.Users.Auth
alias PhoenixKit.Users.Auth.User
@primary_key {:id, :id, autogenerate: true}
@valid_statuses ~w(draft published archived)
@derive {Jason.Encoder,
only: [
:name,
:display_name,
:display_name_plural,
:description,
:icon,
:status,
:fields_definition,
:settings,
:date_created,
:date_updated
]}
schema "phoenix_kit_entities" do
field :uuid, Ecto.UUID
field :name, :string
field :display_name, :string
field :display_name_plural, :string
field :description, :string
field :icon, :string
field :status, :string, default: "published"
field :fields_definition, {:array, :map}
field :settings, :map
field :created_by, :integer
field :date_created, :utc_datetime_usec
field :date_updated, :utc_datetime_usec
belongs_to :creator, User, foreign_key: :created_by, define_field: false
has_many :entity_data, PhoenixKit.Modules.Entities.EntityData, foreign_key: :entity_id
end
@doc """
Creates a changeset for entity creation and updates.
Validates that name is unique, fields_definition is valid, and all required fields are present.
Automatically sets date_created on new records.
"""
def changeset(entity, attrs) do
entity
|> cast(attrs, [
:name,
:display_name,
:display_name_plural,
:description,
:icon,
:status,
:fields_definition,
:settings,
:created_by,
:date_created,
:date_updated
])
|> validate_required([:name, :display_name, :display_name_plural, :created_by])
|> validate_length(:name, min: 2, max: 50)
|> validate_length(:display_name, min: 2, max: 100)
|> validate_length(:display_name_plural, min: 2, max: 100)
|> validate_length(:description, max: 500)
|> validate_inclusion(:status, @valid_statuses)
|> validate_format(:name, ~r/^[a-z][a-z0-9_]*$/,
message:
"must start with a letter and contain only lowercase letters, numbers, and underscores"
)
|> validate_name_uniqueness()
|> validate_fields_definition()
|> unique_constraint(:name)
|> maybe_set_timestamps()
|> maybe_generate_uuid()
end
defp maybe_generate_uuid(changeset) do
case get_field(changeset, :uuid) do
nil -> put_change(changeset, :uuid, UUIDv7.generate())
_ -> changeset
end
end
defp validate_name_uniqueness(changeset) do
case get_field(changeset, :name) do
nil ->
changeset
"" ->
changeset
name ->
case get_entity_by_name(name) do
nil ->
changeset
existing_entity ->
current_id = get_field(changeset, :id)
if current_id && existing_entity.id == current_id do
changeset
else
add_error(changeset, :name, "has already been taken")
end
end
end
end
defp validate_fields_definition(changeset) do
case get_field(changeset, :fields_definition) do
nil ->
put_change(changeset, :fields_definition, [])
fields when is_list(fields) ->
validate_each_field_definition(changeset, fields)
_invalid ->
add_error(changeset, :fields_definition, "must be a list of field definitions")
end
end
defp validate_each_field_definition(changeset, fields) do
Enum.reduce(fields, changeset, fn field, acc ->
validate_single_field_definition(acc, field)
end)
end
defp validate_single_field_definition(changeset, field) when is_map(field) do
required_keys = ["type", "key", "label"]
missing_keys = required_keys -- Map.keys(field)
if Enum.empty?(missing_keys) do
validate_field_type(changeset, field)
else
add_error(
changeset,
:fields_definition,
"field missing required keys: #{Enum.join(missing_keys, ", ")}"
)
end
end
defp validate_single_field_definition(changeset, _invalid) do
add_error(changeset, :fields_definition, "each field must be a map")
end
defp validate_field_type(changeset, field) do
valid_types =
~w(text textarea number boolean date email url select radio checkbox rich_text image file relation)
if field["type"] in valid_types do
changeset
else
add_error(
changeset,
:fields_definition,
"invalid field type '#{field["type"]}' for field '#{field["key"]}'"
)
end
end
defp maybe_set_timestamps(changeset) do
case get_field(changeset, :id) do
nil ->
now = DateTime.utc_now()
changeset
|> put_change(:date_created, now)
|> put_change(:date_updated, now)
_id ->
put_change(changeset, :date_updated, DateTime.utc_now())
end
end
defp notify_entity_event({:ok, %__MODULE__{} = entity}, :created) do
Events.broadcast_entity_created(entity.id)
maybe_mirror_entity(entity)
{:ok, entity}
end
defp notify_entity_event({:ok, %__MODULE__{} = entity}, :updated) do
Events.broadcast_entity_updated(entity.id)
maybe_mirror_entity(entity)
{:ok, entity}
end
defp notify_entity_event({:ok, %__MODULE__{} = entity}, :deleted) do
Events.broadcast_entity_deleted(entity.id)
maybe_delete_mirrored_entity(entity)
{:ok, entity}
end
defp notify_entity_event(result, _event), do: result
# Mirror export helpers for auto-sync (per-entity settings)
defp maybe_mirror_entity(entity) do
if mirror_definitions_enabled?(entity) do
Task.start(fn -> Exporter.export_entity(entity) end)
end
end
defp maybe_delete_mirrored_entity(entity) do
# Delete the file if it exists (regardless of current setting)
# This ensures cleanup when entity is deleted
if Storage.entity_exists?(entity.name) do
Task.start(fn ->
Storage.delete_entity(entity.name)
end)
end
end
@doc """
Returns the list of entities ordered by creation date.
## Examples
iex> PhoenixKit.Modules.Entities.list_entities()
[%PhoenixKit.Entities{}, ...]
"""
def list_entities do
__MODULE__
|> order_by([e], desc: e.date_created)
|> preload([:creator])
|> repo().all()
end
@doc """
Returns the list of active (published) entities.
## Examples
iex> PhoenixKit.Modules.Entities.list_active_entities()
[%PhoenixKit.Entities{status: "published"}, ...]
"""
def list_active_entities do
from(e in __MODULE__,
where: e.status == "published",
order_by: [desc: e.date_created],
preload: [:creator]
)
|> repo().all()
end
@doc """
Gets a single entity by ID.
Returns the entity if found, nil otherwise.
## Examples
iex> PhoenixKit.Modules.Entities.get_entity(123)
%PhoenixKit.Entities{}
iex> PhoenixKit.Modules.Entities.get_entity(456)
nil
"""
def get_entity(id) do
case repo().get(__MODULE__, id) do
nil -> nil
entity -> repo().preload(entity, :creator)
end
end
@doc """
Gets a single entity by ID.
Raises `Ecto.NoResultsError` if the entity does not exist.
## Examples
iex> PhoenixKit.Modules.Entities.get_entity!(123)
%PhoenixKit.Entities{}
iex> PhoenixKit.Modules.Entities.get_entity!(456)
** (Ecto.NoResultsError)
"""
def get_entity!(id), do: repo().get!(__MODULE__, id) |> repo().preload(:creator)
@doc """
Gets a single entity by its unique name.
Returns the entity if found, nil otherwise.
## Examples
iex> PhoenixKit.Modules.Entities.get_entity_by_name("brand")
%PhoenixKit.Entities{}
iex> PhoenixKit.Modules.Entities.get_entity_by_name("invalid")
nil
"""
def get_entity_by_name(name) when is_binary(name) do
repo().get_by(__MODULE__, name: name)
end
@doc """
Creates an entity.
## Examples
iex> PhoenixKit.Modules.Entities.create_entity(%{name: "brand", display_name: "Brand"})
{:ok, %PhoenixKit.Entities{}}
iex> PhoenixKit.Modules.Entities.create_entity(%{name: ""})
{:error, %Ecto.Changeset{}}
Note: `created_by` is auto-filled with the first admin or user ID if not provided,
but only if at least one user exists in the system. If no users exist, the changeset
will fail with a validation error on `created_by`.
"""
def create_entity(attrs \\ %{}) do
attrs = maybe_add_created_by(attrs)
%__MODULE__{}
|> changeset(attrs)
|> repo().insert()
|> notify_entity_event(:created)
end
# Auto-fill created_by with first admin if not provided
defp maybe_add_created_by(attrs) when is_map(attrs) do
has_created_by =
Map.has_key?(attrs, :created_by) or Map.has_key?(attrs, "created_by")
if has_created_by do
attrs
else
case Auth.get_first_admin_id() do
nil ->
# Fall back to first user if no admin exists
case Auth.get_first_user_id() do
nil -> attrs
user_id -> Map.put(attrs, :created_by, user_id)
end
admin_id ->
Map.put(attrs, :created_by, admin_id)
end
end
end
@doc """
Updates an entity.
## Examples
iex> PhoenixKit.Modules.Entities.update_entity(entity, %{display_name: "Updated"})
{:ok, %PhoenixKit.Entities{}}
iex> PhoenixKit.Modules.Entities.update_entity(entity, %{name: ""})
{:error, %Ecto.Changeset{}}
"""
def update_entity(%__MODULE__{} = entity, attrs) do
entity
|> changeset(attrs)
|> repo().update()
|> notify_entity_event(:updated)
end
@doc """
Deletes an entity.
Note: This will also delete all associated entity_data records due to the
ON DELETE CASCADE constraint defined in the database migration (V17).
## Examples
iex> PhoenixKit.Modules.Entities.delete_entity(entity)
{:ok, %PhoenixKit.Entities{}}
iex> PhoenixKit.Modules.Entities.delete_entity(entity)
{:error, %Ecto.Changeset{}}
"""
def delete_entity(%__MODULE__{} = entity) do
repo().delete(entity)
|> notify_entity_event(:deleted)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking entity changes.
## Examples
iex> PhoenixKit.Modules.Entities.change_entity(entity)
%Ecto.Changeset{data: %PhoenixKit.Entities{}}
"""
def change_entity(%__MODULE__{} = entity, attrs \\ %{}) do
changeset(entity, attrs)
end
@doc """
Gets summary statistics for the entities system.
Returns counts and metrics useful for admin dashboards.
## Examples
iex> PhoenixKit.Modules.Entities.get_system_stats()
%{total_entities: 5, active_entities: 4, total_data_records: 150}
"""
def get_system_stats do
entities_query = from(e in __MODULE__)
data_query = from(d in PhoenixKit.Modules.Entities.EntityData)
total_entities = repo().aggregate(entities_query, :count)
active_entities =
repo().aggregate(from(e in entities_query, where: e.status == "published"), :count)
total_data_records = repo().aggregate(data_query, :count)
%{
total_entities: total_entities,
active_entities: active_entities,
total_data_records: total_data_records
}
end
@doc """
Counts the total number of entities created by a user.
## Examples
iex> PhoenixKit.Modules.Entities.count_user_entities(1)
5
"""
def count_user_entities(user_id) when is_integer(user_id) do
from(e in __MODULE__, where: e.created_by == ^user_id, select: count(e.id))
|> repo().one()
end
@doc """
Counts the total number of entities in the system.
## Examples
iex> PhoenixKit.Modules.Entities.count_entities()
15
"""
def count_entities do
from(e in __MODULE__, select: count(e.id))
|> repo().one()
end
@doc """
Counts the total number of entity data records across all entities.
## Examples
iex> PhoenixKit.Modules.Entities.count_all_entity_data()
243
"""
def count_all_entity_data do
from(d in PhoenixKit.Modules.Entities.EntityData, select: count(d.id))
|> repo().one()
end
@doc """
Validates that a user hasn't exceeded their entity creation limit.
Checks the current number of entities created by the user against the system limit.
Returns `{:ok, :valid}` if within limits, `{:error, reason}` if limit exceeded.
## Examples
iex> PhoenixKit.Modules.Entities.validate_user_entity_limit(1)
{:ok, :valid}
iex> PhoenixKit.Modules.Entities.validate_user_entity_limit(1)
{:error, "You have reached the maximum limit of 100 entities"}
"""
def validate_user_entity_limit(user_id) when is_integer(user_id) do
max_entities = get_max_per_user()
current_count = count_user_entities(user_id)
if current_count < max_entities do
{:ok, :valid}
else
{:error, "You have reached the maximum limit of #{max_entities} entities"}
end
end
@doc """
Checks if the entities system is enabled.
Returns true if the "entities_enabled" setting is true.
## Examples
iex> PhoenixKit.Modules.Entities.enabled?()
false
"""
def enabled? do
Settings.get_boolean_setting("entities_enabled", false)
end
@doc """
Enables the entities system.
Sets the "entities_enabled" setting to true.
## Examples
iex> PhoenixKit.Modules.Entities.enable_system()
{:ok, %Setting{}}
"""
def enable_system do
Settings.update_boolean_setting_with_module("entities_enabled", true, "entities")
end
@doc """
Disables the entities system.
Sets the "entities_enabled" setting to false.
## Examples
iex> PhoenixKit.Modules.Entities.disable_system()
{:ok, %Setting{}}
"""
def disable_system do
Settings.update_boolean_setting_with_module("entities_enabled", false, "entities")
end
@doc """
Gets the maximum number of entities a single user can create.
Returns the system-wide limit for entity creation per user.
Defaults to 100 if not set.
## Examples
iex> PhoenixKit.Modules.Entities.get_max_per_user()
100
"""
def get_max_per_user do
Settings.get_integer_setting("entities_max_per_user", 100)
end
@doc """
Gets the current entities system configuration.
Returns a map with the current settings.
## Examples
iex> PhoenixKit.Modules.Entities.get_config()
%{enabled: false, max_per_user: 100, allow_relations: true, file_upload: false, entity_count: 0, total_data_count: 0}
"""
def get_config do
%{
enabled: enabled?(),
max_per_user: get_max_per_user(),
allow_relations: Settings.get_boolean_setting("entities_allow_relations", true),
file_upload: Settings.get_boolean_setting("entities_file_upload", false),
entity_count: count_entities(),
total_data_count: count_all_entity_data()
}
end
# ============================================================================
# Per-Entity Mirror Settings
# ============================================================================
@doc """
Gets the mirror settings for an entity.
Returns a map with mirror_definitions and mirror_data booleans.
Defaults to false if not explicitly set.
## Examples
iex> PhoenixKit.Modules.Entities.get_mirror_settings(entity)
%{mirror_definitions: true, mirror_data: false}
"""
def get_mirror_settings(%__MODULE__{settings: settings}) do
settings = settings || %{}
%{
mirror_definitions: Map.get(settings, "mirror_definitions", false),
mirror_data: Map.get(settings, "mirror_data", false)
}
end
@doc """
Checks if definition mirroring is enabled for this entity.
## Examples
iex> PhoenixKit.Modules.Entities.mirror_definitions_enabled?(entity)
true
"""
def mirror_definitions_enabled?(%__MODULE__{settings: settings}) do
settings = settings || %{}
Map.get(settings, "mirror_definitions", false) == true
end
@doc """
Checks if data mirroring is enabled for this entity.
## Examples
iex> PhoenixKit.Modules.Entities.mirror_data_enabled?(entity)
false
"""
def mirror_data_enabled?(%__MODULE__{settings: settings}) do
settings = settings || %{}
Map.get(settings, "mirror_data", false) == true
end
@doc """
Updates the mirror settings for an entity.
## Parameters
- `entity` - The entity to update
- `mirror_settings` - Map with keys "mirror_definitions" and/or "mirror_data"
## Examples
iex> PhoenixKit.Modules.Entities.update_mirror_settings(entity, %{"mirror_definitions" => true})
{:ok, %PhoenixKit.Entities{}}
"""
def update_mirror_settings(%__MODULE__{} = entity, mirror_settings)
when is_map(mirror_settings) do
current_settings = entity.settings || %{}
new_settings = Map.merge(current_settings, mirror_settings)
update_entity(entity, %{settings: new_settings})
end
@doc """
Lists all entities with their mirror status and data counts.
Returns a list of maps suitable for the settings UI.
## Examples
iex> PhoenixKit.Modules.Entities.list_entities_with_mirror_status()
[%{id: 1, name: "test", display_name: "Test", data_count: 8, mirror_definitions: true, mirror_data: false}, ...]
"""
def list_entities_with_mirror_status do
entities = list_entities()
Enum.map(entities, fn entity ->
mirror_settings = get_mirror_settings(entity)
data_count = EntityData.count_by_entity(entity.id)
file_exists = Storage.entity_exists?(entity.name)
%{
id: entity.id,
name: entity.name,
display_name: entity.display_name,
data_count: data_count,
mirror_definitions: mirror_settings.mirror_definitions,
mirror_data: mirror_settings.mirror_data,
file_exists: file_exists
}
end)
end
@doc """
Enables definition mirroring for all entities.
## Examples
iex> PhoenixKit.Modules.Entities.enable_all_definitions_mirror()
{:ok, count}
"""
def enable_all_definitions_mirror do
entities = list_entities()
results =
Enum.map(entities, fn entity ->
update_mirror_settings(entity, %{"mirror_definitions" => true})
end)
success_count = Enum.count(results, &match?({:ok, _}, &1))
{:ok, success_count}
end
@doc """
Disables definition mirroring for all entities.
## Examples
iex> PhoenixKit.Modules.Entities.disable_all_definitions_mirror()
{:ok, count}
"""
def disable_all_definitions_mirror do
entities = list_entities()
results =
Enum.map(entities, fn entity ->
update_mirror_settings(entity, %{"mirror_definitions" => false})
end)
success_count = Enum.count(results, &match?({:ok, _}, &1))
{:ok, success_count}
end
@doc """
Enables data mirroring for all entities.
## Examples
iex> PhoenixKit.Modules.Entities.enable_all_data_mirror()
{:ok, count}
"""
def enable_all_data_mirror do
entities = list_entities()
results =
Enum.map(entities, fn entity ->
update_mirror_settings(entity, %{"mirror_data" => true})
end)
success_count = Enum.count(results, &match?({:ok, _}, &1))
{:ok, success_count}
end
@doc """
Disables data mirroring for all entities.
## Examples
iex> PhoenixKit.Modules.Entities.disable_all_data_mirror()
{:ok, count}
"""
def disable_all_data_mirror do
entities = list_entities()
results =
Enum.map(entities, fn entity ->
update_mirror_settings(entity, %{"mirror_data" => false})
end)
success_count = Enum.count(results, &match?({:ok, _}, &1))
{:ok, success_count}
end
defp repo do
PhoenixKit.RepoHelper.repo()
end
end