CoolCMS API

AdvertFormTrait uses trait:short, trait:short, trait:short, trait:short, trait:short

Advert add/edit form (`/admin/bazaar/advert`) as a standard admin presenter form (`createForm()`), not a component.

Dynamic parameters are rendered by the shared ParameterFieldBuilder, so the category, user and region pickers work normally through the presenter PickerHostTrait and the template uses admin classes.

Composed of:

meta['suppressNotifications'] makes the Api AdvertManager::completedSave skip all e-mails and notifications; the price is still recomputed from the parameters (sorting). A new advert still feeds the watchdogs (meta['notifyWatchdogs']), the same as one inserted on the front end.

IMPORTANT: the consuming AdvertPresenter must use AdvertFormTrait, PickerHostTrait and DependentSelectBoxAware, have ?Advert $editedAdvert and implement pickers() (delegating to AdvertCategoryFieldsTrait::pickers()); otherwise the form does not work.

Table of Contents

Constants

ADV_DT_FORMAT  : mixed = 'Y-m-d\TH:i'
Value format of `<input type=datetime-local>`.
REGION_PARAMETER_TYPES  : array<string|int, mixed> = [\App\Core\Base\Shared\Shared\Models\Entities\P...
Parameter types that carry the advert region (tree picker and municipality or postcode suggest).

Properties

$advertLanguageManager  : LanguageManager
$advertLocalizationManager  : LocalizationManager
$advertManager  : AdvertManager
$categoryManager  : CategoryManager
$categoryParameterRelManager  : CategoryParameterRelManager
$currencyManager  : CurrencyManager
$parameterGroupManager  : ParameterGroupManager
$parameterManager  : ParameterManager
$pickerFactory  : PickerFactory
$uploaderDropzoneFactory  : UploaderDropzoneFactory
$fieldBuilder  : ParameterFieldBuilder|null
$hasSelectedCategory  : bool
Whether at least one category is selected; the template hides the admin-only fields (`d-none`) until then.
$parameterFormItems  : array<string|int, ParameterFormItem>
$parameterFormPrepared  : bool

Methods

getUploaderControlMap()  : array<string, string>
Returns the map of form control name to uploader child name: the first `IMAGE_UPLOADER` maps to `imgUploader`, the first `FILE_UPLOADER` to `fileUploader`. The template looks controls up here and renders a dropzone for a match, a classic input otherwise.
createComponentEditForm()  : Form
createComponentFileUploader()  : UploaderDropzone
createComponentImgUploader()  : UploaderDropzone
pickers()  : array<string, PickerConfig>
Returns the presenter picker configuration (the {@see \App\UI\Admin\System\Presenters\Traits\Pickers\PickerHostTrait} contract).
addAdminFields()  : void
addCategoryFields()  : void
addOwnerFields()  : void
Adds the hidden field for the advert author (user), filled by the picker (suggest or modal) with `insert_user_id`. {@see AdvertBuildTrait::buildAdvert()} sets it as a managed association.
addUploaderFields()  : void
Adds the hidden fields with the dropzone temp-directory tokens.
advIntOrNull()  : int|null
advLabel()  : string
Translates a relative key through the presenter translator and domain, for labels rendered raw (group legends) where Nette `{label}` does not help.
advStr()  : string
advStrOrNull()  : string|null
aggregateCategoryParameterRels()  : array<int, array{order: int, required: bool, groupId: ?int}>
Aggregates the `CategoryParameterRel` rows across categories into one entry per parameter id: minimum order, required if any category requires it, first non-null group id.
applyCategoryRels()  : void
Builds the object-graph collection `categoryRels` from the submitted multiplier rows.
applyParameterRels()  : void
Builds the object-graph collection `parameterRels` from the submitted rows.
buildAdvert()  : Advert
buildAssetsMeta()  : array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>|null
Builds `meta['assets']` for the Api `afterSave`: the ordered images, existing ids plus new files as a `path` into the temp directory.
buildCategoryRowDefaults()  : array<int, array{categoryId: int, category: string, paid: bool}>
Builds the category row defaults from the edited advert (`categoryId`, label and `paid` from the database).
buildCurrencyItems()  : array<int, string>
buildFilesMeta()  : array<int, array{type: string, fileId?: int, name?: string, path?: string, order: int}>|null
Builds `meta['files']` for the Api `afterSave` (`processFiles` to `fileRels`): the ordered files, existing ids plus new files as a `path` into the temp directory. Same shape as the assets, with `fileId` instead of `imageId`.
buildParameterContext()  : ParameterFormContext
Fills {@see ParameterFormContext} (`languageId` and the items for the currency/region selects).
buildParametersMeta()  : array<int, array{id: int, value: string, value2?: string}>
Builds flat rows `id`, `value` and optional `value2` from the submitted dynamic parameter values through {@see ParameterFieldBuilder::extractValues()} (persistable types only). The input for {@see applyParameterRels()} (object-graph collection), not a meta key.
buildRegionItems()  : array<int, string>
Builds the items of a `SELECT_REGION` parameter: a plain `<select>`, not a Picker.
buildRegionPreviewFieldMap()  : array<string, string>
Builds the preview after selecting a municipality: item data key to the form key of the parameter `city`, `zip` or `country` (only parameters the current form categories have).
categoryMultiPicker()  : PickerConfig
categoryName()  : string
cleanupUploaders()  : void
configureFileUploader()  : void
configureImageUploader()  : void
Configures the image uploader.
extractCategoryRows()  : array<int, array{categoryId: int, paid: bool}>
Builds flat rows `{categoryId, paid}` from the submitted multiplier rows; the input for {@see AdvertFormTrait::applyCategoryRels()} (object-graph collection), not a meta key. Deduplicated, positive `categoryId`.
extractSubmittedCategoryIds()  : array<int, int>
Returns category ids from the submitted multiplier rows (`categories[*][categoryId]`), the same shape that save reads in {@see AdvertCategoryFieldsTrait::extractCategoryRows()}.
findFirstParameterOfType()  : Parameter|null
gateAdvertColumns()  : void
Column gates of the parameters written into advert columns, the same as the front form (`AdvertFormComponent\Traits\FormFieldsTrait::gateAdvertColumns()`): the contact e-mail goes to `email` and the price is copied to the sort column `price` (`AdvertPriceColumnTrait` on the Api). The builder knows only the parameter type, so a negative price was stored (`-5`) and a longer e-mail or a price out of the column range failed the save in the database (admin walkthrough B5).
getEditedAdvert()  : Advert|null
getEntityValues()  : array<int, mixed>
Returns the pre-filled values of the dynamic parameters from the edited advert (`parameterRels`).
getFieldBuilder()  : ParameterFieldBuilder
getRegionLabel()  : string
guardAdvertReferences()  : void
Guards the ids written into foreign keys of the advert: the author (picker) and the insert language and localization (typed numbers). A deleted or mistyped id failed the save on the foreign key with a generic message (admin walkthrough B3/B6 family); the admin-only columns are measured by the column gate.
guardCategoriesSelected()  : void
Enforces at least one selected category; called from `onValidate`.
itemsHaveType()  : bool
loadParameterGroups()  : array<string|int, ParameterGroupInterface>
loadParameters()  : array<string|int, ParameterFormItem>
parseFileTypes()  : array<string|int, string>
prepareExistingAssets()  : array<int, array{imageId: int, thumbPath: string, isMain: bool, order: int}>
prepareExistingFiles()  : array<int, array{imageId: int, thumbPath: string, fileName: string, isMain: bool, order: int}>
Returns the existing files for the dropzone (the key `imageId` is the generic dropzone identifier).
prepareParameterForm()  : void
Loads parameters by the advert categories, context and pre-fill, and initializes the builder.
resolveActiveCategories()  : array<string|int, Category>
Returns the categories that decide which dynamic parameters are shown.
resolveAnyRegionParameterField()  : array{key: string, type: int}|null
Returns the region parameter from the code list, independent of the selected category; used only to register the picker so suggest works on the add form too. Returns the first active root parameter of type `SELECT_REGION` (or legacy 37) or `REGION_PICKER`; null means the module has no region parameter.
resolveCategoryLabel()  : string|null
resolveEditedAdvertCategories()  : array<string|int, Category>
Returns the categories of the edited advert (from `categoryRels`); empty for a new advert.
resolveRegionParameterField()  : array{key: string, type: int}|null
Returns the form field of the region parameter: `key` is `parameter_<id>` (the picker `targetField`) and `type` decides the picker (`SELECT_REGION` or legacy 37: tree picker, `REGION_PICKER`: municipality or postcode suggest). Null means the system has no region parameter, so no picker is registered.
resolveUploaderToken()  : string|null
saveAdvertForm()  : bool
Saves the advert with notifications suppressed (`meta['suppressNotifications']`).
suggestCategories()  : array<int, array{id: int, label: string}>
syncUploaderTokenFields()  : void
Aligns the hidden token fields with the real uploader temp directories.

Constants

ADV_DT_FORMAT

Value format of `<input type=datetime-local>`.

private mixed ADV_DT_FORMAT = 'Y-m-d\TH:i'

REGION_PARAMETER_TYPES

Parameter types that carry the advert region (tree picker and municipality or postcode suggest).

private array<string|int, mixed> REGION_PARAMETER_TYPES = [\App\Core\Base\Shared\Shared\Models\Entities\ParameterType::SELECT_REGION, \App\Core\Form\Parameters\SelectRegionHandler::LEGACY_REGION_WITH_POSTCODE, \App\Core\Base\Shared\Shared\Models\Entities\ParameterType::REGION_PICKER]

Properties

$hasSelectedCategory

Whether at least one category is selected; the template hides the admin-only fields (`d-none`) until then.

private bool $hasSelectedCategory = false

Methods

getUploaderControlMap()

Returns the map of form control name to uploader child name: the first `IMAGE_UPLOADER` maps to `imgUploader`, the first `FILE_UPLOADER` to `fileUploader`. The template looks controls up here and renders a dropzone for a match, a classic input otherwise.

public getUploaderControlMap() : array<string, string>
Return values
array<string, string>

pickers()

Returns the presenter picker configuration (the {@see \App\UI\Admin\System\Presenters\Traits\Pickers\PickerHostTrait} contract).

protected pickers() : array<string, PickerConfig>
Return values
array<string, PickerConfig>

addOwnerFields()

Adds the hidden field for the advert author (user), filled by the picker (suggest or modal) with `insert_user_id`. {@see AdvertBuildTrait::buildAdvert()} sets it as a managed association.

private addOwnerFields(Form $form) : void

The region is not an admin field but the parameter SELECT_REGION rendered as a picker; see AdvertCategoryFieldsTrait::pickers() and the denormalization in buildAdvert().

Parameters
$form : Form

advIntOrNull()

private advIntOrNull(mixed $value) : int|null
Parameters
$value : mixed
Return values
int|null

advLabel()

Translates a relative key through the presenter translator and domain, for labels rendered raw (group legends) where Nette `{label}` does not help.

private advLabel(string $key) : string
Parameters
$key : string
Return values
string

advStr()

private advStr(mixed $value) : string
Parameters
$value : mixed
Return values
string

advStrOrNull()

private advStrOrNull(mixed $value) : string|null
Parameters
$value : mixed
Return values
string|null

aggregateCategoryParameterRels()

Aggregates the `CategoryParameterRel` rows across categories into one entry per parameter id: minimum order, required if any category requires it, first non-null group id.

private aggregateCategoryParameterRels(array<string|int, Category> $categories) : array<int, array{order: int, required: bool, groupId: ?int}>
Parameters
$categories : array<string|int, Category>
Return values
array<int, array{order: int, required: bool, groupId: ?int}>

applyCategoryRels()

Builds the object-graph collection `categoryRels` from the submitted multiplier rows.

private applyCategoryRels(Advert $advert, array<int, array{categoryId: int, paid: bool}> $rows) : void

category is a light reference (new Category()->setId($id), like applyParameterRels()); EntityHydrator::extract() serializes it as {"id":{"id":X}} and the Api hydrateForPersist looks it up as a managed entity. paid comes from the form explicitly (the admin gets it prefilled from the database and may change it; the external path is guarded by CategoryRelsPaidSanitizationTrait).

IMPORTANT: always call it, even with $rows === []. Unlike applyParameterRels(), an empty collection is a legitimate admin remove-all: the Api orphanRemoval deletes all categories of the advert (the opposite empty semantics, see saveAdvertForm()).

Parameters
$advert : Advert
$rows : array<int, array{categoryId: int, paid: bool}>

applyParameterRels()

Builds the object-graph collection `parameterRels` from the submitted rows.

private applyParameterRels(Advert $advert, array<int, array{id: int, value: string, value2?: string}> $rows) : void

parameter is a light reference (new Parameter()->setId($id), like Region and User in AdvertBuildTrait::buildAdvert()); EntityHydrator::extract() serializes it as {"id":{"id":X}} and the Api hydrateForPersist looks it up as a managed entity.

IMPORTANT: call only when $rows !== [] (see saveAdvertForm()). Replacing the collection with an empty array would make orphanRemoval delete all existing parameters of the advert on the Api side. An empty submit leaves the collection untouched (the clone from AdvertBuildTrait::buildAdvert() carries the old state).

Parameters
$advert : Advert
$rows : array<int, array{id: int, value: string, value2?: string}>

buildAssetsMeta()

Builds `meta['assets']` for the Api `afterSave`: the ordered images, existing ids plus new files as a `path` into the temp directory.

private buildAssetsMeta(ArrayHash $values) : array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>|null

IMPORTANT: an empty list is a valid instruction to remove all images (the user deleted the last one) and the caller must send it. "Unknown" (no uploader in the form) is returned as null and the caller must not set the assets key in meta; otherwise a missing component would delete the relations. Same tri-state pattern as ComplaintFileUploaderTrait::buildComplaintFilesMeta().

Parameters
$values : ArrayHash
Return values
array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>|null

buildCategoryRowDefaults()

Builds the category row defaults from the edited advert (`categoryId`, label and `paid` from the database).

private buildCategoryRowDefaults() : array<int, array{categoryId: int, category: string, paid: bool}>
Return values
array<int, array{categoryId: int, category: string, paid: bool}>

buildCurrencyItems()

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

buildFilesMeta()

Builds `meta['files']` for the Api `afterSave` (`processFiles` to `fileRels`): the ordered files, existing ids plus new files as a `path` into the temp directory. Same shape as the assets, with `fileId` instead of `imageId`.

private buildFilesMeta(ArrayHash $values) : array<int, array{type: string, fileId?: int, name?: string, path?: string, order: int}>|null

Same tri-state semantics as buildAssetsMeta(): an empty list removes all files, null means there is no uploader in the form and the files key must not be set in meta.

Parameters
$values : ArrayHash
Return values
array<int, array{type: string, fileId?: int, name?: string, path?: string, order: int}>|null

buildParametersMeta()

Builds flat rows `id`, `value` and optional `value2` from the submitted dynamic parameter values through {@see ParameterFieldBuilder::extractValues()} (persistable types only). The input for {@see applyParameterRels()} (object-graph collection), not a meta key.

private buildParametersMeta(ArrayHash $values) : array<int, array{id: int, value: string, value2?: string}>
Parameters
$values : ArrayHash
Return values
array<int, array{id: int, value: string, value2?: string}>

buildRegionItems()

Builds the items of a `SELECT_REGION` parameter: a plain `<select>`, not a Picker.

private buildRegionItems(int|null $langId) : array<int, string>

IMPORTANT: only country and region (level <= 2). After the municipalities are imported into cms_system_regions (thousands of nodes), an unfiltered findAll() would flood the select. Municipality and district are chosen in the suggester, not here.

Parameters
$langId : int|null
Return values
array<int, string>

buildRegionPreviewFieldMap()

Builds the preview after selecting a municipality: item data key to the form key of the parameter `city`, `zip` or `country` (only parameters the current form categories have).

private buildRegionPreviewFieldMap() : array<string, string>
Return values
array<string, string>

categoryMultiPicker()

private categoryMultiPicker([string $container = 'categories' ][, string $targetField = 'categoryId' ][, string $displayField = 'category' ]) : PickerConfig
Parameters
$container : string = 'categories'
$targetField : string = 'categoryId'
$displayField : string = 'category'
Return values
PickerConfig

configureImageUploader()

Configures the image uploader.

private configureImageUploader(UploaderDropzone $uploader) : void

IMPORTANT: this runs in the component factory (createComponent*()), not in prepareParameterForm(). The dropzone upload signal is a separate request in which the edit form is never built, so the uploader would be created unconfigured (default subDir and mime types) and the upload would fail. It takes the instance instead of $this[...] to avoid recursion inside the factory.

Parameters
$uploader : UploaderDropzone

extractCategoryRows()

Builds flat rows `{categoryId, paid}` from the submitted multiplier rows; the input for {@see AdvertFormTrait::applyCategoryRels()} (object-graph collection), not a meta key. Deduplicated, positive `categoryId`.

private extractCategoryRows(ArrayHash $values) : array<int, array{categoryId: int, paid: bool}>
Parameters
$values : ArrayHash
Return values
array<int, array{categoryId: int, paid: bool}>

extractSubmittedCategoryIds()

Returns category ids from the submitted multiplier rows (`categories[*][categoryId]`), the same shape that save reads in {@see AdvertCategoryFieldsTrait::extractCategoryRows()}.

private extractSubmittedCategoryIds() : array<int, int>

Deduplicated and positive; non-row multiplier keys (creator, remover) are skipped.

Return values
array<int, int>

gateAdvertColumns()

Column gates of the parameters written into advert columns, the same as the front form (`AdvertFormComponent\Traits\FormFieldsTrait::gateAdvertColumns()`): the contact e-mail goes to `email` and the price is copied to the sort column `price` (`AdvertPriceColumnTrait` on the Api). The builder knows only the parameter type, so a negative price was stored (`-5`) and a longer e-mail or a price out of the column range failed the save in the database (admin walkthrough B5).

private gateAdvertColumns(Form $form) : void
Parameters
$form : Form

getEntityValues()

Returns the pre-filled values of the dynamic parameters from the edited advert (`parameterRels`).

private getEntityValues() : array<int, mixed>
Return values
array<int, mixed>

getRegionLabel()

private getRegionLabel(Region $region, int|null $langId) : string
Parameters
$region : Region
$langId : int|null
Return values
string

guardAdvertReferences()

Guards the ids written into foreign keys of the advert: the author (picker) and the insert language and localization (typed numbers). A deleted or mistyped id failed the save on the foreign key with a generic message (admin walkthrough B3/B6 family); the admin-only columns are measured by the column gate.

private guardAdvertReferences(Form $form) : void
Parameters
$form : Form

guardCategoriesSelected()

Enforces at least one selected category; called from `onValidate`.

private guardCategoriesSelected(Form $form, ArrayHash $values) : void

Checked only on a real save (update or save). Add, remove and parametersRefresh are UI submitters with setValidationScope([]) whose only job is the AJAX redraw, but Nette runs onValidate for them too (Container::validate() calls the handlers regardless of scope); without this gate "Add category" would report an error.

IMPORTANT: the error must go to a concrete component, not the form. The admin templates do not render form-level errors (only the flash), and HiddenField::addError() is hard-wired to form level. The carrier is therefore the "Add category" button (multiplier_creator), the only component always rendered in the categories section, even with zero rows; the template prints the error below it (Advert/Tabs/_form.latte). Form level is only a fallback when the multiplier has no such button (maxCopies).

Parameters
$form : Form
$values : ArrayHash

parseFileTypes()

private parseFileTypes(string|null $fileType, array<string|int, string> $fallback) : array<string|int, string>
Parameters
$fileType : string|null
$fallback : array<string|int, string>
Return values
array<string|int, string>

prepareExistingAssets()

private prepareExistingAssets() : array<int, array{imageId: int, thumbPath: string, isMain: bool, order: int}>
Return values
array<int, array{imageId: int, thumbPath: string, isMain: bool, order: int}>

prepareExistingFiles()

Returns the existing files for the dropzone (the key `imageId` is the generic dropzone identifier).

private prepareExistingFiles() : array<int, array{imageId: int, thumbPath: string, fileName: string, isMain: bool, order: int}>
Return values
array<int, array{imageId: int, thumbPath: string, fileName: string, isMain: bool, order: int}>

prepareParameterForm()

Loads parameters by the advert categories, context and pre-fill, and initializes the builder.

private prepareParameterForm() : void

Idempotent: called from createComponentEditForm() and from the render action (for the uploader map).

resolveActiveCategories()

Returns the categories that decide which dynamic parameters are shown.

private resolveActiveCategories() : array<string|int, Category>

On POST (AJAX category refresh and a real save) the live selected categories come from the submitted multiplier rows, so the parameters follow the current selection. On a clean GET the stored advert categories are used (a new advert has none, so no dynamic parameters are shown until a category is chosen, see AdvertLoadingTrait::loadParameters()).

IMPORTANT: reads the raw POST, not the form object: prepareParameterForm() runs in createComponentEditForm() before addCategoryFields(), so the categories multiplier is not attached yet.

Return values
array<string|int, Category>

resolveAnyRegionParameterField()

Returns the region parameter from the code list, independent of the selected category; used only to register the picker so suggest works on the add form too. Returns the first active root parameter of type `SELECT_REGION` (or legacy 37) or `REGION_PICKER`; null means the module has no region parameter.

private resolveAnyRegionParameterField() : array{key: string, type: int}|null
Return values
array{key: string, type: int}|null

resolveCategoryLabel()

private resolveCategoryLabel(int $id) : string|null
Parameters
$id : int
Return values
string|null

resolveEditedAdvertCategories()

Returns the categories of the edited advert (from `categoryRels`); empty for a new advert.

private resolveEditedAdvertCategories() : array<string|int, Category>
Return values
array<string|int, Category>

resolveRegionParameterField()

Returns the form field of the region parameter: `key` is `parameter_<id>` (the picker `targetField`) and `type` decides the picker (`SELECT_REGION` or legacy 37: tree picker, `REGION_PICKER`: municipality or postcode suggest). Null means the system has no region parameter, so no picker is registered.

private resolveRegionParameterField() : array{key: string, type: int}|null

IMPORTANT: on the add form no category is selected yet and parameterFormItems is empty. Without the fallback to the parameter code list the picker would not be registered, the standalone GET signal picker-…-suggest would end with 404 and the region could be chosen only through the modal.

Return values
array{key: string, type: int}|null

resolveUploaderToken()

private resolveUploaderToken(string $componentName, string $postField) : string|null
Parameters
$componentName : string
$postField : string
Return values
string|null

saveAdvertForm()

Saves the advert with notifications suppressed (`meta['suppressNotifications']`).

private saveAdvertForm(ArrayHash $values) : bool

The dynamic parameters (form values) are built directly as the object-graph collection $advert->parameterRels; the Api beforeSave recomputes advert.price (sorting) and geocodes the coordinates, and the rels are written by a real Doctrine cascade and orphanRemoval persist. parametersTouched is only an internal gate flag that keeps geocoding from running on a save without submitted parameters (the collection itself is always truthy, even when unchanged). Categories (categoryRels) are built as an object-graph collection too (applyCategoryRels()) and always applied, even when empty (remove-all is legitimate and orphanRemoval deletes them). meta['assets'] makes the Api sync images.

Parameters
$values : ArrayHash
Return values
bool

suggestCategories()

private suggestCategories(string $query) : array<int, array{id: int, label: string}>
Parameters
$query : string
Return values
array<int, array{id: int, label: string}>

syncUploaderTokenFields()

Aligns the hidden token fields with the real uploader temp directories.

private syncUploaderTokenFields(Form $form) : void

Called from AJAX redraw handlers. addHidden() sets only a default and on a submitted form the POSTed value wins; on the add form it is empty because the uploaders did not exist at submit time. Without this the hidden field renders empty and buildAssetsMeta() cannot tell which temp directory holds the uploaded files on save.

Parameters
$form : Form
On this page

Search results