prestashop-module-development
Complete PrestaShop module development workflow using modern architecture and best practices. Use when: creating new PrestaShop modules, updating legacy modules to modern code, implementing hooks and actions, setting up module configuration pages, adding front office features, handling database operations, implementing security measures, managing translations, or modernizing existing PrestaShop modules from legacy patterns to current standards.
PrestaShop Module Development
When to use
Use this skill for PrestaShop module development tasks such as:
- Creating new modules with modern architecture (Symfony controllers, services, entities)
- Refactoring legacy modules to use modern PrestaShop patterns
- Implementing hooks, actions, and event listeners
- Adding configuration pages (modern Symfony-form approach)
- Creating front office features and widgets
- Setting up database entities and migrations
- Implementing security measures (CSRF, input validation, SQL injection prevention)
- Adding multilingual support and translations
- Converting legacy code patterns (HelperForm, jQuery UI sortable, ObjectModel) to modern equivalents
- Building list pages with the PrestaShop Grid system (filters, pagination, toggle, drag-and-drop position)
Inputs required
- PrestaShop version (target 8.x/9.x for modern development)
- Module scope and functionality requirements
- Existing module path (if updating legacy code)
- Database schema requirements (if applicable)
- Front office integration needs (hooks, widgets, pages)
- Configuration requirements (settings, admin interface)
- Multilingual requirements and supported languages
Procedure
0) Project structure & namespace naming
Read: references/module-structure.md
Key rules:
- Derive PSR-4 namespace from the module name prefix — never use
PrestaShop\Module\ - Use the PrestaShop Module Generator to scaffold new modules
1) Main module class & installer
Read: references/module-class-and-installer.md
Key rules:
- Always
require_once __DIR__ . '/vendor/autoload.php';after the_PS_VERSION_guard - Never put hook registration, DB queries, or
Configuration::calls directly ininstall()— delegate tosrc/Install/Installer.php - Do NOT add
getTabs()to the main module class — manage tabs entirely viaInstaller::installTabs()/uninstallTabs() - Default parent tab is
Adminwswebsenso(shared Websenso group); check existence before creating it getContent()must only redirect to the Symfony route, never render HTML- No SQL in the main module class — all database access (including in hooks like
hookActionShopDataDuplicationand widget methods likegetWidgetVariables) must be delegated to the Repository or Manager class via$this->get('service.id') - Always guard service access — use
$this->has()+ null check in admin context; use try/catch +instanceofin front-office context (stale container can makehas()returntruewhileget()still throws). Seereferences/module-class-and-installer.md→ Guard patterns section.
2) Modern configuration pages
Read: references/configuration-page.md
Key rules:
- Do NOT use
HelperForm— use Symfony form components +FrameworkBundleAdminController - Four classes:
DataConfiguration,FormDataProvider,FormType,Controller - Wire everything in
config/services.ymlandconfig/routes.yml
3) Database operations & entities
Read: references/database-and-entities.md
For translatable entities with Grid: read references/entity-doctrine.md
- Always use Doctrine ORM (Entity + LangEntity + Repository + Manager) for any entity that has a Grid list page or translatable fields
- ObjectModel is legacy — do not use in new or modernised modules
- Entity class name = table name without
_DB_PREFIX_— PS adds the prefix globally; use@ORM\Table()with noname=parameter - All table names must start with
ws_— e.g.ws_mymodule_items,ws_mymodule_items_lang— to group Websenso tables together - Do NOT create
MetadataListeneror Doctrine event listeners for table naming - Always sanitize raw DBAL SQL: cast IDs with
(int), use bound parameters - No raw SQL (
Db::getInstance(),pSQL(),_DB_PREFIX_string concatenation) outside Repository and Manager classes. This applies everywhere: main module class, Installer, FixturesInstaller, hooks, widget methods. The only exception isInstallerSQL schema queries (CREATE TABLE,DROP TABLE) which have no Repository equivalent. - FixturesInstaller must NOT call module-own services — the module's services are not in the container at install time. Instantiate Manager directly using core Doctrine services. See
references/module-class-and-installer.md→ FixturesInstaller — service resolution section.
Services split & components architecture
Read: references/services-split.md
Key rules:
- Repository services only in
config/common.yml(Doctrine-level, noPrestaShopBundledeps) - All
PrestaShopBundle-dependent services go inconfig/admin/services.yml - Always split into component sub-folders under
config/components/— never one flatservices.yml
4) Security (mandatory)
Read: references/security.md
- CSRF: handled by Symfony forms automatically; validate manually for raw AJAX endpoints
- SQL injection:
(int)+pSQL()on every value, or useDbQuerybuilder - File uploads: pass full
$_FILES-compatible array (includingtype,size,error) toImageManager::validateUpload()
5) Hooks & front office integration
Read: references/hooks-and-front-office.md
- Register hooks in
Installer, not ininstall()directly - Load assets only for the relevant controller in
hookDisplayBackOfficeHeader - Implement
WidgetInterfacefor front office widgets
6) Translations
Read: references/translations.md
- Use
$this->trans('Text', [], 'Modules.Mymodule.Admin')in PHP - Use
'Text'|trans({}, 'Modules.Mymodule.Admin')in Twig - Declare
isUsingNewTranslationSystem(): truein the module class
7) Legacy code conversion
Read: references/legacy-conversion.md
Common conversions: HelperForm → Symfony form, jQuery UI sortable → Grid PositionColumn, ModuleAdminController → FrameworkBundleAdminController.
8) Services & dependency injection
Read: references/services-and-di.md
- Define services in
config/services.yml - Use
$this->get('service.id')in Symfony controllers - Use Expression Language (
@=) for computed constructor arguments
9) Grid system (list pages with drag-and-drop position)
Read: references/grid-system.md
Full pattern for building CRUD list pages with the PS Grid system:
GridDefinitionFactory— columns (PositionColumn,ToggleColumn,ActionColumn), filters, row actionsQueryBuilder— Doctrine DBAL query with sorting, pagination, and filtersFilters— default sort/limit settings- 5 service definitions in
services.yml(factory, query, data, grid, position) - 4 routes in
routes.yml(index, search, toggle, update-position) - 4 controller actions (
indexAction,searchAction,toggleStatus,updatePositionAction) - Pre-built JS bundle (copied from
ws-entity-grid-skeleton, grid ID replaced viased)
Verification
- Module installs without PHP errors:
php bin/console pr:mo install mymodule - Configuration saves correctly with proper validation
- Front office features display and function properly
- Translations work in all configured languages
Validation
AI agent rule — NEVER SKIP EITHER STEP. Read
references/validation.mdfor full instructions.
Step 1 — lotr (run from the module root)
Before running lotr, check that the binary is present. If not, install it first:
# Check presence
[ -f vendor/bin/lotr ] || composer require websenso/prestashop-module-devtools
# Run lotr
vendor/bin/lotr
Note:
vendor/bin/lotris the Composer-installed symlink. The direct pathvendor/websenso/prestashop-module-devtools/bin/lotralso works if present.
Expected: 🎉 All commands completed successfully! Executed: 6/6
Step 2 — Install test (run from the PS root)
php bin/console pr:mo install mymodule
Expected: L'action Install sur le module … a réussi.
Failure modes / debugging
Read: references/debugging.md
Common failure areas:
references/debugging.md— all symptom/cause/fix tables (install, config page, Grid, InputBag, ImageManager, lotr steps)
Escalation
- PrestaShop 9 Module Creation
- Module Good Practices
- Official Example Modules
- demosymfonyform — canonical Symfony form config page
- Module Validator
- PrestaShop Developer Slack