CompanyFormTrait uses trait:short, trait:short
Own company add/edit form: fieldset sections (basics, contact and address, presentation, opening hours, highlights, content blocks, team, switches) plus the `activated` header toggle.
The skeleton (check, domain translator, save/apply/cancel buttons and return) is
BasePresenter::createForm(). The seller (invoicerSellerId) is a Picker
(SellerPickerTrait);
invoicerBillingLineId, invoicerInvoiceTypeId and currencyId are DB-driven selects.
Image channels:
- logo via Form::addImageUpload(): the bytes go to
$meta['logo']and CompanyManager creates theAssetvariants, - profile gallery (photos and videos; replaces the single-image
photochannel, kept in the Api only for backward compatibility) via theuploaderDropzone component: the ordered list (existing + new) goes to the Api in$meta['orderedAssets']; the first item is the hero photo (photoImageId). The form carries only the hiddendropzoneToken(same as ReferenceFormTrait).
IMPORTANT: highlights, content blocks and team members are not owned collections of
Company (their entities hold a scalar companyId, not a ManyToOne association), so
they are not saved by the object graph with the company. Each has its own sync
(saveHighlights(), saveBlocks(), saveTeamMembers()) through its
Admin manager: load existing, build entities from the form, save, delete removed rows.
This is the only deviation from the object-graph rule in this form.
Team rows carry an <input type=file> (portrait), so the team uses the client-side
multiplier (addTeamFields()), not the server-side/Naja one used by highlights and
blocks; see also GalleryFormTrait.
Table of Contents
Constants
- SUPPLY_KIND_SETTING_MODULE_INVOICER : mixed = 7
- Module "invoicer" in the settings table (`cms_system_settings.module`); holds the portal `sellerId`.
Properties
- $supplyKindFieldsManager : SupplyKindManager
- $supplyKindFieldsSellerManager : SellerManager
- $supplyKindFieldsVatRateManager : VatRateManager
Methods
- addSupplyKindSelect() : SelectBox
- Adds the supply kind select. The options are database names, so the select translator is off and the prompt is translated here. IMPORTANT: the label must stay a translation key (`BaseControl::getLabel()` takes the translator from the form); a translated label would be translated twice.
- buildSupplyKindRegionVatMaps() : array<int, array<int, string>>
- Returns the rate map for the helper field "price with VAT": `regionId => supplyKindId => percent` (valid today).
-
buildSupplyKindVatMapAttributes()
: array<int, array{json: string, map: array
}> - Returns the same as {@see buildSupplyKindRegionVatMaps()} plus JSON for the `data-price-vat-map` attribute of the region matrix (keys as strings, because JS reads `map[select.value]`).
- createComponentEditForm() : Form
- getSupplyKindOptions() : array<int, string>
- Returns active supply kinds as `id => name` in the admin language.
- resolveSupplyKindDefaultId() : int|null
- supplyKindIdValue() : int|null
- Converts the supply kind select value to an id, or null for the empty prompt.
- addBasicFields() : void
- Basics section: name, seller (picker), invoicing defaults, currency, default hourly rate and VAT rate.
- addBehaviorFields() : void
- Switches section: `bookingEnabled`/`ratingEnabled`. `activated` is added here too (a PHP form field) but the template renders it as the header toggle (`admin-status-toggle`), not in this fieldset (the single global activation flag).
- addBlockFields() : void
- Content blocks section: a server-side Multiplier, same mechanics as highlights. A row is hidden `id`, `identifier` (anchor for the template), `public` and per language `title_<langId>`/`content_<langId>`. Saved by sync, see {@see saveBlocks()}.
- addContactFields() : void
- Contact and address section.
- addGalleryFields() : void
- The profile gallery is a Dropzone uploader (`uploader` component). The form carries only the hidden `dropzoneToken` (identifies the temp folder); the component holds the selected files (AJAX upload to temp) and the ordered list (existing + new) arrives in the POST as `uploaderItems[]`. On re-render (validation error) the token is read from the POST.
- addHighlightFields() : void
- Highlights section: a server-side Multiplier (no file inputs, Naja add/remove works).
- addOpeningHoursFields() : void
- Opening hours section: day 1..7 (Mon..Sun) x interval 0..1 (`oh_{dow}_{i}_from/_to`) plus slot length and reservation lead time. Normalized on save by {@see buildOpeningHours()}.
- addTeamFields() : void
- Team section: a client-side Multiplier (`client-multiplier.js`, opt-in `[data-multiplier]` in the template), unlike highlights/blocks.
- addTranslationFields() : void
- Presentation section: language tabs (title/slogan/annotation/description) plus the logo (not per language). `CompanyText` has no own `name` (it is on `Company`), only the `active` translation. The hero photo is the first item of the profile gallery, see `addGalleryFields()`.
- buildCompanyEntity() : Company
- Builds the company entity. On edit the existing `$this->editedCompany` is mutated (not a new instance), so fields the form does not edit (`logoImageId`/`photoImageId`, `ratingThreadId`, `deleted`, insert/update datetime) stay unchanged. The logo/photo FK is set later by the Api `processCompanyImage()` after the main save (`updateColumn`).
- buildOpeningHours() : array<int, array<int, array{0: string, 1: string}>>|null
- Builds `[dow => [[from,to],…]]` from the filled pairs only and normalizes it via {@see OpeningHours}. An empty result returns null (no opening hours).
- collectImageMeta() : array<string, mixed>
- Builds `$meta` for the save: a new logo (base64) and/or the delete flag. The Api creates the `Asset` (variants) and sets the FK. The hero photo/gallery goes separately via {@see collectOrderedAssets()}.
- collectOrderedAssets() : array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>
- Builds `meta['orderedAssets']` for the Api `afterSave` from the ordered dropzone list (`uploaderItems[]` from the POST): `existing` = id of an existing item, `dropzone` = a newly uploaded file in the temp folder (its `path` is looked up by name). The order is the sequence; the first item is the hero photo. Same as {@see ReferenceFormTrait::collectOrderedAssets()}.
- collectSingleImageMeta() : void
- collectTeamPhotoMeta() : array<string, mixed>
- Builds the portrait `$meta` of one team row for `save()`, same mechanics as {@see ReferenceFormTrait::collectLogoMeta()} but over the values of one multiplier row (not the whole `$values`).
- currentLangId() : int|null
- Returns the current language id (for labels), or null when unknown.
- getFormLanguages() : array<int, Language>
- Returns the languages for the multilingual form fields (admin `LanguageManager`).
- loadBlockDefaults() : array<int, array<string, mixed>>
- Returns the company content blocks as Multiplier rows (edit prefill), sorted by the stored `order`.
- loadHighlightDefaults() : array<int, array<string, mixed>>
- Returns the company highlights as Multiplier rows (edit prefill), sorted by the stored `order`.
- loadTeamDefaults() : array<int, array<string, mixed>>
- Returns the team members as Multiplier rows (edit prefill), sorted by the stored `order`.
- prepareBillingLineOptions() : array<int, NotTranslate>
- prepareCurrencyOptions() : array<int, NotTranslate>
- The name is already translated DB data, not a key, hence `NotTranslate`.
- prepareInvoiceTypeOptions() : array<int, NotTranslate>
- The name is already translated DB data, not a key, hence `NotTranslate`.
- resolveSupplyKindSellerCountryCode() : string|null
- Returns the portal seller country (`7/invoice.default.sellerId`) as a fallback for a region without a country; null when the seller is not found.
- saveBlocks() : bool
- Syncs content blocks, same procedure as {@see saveHighlights()}: find the existing blocks of the company, build entities from the form (a new instance with `setId` for existing rows), save and delete removed rows.
- saveEditForm() : bool
- Saves the form data. The redirect (to the previous page / "this") is handled by {@see \App\Core\Base\Admin\BasePresenter::createForm()}.
- saveHighlights() : bool
- Syncs highlights: the only deviation from the object-graph rule in this form (see the trait docblock). Finds the existing highlights of the company, builds entities from the form (a new instance with `setId` for existing rows), saves them and deletes removed rows (ids among the existing ones but not in the form).
- saveTeamMembers() : bool
- Syncs team members, same principle as {@see saveHighlights()} and {@see saveBlocks()} (`TeamMember` is a separate entity with a scalar `companyId`, not an owned collection).
- setEditFormDefaults() : void
- str() : string
- Safely converts a mixed form value (NEON/ArrayHash) to a string.
- tr() : NotTranslate
- Translates a key in the form domain into a ready string (for fields with `setTranslator(null)`).
- translatedName() : string
- Returns the entity name (with `translations`) in the current language, falling back to the first non-empty one.
Constants
SUPPLY_KIND_SETTING_MODULE_INVOICER
Module "invoicer" in the settings table (`cms_system_settings.module`); holds the portal `sellerId`.
private
mixed
SUPPLY_KIND_SETTING_MODULE_INVOICER
= 7
Properties
$supplyKindFieldsManager
public
SupplyKindManager
$supplyKindFieldsManager
Attributes
- #[Inject]
$supplyKindFieldsSellerManager
public
SellerManager
$supplyKindFieldsSellerManager
Attributes
- #[Inject]
$supplyKindFieldsVatRateManager
public
VatRateManager
$supplyKindFieldsVatRateManager
Attributes
- #[Inject]
Methods
addSupplyKindSelect()
Adds the supply kind select. The options are database names, so the select translator is off and the prompt is translated here. IMPORTANT: the label must stay a translation key (`BaseControl::getLabel()` takes the translator from the form); a translated label would be translated twice.
protected
addSupplyKindSelect(Form $form, string $name, string $labelKey, string|null $promptKey[, string|null $descriptionKey = null ][, int|null $currentId = null ]) : SelectBox
Parameters
- $form : Form
- $name : string
- $labelKey : string
- $promptKey : string|null
-
null = no prompt (the field is required, the first or current kind is preselected)
- $descriptionKey : string|null = null
- $currentId : int|null = null
Return values
SelectBoxbuildSupplyKindRegionVatMaps()
Returns the rate map for the helper field "price with VAT": `regionId => supplyKindId => percent` (valid today).
protected
buildSupplyKindRegionVatMaps(iterable<string|int, PaymentRegion> $regions) : array<int, array<int, string>>
Parameters
- $regions : iterable<string|int, PaymentRegion>
Return values
array<int, array<int, string>>buildSupplyKindVatMapAttributes()
Returns the same as {@see buildSupplyKindRegionVatMaps()} plus JSON for the `data-price-vat-map` attribute of the region matrix (keys as strings, because JS reads `map[select.value]`).
protected
buildSupplyKindVatMapAttributes(iterable<string|int, PaymentRegion> $regions) : array<int, array{json: string, map: array}>
For the template: regionId => {json, map}.
Parameters
- $regions : iterable<string|int, PaymentRegion>
Return values
array<int, array{json: string, map: arraycreateComponentEditForm()
protected
createComponentEditForm() : Form
Return values
FormgetSupplyKindOptions()
Returns active supply kinds as `id => name` in the admin language.
protected
getSupplyKindOptions([int|null $keepId = null ]) : array<int, string>
Parameters
- $keepId : int|null = null
Return values
array<int, string>resolveSupplyKindDefaultId()
protected
resolveSupplyKindDefaultId(SupplyKindManager $manager, array<int, mixed> $options[, string|null $preferredKey = null ]) : int|null
Parameters
- $manager : SupplyKindManager
- $options : array<int, mixed>
-
select options (
SupplyKindManager::findSelectOptions()) - $preferredKey : string|null = null
Return values
int|nullsupplyKindIdValue()
Converts the supply kind select value to an id, or null for the empty prompt.
protected
supplyKindIdValue(mixed $value) : int|null
Parameters
- $value : mixed
Return values
int|nulladdBasicFields()
Basics section: name, seller (picker), invoicing defaults, currency, default hourly rate and VAT rate.
private
addBasicFields(Form $form) : void
Parameters
- $form : Form
addBehaviorFields()
Switches section: `bookingEnabled`/`ratingEnabled`. `activated` is added here too (a PHP form field) but the template renders it as the header toggle (`admin-status-toggle`), not in this fieldset (the single global activation flag).
private
addBehaviorFields(Form $form) : void
Parameters
- $form : Form
addBlockFields()
Content blocks section: a server-side Multiplier, same mechanics as highlights. A row is hidden `id`, `identifier` (anchor for the template), `public` and per language `title_<langId>`/`content_<langId>`. Saved by sync, see {@see saveBlocks()}.
private
addBlockFields(Form $form) : void
content is a plain textarea on purpose, not addEditor(): a block holds ready-made HTML
and a rich-text editor would rewrite or trim it on save.
IMPORTANT: there is no order field; as for highlights, row order is the DOM position
and saveBlocks() derives it.
Parameters
- $form : Form
addContactFields()
Contact and address section.
private
addContactFields(Form $form) : void
Parameters
- $form : Form
addGalleryFields()
The profile gallery is a Dropzone uploader (`uploader` component). The form carries only the hidden `dropzoneToken` (identifies the temp folder); the component holds the selected files (AJAX upload to temp) and the ordered list (existing + new) arrives in the POST as `uploaderItems[]`. On re-render (validation error) the token is read from the POST.
private
addGalleryFields(Form $form) : void
Parameters
- $form : Form
addHighlightFields()
Highlights section: a server-side Multiplier (no file inputs, Naja add/remove works).
private
addHighlightFields(Form $form) : void
A row is hidden id (existing highlight), icon, public and per language
label_<langId>/value_<langId>. Saved by sync, not the object graph, see
saveHighlights() and the trait docblock.
IMPORTANT: there is no order field. Row order is the DOM position (drag handle,
[data-sortable] in the template) and the server derives it from the order of the posted
array in saveHighlights().
Parameters
- $form : Form
addOpeningHoursFields()
Opening hours section: day 1..7 (Mon..Sun) x interval 0..1 (`oh_{dow}_{i}_from/_to`) plus slot length and reservation lead time. Normalized on save by {@see buildOpeningHours()}.
private
addOpeningHoursFields(Form $form) : void
Parameters
- $form : Form
addTeamFields()
Team section: a client-side Multiplier (`client-multiplier.js`, opt-in `[data-multiplier]` in the template), unlike highlights/blocks.
private
addTeamFields(Form $form) : void
IMPORTANT: a row carries an <input type=file> (portrait), and any Naja snippet redraw
would wipe the file the browser selected (a file input cannot be filled programmatically).
So the server-side Multiplier has no addCreateButton()/addRemoveButton() and serves only
as a container; adding/removing rows in the DOM is done by client JS (see edit.latte and
GalleryFormTrait::addImageMultiplier()).
A row is a plain Container, so addText()/addInteger() do not add the
Bootstrap form-control class (only BootstrapForm decorates itself); it is set by hand.
photoUrl is an extra hidden field (not saved, ignored on the save path) that carries the
preview URL of the existing portrait so the template renders it without an Asset query
in the loop (same trick as imageUrl in
GalleryFormTrait::addImageRowControls()).
description_<langId> is addSimpleEditor() (trimmed TinyMCE), not addTextArea() as in
blocks: it is a short editorial text written in the admin, not ready-made HTML.
addEditor()/addImageUpload() work on a plain Container through the extension methods
(Form::registerExtensionMethods()).
IMPORTANT: there is no order field; as for highlights/blocks, row order is the DOM
position (drag handle, [data-sortable]) and the server derives it in saveTeamMembers().
SortableJS binds to the container on page load, so a freshly cloned row fits in
automatically. Saved by sync, see saveTeamMembers().
Parameters
- $form : Form
addTranslationFields()
Presentation section: language tabs (title/slogan/annotation/description) plus the logo (not per language). `CompanyText` has no own `name` (it is on `Company`), only the `active` translation. The hero photo is the first item of the profile gallery, see `addGalleryFields()`.
private
addTranslationFields(Form $form) : void
Parameters
- $form : Form
buildCompanyEntity()
Builds the company entity. On edit the existing `$this->editedCompany` is mutated (not a new instance), so fields the form does not edit (`logoImageId`/`photoImageId`, `ratingThreadId`, `deleted`, insert/update datetime) stay unchanged. The logo/photo FK is set later by the Api `processCompanyImage()` after the main save (`updateColumn`).
private
buildCompanyEntity(ArrayHash<string|int, mixed> $values) : Company
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
CompanybuildOpeningHours()
Builds `[dow => [[from,to],…]]` from the filled pairs only and normalizes it via {@see OpeningHours}. An empty result returns null (no opening hours).
private
buildOpeningHours(ArrayHash<string|int, mixed> $values) : array<int, array<int, array{0: string, 1: string}>>|null
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
array<int, array<int, array{0: string, 1: string}>>|nullcollectImageMeta()
Builds `$meta` for the save: a new logo (base64) and/or the delete flag. The Api creates the `Asset` (variants) and sets the FK. The hero photo/gallery goes separately via {@see collectOrderedAssets()}.
private
collectImageMeta(ArrayHash<string|int, mixed> $values) : array<string, mixed>
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
array<string, mixed>collectOrderedAssets()
Builds `meta['orderedAssets']` for the Api `afterSave` from the ordered dropzone list (`uploaderItems[]` from the POST): `existing` = id of an existing item, `dropzone` = a newly uploaded file in the temp folder (its `path` is looked up by name). The order is the sequence; the first item is the hero photo. Same as {@see ReferenceFormTrait::collectOrderedAssets()}.
private
collectOrderedAssets(ArrayHash<string|int, mixed> $values) : array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
array<int, array{type: string, imageId?: int, name?: string, path?: string, order: int}>collectSingleImageMeta()
private
collectSingleImageMeta(ArrayHash<string|int, mixed> $values, string $field, array<string, mixed> &$meta) : void
Parameters
- $values : ArrayHash<string|int, mixed>
- $field : string
- $meta : array<string, mixed>
collectTeamPhotoMeta()
Builds the portrait `$meta` of one team row for `save()`, same mechanics as {@see ReferenceFormTrait::collectLogoMeta()} but over the values of one multiplier row (not the whole `$values`).
private
collectTeamPhotoMeta(ArrayHash<string|int, mixed>|array<string, mixed> $row) : array<string, mixed>
Parameters
- $row : ArrayHash<string|int, mixed>|array<string, mixed>
Return values
array<string, mixed>currentLangId()
Returns the current language id (for labels), or null when unknown.
private
currentLangId() : int|null
Return values
int|nullgetFormLanguages()
Returns the languages for the multilingual form fields (admin `LanguageManager`).
private
getFormLanguages() : array<int, Language>
Return values
array<int, Language>loadBlockDefaults()
Returns the company content blocks as Multiplier rows (edit prefill), sorted by the stored `order`.
private
loadBlockDefaults(int $companyId) : array<int, array<string, mixed>>
Parameters
- $companyId : int
Return values
array<int, array<string, mixed>>loadHighlightDefaults()
Returns the company highlights as Multiplier rows (edit prefill), sorted by the stored `order`.
private
loadHighlightDefaults(int $companyId) : array<int, array<string, mixed>>
Parameters
- $companyId : int
Return values
array<int, array<string, mixed>>loadTeamDefaults()
Returns the team members as Multiplier rows (edit prefill), sorted by the stored `order`.
private
loadTeamDefaults(int $companyId) : array<int, array<string, mixed>>
photoUrl (portrait preview) is resolved by the presenter's
CompanyPresenter::resolveAssetUrl(), which the company
logo uses too.
Parameters
- $companyId : int
Return values
array<int, array<string, mixed>>prepareBillingLineOptions()
private
prepareBillingLineOptions() : array<int, NotTranslate>
Return values
array<int, NotTranslate>prepareCurrencyOptions()
The name is already translated DB data, not a key, hence `NotTranslate`.
private
prepareCurrencyOptions() : array<int, NotTranslate>
Return values
array<int, NotTranslate>prepareInvoiceTypeOptions()
The name is already translated DB data, not a key, hence `NotTranslate`.
private
prepareInvoiceTypeOptions() : array<int, NotTranslate>
Return values
array<int, NotTranslate>resolveSupplyKindSellerCountryCode()
Returns the portal seller country (`7/invoice.default.sellerId`) as a fallback for a region without a country; null when the seller is not found.
private
resolveSupplyKindSellerCountryCode() : string|null
Return values
string|nullsaveBlocks()
Syncs content blocks, same procedure as {@see saveHighlights()}: find the existing blocks of the company, build entities from the form (a new instance with `setId` for existing rows), save and delete removed rows.
private
saveBlocks(ArrayHash<string|int, mixed> $values, int $companyId) : bool
Parameters
- $values : ArrayHash<string|int, mixed>
- $companyId : int
Return values
boolsaveEditForm()
Saves the form data. The redirect (to the previous page / "this") is handled by {@see \App\Core\Base\Admin\BasePresenter::createForm()}.
private
saveEditForm(ArrayHash<string|int, mixed> $values) : bool
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
boolsaveHighlights()
Syncs highlights: the only deviation from the object-graph rule in this form (see the trait docblock). Finds the existing highlights of the company, builds entities from the form (a new instance with `setId` for existing rows), saves them and deletes removed rows (ids among the existing ones but not in the form).
private
saveHighlights(ArrayHash<string|int, mixed> $values, int $companyId) : bool
Parameters
- $values : ArrayHash<string|int, mixed>
- $companyId : int
Return values
boolsaveTeamMembers()
Syncs team members, same principle as {@see saveHighlights()} and {@see saveBlocks()} (`TeamMember` is a separate entity with a scalar `companyId`, not an owned collection).
private
saveTeamMembers(ArrayHash<string|int, mixed> $values, int $companyId) : bool
For every row it also builds the portrait $meta (photo/photoDelete) like
ReferenceFormTrait::collectLogoMeta() and passes it to save($member, $meta); the Api
TeamMemberManager::processPhoto() already handles
$meta['photo'].
Parameters
- $values : ArrayHash<string|int, mixed>
- $companyId : int
Return values
boolsetEditFormDefaults()
private
setEditFormDefaults(Form $form) : void
Parameters
- $form : Form
str()
Safely converts a mixed form value (NEON/ArrayHash) to a string.
private
str(mixed $value) : string
Parameters
- $value : mixed
Return values
stringtr()
Translates a key in the form domain into a ready string (for fields with `setTranslator(null)`).
private
tr(string $key) : NotTranslate
IMPORTANT: the result goes to setPrompt()/setRequired() on a field with
setTranslator(null). Prompt and items respect the control override, but Nette
getLabel() and Validator::formatMessage() always take the translator from the form;
without NotTranslate the finished text would be translated again as a key and Tracy
would report a missing translation.
Parameters
- $key : string
Return values
NotTranslatetranslatedName()
Returns the entity name (with `translations`) in the current language, falling back to the first non-empty one.
private
translatedName(object $entity) : string
Parameters
- $entity : object