PhoenixKitEcommerce.ProductSource behaviour (PhoenixKitEcommerce v0.5.11)

Copy Markdown View Source

Behaviour for the storefront's product/category read path.

Two adapters implement it: PhoenixKitEcommerce.ProductSource.Legacy (today's phoenix_kit_shop_products/phoenix_kit_shop_categories tables, unchanged) and, once phoenix_kit_catalogue is present, PhoenixKitEcommerce.ProductSource.Catalogue (reads catalogue items and returns hand-built %Product{}/%Category{} view-structs so the facade, guards, CartItem, Options and SeoHelpers need no changes).

current/0 picks the adapter at runtime; PhoenixKitEcommerce's public read functions delegate to it so callers never choose an adapter themselves.

Summary

Functions

Returns the adapter module for the currently active product source.

Callbacks

aggregate_filter_values(keyword)

@callback aggregate_filter_values(keyword()) :: map()

get_category(t, keyword)

@callback get_category(String.t(), keyword()) :: PhoenixKitEcommerce.Category.t() | nil

get_category_by_any_slug(t, keyword)

@callback get_category_by_any_slug(String.t(), keyword()) ::
  {:ok, PhoenixKitEcommerce.Category.t(), String.t()} | {:error, :not_found}

get_category_by_slug_localized(t, t, keyword)

@callback get_category_by_slug_localized(String.t(), String.t(), keyword()) ::
  {:ok, PhoenixKitEcommerce.Category.t()} | {:error, :not_found}

get_price_range_for(keyword)

@callback get_price_range_for(keyword()) :: {Decimal.t() | nil, Decimal.t() | nil}

get_product(t, keyword)

@callback get_product(String.t(), keyword()) :: PhoenixKitEcommerce.Product.t() | nil

get_product_by_any_slug(t, keyword)

@callback get_product_by_any_slug(String.t(), keyword()) ::
  {:ok, PhoenixKitEcommerce.Product.t(), String.t()} | {:error, :not_found}

get_product_by_slug_localized(t, t, keyword)

@callback get_product_by_slug_localized(String.t(), String.t(), keyword()) ::
  {:ok, PhoenixKitEcommerce.Product.t()} | {:error, :not_found}

list_categories(keyword)

@callback list_categories(keyword()) :: [PhoenixKitEcommerce.Category.t()]

list_products(keyword)

@callback list_products(keyword()) :: [PhoenixKitEcommerce.Product.t()]

list_products_by_ids(list)

@callback list_products_by_ids([String.t()]) :: [PhoenixKitEcommerce.Product.t()]

list_products_with_count(keyword)

@callback list_products_with_count(keyword()) ::
  {[PhoenixKitEcommerce.Product.t()], non_neg_integer()}

product_counts_by_category()

@callback product_counts_by_category() :: %{required(String.t()) => non_neg_integer()}

Functions

current()

Returns the adapter module for the currently active product source.

Catalogue only when phoenix_kit_catalogue is loaded AND the shop_product_source config key (phoenix_kit_shop_config) is "catalogue"; Legacy otherwise — including when the key is absent or the catalogue module isn't loaded, so a host without the optional phoenix_kit_catalogue dependency always gets Legacy regardless of the stored key.

Reads the config on every call rather than caching it here: PhoenixKitEcommerce.get_config/1 is a plain primary-key repo().get/2 against phoenix_kit_shop_config (no ETS/settings-cache layer sits in front of it — the key lives in the shop config table, not in PhoenixKit.Settings, and every writer, the test suite included, updates that row directly), so this is one point read per facade call. Accepted so that the switch takes effect without a restart: a per-process memo would go stale in a long-lived LiveView process, and a node-wide cache has no invalidation hook because the row is written without going through this module. The Code. ensure_loaded?/1 guard runs FIRST so a host without the optional phoenix_kit_catalogue dependency never pays for the read at all.