CoolCMS API

FeedFieldsTrait

Feeds tab: per-parameter overrides of XML feed values for this product set ({@see FeedParameterRel}).

The data hangs on the pair (feed parameter x product set), so it is edited on the set. The tab is shared by ProductSetPresenter (mode bundle) and ProductPresenter (mode product-variant). It is a separate sub-form, not part of editForm: tabs are siblings of {form editForm} and nested forms are forbidden, so the main submit would never send its fields. Saving uses a set-scoped bridge action (FeedManager::saveParameterRelsForSet()).

Every parameter is a multiplier feedMulti_<id>; only limits and buttons differ:

  • non-multiple, 0-1 saved rows: one copy, no buttons.
  • non-multiple, more than one saved row (anomaly): maxCopies = row count, only "remove"; the XML gets one element with rows[0].
  • multiple: unlimited copies, one node per override row; with children, repetitions = parent rows and the child of repetition i takes its own i-th row.

IMPORTANT: every saved row must be rendered. Saving is a set-scoped delete+insert of what the form sent, so a row hidden in the UI would be silently deleted on the next save. That is why the anomaly renders all rows and shows form.feeds.anomaly.warning.

IMPORTANT: a node with children still stores a parent row with the node tag as value. It carries the value mode of the pair (the generator and the prefill read the mode from the parent's first row) and counts the repetitions for the editor. The tag is never printed (resolveNodeText() is not called for nodes with children).

IMPORTANT: parent and children are paired by position, so rows must not shift. An empty child in a filled copy is stored as an empty string (the generator treats it as "no override"); a copy empty everywhere is dropped. Children have no standalone field, otherwise one value would live in two places and the pairing would break.

Value mode: every root node gets a select feedMode_<id> (change, prepend, append, none; default change), stored in FeedParameterRel::$valueMode and evaluated by FeedXmlGenerator. Children never get one; a multiple node has one select in the card header for all repetitions.

IMPORTANT: a mode other than change needs a row to be stored on, otherwise "empty field = no row" would drop none for an unfilled node. Collection creates a carrier row with an empty value (collectFeedOverrideNodeRows()). In mode change an empty field still creates no row.

Table of Contents

Properties

$feedOverrideModesCache  : array<int, string>|null
Memoized value modes of the edited set, `feedParameterId => mode`. Taken from the first row of the (parameter, set) pair, like the generator does.
$feedOverrideRowsCache  : array<int, array<int, string>>|null
Memoized override rows of the edited set, `feedParameterId => list<value>` ordered by `r.id ASC` (the order of repetitions in the XML).
$feedOverrideStructureCache  : array<int, array{feedId: int, feedName: string, tree: list>}>|null
Memoized tab axis: feeds with their parameter trees (including saved repetition counts).
$feedParameterActionLabelsCache  : array<int, string>|null

Methods

onFeedOverridesFormSuccess()  : void
Saves the Feeds tab, set-scoped so it never touches the overrides of other sets ({@see \App\UI\Api\Eshop\Models\Managers\FeedManager::saveParameterRelsForSet()}).
createComponentFeedOverridesForm()  : Form
Builds the sub-form of the Feeds tab: one multiplier `feedMulti_<id>` per parameter (a non-multiple parameter is a multiplier with `maxCopies = 1` and no buttons).
addFeedOverrideModeSelect()  : void
Builds the value mode select: one per root node (children and repetitions of a `multiple` node do not get one).
addFeedOverrideMultiplier()  : void
Builds the multiplier of one node; a copy is one repetition of the XML node.
buildFeedOverrideDefaults()  : array<string, mixed>
Builds the prefill from {@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::getParameterRelsForSet()} (order `r.id ASC`, the order of repetitions in the XML).
buildFeedParameterRenderTree()  : array<int, array<string, mixed>>
Converts parameter tree nodes into the shape used by the form and the rendering.
collectFeedOverrideDefaults()  : void
Fills defaults of one node. Children are not reached by recursion: they live in the parent copy.
collectFeedOverrideNodeRows()  : void
Collects the rows of one node (see the trait docblock: why a node with children also gets a parent row and why an empty child is stored as an empty string).
collectFeedOverrideRows()  : array<int, array{feedParameterId: int, value: string, valueMode: string}>
Converts form values into flat bridge rows, in the order the repetitions must appear in the XML (the bridge stores them sequentially and the generator reads them `ORDER BY r.id`).
loadFeedOverrideModes()  : array<int, string>
Returns the value modes of the edited set, `feedParameterId => mode`. Filled together with the rows ({@see loadFeedOverrideRows()}), it is the same bridge query.
loadFeedOverrideRows()  : array<int, array<int, string>>
Returns the override rows of the edited set across all feeds, `feedParameterId => list<value>`.
loadFeedOverrideStructure()  : array<int, array{feedId: int, feedName: string, tree: list>}>
Returns the feeds with their parameter trees, using one bridge query per feed (memoized).
loadFeedParameterActionLabels()  : array<int, string>
Returns the catalog of parameter actions, `actionId => translated name` (read-only).

Properties

$feedOverrideModesCache

Memoized value modes of the edited set, `feedParameterId => mode`. Taken from the first row of the (parameter, set) pair, like the generator does.

private array<int, string>|null $feedOverrideModesCache = null

$feedOverrideRowsCache

Memoized override rows of the edited set, `feedParameterId => list<value>` ordered by `r.id ASC` (the order of repetitions in the XML).

private array<int, array<int, string>>|null $feedOverrideRowsCache = null

$feedOverrideStructureCache

Memoized tab axis: feeds with their parameter trees (including saved repetition counts).

private array<int, array{feedId: int, feedName: string, tree: list>}>|null $feedOverrideStructureCache = null

$feedParameterActionLabelsCache

private array<int, string>|null $feedParameterActionLabelsCache = null

memoized action catalog, actionId => translated name.

Methods

onFeedOverridesFormSuccess()

Saves the Feeds tab, set-scoped so it never touches the overrides of other sets ({@see \App\UI\Api\Eshop\Models\Managers\FeedManager::saveParameterRelsForSet()}).

public onFeedOverridesFormSuccess(Form $form) : void

It never calls redirect() on success (in mode product-variant the PRG would close the variant modal); it only adds a notice and redraws the section, like stock operations do.

Parameters
$form : Form

createComponentFeedOverridesForm()

Builds the sub-form of the Feeds tab: one multiplier `feedMulti_<id>` per parameter (a non-multiple parameter is a multiplier with `maxCopies = 1` and no buttons).

protected createComponentFeedOverridesForm() : Form

IMPORTANT: defaults are filled unconditionally, with no isMethod('GET') guard like in editForm. Form::setDefaults() is a no-op on a submitted form, so the multiplier builds its copies from the submitted data and the prefill does not interfere. A guard would render the tab with empty fields on any other signal (opening the price list modal, a stock operation calling redrawVariantModal()).

Return values
Form

addFeedOverrideModeSelect()

Builds the value mode select: one per root node (children and repetitions of a `multiple` node do not get one).

private addFeedOverrideModeSelect(Form $form, int $nodeId) : void

It sits next to the multiplier, not in its copy: the mode belongs to the whole (parameter x set) pair, so inside copies it would exist several times and repetitions could contradict each other. The template renders it as the second input-group element of a single-field node, otherwise in the card header.

NOTE: aria-label is translated manually through the translator (not by key): the field label is not rendered (it would break the [text][select] pair in an input-group, and the header already shows the tag), so without it the select would have no accessible name.

Parameters
$form : Form
$nodeId : int

addFeedOverrideMultiplier()

Builds the multiplier of one node; a copy is one repetition of the XML node.

private addFeedOverrideMultiplier(Form $form, array<string|int, mixed> $node) : void

A node without children has the field value in a copy, a node with children has child_<childId> per child (the parent value is not shown).

minCopies = 1: an empty copy is dropped on save, so one empty row creates nothing but shows where the value is entered. IMPORTANT: for a node without override rows the prefill therefore gives no key at all; an empty array would tell the multiplier "0 copies" instead of letting minCopies apply.

maxCopies: unlimited (null) for a multiple node, exactly the saved row count (normally 1) otherwise. The hard cap guarantees that all saved data is rendered and cannot be dropped silently.

Only a multiple node gets the "add" button. "Remove" is always registered but the library detaches it while there are only minCopies copies, so a non-multiple node shows it only in the anomaly (more than one saved row), where it is the only way to remove the surplus.

Parameters
$form : Form
$node : array<string|int, mixed>

buildFeedOverrideDefaults()

Builds the prefill from {@see \App\UI\Admin\Eshop\Models\Managers\FeedManager::getParameterRelsForSet()} (order `r.id ASC`, the order of repetitions in the XML).

private buildFeedOverrideDefaults() : array<string, mixed>

IMPORTANT: the multiplier is filled by a nested array in a single setDefaults(), never by a manual addCopy(). The copy count is the node rowCount, child values of copy i come from the i-th row of each child.

Return values
array<string, mixed>

buildFeedParameterRenderTree()

Converts parameter tree nodes into the shape used by the form and the rendering.

private buildFeedParameterRenderTree(array<string|int, mixed> $nodes, string $prefix, array<int, array<int, string>> $rows) : array<int, array<string, mixed>>

Node keys: id, tag (bare XML tag shown in the UI, indentation carries the hierarchy), caption (full path through the parent, PARAM > VAL, kept by the form control for error messages), multiple, actionLabel (source of the computed value), rowCount (repetitions already saved), anomaly (non-multiple parameter with more than one row) and children.

Parameters
$nodes : array<string|int, mixed>
$prefix : string
$rows : array<int, array<int, string>>
Return values
array<int, array<string, mixed>>

collectFeedOverrideDefaults()

Fills defaults of one node. Children are not reached by recursion: they live in the parent copy.

private collectFeedOverrideDefaults(array<string|int, mixed> $node, array<int, array<int, string>> $byParameter, array<string, mixed> &$defaults) : void
Parameters
$node : array<string|int, mixed>
$byParameter : array<int, array<int, string>>
$defaults : array<string, mixed>

collectFeedOverrideNodeRows()

Collects the rows of one node (see the trait docblock: why a node with children also gets a parent row and why an empty child is stored as an empty string).

private collectFeedOverrideNodeRows(array<string|int, mixed> $node, array<string, mixed> $values, array<int, array{feedParameterId: int, value: string, valueMode: string}> &$rows) : void

IMPORTANT: a node in a mode other than change that produced no repetition (the admin filled nothing) gets one carrier row with an empty value; otherwise there is nowhere to store the mode and none would be lost silently. In mode change an empty field still creates no row, otherwise sets would fill with empty rows and rowCount would lie. The generator skips a carrier row in prepend/append as an empty repetition (FeedXmlGenerator::hasManualValue()) and renders no node in none.

Children always get change: the mode belongs to the parent and the generator ignores it for children.

Parameters
$node : array<string|int, mixed>
$values : array<string, mixed>
$rows : array<int, array{feedParameterId: int, value: string, valueMode: string}>

collectFeedOverrideRows()

Converts form values into flat bridge rows, in the order the repetitions must appear in the XML (the bridge stores them sequentially and the generator reads them `ORDER BY r.id`).

private collectFeedOverrideRows(array<string, mixed> $values) : array<int, array{feedParameterId: int, value: string, valueMode: string}>
Parameters
$values : array<string, mixed>
Return values
array<int, array{feedParameterId: int, value: string, valueMode: string}>

loadFeedOverrideModes()

Returns the value modes of the edited set, `feedParameterId => mode`. Filled together with the rows ({@see loadFeedOverrideRows()}), it is the same bridge query.

private loadFeedOverrideModes() : array<int, string>
Return values
array<int, string>

loadFeedOverrideRows()

Returns the override rows of the edited set across all feeds, `feedParameterId => list<value>`.

private loadFeedOverrideRows() : array<int, array<int, string>>
Return values
array<int, array<int, string>>

loadFeedOverrideStructure()

Returns the feeds with their parameter trees, using one bridge query per feed (memoized).

private loadFeedOverrideStructure() : array<int, array{feedId: int, feedName: string, tree: list>}>

Form building, value collection on save and render() all read it. It is the only axis: the form is not "one field per parameter" but a multiplier whose limits depend on multiple and on the number of saved rows, so a separate flat parameter list would drift from the tree.

Return values
array<int, array{feedId: int, feedName: string, tree: list>}>

loadFeedParameterActionLabels()

Returns the catalog of parameter actions, `actionId => translated name` (read-only).

private loadFeedParameterActionLabels() : array<int, string>

Used only for the "value source" caption in the parameter card header; no action is evaluated here because that would require running the generator over the set.

Return values
array<int, string>
On this page

Search results