ProductCategoryFormTrait
Edit/add form of an eshop category, modeled on {@see \App\UI\Admin\Blog\Presenters\Traits\Forms\CategoryFormTrait}.
Multilingual fields (name, URL, editor info, meta*); untranslated: parent category (tree), icon CSS
class and main photo. Unlike the Blog category it has no active/forFree/topedFirst, so no header
toggles. Nested-set indexes (lft/rgt/level) are handled by the Api: save() (insertTreeNode) for a new
category, moveSubtree() on a parent change. The skeleton comes from
BasePresenter::createForm().
Category parameters section: targeting rows (parameterRels) chosen per row. The checkbox "use
default parameters" (useDefaultParameters) sits above it and gates both the save (rels are not
overwritten for defaults) and the consumer (Api ProductCategoryManager::fetchParametersForProduct()).
Feed category names section: one text field feedCategoryName_<feedId> per feed
(addCategoryFeedFields()), written by the category-scoped bridge action
(FeedManager::saveCategoryRels()) outside the object-graph save of the category.
Table of Contents
Properties
- $categoryFeedList : array<int, Feed>|null
Methods
- createComponentEditForm() : Form
- parameterRowPicker() : PickerConfig
- Parameter picker for a `parameterRows` multiplier row; like `ParameterFieldsTrait::parameterRowPicker()` but without `submitButton` (the category has no dependent typed field to render after a choice, unlike set parameters). The closest precedent is `ProductCategoryPickerTrait::productCategoryMultiPicker()`, a plain M:N without a submit button.
- addCategoryFeedFields() : void
- One optional text field per feed. The label is the real feed name from the DB, not a translation key, hence `NotTranslate` (otherwise `PrefixedTranslator` would try to translate it and render the raw string with the domain; see `InvoiceFormTrait`).
- addCategoryParameterFields() : void
- Section "category parameters": multiplier `parameterRows` (hidden `parameterId` + text `parameterName`, filled by the Picker) and add/remove buttons with an AJAX redraw of the `categoryParameters` snippet (a literal name; Latte snippet names must be literals).
- addCategoryUseDefaultParametersField() : void
- Checkbox "use default parameters", above the parameters section (modeled on the Bazaar `CategoryFormTrait::addCategoryParameterFields()`). Checked (the default, also the DB default) means the category contributes the global default set to the parameter aggregation and the targeting editor (`categoryParametersWrapper`) is hidden (Nette `toggle()`, netteForms.js). Unchecked means own targeting and the editor is visible. Saved as a scalar by the object-graph save ({@see buildProductCategoryEntity()}); unlike `parameterRows` (meta channel) it is not part of `$meta`.
- addStructureFields() : void
- Structure: parent category (tree), icon CSS class and main photo.
- addTranslationFields() : void
- Multilingual fields: one set per language (`<field>_<langId>`), active per checkbox.
- applyCategoryFeedDefaults() : void
- GET prefill from {@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::getCategoryRels()} (category-scoped read), not via the `Feed::$categoryRels` collection: the category does not map that collection at all (see {@see saveCategoryFeedRels()}).
- applyCategoryParameterDefaults() : void
- GET defaults of the parameter rows via its own fetch {@see \App\UI\Admin\Eshop\Models\Managers\ProductCategoryManager::fetchParameterRelsForCategory()}, not via the non-owned collection `parameterRels` (sync runs through the meta channel and the collection is in Api `defaultSkipFields`). Ordered by the stored `order`; parameter names are looked up in a batch (one query, not N).
- buildCategoryParameterRelsMeta() : array<int, array{parameterId: int, order: int}>
- Builds `meta['parameterRels']` from the multiplier rows; called only for own targeting (`useDefaultParameters` unchecked, see the caller {@see saveProductCategoryForm()}). An empty array means remove-all (the last own-targeting row was removed), same as for set parameters and the Bazaar category.
- buildProductCategoryEntity() : ProductCategory
- categoryParameterDisplayName() : string
- Parameter name in the current language (falls back to the first non-empty one).
- categoryRowField() : mixed
- Safely reads a field from a multiplier row (ArrayHash|array).
- categoryRowInt() : int
- Row field as a positive int (0 when missing or non-numeric).
- collectImageMeta() : array<string, mixed>
- Builds `$meta` for the save: a new image (base64) and/or the delete flag.
- extractSubmittedCategoryParameterIds() : array<int, int>
- Ids of parameters from the submitted multiplier rows (`parameterRows[*][parameterId]`), deduplicated and positive. Non-row multiplier keys are skipped.
- isHrefUnique() : bool
- Validates href uniqueness in the given language against other categories (the edited category is not counted on edit). An empty href (nullable) is not checked.
- loadCategoryFeeds() : array<int, Feed>
- Feeds as the section axis, memoized so form build, prefill and `render()` hit the bridge once.
- loadCategoryParameterNames() : array<int, string>
- resolveCategoryParameterLabel() : string|null
- saveCategoryFeedRels() : bool
- Saves the category feed names through the category-scoped bridge action ({@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::saveCategoryRels()}), whose delete removes only `category_id = :categoryId`.
- saveProductCategoryForm() : bool
- setProductCategoryFormDefaults() : void
- suggestCategoryParameters() : array<int, array{id: int, label: string}>
- Suggests root active parameters by name, skipping rows already chosen (the picker suggest AJAX call carries the whole current form state in a POST, so `parameterRows` reflects the live rows, not just the stored state).
Properties
$categoryFeedList
private
array<int, Feed>|null
$categoryFeedList
= null
memoized feeds (the section axis), read by the form build and the template
Methods
createComponentEditForm()
protected
createComponentEditForm() : Form
Return values
FormparameterRowPicker()
Parameter picker for a `parameterRows` multiplier row; like `ParameterFieldsTrait::parameterRowPicker()` but without `submitButton` (the category has no dependent typed field to render after a choice, unlike set parameters). The closest precedent is `ProductCategoryPickerTrait::productCategoryMultiPicker()`, a plain M:N without a submit button.
protected
parameterRowPicker() : PickerConfig
Registered in pickers() of the presenter (ProductCategoryPresenter).
Return values
PickerConfigaddCategoryFeedFields()
One optional text field per feed. The label is the real feed name from the DB, not a translation key, hence `NotTranslate` (otherwise `PrefixedTranslator` would try to translate it and render the raw string with the domain; see `InvoiceFormTrait`).
private
addCategoryFeedFields(Form $form) : void
NOTE: setTranslator(null) on the label is not enough: Nette BaseControl::getLabel() always takes the
translator from the form ($this->getForm()->getTranslator()), not from the per-control override
(which only affects select items). It must be NotTranslate.
Parameters
- $form : Form
addCategoryParameterFields()
Section "category parameters": multiplier `parameterRows` (hidden `parameterId` + text `parameterName`, filled by the Picker) and add/remove buttons with an AJAX redraw of the `categoryParameters` snippet (a literal name; Latte snippet names must be literals).
private
addCategoryParameterFields(Form $form) : void
Parameters
- $form : Form
addCategoryUseDefaultParametersField()
Checkbox "use default parameters", above the parameters section (modeled on the Bazaar `CategoryFormTrait::addCategoryParameterFields()`). Checked (the default, also the DB default) means the category contributes the global default set to the parameter aggregation and the targeting editor (`categoryParametersWrapper`) is hidden (Nette `toggle()`, netteForms.js). Unchecked means own targeting and the editor is visible. Saved as a scalar by the object-graph save ({@see buildProductCategoryEntity()}); unlike `parameterRows` (meta channel) it is not part of `$meta`.
private
addCategoryUseDefaultParametersField(Form $form) : void
Parameters
- $form : Form
addStructureFields()
Structure: parent category (tree), icon CSS class and main photo.
private
addStructureFields(Form $form) : void
Parameters
- $form : Form
addTranslationFields()
Multilingual fields: one set per language (`<field>_<langId>`), active per checkbox.
private
addTranslationFields(Form $form) : void
Parameters
- $form : Form
applyCategoryFeedDefaults()
GET prefill from {@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::getCategoryRels()} (category-scoped read), not via the `Feed::$categoryRels` collection: the category does not map that collection at all (see {@see saveCategoryFeedRels()}).
private
applyCategoryFeedDefaults(Form $form) : void
Parameters
- $form : Form
applyCategoryParameterDefaults()
GET defaults of the parameter rows via its own fetch {@see \App\UI\Admin\Eshop\Models\Managers\ProductCategoryManager::fetchParameterRelsForCategory()}, not via the non-owned collection `parameterRels` (sync runs through the meta channel and the collection is in Api `defaultSkipFields`). Ordered by the stored `order`; parameter names are looked up in a batch (one query, not N).
private
applyCategoryParameterDefaults(Form $form) : void
Parameters
- $form : Form
buildCategoryParameterRelsMeta()
Builds `meta['parameterRels']` from the multiplier rows; called only for own targeting (`useDefaultParameters` unchecked, see the caller {@see saveProductCategoryForm()}). An empty array means remove-all (the last own-targeting row was removed), same as for set parameters and the Bazaar category.
private
buildCategoryParameterRelsMeta(ArrayHash<string|int, mixed> $values) : array<int, array{parameterId: int, order: int}>
order is the row order in the form. required/useInSearch/group are not exposed in the UI; the Api
sync trait defaults them to false/false/null.
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
array<int, array{parameterId: int, order: int}>buildProductCategoryEntity()
private
buildProductCategoryEntity(ArrayHash<string|int, mixed> $values) : ProductCategory
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
ProductCategorycategoryParameterDisplayName()
Parameter name in the current language (falls back to the first non-empty one).
private
categoryParameterDisplayName(Parameter $parameter) : string
Parameters
- $parameter : Parameter
Return values
stringcategoryRowField()
Safely reads a field from a multiplier row (ArrayHash|array).
private
categoryRowField(mixed $row, string $key) : mixed
Parameters
- $row : mixed
- $key : string
categoryRowInt()
Row field as a positive int (0 when missing or non-numeric).
private
categoryRowInt(mixed $row, string $key) : int
Parameters
- $row : mixed
- $key : string
Return values
intcollectImageMeta()
Builds `$meta` for the save: a new image (base64) and/or the delete flag.
private
collectImageMeta(ArrayHash<string|int, mixed> $values) : array<string, mixed>
The Api makes an Asset (variants) from it and sets the FK to the main image.
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
array<string, mixed>extractSubmittedCategoryParameterIds()
Ids of parameters from the submitted multiplier rows (`parameterRows[*][parameterId]`), deduplicated and positive. Non-row multiplier keys are skipped.
private
extractSubmittedCategoryParameterIds() : array<int, int>
Return values
array<int, int>isHrefUnique()
Validates href uniqueness in the given language against other categories (the edited category is not counted on edit). An empty href (nullable) is not checked.
private
isHrefUnique(TextInput $input, int $langId) : bool
Parameters
- $input : TextInput
- $langId : int
Return values
boolloadCategoryFeeds()
Feeds as the section axis, memoized so form build, prefill and `render()` hit the bridge once.
private
loadCategoryFeeds() : array<int, Feed>
Return values
array<int, Feed>loadCategoryParameterNames()
private
loadCategoryParameterNames(array<int, int> $parameterIds) : array<int, string>
Parameters
- $parameterIds : array<int, int>
Return values
array<int, string> —id to name
resolveCategoryParameterLabel()
private
resolveCategoryParameterLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullsaveCategoryFeedRels()
Saves the category feed names through the category-scoped bridge action ({@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::saveCategoryRels()}), whose delete removes only `category_id = :categoryId`.
private
saveCategoryFeedRels(int $categoryId, ArrayHash<string|int, mixed> $values) : bool
IMPORTANT: deliberately outside the object-graph save of the category. Mapping FeedProductCategoryRel
as an owned collection of ProductCategory would make every category save without the matching
includes (subtree move, grid actions, other forms) delete the mappings via orphanRemoval. The
dedicated action does not depend on the caller's includes.
An empty field means no mapping for that feed (the row disappears).
Parameters
- $categoryId : int
- $values : ArrayHash<string|int, mixed>
Return values
boolsaveProductCategoryForm()
private
saveProductCategoryForm(ArrayHash<string|int, mixed> $values) : bool
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
boolsetProductCategoryFormDefaults()
private
setProductCategoryFormDefaults(Form $form) : void
Parameters
- $form : Form
suggestCategoryParameters()
Suggests root active parameters by name, skipping rows already chosen (the picker suggest AJAX call carries the whole current form state in a POST, so `parameterRows` reflects the live rows, not just the stored state).
private
suggestCategoryParameters(string $query) : array<int, array{id: int, label: string}>
Parameters
- $query : string