PrestaShop Module Development
Overview
PrestaShop is an open-source PHP e-commerce platform that has undergone significant architectural evolution. PrestaShop 1.7+ introduced Symfony components for the back office, and PrestaShop 8.x completed the transition to a Symfony-first architecture for all admin pages. Modules remain the primary extension mechanism, built as self-contained PHP packages that integrate through a powerful hook system, ObjectModel ORM (legacy), Doctrine ORM (modern), and Symfony service containers.
A PrestaShop module is a directory inside /modules/ containing at minimum a main PHP class file and a config.xml descriptor. The main class extends Module and implements install(), uninstall(), and hook callback methods. Modules can add admin pages, modify the storefront, extend the checkout, integrate payment gateways, add shipping carriers, and interact with the PrestaShop webservices API.
PrestaShop 8.x runs on PHP 7.2.5+ (PHP 8.1 recommended), Symfony 4.4, and supports MySQL 5.6+ or MariaDB 10.x. The template engine is Smarty for the front office and Twig for the back office (Symfony controllers). Understanding both rendering engines is essential for full-stack module development.
For detailed directory structure, module lifecycle, ObjectModel, and Doctrine entity patterns, see references/module-architecture.md.
Module Structure
Every module follows a predictable directory layout. The module name must match the directory name and the main class name, using lowercase alphanumeric characters and underscores only.
Main Module File
The main file (mymodule.php) defines a class extending Module. The constructor sets metadata:
class MyModule extends Module
{
public function __construct()
{
$this->name = 'mymodule';
$this->tab = 'front_office_features';
$this->version = '1.0.0';
$this->author = 'Author Name';
$this->need_instance = 0;
$this->ps_versions_compliancy = ['min' => '1.7.0.0', 'max' => '8.99.99'];
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('My Module');
$this->description = $this->l('Module description.');
}
}
config.xml
PrestaShop reads config.xml to display module information in the back office before loading PHP. This file mirrors the constructor metadata and is auto-generated on first install but should be maintained manually for marketplace submissions.
composer.json
Modern modules include composer.json for autoloading and dependency management. Use PSR-4 autoloading to organize classes into namespaces. Register the autoloader in the main module file or rely on PrestaShop's module autoloader for simple structures.
Controllers
Modules can register front controllers (customer-facing pages) and admin controllers (back office pages). Front controllers extend ModuleFrontController and live in controllers/front/. Admin controllers extend ModuleAdminController (legacy) or use Symfony controllers with routes defined in config/routes.yml.
Views
Template files for the front office use Smarty (.tpl extension) stored in views/templates/front/. Admin templates for Symfony controllers use Twig (.html.twig) stored in views/templates/admin/. CSS and JavaScript assets go in views/css/ and views/js/.
For the complete directory tree and file conventions, see references/module-architecture.md.
Hook System
Hooks are the primary integration mechanism in PrestaShop. They allow modules to inject content into specific locations (display hooks) or react to system events (action hooks). PrestaShop defines hundreds of hooks across the front office, back office, and core business logic.
Display Hooks
Display hooks inject HTML at specific template positions:
displayHeader-- Add CSS/JS to the page<head>.displayHome-- Render content on the homepage.displayProductExtraContent-- Add tabs or sections to the product page.displayShoppingCartFooter-- Add content below the cart summary.displayPaymentReturn-- Show confirmation content after payment.displayBackOfficeHeader-- Add assets to the admin header.
Action Hooks
Action hooks fire when business events occur:
actionProductSave-- Triggered after a product is saved in the back office.actionOrderStatusUpdate-- Triggered when an order status changes.actionCartSave-- Triggered when the cart is modified.actionCustomerAccountAdd-- Triggered when a new customer registers.actionValidateOrder-- Triggered during order validation (before finalization).
Hook Registration
Register hooks in the install() method. Every hook the module uses must be explicitly registered:
public function install()
{
return parent::install()
&& $this->registerHook('displayHome')
&& $this->registerHook('actionOrderStatusUpdate');
}
Implement the hook callback as a method named hook + hook name (camelCase):
public function hookDisplayHome($params)
{
$this->context->smarty->assign(['variable' => 'value']);
return $this->display(__FILE__, 'views/templates/front/home.tpl');
}
For the complete hook catalog, custom hook creation, and advanced registration patterns, see references/hooks-reference.md.
Development Setup
Docker Environment
Use the official PrestaShop Docker image for local development:
services:
prestashop:
image: prestashop/prestashop:8-apache
ports:
- "8080:80"
environment:
DB_SERVER: db
DB_NAME: prestashop
DB_USER: root
DB_PASSWD: admin
PS_INSTALL_AUTO: 1
PS_DOMAIN: localhost:8080
PS_FOLDER_ADMIN: admin-dev
volumes:
- ./modules/mymodule:/var/www/html/modules/mymodule
depends_on:
- db
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: prestashop
Mount the module directory as a volume to enable live code reloading. Access the admin panel at http://localhost:8080/admin-dev.
Module Generator
Use the PrestaShop module generator to scaffold a new module:
# Install the generator globally
composer global require prestashop/module-generator
# Generate a module skeleton
prestashop:module:generate mymodule
Alternatively, copy a minimal module template and customize. Enable debug mode in config/defines.inc.php by setting _PS_MODE_DEV_ to true for detailed error reporting during development.
Development Tools
- PrestaShop Console -- CLI tool for cache clearing, module installation, and database migrations.
- Symfony Debug Toolbar -- Available in the back office when debug mode is enabled. Shows queries, hooks, and service container.
- PHP CS Fixer -- Use the PrestaShop coding standards configuration for consistent formatting.
Key Integration Patterns
Payment Modules
Payment modules extend PaymentModule and implement specific hooks and interfaces. The payment flow in PrestaShop 1.7+/8.x uses the paymentOptions hook:
public function hookPaymentOptions($params)
{
$option = new PrestaShop\PrestaShop\Core\Payment\PaymentOption();
$option->setModuleName($this->name)
->setCallToActionText($this->l('Pay by Card'))
->setAction($this->context->link->getModuleLink($this->name, 'validation', [], true));
return [$option];
}
Implement a validation front controller that processes the payment, calls the gateway API, and uses $this->module->validateOrder() to create the order. Handle success, failure, and pending states. Store the transaction ID on the order for reference.
Carrier Modules
Carrier modules extend CarrierModule and implement getOrderShippingCost() and getOrderShippingCostExternal(). Register the carrier in install() using the Carrier ObjectModel. Return shipping rates based on cart weight, dimensions, and destination.
Admin Controllers
Legacy admin controllers extend ModuleAdminController and use HelperForm and HelperList for rendering. Modern modules should use Symfony controllers with Twig templates and register routes in config/routes.yml. Both approaches are covered in detail in references/admin-and-backoffice.md.
Symfony Integration (PrestaShop 8.x)
PrestaShop 8.x embraces Symfony conventions for back office development. Modules can define services, use dependency injection, register Symfony routes, and render Twig templates.
Service Definition
Define services in config/services.yml:
services:
mymodule.product_service:
class: MyModule\Service\ProductService
arguments:
- '@doctrine.orm.entity_manager'
- '@prestashop.adapter.legacy.context'
Routing
Register routes in config/routes.yml:
mymodule_admin_settings:
path: /mymodule/settings
methods: [GET, POST]
defaults:
_controller: 'MyModule\Controller\Admin\SettingsController::indexAction'
_legacy_controller: AdminMyModuleSettings
_legacy_link: AdminMyModuleSettings
Modern Controllers
Symfony controllers extend FrameworkBundleAdminController and use Twig for rendering:
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
class SettingsController extends FrameworkBundleAdminController
{
public function indexAction(Request $request)
{
return $this->render('@Modules/mymodule/views/templates/admin/settings.html.twig', [
'layoutTitle' => 'My Module Settings',
]);
}
}
For complete Symfony integration patterns including form handling, CQRS commands, and Grid components, see references/admin-and-backoffice.md.
Webservices API
PrestaShop includes a built-in REST-like webservices API for external integrations. The API exposes resources (products, orders, customers, categories) as XML or JSON endpoints with API key authentication.
Modules can extend the webservices by registering custom resources through the addWebserviceResources hook. This allows external systems to interact with module-specific data through the standard PrestaShop API.
For complete webservices documentation including CRUD operations, filtering, and custom resource creation, see references/webservices-api.md.
Anti-Patterns
Avoid these common mistakes in PrestaShop module development:
- Direct database queries bypassing ObjectModel -- Use
ObjectModelsubclasses or Doctrine entities for data access. Raw SQL creates upgrade fragility and skips validation hooks. - Overriding core classes -- PrestaShop supports class overrides in
/override/, but overrides conflict between modules and break on core updates. Use hooks instead. - Hardcoded SQL table prefixes -- Always use
_DB_PREFIX_constant. PrestaShop installations may use custom table prefixes. - Ignoring multishop context -- PrestaShop supports multiple shops from a single installation. Always check
Shop::getContext()and scope queries to the active shop. - Storing configuration in files -- Use
Configuration::updateValue()andConfiguration::get()for module settings. File-based config breaks in multi-server and multishop environments. - Skipping uninstall cleanup -- Remove database tables, configuration values, hooks, and tabs in
uninstall(). Leftover data causes errors on reinstall. - Using deprecated payment hooks -- PrestaShop 1.7+ replaced
displayPaymentanddisplayPaymentEUwithpaymentOptions. Use the modern approach for compatibility.
For deeper guidance on each anti-pattern and recommended alternatives, consult the relevant reference files linked throughout this document.
Reference Files
- Module Architecture -- Directory structure, module lifecycle, ObjectModel, Doctrine entities, configuration management, auto-upgrade
- Hooks Reference -- Display hooks, action hooks, custom hooks, hook registration, widget hooks, dynamic hooks
- Webservices API -- REST API, XML/JSON output, API key auth, resource endpoints, CRUD operations, filtering, custom resources
- Admin and Back Office -- Legacy vs Symfony controllers, HelperForm, HelperList, Twig templates, Grid components, module configuration pages
- Marketplace Publishing -- PrestaShop Addons submission, technical validation, module checker, documentation requirements, pricing, version compatibility