PhoenixKitDocumentCreator.Taxonomy (PhoenixKitDocumentCreator v0.4.4)

Copy Markdown View Source

Context module for managing the Document Creator Category → Type hierarchy.

Provides CRUD, cascade soft-delete/restore, reorder, and picker helpers for Category and Type schemas. Modeled on PhoenixKitCatalogue.Catalogue.

Soft-Delete Cascade

  • trash_category/1 — soft-deletes the category, all its types, and all templates reachable via category_uuid or type_uuid. Records affected template uuids in the activity log so restore_category/1 can scope its restore precisely.
  • trash_type/1 — soft-deletes the type and templates whose type_uuid points at it.
  • Documents are never cascaded; they keep pointing at their (now trashed) category and remain usable.

PubSub

Every successful write broadcasts {:doc_taxonomy_changed, level, uuid} on the shared PhoenixKit PubSub. level is :category or :type. Consumers subscribe via PhoenixKit.PubSubHelper.subscribe/1.

Summary

Functions

Returns [{label, value}] for all active categories, ordered by position, preceded by a {"No category", nil} empty option.

Counts categories, applying the same :status filter semantics as list_categories/1. Counts in SQL instead of loading rows — use when only the number is needed (e.g. a "Trash (N)" badge).

Counts types for a category, applying the same :status filter semantics as list_types_for_category/2. Counts in SQL instead of loading rows.

Fetches a category by UUID. Returns nil if not found.

Fetches a category by UUID. Raises Ecto.NoResultsError if not found.

Fetches a type by UUID. Returns nil if not found.

Fetches a type by UUID. Raises Ecto.NoResultsError if not found.

Lists categories ordered by position then name.

Returns a list of {category, [types]} tuples for all active categories, ordered by position. Types within each category are ordered by position.

Lists types for a category ordered by position then name.

Permanently deletes a category and all its types from the database.

Permanently deletes a type from the database.

Reorders categories by assigning positions from the given ordered list.

Reorders types within a category by assigning positions from the given ordered list.

Restores a soft-deleted category.

Restores a soft-deleted type and templates trashed by its cascade.

Subscribes the caller to taxonomy change events.

Soft-deletes a category by setting its status to "deleted".

Soft-deletes a type by setting its status to "deleted".

Returns [{label, value}] for all active types within a category, ordered by position, preceded by a {"No type", nil} empty option.

Updates a category with the given attributes.

Updates a type with the given attributes.

Functions

category_options()

@spec category_options() :: [{String.t(), Ecto.UUID.t() | nil}]

Returns [{label, value}] for all active categories, ordered by position, preceded by a {"No category", nil} empty option.

Suitable for options_for_select/2. The empty option lets users clear the FK (which is nullable).

Drop-in replacement for the hard-coded category_options/0 in documents_live.ex.

count_categories(opts \\ [])

@spec count_categories(keyword()) :: non_neg_integer()

Counts categories, applying the same :status filter semantics as list_categories/1. Counts in SQL instead of loading rows — use when only the number is needed (e.g. a "Trash (N)" badge).

count_types_for_category(category_uuid, opts \\ [])

@spec count_types_for_category(
  Ecto.UUID.t(),
  keyword()
) :: non_neg_integer()

Counts types for a category, applying the same :status filter semantics as list_types_for_category/2. Counts in SQL instead of loading rows.

create_category(attrs, opts \\ [])

Creates a category.

Required attributes

  • :name — category name (1-255 chars)

Optional attributes

  • :description, :position (default 0), :status (default "active"), :data

create_type(attrs, opts \\ [])

Creates a type.

Required attributes

  • :name — type name (1-255 chars)
  • :category_uuid — parent category

Optional attributes

  • :description, :position, :status, :data

get_category(uuid)

Fetches a category by UUID. Returns nil if not found.

get_category!(uuid)

Fetches a category by UUID. Raises Ecto.NoResultsError if not found.

get_type(uuid)

Fetches a type by UUID. Returns nil if not found.

get_type!(uuid)

Fetches a type by UUID. Raises Ecto.NoResultsError if not found.

list_categories(opts \\ [])

@spec list_categories(keyword()) :: [PhoenixKitDocumentCreator.Schemas.Category.t()]

Lists categories ordered by position then name.

Options

  • :status — when provided, returns only categories with this exact status (e.g. "active", "deleted"). When nil (default), returns all non-deleted categories.

list_category_tree()

Returns a list of {category, [types]} tuples for all active categories, ordered by position. Types within each category are ordered by position.

Useful for building grouped pickers in LiveViews.

list_types_for_category(category_uuid, opts \\ [])

@spec list_types_for_category(
  Ecto.UUID.t(),
  keyword()
) :: [PhoenixKitDocumentCreator.Schemas.Type.t()]

Lists types for a category ordered by position then name.

Options

  • :status — when provided, returns only types with this exact status. Defaults to non-deleted types.

permanently_delete_category(category, opts \\ [])

@spec permanently_delete_category(
  PhoenixKitDocumentCreator.Schemas.Category.t(),
  keyword()
) :: {:ok, PhoenixKitDocumentCreator.Schemas.Category.t()} | {:error, term()}

Permanently deletes a category and all its types from the database.

Relies on ON DELETE CASCADE for child types and ON DELETE SET NULL for template/document FK columns.

permanently_delete_type(type, opts \\ [])

@spec permanently_delete_type(
  PhoenixKitDocumentCreator.Schemas.Type.t(),
  keyword()
) :: {:ok, PhoenixKitDocumentCreator.Schemas.Type.t()} | {:error, term()}

Permanently deletes a type from the database.

Relies on ON DELETE SET NULL for template/document FK columns.

reorder_categories(ordered_uuids, opts \\ [])

@spec reorder_categories(
  [Ecto.UUID.t()],
  keyword()
) :: :ok | {:error, term()}

Reorders categories by assigning positions from the given ordered list.

Each uuid in the list gets position = index. UUIDs not present keep their existing positions.

reorder_types(category_uuid, ordered_uuids, opts \\ [])

@spec reorder_types(Ecto.UUID.t(), [Ecto.UUID.t()], keyword()) ::
  :ok | {:error, term()}

Reorders types within a category by assigning positions from the given ordered list.

restore_category(category, opts \\ [])

Restores a soft-deleted category.

Cascades in one transaction:

  1. Category status → "active"
  2. Types whose uuids were recorded in the trash activity log status → "active" (only those, so types the user had trashed manually before the cascade stay trashed)
  3. Templates whose uuids were recorded in the trash activity log status → "published" (only those, to avoid restoring manually trashed templates)

When PhoenixKit.Activity is not loaded (or no matching activity entry exists), cascade-trashed types and templates are not restored — they must be restored manually.

restore_type(type, opts \\ [])

Restores a soft-deleted type and templates trashed by its cascade.

subscribe()

@spec subscribe() :: :ok | {:error, term()}

Subscribes the caller to taxonomy change events.

trash_category(category, opts \\ [])

Soft-deletes a category by setting its status to "deleted".

Cascades in one transaction:

  1. Category status → "deleted"
  2. Its currently-active types status → "deleted"
  3. All templates reachable via category_uuid (directly) or via any of the category's type_uuid values → status → "trashed"

Documents are NOT cascaded.

Affected type and template uuids are stored in the activity log payload so restore_category/1 restores only what this cascade trashed — types the user had already trashed manually stay trashed.

trash_type(type, opts \\ [])

Soft-deletes a type by setting its status to "deleted".

Cascades to templates whose type_uuid points at this type. Affected template uuids are stored in the activity log.

type_options(category_uuid)

@spec type_options(Ecto.UUID.t() | nil) :: [{String.t(), Ecto.UUID.t() | nil}]

Returns [{label, value}] for all active types within a category, ordered by position, preceded by a {"No type", nil} empty option.

Pass nil as category_uuid (or when no category is selected) to get only the empty option.

Suitable for options_for_select/2.

update_category(category, attrs, opts \\ [])

Updates a category with the given attributes.

update_type(type, attrs, opts \\ [])

Updates a type with the given attributes.