PhoenixKitEcommerce.NamePrefix (PhoenixKitEcommerce v0.5.11)

Copy Markdown View Source

Hides a redundant vocabulary prefix from storefront-displayed category and product names — "3D Printed Costume Masks" -> "Costume Masks" — driven by the shop_name_prefixes setting: a comma-separated list of prefixes (a shop may have more than one), empty by default so no existing install changes behaviour.

Display-time only

No STORED value is ever rewritten — not a product/category's own name, and not a cart or order line's snapshotted product_title/ "name". Category and product names in this shop come from Shopify collection/product titles and are re-synced from Shopify on every sync run — a persisted rename would either be silently overwritten on the next sync, or would have to be excluded from sync, which then hides real upstream renames. A cart/order line's snapshot exists so a later price or catalogue change can't retroactively alter what the shopper was shown (the same principle as its snapshotted price); rewriting that stored string would defeat the snapshot for no reason, since a pure display-time strip achieves the same visible result.

strip/1 is a pure string function with no persistence and no Shopify reach, called from two places: PhoenixKitEcommerce. Translations.get_display/3 (for a live %Product{}/%Category{} read on a storefront page) and directly, on a cart/order line's snapshotted title string, from the storefront pages that render one (cart, checkout, order confirmation, the customer's own order history) — see Translations.get_display/3's doc for why the snapshot itself stays untouched while its on-page rendering doesn't. Nothing in the Shopify diff/apply path (PhoenixKitEcommerce.Shopify.ProductDiff, PhoenixKitEcommerce.Shopify.CollectionSync), an admin edit form, or any write path calls it — all of those read or persist the raw stored value exactly as before.

A library cannot ship one shop's vocabulary, so this is a setting rather than a hardcoded literal — read through this wrapper, never directly, tolerating a malformed stored value by falling back to the safe default (no prefixes, i.e. no stripping) rather than raising.

Summary

Functions

The configured prefixes to hide, trimmed and with blanks dropped.

The separators strip/1 consumes after a matched prefix, so a test can iterate the real list rather than a second, independently-maintained copy that could silently drop an entry the source still recognizes.

The setting key, so the settings UI and tests do not re-spell it.

Strips the LONGEST configured prefix that matches the START of name, case-insensitively, consuming any following whitespace and then an optional -///|/: separator plus its whitespace.

strip/1 with the configured prefix list already in hand — the form every per-row render should use (see strip/1). prefixes is what prefixes/0 returns; anything else (a malformed value) is treated as "no prefixes", so the name renders exactly as stored.

Functions

prefixes()

@spec prefixes() :: [String.t()]

The configured prefixes to hide, trimmed and with blanks dropped.

separators()

@spec separators() :: [String.t()]

The separators strip/1 consumes after a matched prefix, so a test can iterate the real list rather than a second, independently-maintained copy that could silently drop an entry the source still recognizes.

setting_key()

@spec setting_key() :: String.t()

The setting key, so the settings UI and tests do not re-spell it.

strip(name)

@spec strip(any()) :: any()

Strips the LONGEST configured prefix that matches the START of name, case-insensitively, consuming any following whitespace and then an optional -///|/: separator plus its whitespace.

Longest, not first-configured: with "3D, 3D Printed" configured, a first-match-wins rule would strip only "3D" from "3D Printed Costume Masks" and leave the dangling fragment "Printed Costume Masks". There is no shop-visible upside to first-match, so the more specific (longer) prefix always wins regardless of configuration order.

Leaves name untouched when:

  • no configured prefix matches
  • the prefix match isn't followed by whitespace, a separator, or the end of the string (so "3D Printedstuff" is never mangled into "stuff")
  • stripping would leave nothing (a category literally named "3D Printed" keeps its full name rather than rendering blank)

Any non-binary (nil included) passes through unchanged.

strip/1 reads the setting (prefixes/0) on every call — a cached read, but one that goes through core's settings-cache process, so a page rendering a hundred names serializes a hundred round-trips through it. A component rendering a LIST computes prefixes/0 once and calls strip/2 with it instead.

strip(name, prefixes)

@spec strip(any(), [String.t()] | any()) :: any()

strip/1 with the configured prefix list already in hand — the form every per-row render should use (see strip/1). prefixes is what prefixes/0 returns; anything else (a malformed value) is treated as "no prefixes", so the name renders exactly as stored.