CoolCMS API

ProductCategoryList extends BaseComponent
in package
uses InitializationTrait, CategoryRailTrait, PreferredCategoryCutTrait

Renders the sidebar category tree.

A nested-set tree built with TreeStructure; the active path comes from lft/rgt containment and links use {plink} to :Front:Eshop:ProductSet:list (route mask parameter category). $routerDispatcher is inherited from BaseComponent (filled by setup() from the presenter); it is not constructor-injected because that would collide with the non-readonly parent property.

Table of Contents

Constants

CATEGORY_RAIL_STYLES  : array<string|int, mixed> = ['Style_2', 'Style_4', 'Style_5', 'Style_6']
Styles that can be drawn from the shared template.
CATEGORY_RAIL_TEMPLATE_DIR  : string = __DIR__ . '/../../../Base/Front/Components/Cate...
Shared templates of {@see CATEGORY_RAIL_STYLES} for all modules.

Properties

$container  : Container
$routerDispatcher  : RouterDispatcher
$showAll  : bool
Expands the preferred list to all root categories.
$translator  : ITranslator
$appContext  : AppContext
$componentParameters  : array<string, mixed>
$currencyContext  : CurrencyContext|null
$deviceDetector  : DeviceDetector|null
$languageContext  : LanguageContext|null
$localizationContext  : LocalizationContext|null
$settingContext  : SettingContext
$themeContext  : ThemeContext|null
$translatorDomain  : string|null
$categoryRailAllCategories  : array<string|int, object>
Flat tree (`lft`/`rgt`) filled in `InitializationTrait::initializeRender()`; the base of the subtree sum in {@see categoryRailCount()}.
$categoryRailCountMap  : array<int, int>|null
Memoized {@see ProductCategoryManager::fetchProductCounts()}: one bridge call per render, although `categoryRailCount()` is called for every row separately.
$categoryRailWithCount  : bool
Whether the category list carries the product count (`count` in the rows of shared templates).
$categoryTree  : array<int, array<string, mixed>>|null
Tree from {@see TreeStructure::prepareForTemplate()}; never mutated, assigned to the template by value.
$loadedCategories  : array<string|int, BaseEntity>|null
Categories loaded once per component instance (the query does not depend on render arguments).
$manager  : ProductCategoryManager

Methods

__call()  : mixed
Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
__construct()  : mixed
createComponent()  : IComponent|null
getAppContext()  : AppContext
getComponentParameter()  : mixed
Returns a component parameter, or null when it is not set.
getContainer()  : Container
getCurrencyContext()  : CurrencyContext|null
getLanguageContext()  : LanguageContext|null
getLocalizationContext()  : LocalizationContext|null
getRouterDispatcher()  : RouterDispatcher
getSettingContext()  : SettingContext
getSettings()  : array<int, array<string, mixed>>
getThemeContext()  : ThemeContext|null
hasServiceByType()  : bool
Returns whether the container has a service of the given type.
initializeAllContexts()  : void
injectDeviceDetector()  : void
onCreate()  : void
render()  : void
renderHorizontalCompact()  : void
Renders a row tile: icon on the left, name next to it.
renderLink()  : void
Renders a row of links: the text variant of {@see renderHorizontalCompact()} with the count in parentheses after the name (computed, see {@see categoryRailCount()}).
renderTile()  : void
Renders a tile: a flat signpost of root categories with the icon centered on top and no subcategories (see {@see renderTileGroup()} for those).
renderTileGroup()  : void
Renders a tile with a group of subcategories: a colored area with an icon and the first `$subItemCount` children (default 3), below it the category name and the subtree product count (computed, see {@see categoryRailCount()}).
setAppContext()  : $this
setComponentParameter()  : void
Sets a component parameter.
setCurrencyContext()  : $this
setLanguageContext()  : $this
setLocalizationContext()  : $this
setSettingContext()  : $this
setThemeContext()  : $this
setup()  : void
setupAppContext()  : void
typedComponent()  : T
Returns a subcomponent with a verified type.
applyPreferredCut()  : void
Cuts `sortedItems` to the preferred roots and fills `preferredOnly`, `showAll` and `hiddenCount` for the template. Called after the current category context is resolved (it must search the whole tree) and before the rail rows are built from `sortedItems`.
buildCategoryRailParent()  : array{name: string, href: string}|null
Returns the way one level up: the first item of the strip when the current category has a parent.
buildCategoryRailRows()  : array<int, array{id: int|null, name: string, href: string, count: int|null, iconPath: string|null, asset: object|null, iconClass: string|null, active: bool, children: array}>
Converts tree nodes into plain arrays for the template.
categoryRailCount()  : int|null
Returns the number of products in the category including its subtree.
categoryRailHref()  : string
Returns the category list target for the shared `Style_4` and `Style_5` styles ({@see CategoryRailTrait}).
categoryRailTemplateDir()  : string
Returns the directory to load the template from.
frontPresenter()  : BasePresenter|null
Returns the front presenter the component is attached to (null outside the front end or when detached).
getTranslator()  : ITranslator
prepareCategoryRail()  : void
Fills `$railRows` and `$railParent` for the shared templates.
setTemplateFile()  : void
setTranslator()  : self
categoryRailCurrentId()  : int|null
categoryRailIconPath()  : string|null
Returns the legacy icon path, or `null` when unusable.
categoryRailName()  : string
createMultiplierComponent()  : Multiplier<string|int, IComponent>
Creates a multiplier for the component.
createNewComponentInstance()  : IComponent|null
Creates a new component instance without adding it to the presenter; used by the multiplier.
createStandardComponent()  : IComponent|null
Creates a standard component (not a multiplier).
getActivePath()  : array<int, int>
getCurrentCategory()  : ProductCategory|null
initializeRender()  : void

Constants

CATEGORY_RAIL_STYLES

Styles that can be drawn from the shared template.

private array<string|int, mixed> CATEGORY_RAIL_STYLES = ['Style_2', 'Style_4', 'Style_5', 'Style_6']

CATEGORY_RAIL_TEMPLATE_DIR

Shared templates of {@see CATEGORY_RAIL_STYLES} for all modules.

private string CATEGORY_RAIL_TEMPLATE_DIR = __DIR__ . '/../../../Base/Front/Components/CategoryList'

Properties

$showAll

Expands the preferred list to all root categories.

public bool $showAll = false
Attributes
#[Persistent]

$componentParameters

protected array<string, mixed> $componentParameters = []

component parameters set by setComponentParameter()

$translatorDomain

protected string|null $translatorDomain = 'front.eshop.components.productCategoryList'

$categoryRailAllCategories

Flat tree (`lft`/`rgt`) filled in `InitializationTrait::initializeRender()`; the base of the subtree sum in {@see categoryRailCount()}.

private array<string|int, object> $categoryRailAllCategories = []

NOTE: the type is deliberately array<object>, not ProductCategory[]: findAll() is generic and returns entities typed as BaseEntity, without guaranteed contiguous keys, as loose as the parameter of TreeStructure::prepareForTemplate().

all categories of the tree

$categoryRailCountMap

Memoized {@see ProductCategoryManager::fetchProductCounts()}: one bridge call per render, although `categoryRailCount()` is called for every row separately.

private array<int, int>|null $categoryRailCountMap = null

category id mapped to its own product count

$categoryRailWithCount

Whether the category list carries the product count (`count` in the rows of shared templates).

private bool $categoryRailWithCount = false

IMPORTANT: the default false means no count. The shared Style_4 and Style_5 templates render the count whenever it is above 0, so a hook returning counts always would show a badge in lists of older templates that did not ask for it. A template must request the count with a render argument, for example {multicontrol frontEshopProductCategoryList-tiles:Tile null, true}.

$categoryTree

Tree from {@see TreeStructure::prepareForTemplate()}; never mutated, assigned to the template by value.

private array<int, array<string, mixed>>|null $categoryTree = null

$loadedCategories

Categories loaded once per component instance (the query does not depend on render arguments).

private array<string|int, BaseEntity>|null $loadedCategories = null

Methods

__call()

Dispatches `renderXyz()` to `render('Xyz', ...$args)`.

public __call(string $name, array<string|int, mixed> $args) : mixed

Called only when the component has no renderXyz() method of its own.

Parameters
$name : string
$args : array<string|int, mixed>

createComponent()

public createComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

getComponentParameter()

Returns a component parameter, or null when it is not set.

public getComponentParameter(string $name) : mixed
Parameters
$name : string

getContainer()

public getContainer() : Container
Return values
Container

getSettings()

public getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>

hasServiceByType()

Returns whether the container has a service of the given type.

public hasServiceByType(string $className) : bool

The name is composed dynamically (module plus component), so class_exists() runs first, and a true result guarantees a valid type.

Parameters
$className : string
Tags
phpstan-assert-if-true

class-string $className

Return values
bool

render()

public render([string $style = 'Style_1' ][, int|null $subItemCount = null ][, bool $withCount = false ][, bool $preferredOnly = false ]) : void
Parameters
$style : string = 'Style_1'

template in Templates/Default/ without the extension

$subItemCount : int|null = null

how many children to list for styles that show them (Style_5); null means their default

$withCount : bool = false

print the subtree product count for categories (see $categoryRailWithCount)

$preferredOnly : bool = false

preferred categories only, plus "All categories" (PreferredCategoryCutTrait)

renderHorizontalCompact()

Renders a row tile: icon on the left, name next to it.

public renderHorizontalCompact([int|null $subItemCount = null ][, bool $withCount = false ]) : void

The layout comes from the shared .list--horizontal-compact variant that article and advert lists use too.

NOTE: it lists one level: inside a category its children with a link to the parent first, outside a category the roots.

Parameters
$subItemCount : int|null = null
$withCount : bool = false

Renders a row of links: the text variant of {@see renderHorizontalCompact()} with the count in parentheses after the name (computed, see {@see categoryRailCount()}).

public renderLink([int|null $subItemCount = null ][, bool $withCount = false ]) : void
Parameters
$subItemCount : int|null = null
$withCount : bool = false

renderTile()

Renders a tile: a flat signpost of root categories with the icon centered on top and no subcategories (see {@see renderTileGroup()} for those).

public renderTile([int|null $subItemCount = null ][, bool $withCount = false ][, bool $preferredOnly = false ]) : void

The count label uses the count key of the component domain (Style_2.latte). $preferredOnly lists only preferred categories plus "All categories" (PreferredCategoryCutTrait).

Parameters
$subItemCount : int|null = null
$withCount : bool = false
$preferredOnly : bool = false

renderTileGroup()

Renders a tile with a group of subcategories: a colored area with an icon and the first `$subItemCount` children (default 3), below it the category name and the subtree product count (computed, see {@see categoryRailCount()}).

public renderTileGroup([int|null $subItemCount = null ][, bool $withCount = false ]) : void
Parameters
$subItemCount : int|null = null
$withCount : bool = false

setComponentParameter()

Sets a component parameter.

public setComponentParameter(string $name, mixed $value) : void
Parameters
$name : string
$value : mixed

typedComponent()

Returns a subcomponent with a verified type.

public typedComponent(string $name, T> $type) : T

Use it instead of an inline @var above $this['x'] (which does not narrow the type) and instead of the generic IComponent that $this['x'] returns. See Form::typedComponent().

Parameters
$name : string
$type : T>
Tags
template
Return values
T

applyPreferredCut()

Cuts `sortedItems` to the preferred roots and fills `preferredOnly`, `showAll` and `hiddenCount` for the template. Called after the current category context is resolved (it must search the whole tree) and before the rail rows are built from `sortedItems`.

protected applyPreferredCut(bool $preferredOnly) : void
Parameters
$preferredOnly : bool

buildCategoryRailParent()

Returns the way one level up: the first item of the strip when the current category has a parent.

protected buildCategoryRailParent(mixed $parent) : array{name: string, href: string}|null
Parameters
$parent : mixed
Return values
array{name: string, href: string}|null —

null for a root category or a page outside categories; the item is then not rendered

buildCategoryRailRows()

Converts tree nodes into plain arrays for the template.

protected buildCategoryRailRows(array<string|int, mixed> $nodes[, int $childLimit = 0 ]) : array<int, array{id: int|null, name: string, href: string, count: int|null, iconPath: string|null, asset: object|null, iconClass: string|null, active: bool, children: array}>

NOTE: the name is neither trimmed nor escaped; striptags|trim stays in the template so the output is identical to the former per-module copies.

Parameters
$nodes : array<string|int, mixed>

nodes from TreeStructure::prepareForTemplate()

$childLimit : int = 0

how many children to list in a tile (0 = none, Style_4)

Return values
array<int, array{id: int|null, name: string, href: string, count: int|null, iconPath: string|null, asset: object|null, iconClass: string|null, active: bool, children: array}>

categoryRailCount()

Returns the number of products in the category including its subtree.

protected categoryRailCount(object $category) : int|null

Unlike Bazaar and Comcat, the eshop entity has no denormalized column, so the count is computed here instead of using the default in CategoryRailTrait.

The base is one bridge call for the whole tree (ProductCategoryManager::fetchProductCounts(), a map of category id to its own count), memoized in $categoryRailCountMap; several styles and rows of one render call this hook repeatedly and a per-category query would split the tree into N calls again. The subtree is summed in PHP from nested-set containment (lft within [target.lft, target.rgt]) over the flat $categoryRailAllCategories.

NOTE: a product in several categories of the same subtree (Product.categories is M:N) is counted more than once; the badge is an approximate number, not an exact match with the paged list, which uses a distinct query across the whole subtree.

Parameters
$category : object
Return values
int|null

categoryRailHref()

Returns the category list target for the shared `Style_4` and `Style_5` styles ({@see CategoryRailTrait}).

protected categoryRailHref(int $categoryId) : string

IMPORTANT: slug is the id of the target page ($targetSlug), not the current one; without it the link to another module ends with 404.

Parameters
$categoryId : int
Return values
string

categoryRailTemplateDir()

Returns the directory to load the template from.

protected categoryRailTemplateDir(string $ownDir, string $style) : string

A module's own copy (also a theme-specific one) takes precedence: whoever needs to deviate puts the file back into their Templates/…. The lookup mirrors BaseComponent::setTemplateFile() and only the final fallback returns the shared directory:

  • Templates/<Theme>/<Variant>/<style>.latte is a copy for the current theme;
  • Templates/Default/<style>.latte is a module copy for all themes;
  • otherwise the shared template in Base/Front/Components/CategoryList.

IMPORTANT: the lookup must never glob across all themes of the module. A theme-specific copy of another theme would be picked up while rendering a different theme, and when Templates/Default lacks the style the template file does not exist, which is a 500 for every theme without its own copy. Without a theme (CLI, no presenter) step one is skipped.

Parameters
$ownDir : string
$style : string
Return values
string

frontPresenter()

Returns the front presenter the component is attached to (null outside the front end or when detached).

protected frontPresenter() : BasePresenter|null
Return values
BasePresenter|null

getTranslator()

protected getTranslator() : ITranslator
Return values
ITranslator

prepareCategoryRail()

Fills `$railRows` and `$railParent` for the shared templates.

protected prepareCategoryRail(string $style[, int|null $childLimit = null ]) : void

IMPORTANT: call it right before setTemplateFile(). Bazaar still filters $sortedItems (preferred categories) in between, so an earlier call would hand the tiles the unfiltered tree.

Style_4 and Style_6 take the children of the current category ($currentBranch), or the roots when there is no context. null and [] differ: null means "not in a category, show roots", an empty array means "in a leaf category, nothing to show"; hence ??, not ?:.

  • Style_2 is Style_5 (tiles, roots/sortedItems) without the children list (childLimit 0): a flatter signpost, not a group with subcategories.
  • Style_6 is Style_4 (contextual nodes plus the way back $railParent) with a different template (a row of links instead of a tile with an icon).

NOTE: Bazaar, Comcat, Discussion and Blog have their own Templates/Default/Style_2.latte (categoryRailTemplateDir() prefers the module copy), so the shared Style_2 serves only the shop until the others switch to it.

Parameters
$style : string
$childLimit : int|null = null

setTemplateFile()

protected setTemplateFile(string $dir, string $filename) : void
Parameters
$dir : string
$filename : string

setTranslator()

protected setTranslator(ITranslator $translator) : self
Parameters
$translator : ITranslator
Return values
self

categoryRailCurrentId()

private categoryRailCurrentId() : int|null
Return values
int|null

categoryRailIconPath()

Returns the legacy icon path, or `null` when unusable.

private categoryRailIconPath(object $category) : string|null

IMPORTANT: getAssetPath() can return ".", a non-empty but unusable string; without this check <img src="."> renders a broken image. Entities without getAssetPath() (articles, discussions, shop) return null and are drawn through the asset renderer only.

Parameters
$category : object
Return values
string|null

categoryRailName()

private categoryRailName(object $category) : string
Parameters
$category : object
Return values
string

createMultiplierComponent()

Creates a multiplier for the component.

private createMultiplierComponent(string $name) : Multiplier<string|int, IComponent>
Parameters
$name : string
Return values
Multiplier<string|int, IComponent>

createNewComponentInstance()

Creates a new component instance without adding it to the presenter; used by the multiplier.

private createNewComponentInstance(string $baseName[, int|string|null $uniqueKey = null ]) : IComponent|null
Parameters
$baseName : string
$uniqueKey : int|string|null = null
Return values
IComponent|null

createStandardComponent()

Creates a standard component (not a multiplier).

private createStandardComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

getActivePath()

private getActivePath(array<string|int, object> $categories, ProductCategory|null $currentCategory) : array<int, int>
Parameters
$categories : array<string|int, object>
$currentCategory : ProductCategory|null
Return values
array<int, int>
On this page

Search results