Infrastructure for building, populating, and handling back-office forms. PrestaShop has two distinct form patterns that share almost no code: settings forms (config blocks) and CRUD forms (entity add/edit). Picking the wrong pattern is the most common AI mistake — see the decision section below.
Pattern-specific rules live in companion files. Read them on demand, not preemptively:
- Settings forms:
SETTINGS.md— baseHandler, hooks, anti-patterns, allowed exception, YAML folder, canonical example. - CRUD forms:
CRUD.md— base FormBuilder/FormHandler factories, hooks, anti-pattern, YAML folder.
Three yes/no questions, in order:
- Does the page persist values into
ps_configuration(or another flat key/value store)? → settings form. - Is the data identified by an entity ID and listed in a grid for create/edit/delete? → CRUD form (a.k.a. identifiable form).
- Is there (or should there be) a CQRS
Add{Domain}Command/Edit{Domain}Commandfor it? → CRUD form. Otherwise → settings form.
A single admin page may carry both patterns side-by-side (e.g. Shop parameters > Contact > Stores has a CRUD grid for store entities and a settings form for global contact details). Treat each block independently.
| Pattern | Use case | Read | Skill |
|---|---|---|---|
| Settings form | options block, page-level configuration, fields_options migration |
SETTINGS.md |
create-settings-form |
| CRUD form | entity add/edit (with grid listing) | CRUD.md |
create-crud-form-type + create-crud-form-data-handling |
The two patterns use different service-definition folders:
src/PrestaShopBundle/Resources/config/services/bundle/form/→ settings forms (baseHandler)src/PrestaShopBundle/Resources/config/services/core/form/→ CRUD forms (IdentifiableObject factories)
Easy to mix up. Wrong folder won't raise a hard error — the service simply isn't picked up by auto-discovery, and the page fails at runtime with "service not found". Details in each pattern's companion file.
| Topic | Note |
|---|---|
| Reusable form types | PrestaShopBundle\Form\Admin\Type\ holds 80+ generic, PrestaShop-specific form types (SwitchType, TranslatableType, MoneyType, CountryChoiceType, CurrencyChoiceType, ColorPickerType, IpAddressType, EmailType, MaterialChoiceTreeType, etc., plus the Common/ and Material/ sub-namespaces). Check this namespace before reinventing a field — there is almost always a ready-made type. |
| Form extensions | PrestaShopBundle\Form\Extension\ holds 24+ Symfony AbstractTypeExtension classes that add custom options to all form types globally: help, hint, external_link, modify_all_shops, autocomplete, multistore_configuration_key, download_file, disabling_switch, etc. Check this namespace before inventing a new field option — the option you want probably already exists. |
| Field validation | For character-set / format validation, prefer TypedRegex (Core\ConstraintValidator\Constraints\TypedRegex) over an inline Symfony\...\Regex. Pass a type (e.g. TypedRegex::TYPE_GENERIC_NAME, TYPE_ZIP_CODE_FORMAT); the pattern lives once in TypedRegexValidator::getPattern(). Reuse an existing type first; if none fits, add a TYPE_* constant + a case in the validator (referencing the domain VO's *_PATTERN constant when one exists, like CallPrefix::CALL_PREFIX_PATTERN) — never hard-code a raw pattern in the FormType. NotBlank / Length are fine inline. |
| Translatable fields | TranslatableType (in Form\Admin\Type\) wrapping the inner type. Data is an array keyed by language ID. Multilingual textareas wrap TextareaType / FormattedTextareaType. |
| Money / decimal | MoneyType for static currency, AmountType for multi-currency. PrestaShop stores prices with 6 decimal places — always set explicit decimal scale, never round to float. Commands carry DecimalNumber, never native float. |
| File uploads | FileType with 'mapped' => false, 'required' => false. Actual file saving happens in the DataHandler (CRUD) or DataConfiguration (settings), not the form type. |
| Choice providers | ChoiceProviderInterface services injected into form types for dynamic select options. FormOptionsProviderInterface for options evaluated at render time (e.g. carrier lists). Located under src/Core/Form/ChoiceProvider/ (61+) + src/Adapter/Form/ChoiceProvider/ (26). |
| Tab layout | NavigationTabType is a generic Symfony composition pattern — applicable in either pattern but used today almost exclusively by complex CRUD forms (Carrier, Product). See create-form-tab-layout. |
| Form utilities | FormBuilderModifier, FormCloner, FormHelper under src/PrestaShopBundle/Form/ — tools for mutating form builders at runtime (mostly module use). |
| Skill | Trigger | Pattern detail to load |
|---|---|---|
create-settings-form |
"create settings form for {Page}" | SETTINGS.md |
create-crud-form-type |
"create CRUD form type for {Domain}" | CRUD.md |
create-crud-form-data-handling |
"create CRUD form data handling for {Domain}" | CRUD.md |
create-form-tab-layout |
"create tab layout for {Domain} form" | — (generic) |