PhoenixKitCatalogue.Extension behaviour (PhoenixKitCatalogue v0.44.2)

Copy Markdown View Source

Behaviour for a module that adds a "Shop"-style section to the catalogue item and/or category forms and owns a namespace under data (spec §2 principle 8, §4 row C4).

Catalogue never names an implementer — discovery is duck-typed through PhoenixKit.ModuleRegistry, the same pattern PhoenixKitAI.Translatable uses for ai_translatables/0 (see PhoenixKitCatalogue.Extensions). A host module (e.g. phoenix_kit_ecommerce) contributes an implementer via its own catalogue_extensions/0 callback; it structurally implements this behaviour without declaring @behaviour PhoenixKitCatalogue.Extension, so it isn't forced to depend on phoenix_kit_catalogue at compile time.

All callbacks except key/0 and enabled?/0 are optional — an extension can own a namespace and render on only one of the two forms, or hold data without rendering anything at all.

Summary

Types

One column this extension contributes to the catalogue admin's configurable item/category tables (item_columns/0 / category_columns/0) — the "Columns" modal on CatalogueDetailLive.

Callbacks

Same as cast_item/2, for the catalogue category form.

Validates and shapes the item form's submap for this extension's namespace (params[key()], a plain map) into the value to store under data[key()]. current is the namespace's existing value (data[key()] before this submission, or %{}).

Same as item_columns/0, for the catalogue category table (:detail_categories).

Same as item_section/1, for the catalogue category form (:category instead of :item).

The namespace a copied item or category should carry (Duplicate, including a whole catalogue's copy). data is this extension's current data[key()]; return the map the copy stores, or nil for none. Drop anything that must stay unique to the original — an external system's id for the product is the case this exists for. Without the callback the namespace is copied unchanged. Called even while the extension is disabled; a raise, throw or exit, or a return that is neither a map nor nil, drops the namespace from the copy.

Whether the extension's section should render and its namespace be absorbed.

Extra columns for the catalogue item table's Columns modal (PhoenixKitCatalogue.Web.TableConfig's :detail_items scope). Off by default — an admin opts in exactly like any catalogue column.

Renders the extension's section inside the catalogue item form.

Namespace under data this extension owns, e.g. "ecommerce".

Types

column()

@type column() :: %{
  id: String.t(),
  label: (-> String.t()),
  render: (record :: map() -> Phoenix.LiveView.Rendered.t())
}

One column this extension contributes to the catalogue admin's configurable item/category tables (item_columns/0 / category_columns/0) — the "Columns" modal on CatalogueDetailLive.

  • :id — this extension's own identifier, unique among its own columns only. PhoenixKitCatalogue.Extensions.columns/1 namespaces it under key/0 ("<key>:<id>") before it ever reaches the catalogue, so it cannot collide with a catalogue column or another extension's.

  • :label — zero-arity fn returning the display label (a fn so it resolves in the request's current locale, matching PhoenixKitCatalogue.Web.TableConfig's own columns).

  • :render — one-arity fn (record) -> Phoenix.LiveView.Rendered.t(); record is the item or category struct the table is currently rendering a row for. Return the cell's inner content only — the catalogue supplies the surrounding table cell.

    A raise, throw, exit, or a return value with no Phoenix.HTML.Safe representation degrades to an empty cell instead of breaking the page — see PhoenixKitCatalogue.Extensions.columns/1. Never give the returned markup an id scoped only by record: the SAME call renders the row's desktop-table cell AND its mobile-card fact, both present in the DOM on one page load (CSS/JS, not the server, decides which is visible) — an id that repeats across them is invalid HTML. Use a data-* attribute instead if the cell needs to be addressable.

Callbacks

cast_category(params, current)

(optional)
@callback cast_category(params :: map(), current :: map()) ::
  {:ok, map()} | {:error, [{atom(), String.t()}]}

Same as cast_item/2, for the catalogue category form.

cast_item(params, current)

(optional)
@callback cast_item(params :: map(), current :: map()) ::
  {:ok, map()} | {:error, [{atom(), String.t()}]}

Validates and shapes the item form's submap for this extension's namespace (params[key()], a plain map) into the value to store under data[key()]. current is the namespace's existing value (data[key()] before this submission, or %{}).

category_columns()

(optional)
@callback category_columns() :: [column()]

Same as item_columns/0, for the catalogue category table (:detail_categories).

category_section(assigns)

(optional)
@callback category_section(assigns :: map()) :: Phoenix.LiveView.Rendered.t()

Same as item_section/1, for the catalogue category form (:category instead of :item).

duplicate_data(kind, data)

(optional)
@callback duplicate_data(kind :: :item | :category, data :: map()) :: map() | nil

The namespace a copied item or category should carry (Duplicate, including a whole catalogue's copy). data is this extension's current data[key()]; return the map the copy stores, or nil for none. Drop anything that must stay unique to the original — an external system's id for the product is the case this exists for. Without the callback the namespace is copied unchanged. Called even while the extension is disabled; a raise, throw or exit, or a return that is neither a map nor nil, drops the namespace from the copy.

enabled?()

@callback enabled?() :: boolean()

Whether the extension's section should render and its namespace be absorbed.

item_columns()

(optional)
@callback item_columns() :: [column()]

Extra columns for the catalogue item table's Columns modal (PhoenixKitCatalogue.Web.TableConfig's :detail_items scope). Off by default — an admin opts in exactly like any catalogue column.

item_section(assigns)

(optional)
@callback item_section(assigns :: map()) :: Phoenix.LiveView.Rendered.t()

Renders the extension's section inside the catalogue item form.

assigns carries :form, :item, :data (the item's data map, so data[key()] is the extension's own current values) and :current_language.

key()

@callback key() :: String.t()

Namespace under data this extension owns, e.g. "ecommerce".