PhoenixKitEntities.EntityData (PhoenixKitEntities v0.2.1)

Copy Markdown View Source

Entity data records for PhoenixKit entities system.

This module manages actual data records that follow entity blueprints. Each record is associated with an entity type and stores its field values in a JSONB column for flexibility.

Schema Fields

  • entity_uuid: Foreign key to the entity blueprint
  • title: Display title/name for the record
  • slug: URL-friendly identifier (optional)
  • status: Record status ("draft", "published", "archived", "trashed")
  • data: JSONB map of all field values based on entity definition
  • metadata: JSONB map for additional information (tags, categories, etc.)
  • created_by: User UUID who created the record
  • date_created: When the record was created
  • date_updated: When the record was last modified

Core Functions

Data Management

Query Helpers

Usage Examples

# Create a brand data record
{:ok, data} = PhoenixKitEntities.EntityData.create(%{
  entity_uuid: brand_entity.uuid,
  title: "Acme Corporation",
  slug: "acme-corporation",
  status: "published",
  created_by_uuid: user.uuid,
  data: %{
    "name" => "Acme Corporation",
    "tagline" => "Quality products since 1950",
    "description" => "<p>Leading manufacturer of innovative products</p>",
    "industry" => "Manufacturing",
    "founded_date" => "1950-03-15",
    "featured" => true
  },
  metadata: %{
    "tags" => ["manufacturing", "industrial"],
    "contact_email" => "info@acme.com"
  }
})

# Get all records for an entity
records = PhoenixKitEntities.EntityData.list_by_entity(brand_entity.uuid)

# Search by title
results = PhoenixKitEntities.EntityData.search_by_title("Acme", brand_entity.uuid)

Summary

Functions

Bulk hard-deletes records by UUIDs.

Bulk restores trashed records to "published" status.

Bulk soft-deletes records by setting their status to "trashed".

Bulk updates positions for multiple records.

Bulk updates the status of multiple records by UUIDs.

Returns an %Ecto.Changeset{} for tracking entity data changes.

Creates a changeset for entity data creation and updates.

Counts records for an entity.

Counts external (parent-app) rows that reference this record.

Creates an entity data record.

Hard-deletes an entity data record.

Alias for delete/1 for consistency with LiveView naming.

Gets records filtered by status across all entities.

Gets a single entity data record by UUID.

Gets a single entity data record by UUID.

Gets all title translations for a record.

Gets translations for all languages in a record.

Gets a single entity data record by entity and slug.

Alias for get!/2 for consistency with LiveView naming.

Gets statistical data about entity data records.

Gets the raw (non-merged) data for a specific language.

Gets the title translation for a specific language.

Gets the data fields for a specific language, merged with primary language defaults.

Returns all entity data records ordered by creation date.

Alias for list_all/1 for consistency with LiveView naming.

Returns all entity data records for a specific entity.

Returns entity data records filtered by entity and status.

Alias for list_by_entity/2 for consistency with LiveView naming.

Alias for filter_by_status/2 for consistency with LiveView naming.

Returns trashed records for a specific entity, ordered by most recently updated (the default trash-bin view).

Moves a record to a specific position within its entity, shifting other records.

Gets the next available position for an entity's data records.

Returns hreflang alternates and a canonical URL for a record across all enabled languages.

Returns a public path for a record, respecting locale and the configured URL pattern.

Returns a full public URL for a record by prepending a base URL to public_path/3.

Gets all published records for a specific entity.

Removes all data for a specific language from a record.

Reorders all records for an entity based on a list of UUIDs in the desired order.

Resolves translated fields on an entity data record for a given language.

Resolves translations on a list of entity data records.

Restores a trashed entity data record by setting its status back to "published".

Searches entity data records by title.

Alias for search_by_title for consistency with LiveView naming.

Checks if a secondary language slug exists for another record within the same entity.

Sets the title translation for a specific language.

Sets the data translation for a specific language on a record.

Soft-deletes an entity data record by setting its status to "trashed".

Counts trashed records for an entity. Drives the trash-bin badge.

Updates an entity data record.

Alias for update/2 for consistency with LiveView naming.

Updates the position of a single entity data record.

Types

t()

@type t() :: %PhoenixKitEntities.EntityData{
  __meta__: term(),
  created_by_uuid: term(),
  creator: term(),
  data: term(),
  date_created: term(),
  date_updated: term(),
  entity: term(),
  entity_uuid: term(),
  metadata: term(),
  position: term(),
  slug: term(),
  status: term(),
  title: term(),
  uuid: term()
}

Functions

bulk_delete(uuids, opts \\ [])

@spec bulk_delete(
  [String.t()],
  keyword()
) :: {non_neg_integer(), nil} | {:error, :referenced_by_external}

Bulk hard-deletes records by UUIDs.

Prefer bulk_trash/2 for soft-delete. Use this for emptying-the-trash flows where the records are confirmed unreferenced.

Catches Postgrex.Error for FK / NOT NULL violations and returns {:error, :referenced_by_external} so the admin UI can flash a friendly message rather than 500. The transaction rolls back on this error — no records are deleted.

Options

  • :actor_uuid — UUID of the user performing the bulk delete.

Examples

iex> PhoenixKitEntities.EntityData.bulk_delete(["uuid1", "uuid2"], actor_uuid: admin.uuid)
{2, nil}

iex> PhoenixKitEntities.EntityData.bulk_delete([referenced_uuid])
{:error, :referenced_by_external}

bulk_restore_from_trash(uuids, opts \\ [])

@spec bulk_restore_from_trash(
  [String.t()],
  keyword()
) :: {non_neg_integer(), nil}

Bulk restores trashed records to "published" status.

Only rows currently "trashed" are touched. Logs entity_data.bulk_restored with the affected count.

Examples

iex> EntityData.bulk_restore_from_trash(["uuid1", "uuid2"], actor_uuid: admin.uuid)
{2, nil}

bulk_trash(uuids, opts \\ [])

@spec bulk_trash(
  [String.t()],
  keyword()
) :: {non_neg_integer(), nil}

Bulk soft-deletes records by setting their status to "trashed".

Returns {count, nil} for the number of records actually trashed (already-trashed records are skipped via the WHERE clause). Logs a single entity_data.bulk_trashed row.

Examples

iex> EntityData.bulk_trash(["uuid1", "uuid2"], actor_uuid: admin.uuid)
{2, nil}

bulk_update_positions(uuid_position_pairs, opts \\ [])

Bulk updates positions for multiple records.

Accepts a list of {uuid, position} tuples. Each record is updated individually to trigger events and maintain consistency.

Options

  • :entity_uuid — when provided, the WHERE clause also filters on entity_uuid, so a stray UUID from another entity in the input list cannot have its position rewritten by the wrong scope. The LV reorder paths always pass this; programmatic callers should too unless they really intend cross-entity rewrites.

Returns {:error, :too_many_uuids} if more than 1000 pairs are supplied — the cap protects the transaction from N+1 unbounded update_all work and from a malformed LV payload turning into a long-running write storm.

Examples

iex> bulk_update_positions([{"uuid1", 1}, {"uuid2", 2}, {"uuid3", 3}], entity_uuid: e_uuid)
:ok

bulk_update_status(uuids, status, opts \\ [])

@spec bulk_update_status([String.t()], String.t(), keyword()) ::
  {non_neg_integer(), nil}

Bulk updates the status of multiple records by UUIDs.

Returns a tuple with the count of updated records and nil. Logs a single entity_data.bulk_status_changed activity row carrying the count + new status. Per-record entity_data.updated rows are NOT emitted — bulk operations log at the operation level so the audit trail doesn't explode.

Options

  • :actor_uuid — UUID of the user performing the bulk update. Threaded through to the activity log entry.

Examples

iex> PhoenixKitEntities.EntityData.bulk_update_status(["uuid1", "uuid2"], "archived",
...>   actor_uuid: admin.uuid)
{2, nil}

change(entity_data, attrs \\ %{})

Returns an %Ecto.Changeset{} for tracking entity data changes.

Examples

iex> PhoenixKitEntities.EntityData.change(record)
%Ecto.Changeset{data: %PhoenixKitEntities.EntityData{}}

changeset(entity_data, attrs)

Creates a changeset for entity data creation and updates.

Validates that entity exists, title is present, and data validates against entity definition. Automatically sets date_created on new records.

count_by_entity(entity_uuid, opts \\ [])

Counts records for an entity.

Excludes trashed records by default. Pass include_trashed: true to count everything.

Examples

iex> PhoenixKitEntities.EntityData.count_by_entity(entity_uuid)
42

iex> PhoenixKitEntities.EntityData.count_by_entity(entity_uuid, include_trashed: true)
45

count_external_references(entity_data, entity \\ nil)

@spec count_external_references(t(), map() | nil) :: non_neg_integer()

Counts external (parent-app) rows that reference this record.

Reads :reverse_references from Application.get_env/2 — a list of {entity_name, count_fn} tuples where count_fn is a 1-arity function that takes the entity_data uuid and returns a non-negative integer. Returns the total count across all matching callbacks for the record's entity, or 0 if no callbacks are registered.

Informational only — NOT a delete-blocker. Soft-delete keeps the row alive regardless of how many parent rows reference it; this count is just for the admin UI to surface "Used by N rows" hints before the operator clicks Trash or Permanently delete. Don't wire it into the actual delete path.

Performance — pass entity when the caller already has it

The single-arg form preloads :entity on every call. When rendering many records (e.g. the admin trash bin), pass the already-loaded entity as the second arg to skip the per-call N+1:

entity = Entities.get_entity!(entity_uuid)

Enum.map(records, fn record ->
  EntityData.count_external_references(record, entity)
end)

Configuration

# In parent app config:
config :phoenix_kit_entities,
  reverse_references: [
    {"order_status", &MyApp.Orders.count_orders_with_status/1},
    {"sub_order_status", &MyApp.Orders.count_sub_orders_with_status/1}
  ]

Multiple callbacks per entity name are supported — every matching tuple's count contributes to the total. Useful when the same entity_data acts as a controlled vocabulary for several parent tables (e.g. orders and audit_log both referencing order_status).

create(attrs \\ %{}, opts \\ [])

@spec create(
  map(),
  keyword()
) :: {:ok, t()} | {:error, Ecto.Changeset.t()}

Creates an entity data record.

Examples

iex> PhoenixKitEntities.EntityData.create(%{entity_uuid: entity_uuid, title: "Test"})
{:ok, %PhoenixKitEntities.EntityData{}}

iex> PhoenixKitEntities.EntityData.create(%{title: ""})
{: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.

delete(entity_data, opts \\ [])

@spec delete(
  t(),
  keyword()
) :: {:ok, t()} | {:error, Ecto.Changeset.t() | :referenced_by_external}

Hard-deletes an entity data record.

Prefer trash/2 for soft-delete — it keeps the row alive so parent-app FK references stay valid. Use this only when the record is genuinely unreferenced (e.g. emptying the trash bin).

Catches Postgrex.Error for FK / NOT NULL violations and returns {:error, :referenced_by_external} so the admin UI can render a friendly flash instead of a 500. The row stays in the DB on this error.

Examples

iex> PhoenixKitEntities.EntityData.delete(record)
{:ok, %PhoenixKitEntities.EntityData{}}

iex> # parent_app.orders has a NOT NULL FK to this row
iex> PhoenixKitEntities.EntityData.delete(record)
{:error, :referenced_by_external}

delete_data(entity_data, opts \\ [])

@spec delete_data(
  t(),
  keyword()
) :: {:ok, t()} | {:error, Ecto.Changeset.t() | :referenced_by_external}

Alias for delete/1 for consistency with LiveView naming.

filter_by_status(status, opts \\ [])

Gets records filtered by status across all entities.

When status is "trashed", returns trashed records. For all other statuses, the query is implicitly scoped (status filter excludes trashed by virtue of matching a different status).

Examples

iex> PhoenixKitEntities.EntityData.filter_by_status("draft")
[%PhoenixKitEntities.EntityData{status: "draft"}, ...]

get(uuid, opts \\ [])

Gets a single entity data record by UUID.

Returns the record if found, nil otherwise.

Examples

iex> PhoenixKitEntities.EntityData.get("550e8400-e29b-41d4-a716-446655440000")
%PhoenixKitEntities.EntityData{}

iex> PhoenixKitEntities.EntityData.get("invalid")
nil

get!(id, opts \\ [])

Gets a single entity data record by UUID.

Raises Ecto.NoResultsError if the record does not exist.

Examples

iex> PhoenixKitEntities.EntityData.get!("550e8400-e29b-41d4-a716-446655440000")
%PhoenixKitEntities.EntityData{}

iex> PhoenixKitEntities.EntityData.get!("nonexistent-uuid")
** (Ecto.NoResultsError)

get_all_title_translations(entity_data)

Gets all title translations for a record.

Returns a map of language codes to title strings.

Examples

iex> get_all_title_translations(record)
%{"en-US" => "My Product", "es-ES" => "Mi Producto", "fr-FR" => "Mon Produit"}

get_all_translations(entity_data)

Gets translations for all languages in a record.

Returns a map of language codes to their merged data. For flat records, returns the data under the primary language key.

Examples

iex> get_all_translations(record)
%{
  "en-US" => %{"name" => "Acme", "category" => "Tech"},
  "es-ES" => %{"name" => "Acme España", "category" => "Tech"}
}

get_by_slug(entity_uuid, slug, opts \\ [])

Gets a single entity data record by entity and slug.

Returns the record if found, nil otherwise.

Examples

iex> PhoenixKitEntities.EntityData.get_by_slug(entity_uuid, "acme-corporation")
%PhoenixKitEntities.EntityData{}

iex> PhoenixKitEntities.EntityData.get_by_slug(entity_uuid, "invalid")
nil

get_data!(id, opts \\ [])

Alias for get!/2 for consistency with LiveView naming.

get_data_stats(entity_uuid \\ nil)

Gets statistical data about entity data records.

total_records is the count of non-trashed records (the visible total). trashed_records is reported separately so the admin can surface the trash-bin badge.

Examples

iex> PhoenixKitEntities.EntityData.get_data_stats()
%{
  total_records: 150,
  published_records: 120,
  draft_records: 25,
  archived_records: 5,
  trashed_records: 3
}

get_raw_translation(entity_data, lang_code)

Gets the raw (non-merged) data for a specific language.

For secondary languages, returns only the override fields (not merged with primary). Useful for seeing which fields have explicit translations.

Examples

iex> get_raw_translation(record, "es-ES")
%{"name" => "Acme España"}

get_title_translation(entity_data, lang_code)

Gets the title translation for a specific language.

Reads from data[lang]["_title"] (unified JSONB storage). Falls back to the old metadata["translations"] location for unmigrated records, and finally to the title column.

Examples

iex> get_title_translation(record, "en-US")
"My Product"

iex> get_title_translation(record, "es-ES")
"Mi Producto"

get_translation(entity_data, lang_code)

Gets the data fields for a specific language, merged with primary language defaults.

For multilang records, returns Map.merge(primary_data, language_overrides). For flat (non-multilang) records, returns the data as-is.

Examples

iex> get_translation(record, "es-ES")
%{"name" => "Acme España", "category" => "Tech"}

iex> get_translation(flat_record, "en-US")
%{"name" => "Acme", "category" => "Tech"}

list_all(opts \\ [])

Returns all entity data records ordered by creation date.

Trashed records are excluded by default. Pass include_trashed: true to return them too — used by admin trash views and reverse-reference checks.

Examples

iex> PhoenixKitEntities.EntityData.list_all()
[%PhoenixKitEntities.EntityData{}, ...]

list_all_data(opts \\ [])

Alias for list_all/1 for consistency with LiveView naming.

list_by_entity(entity_uuid, opts \\ [])

Returns all entity data records for a specific entity.

Trashed records are excluded by default. Pass include_trashed: true to return them too.

Examples

iex> PhoenixKitEntities.EntityData.list_by_entity(entity_uuid)
[%PhoenixKitEntities.EntityData{}, ...]

list_by_entity_and_status(entity_uuid, status, opts \\ [])

Returns entity data records filtered by entity and status.

Examples

iex> PhoenixKitEntities.EntityData.list_by_entity_and_status(entity_uuid, "published")
[%PhoenixKitEntities.EntityData{status: "published"}, ...]

list_data_by_entity(entity_uuid, opts \\ [])

Alias for list_by_entity/2 for consistency with LiveView naming.

list_data_by_status(status, opts \\ [])

Alias for filter_by_status/2 for consistency with LiveView naming.

list_trashed_by_entity(entity_uuid, opts \\ [])

@spec list_trashed_by_entity(
  binary(),
  keyword()
) :: [t()]

Returns trashed records for a specific entity, ordered by most recently updated (the default trash-bin view).

Examples

iex> PhoenixKitEntities.EntityData.list_trashed_by_entity(entity_uuid)
[%PhoenixKitEntities.EntityData{status: "trashed"}, ...]

move_to_position(record, new_position)

Moves a record to a specific position within its entity, shifting other records.

Records between the old and new positions are shifted up or down by 1 to make room. This is the operation that a drag-and-drop UI would call.

Examples

iex> move_to_position(record, 3)
:ok

next_position(entity_uuid)

Gets the next available position for an entity's data records.

Examples

iex> next_position(entity_uuid)
6

public_alternates(entity, record, opts \\ [])

@spec public_alternates(map(), map(), keyword()) :: %{
  canonical: String.t(),
  alternates: [%{locale: String.t(), href: String.t()}]
}

Returns hreflang alternates and a canonical URL for a record across all enabled languages.

When the same record serves at both the unprefixed primary-language URL (/products/my-item) and a per-locale prefixed URL (/es/products/mi-item), search engines can index both as duplicate content. Use this helper to emit <link rel="alternate" hreflang="..."> and <link rel="canonical"> tags alongside public_path/3 so the duplication is declared rather than competing.

Output shape

%{
  canonical: "https://site.com/products/my-item",
  alternates: [
    %{locale: "en", href: "https://site.com/products/my-item"},
    %{locale: "es", href: "https://site.com/es/products/mi-item"},
    %{locale: "x-default", href: "https://site.com/products/my-item"}
  ]
}

Locales are emitted as base codes ("en", "es") — matching the locale prefix policy used in public_path/3 and Google's hreflang docs (which recommend xx over xx-XX unless region targeting is required).

Options

  • :base_url — explicit absolute URL prefix (e.g. "https://shop.example.com"). Falls back to the site_url setting, then "" (which yields path-only href values — useful in tests but not in production HTML).
  • :routes_cache — pre-built cache from UrlResolver.build_routes_cache/0 (avoid rebuilding per call when emitting many records).
  • :primary_locale — overrides the locale considered "canonical". Defaults to Multilang.primary_language/0 with rescue fallback to the first enabled locale.

When the Multilang module is unavailable or only one language is enabled, the result has a single canonical entry and no :alternates.

public_path(entity, record, opts \\ [])

@spec public_path(map(), map(), keyword()) :: String.t()

Returns a public path for a record, respecting locale and the configured URL pattern.

URL pattern resolution chain (shared with PhoenixKitEntities.SitemapSource):

  1. entity.settings["sitemap_url_pattern"]
  2. Router introspection (via PhoenixKit.Modules.Sitemap.RouteResolver)
  3. Per-entity setting sitemap_entity_<name>_pattern
  4. Global pattern setting sitemap_entities_pattern
  5. Fallback /<entity_name>/:slug

Locale prefix policy (matches PhoenixKit.Utils.Routes.path/2):

  • :locale omitted or nil → no prefix
  • Single-language mode → no prefix
  • Primary language → no prefix (default locale served at unprefixed URL)
  • Other locales → prefixed with the base code (/es/..., /ru/...)

Slug resolution:

  • When :locale is given and the record has a secondary-language slug override stored as data[locale]["_slug"], that override is substituted for :slug in the pattern. Otherwise the primary record.slug is used (falling back to the UUID when slug is nil).

Options

  • :locale — locale code (dialect like "es-ES" or base "es"). Omit to skip prefixing.
  • :routes_cache — pre-built cache from UrlResolver.build_routes_cache/0 (for batches).

Examples

iex> EntityData.public_path(entity, record)
"/products/my-item"

iex> EntityData.public_path(entity, record, locale: "es-ES")
"/es/products/my-item"

iex> EntityData.public_path(entity, record, locale: "en-US")  # primary language
"/products/my-item"

public_url(entity, record, opts \\ [])

@spec public_url(map(), map(), keyword()) :: String.t()

Returns a full public URL for a record by prepending a base URL to public_path/3.

Options

  • :base_url — explicit base (e.g. "https://site.com"). Falls back to the site_url setting, then an empty string.
  • :locale — forwarded to public_path/3.
  • :routes_cache — forwarded to public_path/3.

Examples

iex> EntityData.public_url(entity, record, base_url: "https://shop.example.com")
"https://shop.example.com/products/my-item"

published_records(entity_uuid, opts \\ [])

Gets all published records for a specific entity.

Examples

iex> PhoenixKitEntities.EntityData.published_records(entity_uuid)
[%PhoenixKitEntities.EntityData{status: "published"}, ...]

remove_translation(entity_data, lang_code)

Removes all data for a specific language from a record.

Cannot remove the primary language. Returns {:error, :cannot_remove_primary} if the primary language is targeted.

Examples

iex> remove_translation(record, "es-ES")
{:ok, %EntityData{}}

iex> remove_translation(record, "en-US")
{:error, :cannot_remove_primary}

reorder(entity_uuid, ordered_uuids, opts \\ [])

Reorders all records for an entity based on a list of UUIDs in the desired order.

This is the full reorder operation — takes a list of UUIDs representing the new order and assigns positions 1, 2, 3, ... accordingly.

Examples

iex> reorder(entity_uuid, ["uuid3", "uuid1", "uuid2"])
:ok

resolve_language(record, lang_code)

@spec resolve_language(t(), String.t()) :: t()

Resolves translated fields on an entity data record for a given language.

Resolves the title from _title in the language's data, and replaces the data field with the merged language data (primary as base + overrides).

For the primary language or flat (non-multilang) data, the struct is returned with the primary language data resolved. When no translation exists for a field, the primary language value is used as fallback.

Examples

iex> resolve_language(record, "es-ES")
%EntityData{title: "Mi Producto", data: %{"name" => "Acme España", ...}}

iex> resolve_language(record, "en-US")  # primary language
%EntityData{title: "My Product", data: %{"name" => "Acme", ...}}

resolve_languages(records, lang_code)

@spec resolve_languages([t()], String.t()) :: [t()]

Resolves translations on a list of entity data records.

Examples

iex> resolve_languages(records, "es-ES")
[%EntityData{title: "Mi Producto"}, ...]

restore_from_trash(entity_data, opts \\ [])

@spec restore_from_trash(
  t(),
  keyword()
) :: {:ok, t()} | {:error, :not_trashed | Ecto.Changeset.t()}

Restores a trashed entity data record by setting its status back to "published".

Returns {:error, :not_trashed} if the record isn't currently trashed — this is a guardrail against re-publishing arbitrary records via the trash-restore path. Use update/2 for general status changes.

Examples

iex> EntityData.restore_from_trash(trashed_record, actor_uuid: admin.uuid)
{:ok, %EntityData{status: "published"}}

search_by_title(search_term)

Searches entity data records by title.

Examples

iex> PhoenixKitEntities.EntityData.search_by_title("Acme")
[%PhoenixKitEntities.EntityData{}, ...]

iex> PhoenixKitEntities.EntityData.search_by_title("Acme", entity_uuid)
[%PhoenixKitEntities.EntityData{}, ...]

iex> PhoenixKitEntities.EntityData.search_by_title("Acme", entity_uuid, lang: "es")
[%PhoenixKitEntities.EntityData{}, ...]

search_by_title(search_term, entity_uuid, opts \\ [])

search_data(search_term)

Alias for search_by_title for consistency with LiveView naming.

search_data(search_term, entity_uuid, opts \\ [])

secondary_slug_exists?(entity_uuid, lang_code, slug, exclude_record_uuid)

Checks if a secondary language slug exists for another record within the same entity.

Queries the JSONB data column for data->lang_code->>'_slug' matches. Used for uniqueness checks on translated slugs.

set_title_translation(entity_data, lang_code, title)

Sets the title translation for a specific language.

Stores _title in the JSONB data column using put_language_data. For the primary language, also updates the title DB column.

Examples

iex> set_title_translation(record, "es-ES", "Mi Producto")
{:ok, %EntityData{}}

iex> set_title_translation(record, "en-US", "My Product")
{:ok, %EntityData{}}

set_translation(entity_data, lang_code, field_data)

Sets the data translation for a specific language on a record.

For the primary language, stores all fields. For secondary languages, only stores fields that differ from primary (overrides). Persists to the database.

Examples

iex> set_translation(record, "es-ES", %{"name" => "Acme España"})
{:ok, %EntityData{}}

iex> set_translation(record, "en-US", %{"name" => "Acme Corp", "category" => "Tech"})
{:ok, %EntityData{}}

trash(entity_data)

trash(entity_data, opts)

@spec trash(
  t(),
  keyword()
) :: {:ok, t()} | {:error, :already_trashed | Ecto.Changeset.t()}

Soft-deletes an entity data record by setting its status to "trashed".

The row remains in the database so any parent-app FK references stay valid — historical orders, audit logs, etc. continue to resolve. The record is hidden from default list_* queries; use list_trashed_by_entity/2 to surface it for the admin trash view.

Logs entity_data.trashed activity. Refuses with {:error, :already_trashed} if the record is already trashed.

Examples

iex> EntityData.trash(record, actor_uuid: admin.uuid)
{:ok, %EntityData{status: "trashed"}}

trashed_count(entity_uuid)

@spec trashed_count(binary()) :: non_neg_integer()

Counts trashed records for an entity. Drives the trash-bin badge.

update(entity_data, attrs, opts \\ [])

@spec update(t(), map(), keyword()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}

Updates an entity data record.

Examples

iex> PhoenixKitEntities.EntityData.update(record, %{title: "Updated"})
{:ok, %PhoenixKitEntities.EntityData{}}

iex> PhoenixKitEntities.EntityData.update(record, %{title: ""})
{:error, %Ecto.Changeset{}}

update_data(entity_data, attrs, opts \\ [])

@spec update_data(t(), map(), keyword()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}

Alias for update/2 for consistency with LiveView naming.

update_position(entity_data, position)

Updates the position of a single entity data record.

Examples

iex> update_position(record, 3)
{:ok, %EntityData{position: 3}}