PrestaShop modules are not just a PHP file with some hooks. A serious module has controllers, templates, assets, translations, upgrade scripts, Composer dependencies and sometimes service definitions. When the folder structure is clear, the module is easier to debug, update and support.
The official PrestaShop documentation has a useful module file structure guide. This article is the practical version: what each folder is for, what usually goes wrong, and what I look for when reviewing a module.
The root module folder
A module lives in /modules/<module_name>/. The folder name should be lowercase and predictable. The main PHP file should use the same name: mymodule/mymodule.php. This file owns installation, hooks, configuration entry points and the module metadata used by PrestaShop.
If the main file becomes thousands of lines long, it is usually a sign that business logic belongs somewhere else. Keep the module class responsible for wiring and move real logic into classes under src/.
controllers/
The controllers folder contains legacy-style front and admin controllers. Front controllers usually live in controllers/front and create public module pages, AJAX endpoints or customer-facing actions. Admin controllers live in controllers/admin and power back office screens.
The common mistake is mixing controller code with business rules. A controller should read the request, call services or models, assign template variables and return a response. It should not contain every query and every rendering rule.
views/
The views folder contains assets and templates. A clean module separates views/templates/front, views/templates/admin and views/templates/hook. CSS, JavaScript and images belong in their own asset folders.
This matters because PrestaShop renders the same module in very different contexts: a product page hook, an account page, a back office configuration screen, or a standalone front controller. Predictable template locations make theme overrides and debugging much easier.
src/
The src folder is where modern module classes should live: services, entities, query builders, validators, form helpers and integration clients. If a class is reusable and testable, it probably belongs here rather than in the main module file.
For larger PrestaShop 8 and 9 modules, src is also where Symfony-style controllers and services may appear. Even in a legacy-compatible module, using src for real logic keeps the codebase easier to reason about.
translations/
Translations are not decoration. A module sold across Europe needs predictable wording in every supported language. The translations folder and modern translation domains should be treated as part of the product, not as an afterthought.
When reviewing a module, I check whether labels, error messages, emails, admin help text and front office strings all have a translation path. Mixed hard-coded English is one of the fastest ways to make a module feel unfinished.
upgrade/
The upgrade folder contains versioned upgrade scripts. This is where existing shops get new database columns, hooks, tabs, configuration values and migrated data. If a module needs a table or a config value, it should be created during install and upgrade, not checked on every request with a runtime ensureX() helper.
Good upgrade scripts are boring. They are small, idempotent where possible, and easy to inspect. That boringness is exactly what makes customer updates safe.
vendor/
The vendor folder contains Composer dependencies. It should not become a place where you manually edit third-party or shared-package code. If a bug is in a shared package, fix the package source and sync it properly. Editing a vendored copy only guarantees that the fix will disappear later.
config.xml and logo.png
config.xml is a cached description of the module used by PrestaShop in module listings and upgrades. It should match the module version. logo.png is the module icon shown in the back office. These files seem minor, but stale metadata creates confusion during support and updates.
override/
PrestaShop supports overrides, but they should be treated as a last resort. Overrides are powerful because they change core behavior. They are risky for exactly the same reason. Most module behavior should be achieved with hooks, services, controllers and templates.
Final rule
A good folder structure does not make a module good by itself. But a bad folder structure makes every future bug harder. If a module is easy to inspect, it is easier to support, easier to translate, easier to upgrade and easier to make compatible with PrestaShop 9.
A practical module skeleton
A clean module does not need to be complicated, but it should be predictable. A typical modern structure looks like this:
modules/
mymodule/
mymodule.php
config.xml
composer.json
controllers/
front/
admin/
src/
Service/
Repository/
Form/
Installer/
views/
templates/
front/
admin/
hook/
css/
js/
img/
translations/
upgrade/
vendor/
The exact folders depend on the module. A tiny hook-only module may not need a large src/ tree. A payment, checkout or SEO module almost certainly does. The point is not to create empty folders. The point is to make responsibilities obvious.
What belongs in the main module file
The main PHP file should describe the module and connect it to PrestaShop. It is the right place for metadata, install and uninstall entry points, hook registration and thin hook methods. It is not the right place for thousands of lines of business logic, raw SQL scattered through templates, or complex form rendering.
When the main file becomes a dumping ground, every future bug gets harder. The developer has to understand install logic, admin UI, frontend rendering and database changes in one file. Moving real logic into services is not fashionable architecture; it is a maintenance tool.
controllers/ should stay thin
Front controllers and admin controllers should coordinate work, not own all of it. A controller can validate a request, call a service, assign variables and return a response. It should not contain the full pricing engine, the full sitemap generator or a long migration routine.
This separation matters for PrestaShop because the same logic often needs to run from different places: a front controller, an AJAX endpoint, a cron task, an upgrade script or a hook. If the logic is trapped in one controller, reuse becomes copy-paste.
src/ is where the module becomes maintainable
The src/ folder is where reusable code should live: installers, repositories, validators, API clients, DTOs, services, exporters, importers and helper classes. In a PrestaShop 8 or 9 context, this is also where Symfony-style service organization becomes easier.
A useful rule: if a class can be tested or reasoned about without rendering a Smarty template, it probably belongs in src/. That makes debugging easier and helps AI coding tools understand the module without reading one giant procedural file.
views/ is not a logic folder
Templates should render data. They can contain conditions that are clearly about presentation, but they should not query the database or calculate business rules. Keeping views/templates/front, views/templates/admin and views/templates/hook separate makes it easier to understand where a template is used.
Assets should also be traceable. If a module ships SCSS or source JavaScript, the compiled files should have a clear build path. If a shop reports a frontend issue, the developer should be able to answer which source file produced the runtime CSS or JS.
upgrade/ is part of the contract
Many module bugs come from pretending that installation is the only lifecycle. Real modules evolve. They add database columns, register new hooks, create tabs, change configuration names and migrate old data. That belongs in upgrade scripts.
A good upgrade script is idempotent enough to survive partial deployments, but not so vague that every request tries to self-heal the entire module forever. Install and upgrade should create the expected state. Runtime should use it.
vendor/ needs discipline
Composer dependencies make modules easier to build, but they also create a risk: developers sometimes patch copied vendor files directly. That may fix one shop for one day, but it is not a maintainable fix. If a shared package has a bug, fix the package source, propagate it properly, and make sure consuming modules receive the updated version.
What not to put in override/
PrestaShop overrides can be powerful, but they are also one of the easiest ways to make upgrades painful. A module that changes core behavior through overrides should have a very strong reason and clear documentation. In many cases, hooks, service decoration, controller routes, Symfony events or module-owned logic are safer.
How this guide fits with the other folder guides
This article focuses on one module. For the wider platform layout, see the PrestaShop folder structure guide. For themes, templates, assets and child themes, see the /themes/ directory deep dive. Together, these three guides cover core folders, theme folders and module folders.
Final checklist
- The main module file is readable and not overloaded.
- Controllers call services instead of owning all business logic.
- Templates render data and avoid database work.
- Assets have a clear source-to-runtime path.
- Install and upgrade scripts create tabs, hooks, configuration and schema changes.
- Translations are complete for every public and admin string.
- Vendor fixes happen upstream, not by editing copied files.
- Overrides are avoided unless there is no safer PrestaShop-native option.
The folder sorted in this article is the module folder: /modules/<module_name>/. That completes the set with the existing core folder and theme folder guides.
Comments
No comments yet. Be the first!
Be the first to ask a question or share useful feedback.
Leave a comment
Share a question, an installation detail, or feedback that could help another reader.