PickerTrait
Pickers of the product set edit form.
- productPicker(): top-level field of the owning
Product(categories stay on the product; the picker only chooses which product the set sells). - productSetVariantPicker(): top-level
variantId, the sold variant of a set withtype = PRODUCT. - productSetItemPicker():
setItemsrow, a set composition item that is another set (nested sets allowed). - productTagPicker(), productSetFeePicker(), productSetLinkPicker():
tags,fees,linksrows (many-to-many). - productSetAccessoryPicker():
accessoriesrow (set to set, excludes the edited set).
IMPORTANT: displayField is required in every multiplier picker; without it the label is lost
on the AJAX redraw of a row.
The label helpers (productLabel(), productSetLabel(), productTagLabel(),
productSetFeeLabel(), productSetLinkLabel()) take an already loaded entity. The picker
callbacks and the form trait (row prefill on edit) both call them, so their names and
signatures are a stable contract.
$this->getLanguageContext() is used nullsafe (?->getLang()): on a component the context is nullable.
Requires $this->productManager, $this->productVariantManager, $this->productSetManager,
$this->productTagManager, $this->productSetFeeManager and $this->productSetLinkManager
(constructor injection on the host).
Table of Contents
Methods
- productPicker() : PickerConfig
- Picker of the owning `Product`: a single top-level field outside the multiplier, so no `targetContainer` or `displayField`.
- productSetAccessoryPicker() : PickerConfig
- Picker of an accessory in the `accessories` multiplier row (many-to-many `ProductSet` to `ProductSet`).
- productSetFeePicker() : PickerConfig
- Picker of a fee in the `fees` multiplier row (many-to-many `ProductSet` to `ProductSetFee`).
- productSetItemPicker() : PickerConfig
- Picker of a set composition item: `setItems` row targeting another set (a ready bundle too, i.e. nested sets). Links to `:Admin:Eshop:ProductSet` so the item detail opens from the row.
- productSetLinkPicker() : PickerConfig
- Picker of a link in the `links` multiplier row (many-to-many `ProductSet` to `ProductSetLink`).
- productSetVariantPicker() : PickerConfig
- Picker of the sold variant: top-level `variantId` of a set with `type = PRODUCT`. The 1:1 relation is guarded by the Api guard and a unique index.
- productTagPicker() : PickerConfig
- Picker of a tag in the `tags` multiplier row (many-to-many `ProductSet` to `ProductTag`).
- createProductSetLink() : array{id: int, label: string}|null
- Creates a link from the typed URL and returns it to the picker for selection.
- productLabel() : string
- Returns the product name in the current language (first non-empty as fallback).
- productSetAncestorIds() : array<int, int>
- Returns the ancestors of a set in the composition tree, i.e. the sets that contain it directly or indirectly.
- productSetFeeLabel() : string
- Returns the fee name in the current language (first non-empty as fallback).
- productSetLabel() : string
- Returns `<code> — <name>` in the current language (falls back to whichever part is non-empty).
- productSetLinkLabel() : string
- Returns the link label: its URL in the current language (first non-empty address as fallback).
- productTagLabel() : string
- Returns the tag name in the current language (first non-empty as fallback).
- productVariantLabelsOf() : array<int, string>
- Returns the labels of all variants of a product: `variantId => "<product name> (#<variant id>)"`.
- resolveProductLabel() : string|null
- resolveProductSetAccessoryLabel() : string|null
- resolveProductSetFeeLabel() : string|null
- resolveProductSetLinkLabel() : string|null
- resolveProductTagLabel() : string|null
- resolveProductVariantLabel() : string|null
- suggestProducts() : array<int, array{id: int, label: string}>
- Suggests products by a `like` match on the product name.
- suggestProductSetAccessories() : array<int, array{id: int, label: string}>
- Suggests sets by a `like` match on the set name, optionally without the edited set (a set cannot be its own accessory).
- suggestProductSetFees() : array<int, array{id: int, label: string}>
- Suggests fees by a `like` match on the fee name.
- suggestProductSetItems() : array<int, array{id: int, label: string}>
- Suggests composition items by a `like` match on the set name, excluding `$excludeIds`.
- suggestProductSetLinks() : array<int, array{id: int, label: string}>
- Suggests links by a `like` match on the URL (`translations.value`) only, not on the name: names are often identical or missing, so only the address tells links apart.
- suggestProductTags() : array<int, array{id: int, label: string}>
- Suggests tags by a `like` match on the tag name.
- suggestProductVariants() : array<int, array{id: int, label: string}>
- Suggests variants by a `like` match on the owning product name (a variant has no text of its own).
Methods
productPicker()
Picker of the owning `Product`: a single top-level field outside the multiplier, so no `targetContainer` or `displayField`.
protected
productPicker([string $formName = 'editForm' ][, string $targetField = 'productId' ]) : PickerConfig
Parameters
- $formName : string = 'editForm'
- $targetField : string = 'productId'
Return values
PickerConfigproductSetAccessoryPicker()
Picker of an accessory in the `accessories` multiplier row (many-to-many `ProductSet` to `ProductSet`).
protected
productSetAccessoryPicker([string $container = 'accessories' ][, string $targetField = 'accessorySetId' ][, string $displayField = 'accessorySet' ][, string $formName = 'editForm' ][, int|null $excludeId = null ]) : PickerConfig
Self-reference: $excludeId (id of the edited set) is excluded from the suggest
(NotEqualFilter) and from the modal (pickerExclude), same technique as
ProductCategoryPickerTrait::productCategoryParentPicker().
Parameters
- $container : string = 'accessories'
- $targetField : string = 'accessorySetId'
- $displayField : string = 'accessorySet'
- $formName : string = 'editForm'
- $excludeId : int|null = null
-
id of the edited
ProductSet(self-reference)
Return values
PickerConfigproductSetFeePicker()
Picker of a fee in the `fees` multiplier row (many-to-many `ProductSet` to `ProductSetFee`).
protected
productSetFeePicker([string $container = 'fees' ][, string $targetField = 'feeId' ][, string $displayField = 'fee' ][, string $formName = 'editForm' ]) : PickerConfig
Parameters
- $container : string = 'fees'
- $targetField : string = 'feeId'
- $displayField : string = 'fee'
- $formName : string = 'editForm'
Return values
PickerConfigproductSetItemPicker()
Picker of a set composition item: `setItems` row targeting another set (a ready bundle too, i.e. nested sets). Links to `:Admin:Eshop:ProductSet` so the item detail opens from the row.
protected
productSetItemPicker([string $container = 'setItems' ][, string $targetField = 'childSetId' ][, string $displayField = 'childSet' ][, string $formName = 'editForm' ][, array<int, int> $excludeIds = [] ][, int|null $excludeId = null ]) : PickerConfig
IMPORTANT: the suggest list excludes the set itself and all its ancestors ($excludeIds,
see productSetAncestorIds()), otherwise a cycle A contains B contains A could be
offered. The modal grid can exclude only one id (pickerExclude), so only the set itself
is excluded there; the authoritative backstop is the guard in the Api ProductSetManager,
which rejects the save with a clear message.
Parameters
- $container : string = 'setItems'
- $targetField : string = 'childSetId'
- $displayField : string = 'childSet'
- $formName : string = 'editForm'
- $excludeIds : array<int, int> = []
-
the set itself plus its ancestors (suggest)
- $excludeId : int|null = null
-
id of the edited set (modal)
Return values
PickerConfigproductSetLinkPicker()
Picker of a link in the `links` multiplier row (many-to-many `ProductSet` to `ProductSetLink`).
protected
productSetLinkPicker([string $container = 'links' ][, string $targetField = 'linkId' ][, string $displayField = 'link' ][, string $formName = 'editForm' ]) : PickerConfig
Parameters
- $container : string = 'links'
- $targetField : string = 'linkId'
- $displayField : string = 'link'
- $formName : string = 'editForm'
Return values
PickerConfigproductSetVariantPicker()
Picker of the sold variant: top-level `variantId` of a set with `type = PRODUCT`. The 1:1 relation is guarded by the Api guard and a unique index.
protected
productSetVariantPicker([string $formName = 'editForm' ][, string $targetField = 'variantId' ]) : PickerConfig
Outside the multiplier, so no targetContainer or displayField.
NOTE: targetPresenter is null because ProductVariant has no admin grid (it lives only
as a multiplier row on the product), so the picker is suggest-only.
Parameters
- $formName : string = 'editForm'
- $targetField : string = 'variantId'
Return values
PickerConfigproductTagPicker()
Picker of a tag in the `tags` multiplier row (many-to-many `ProductSet` to `ProductTag`).
protected
productTagPicker([string $container = 'tags' ][, string $targetField = 'tagId' ][, string $displayField = 'tag' ][, string $formName = 'editForm' ]) : PickerConfig
Parameters
- $container : string = 'tags'
- $targetField : string = 'tagId'
- $displayField : string = 'tag'
- $formName : string = 'editForm'
Return values
PickerConfigcreateProductSetLink()
Creates a link from the typed URL and returns it to the picker for selection.
private
createProductSetLink(string $text) : array{id: int, label: string}|null
IMPORTANT: an exact, case-insensitive match of the address is looked up first (via the existing suggest) and returned as is, so the picker cannot generate duplicates in the code list. The JS check is only a courtesy; this one is binding.
The address is written to all languages because ProductSetLinkText::$value is required
and a link without an address would be unusable in the other languages. The name stays
empty and can be filled in on the target presenter.
Parameters
- $text : string
Return values
array{id: int, label: string}|nullproductLabel()
Returns the product name in the current language (first non-empty as fallback).
private
productLabel(Product $product) : string
Parameters
- $product : Product
Return values
stringproductSetAncestorIds()
Returns the ancestors of a set in the composition tree, i.e. the sets that contain it directly or indirectly.
private
productSetAncestorIds(int $setId[, int $maxDepth = 10 ]) : array<int, int>
Walks level by level (one query per level) with a depth cap and a visited set, so the picker cannot loop even if a cycle gets into the data.
The Api has the same logic in SetCompositionService (authoritative guard on save); this
is only UI prevention, because the admin must not use Api services.
Parameters
- $setId : int
- $maxDepth : int = 10
Return values
array<int, int>productSetFeeLabel()
Returns the fee name in the current language (first non-empty as fallback).
private
productSetFeeLabel(ProductSetFee $fee) : string
Parameters
- $fee : ProductSetFee
Return values
stringproductSetLabel()
Returns `<code> — <name>` in the current language (falls back to whichever part is non-empty).
private
productSetLabel(ProductSet $set) : string
Parameters
- $set : ProductSet
Return values
stringproductSetLinkLabel()
Returns the link label: its URL in the current language (first non-empty address as fallback).
private
productSetLinkLabel(ProductSetLink $link) : string
The name is deliberately not used: it is often shared by several links or missing, which would make links indistinguishable in the picker and leave the text field empty.
Parameters
- $link : ProductSetLink
Return values
stringproductTagLabel()
Returns the tag name in the current language (first non-empty as fallback).
private
productTagLabel(ProductTag $tag) : string
Parameters
- $tag : ProductTag
Return values
stringproductVariantLabelsOf()
Returns the labels of all variants of a product: `variantId => "<product name> (#<variant id>)"`.
private
productVariantLabelsOf(Product $product) : array<int, string>
Parameters
- $product : Product
Return values
array<int, string>resolveProductLabel()
private
resolveProductLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullresolveProductSetAccessoryLabel()
private
resolveProductSetAccessoryLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullresolveProductSetFeeLabel()
private
resolveProductSetFeeLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullresolveProductSetLinkLabel()
private
resolveProductSetLinkLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullresolveProductTagLabel()
private
resolveProductTagLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullresolveProductVariantLabel()
private
resolveProductVariantLabel(int $id) : string|null
Parameters
- $id : int
Return values
string|nullsuggestProducts()
Suggests products by a `like` match on the product name.
private
suggestProducts(string $query) : array<int, array{id: int, label: string}>
Parameters
- $query : string
Return values
array<int, array{id: int, label: string}>suggestProductSetAccessories()
Suggests sets by a `like` match on the set name, optionally without the edited set (a set cannot be its own accessory).
private
suggestProductSetAccessories(string $query[, int|null $excludeId = null ]) : array<int, array{id: int, label: string}>
Parameters
- $query : string
- $excludeId : int|null = null
Return values
array<int, array{id: int, label: string}>suggestProductSetFees()
Suggests fees by a `like` match on the fee name.
private
suggestProductSetFees(string $query) : array<int, array{id: int, label: string}>
Parameters
- $query : string
Return values
array<int, array{id: int, label: string}>suggestProductSetItems()
Suggests composition items by a `like` match on the set name, excluding `$excludeIds`.
private
suggestProductSetItems(string $query[, array<int, int> $excludeIds = [] ]) : array<int, array{id: int, label: string}>
Parameters
- $query : string
- $excludeIds : array<int, int> = []
Return values
array<int, array{id: int, label: string}>suggestProductSetLinks()
Suggests links by a `like` match on the URL (`translations.value`) only, not on the name: names are often identical or missing, so only the address tells links apart.
private
suggestProductSetLinks(string $query) : array<int, array{id: int, label: string}>
Parameters
- $query : string
Return values
array<int, array{id: int, label: string}>suggestProductTags()
Suggests tags by a `like` match on the tag name.
private
suggestProductTags(string $query) : array<int, array{id: int, label: string}>
Parameters
- $query : string
Return values
array<int, array{id: int, label: string}>suggestProductVariants()
Suggests variants by a `like` match on the owning product name (a variant has no text of its own).
private
suggestProductVariants(string $query) : array<int, array{id: int, label: string}>
IMPORTANT: query from the product side (productManager with includes: ['translations','variants']), not from the variant side via includes: ['product.translations']. ProductVariant::$product is a back-reference (inversedBy: 'variants') that OrmEntityHydrator always drops on reads, so getProduct() would be
null and the label would fall back to a bare #7.
Parameters
- $query : string