PageFormTrait
Add/edit form of a front-end page.
The skeleton comes from createForm(); fields are split into addXxxFields(), labels use
tree translation keys via $t = $form->getTranslateFnc(). Saving goes through
manager->save() and then flash plus PRG redirect.
The relation field parentId is a hidden form field rendered by
Picker (see pickers() in the
presenter). The route multiselect is filled through
RouteManager and saved via the M:N
Page::$routes. The css/layout file selects are plain text inputs when the theme offers no files.
Table of Contents
Constants
- CHANGEFREQ_VALUES : mixed = ['always', 'hourly', 'daily', 'weekly', 'monthl...
- Allowed `changefreq` (sitemap) values; translated in the template through options.
Methods
- createComponentEditForm() : Form
- addBehaviorFields() : void
- Visibility and behavior switches of the page.
- addCssField() : void
- CSS files of the page: multiselect of theme CSS files ({@see \App\UI\Admin\System\Models\Managers\PageManager::scanTemplateCssFiles()}), stored as a comma-separated list of names. Degrades to a text input without files.
- addLayoutField() : void
- Layout field: select of layout files of the active front-end theme ({@see \App\UI\Admin\System\Models\Managers\PageManager::scanTemplateLayoutFiles()}).
- addNavigationFields() : void
- Navigation and structure: parent page (picker) and theme layout files.
- addRouteFields() : void
- Routes assigned to the page: multiselect filled through {@see \App\UI\Admin\System\Models\Managers\RouteManager}.
- addSeoFields() : void
- SEO: update frequency and sitemap priority.
- addTechnicalFields() : void
- Technical settings: CSS files (multiselect), HTML class, internal notes.
- addTranslationFields() : void
- Multilingual fields: one set per language (`<field>_<langId>`), active by checkbox.
- applyRoutes() : void
- Overwrites the M:N relation of the page to routes by the ids selected in the multiselect.
- buildPageEntity() : Page
- buildRouteMenuProblemMessage() : string
- Builds the finished message text (no translation key); {@see BaseControl::addError()} receives it with `translate: false`.
- collectRouteIds() : array<int, int>
- Returns ids of routes assigned to the page (to prefill the multiselect on edit).
-
collectRouteMenuProblems()
: array<int, array{route: string, params: list
, lang: string|null}> - Decides, for every selected route, what exactly would break the menu link.
- cssToString() : string
- Converts the CSS field value to the stored string: the multiselect returns a name array (joined by commas), the text input a plain string.
- findPageByHref() : Page|null
- Returns a page with the same `href` in the given language (other than the edited one).
- getFormLanguages() : array<int, Language>
- Returns languages for the multilingual form fields (admin `LanguageManager`).
- languageLabel() : string
- Short language code for the message (`cs`, `en`), not the full name: technical context of the route choice.
- pageIsInMenu() : bool
- Tells whether the page `$pageId` is linked in any menu. Only existence, not the item: `cms_system_menu_items.cms_system_pages_page_id` filtered through the `page.id` association ({@see EqualFilter} auto-joins the dotted path, like `CategoryManager::findAll()` does for `'translations.href'`).
- pageTitleForLang() : string
- Returns the colliding page label for the message: title, else name, else `#id`.
- prepareCssOptions() : array<string, string>
- CSS multiselect options: theme files plus names stored on the page (on edit), even when the file has disappeared.
- prepareLayoutOptions() : array<string, string>
- Layout select options: theme files plus the values stored on the page (on edit), even when the file has disappeared, so the select can still be prefilled.
- prepareRouteOptions() : array<int, string>
- Options of the route multiselect: `id => "module presenter:action"`, alphabetical.
- reportCustomAssetTabFailure() : bool
- Adds a danger flash (and an optional log entry) and returns false; shared by all failed tab-save results.
- reportCustomAssetTabSuccess() : bool
- Adds a success flash plus `savedEntityId` (for `apply`) and returns true.
- saveEditForm() : bool
- Saves the form data. The redirect is done by {@see \App\Core\Base\Admin\BasePresenter::createForm()} according to the pressed button.
- savePageWithCustomAssets() : bool
- Saves the page and then its own custom code set (the "custom code" tab). The decision logic (what to call, in which order, how to react to failure) lives in {@see CustomAssetTabSaveTrait::persistPageCustomAssetsTab()}; here the result is only mapped to flash messages.
- setCustomAssetTabDefaults() : void
- Defaults of the custom code tab: set on the whole form (`setDefaults`), not `setValues` on the multiplier container. Edit = codes assigned to the page; add = codes with `addToNewPages = 1`. Without the `assign` privilege the field does not exist in the form ({@see CustomAssetTabTrait::addCustomAssetTab()}), so `setDefaults()` silently skips the key.
- setEditFormDefaults() : void
- splitCss() : array<int, string>
- Splits the stored comma-separated CSS string into file names.
- str() : string
- Safely converts a mixed form value to string (NEON/ArrayHash).
- tr() : string
- Translates a key in the form domain to a finished string (for fields with `setTranslator(null)`).
- validateHrefUniqueness() : bool
- Requires the `href` to be unique within one language.
- validateRouteMenuCompatibility() : bool
- Blocks a route whose mask needs a parameter the menu cannot fill.
Constants
CHANGEFREQ_VALUES
Allowed `changefreq` (sitemap) values; translated in the template through options.
private
mixed
CHANGEFREQ_VALUES
= ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never']
Methods
createComponentEditForm()
protected
createComponentEditForm() : Form
Return values
FormaddBehaviorFields()
Visibility and behavior switches of the page.
private
addBehaviorFields(Form $form) : void
Parameters
- $form : Form
addCssField()
CSS files of the page: multiselect of theme CSS files ({@see \App\UI\Admin\System\Models\Managers\PageManager::scanTemplateCssFiles()}), stored as a comma-separated list of names. Degrades to a text input without files.
private
addCssField(Form $form) : void
Parameters
- $form : Form
addLayoutField()
Layout field: select of layout files of the active front-end theme ({@see \App\UI\Admin\System\Models\Managers\PageManager::scanTemplateLayoutFiles()}).
private
addLayoutField(Form $form, string $name, bool $required) : void
Options are file names, so the control translator is off and the label is translated by hand. Degrades to a text input when the theme offers no file, so the form stays usable.
Parameters
- $form : Form
- $name : string
- $required : bool
addNavigationFields()
Navigation and structure: parent page (picker) and theme layout files.
private
addNavigationFields(Form $form) : void
Parameters
- $form : Form
addRouteFields()
Routes assigned to the page: multiselect filled through {@see \App\UI\Admin\System\Models\Managers\RouteManager}.
private
addRouteFields(Form $form) : void
Options are technical strings (module + presenter:action), so the control translator is
off and the label is translated by hand.
Parameters
- $form : Form
addSeoFields()
SEO: update frequency and sitemap priority.
private
addSeoFields(Form $form) : void
Parameters
- $form : Form
addTechnicalFields()
Technical settings: CSS files (multiselect), HTML class, internal notes.
private
addTechnicalFields(Form $form) : void
Parameters
- $form : Form
addTranslationFields()
Multilingual fields: one set per language (`<field>_<langId>`), active by checkbox.
private
addTranslationFields(Form $form) : void
Parameters
- $form : Form
applyRoutes()
Overwrites the M:N relation of the page to routes by the ids selected in the multiselect.
private
applyRoutes(Page $page, ArrayHash<string|int, mixed> $values) : void
Routes are loaded through RouteManager (InFilter) so that real references are stored
(Doctrine synchronizes the join table cms_system_page_route_rel).
Parameters
- $page : Page
- $values : ArrayHash<string|int, mixed>
buildPageEntity()
private
buildPageEntity(ArrayHash<string|int, mixed> $values) : Page
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
PagebuildRouteMenuProblemMessage()
Builds the finished message text (no translation key); {@see BaseControl::addError()} receives it with `translate: false`.
private
buildRouteMenuProblemMessage(array<int, array{route: string, params: list, lang: string|null}> $problems) : string
Parameters
-
$problems
: array<int, array{route: string, params: list
, lang: string|null}>
Return values
stringcollectRouteIds()
Returns ids of routes assigned to the page (to prefill the multiselect on edit).
private
collectRouteIds(Page $page) : array<int, int>
Parameters
- $page : Page
Return values
array<int, int>collectRouteMenuProblems()
Decides, for every selected route, what exactly would break the menu link.
private
collectRouteMenuProblems(array<int, Route> $routes, ArrayHash<string|int, mixed> $values) : array<int, array{route: string, params: list, lang: string|null}>
Unsatisfiable non-slug parameters (name, id, order_id, ...) are always a problem
because the menu never supplies them. A missing slug is a problem only for languages whose
href_<langId> is empty in the submitted form; all form languages are checked, not only
those with the active checkbox, because Link.latte reads href regardless of active.
Parameters
- $routes : array<int, Route>
- $values : ArrayHash<string|int, mixed>
Return values
array<int, array{route: string, params: listcssToString()
Converts the CSS field value to the stored string: the multiselect returns a name array (joined by commas), the text input a plain string.
private
cssToString(mixed $value) : string
Parameters
- $value : mixed
Return values
stringfindPageByHref()
Returns a page with the same `href` in the given language (other than the edited one).
private
findPageByHref(string $href, int $langId, int|null $excludeId) : Page|null
The translations.href filter matches pages having the address in any language (the
dotted path is auto-joined, see pageIsInMenu()), so the language match is confirmed
on the loaded translations; two filters over one collection would not guarantee the same row.
Parameters
- $href : string
- $langId : int
- $excludeId : int|null
Return values
Page|nullgetFormLanguages()
Returns languages for the multilingual form fields (admin `LanguageManager`).
private
getFormLanguages() : array<int, Language>
Return values
array<int, Language>languageLabel()
Short language code for the message (`cs`, `en`), not the full name: technical context of the route choice.
private
languageLabel(Language $language) : string
Parameters
- $language : Language
Return values
stringpageIsInMenu()
Tells whether the page `$pageId` is linked in any menu. Only existence, not the item: `cms_system_menu_items.cms_system_pages_page_id` filtered through the `page.id` association ({@see EqualFilter} auto-joins the dotted path, like `CategoryManager::findAll()` does for `'translations.href'`).
private
pageIsInMenu(int $pageId) : bool
Parameters
- $pageId : int
Return values
boolpageTitleForLang()
Returns the colliding page label for the message: title, else name, else `#id`.
private
pageTitleForLang(Page $page, int $langId) : string
Parameters
- $page : Page
- $langId : int
Return values
stringprepareCssOptions()
CSS multiselect options: theme files plus names stored on the page (on edit), even when the file has disappeared.
private
prepareCssOptions() : array<string, string>
Return values
array<string, string>prepareLayoutOptions()
Layout select options: theme files plus the values stored on the page (on edit), even when the file has disappeared, so the select can still be prefilled.
private
prepareLayoutOptions() : array<string, string>
Return values
array<string, string>prepareRouteOptions()
Options of the route multiselect: `id => "module presenter:action"`, alphabetical.
private
prepareRouteOptions() : array<int, string>
Return values
array<int, string>reportCustomAssetTabFailure()
Adds a danger flash (and an optional log entry) and returns false; shared by all failed tab-save results.
private
reportCustomAssetTabFailure(string $messageKey[, string|null $logMessage = null ]) : bool
Parameters
- $messageKey : string
- $logMessage : string|null = null
Return values
boolreportCustomAssetTabSuccess()
Adds a success flash plus `savedEntityId` (for `apply`) and returns true.
private
reportCustomAssetTabSuccess(Page $page) : bool
Parameters
- $page : Page
Return values
boolsaveEditForm()
Saves the form data. The redirect is done by {@see \App\Core\Base\Admin\BasePresenter::createForm()} according to the pressed button.
private
saveEditForm(ArrayHash<string|int, mixed> $values) : bool
A parentId change of an existing page is reported by the Api through
meta['moveSubtreeToParentId'], so the lft/rgt/level recalculation of the whole
subtree runs in the same transaction as the parent_id write.
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
bool —true when the page was saved
savePageWithCustomAssets()
Saves the page and then its own custom code set (the "custom code" tab). The decision logic (what to call, in which order, how to react to failure) lives in {@see CustomAssetTabSaveTrait::persistPageCustomAssetsTab()}; here the result is only mapped to flash messages.
private
savePageWithCustomAssets(ArrayHash<string|int, mixed> $values, Page $page, array<string, mixed> $meta, bool $isInsert) : bool
Parameters
- $values : ArrayHash<string|int, mixed>
- $page : Page
- $meta : array<string, mixed>
- $isInsert : bool
-
true when the page was created in this request
Return values
boolsetCustomAssetTabDefaults()
Defaults of the custom code tab: set on the whole form (`setDefaults`), not `setValues` on the multiplier container. Edit = codes assigned to the page; add = codes with `addToNewPages = 1`. Without the `assign` privilege the field does not exist in the form ({@see CustomAssetTabTrait::addCustomAssetTab()}), so `setDefaults()` silently skips the key.
private
setCustomAssetTabDefaults(Form $form) : void
Parameters
- $form : Form
setEditFormDefaults()
private
setEditFormDefaults(Form $form) : void
Parameters
- $form : Form
splitCss()
Splits the stored comma-separated CSS string into file names.
private
splitCss(string|null $css) : array<int, string>
Parameters
- $css : string|null
Return values
array<int, string>str()
Safely converts a mixed form value to string (NEON/ArrayHash).
private
str(mixed $value) : string
Parameters
- $value : mixed
Return values
stringtr()
Translates a key in the form domain to a finished string (for fields with `setTranslator(null)`).
private
tr(string $key) : string
Parameters
- $key : string
Return values
stringvalidateHrefUniqueness()
Requires the `href` to be unique within one language.
private
validateHrefUniqueness(ArrayHash<string|int, mixed> $values) : bool
IMPORTANT: the router derives the page id from the slug through
slugToId[language][href] = pageId (PageSlugTranslator).
On a collision the last written page overrides the previous one, which becomes unreachable
on the website, while idToSlug still generates the same address for both: a redirect
after an action leads to the wrong page and no error appears.
The check is global on purpose (across modules and tree branches) because the router map
is one per language. An empty href is not checked; such a page is not in the map.
Parameters
- $values : ArrayHash<string|int, mixed>
Return values
boolvalidateRouteMenuCompatibility()
Blocks a route whose mask needs a parameter the menu cannot fill.
private
validateRouteMenuCompatibility(ArrayHash<string|int, mixed> $values) : bool
A page placed in the menu (cms_system_menu_items) gets such a route, and the menu would
crash: MenuComponent passes only slug (from the page translation href) to
safeLink() (\App\UI\Front\System\Components\MenuComponent\Templates\Default\parts\Link.latte).
SafeLinkExtension would degrade the link to '#', but that is a safety net, not a goal.
IMPORTANT: the boundary is "the page is in the menu", not "the route has parameters".
Parametric routes (for example article detail with name and id) are legitimate: the list
component supplies the parameters and no link is generated from the menu context. A general
"route has an unsatisfiable parameter" check would forbid that widely used pattern.
A new page ($this->editedPage === null) cannot be in the menu, because the menu links an
existing page_id (MenuItemFormTrait),
so the check is skipped on add.
Every selected route is checked, not only getRoutes()->first() that the menu really uses:
the order of the stored collection is not the order of the multiselect selection.
Parameters
- $values : ArrayHash<string|int, mixed>