PrestaShop is a big project, with several moving parts. In this article you will learn how they are structured and how they work together.
PrestaShop’s architecture can be separated in two main logical sections, represented as blue columns in the figure above:
Each of these sections can be themselves separated in two parts, which are common to all web applications:
This separation has been depicted using a dotted horizontal line.
If we analyze how the back-end is structured, we can find some common elements to BO and FO:
Like most traditional web applications, PrestaShop is heavily Database-driven. This means that all data is stored there. Regardless of whether it’s used in the BO or FO, the database is the Single Source of Truth.
The purple cloud depicted above the Database is what we call the Core Business. It’s the big ensemble of code that manages what makes PrestaShop PrestaShop, also referred as “business logic”. It includes models, controllers, helper classes, and the like.
PrestaShop controllers will generally output HTML pages, but on some cases they can output JSON or even XML. The structure of those pages is defined by Themes (depicted as pink blocks overlapping both the front-end and the back-end), which transform controller-provided data into HTML. This is true both for the FO and BO.
Although PrestaShop 8 is bundled with default themes for FO and BO, only the FO supports third-party themes.
PrestaShop provides two API interfaces:
While Web services can output XML or JSON data, the BO API is JSON-only.
Finally, Modules are independent, optional packages that can extend and customize PrestaShop in many ways. They interact with the Core either by hooking into extension points which are placed throughout the code, or by replacing core components with their own.
This is discussed in further detail in the Themes section.
While controllers will be different in BO and FO, pretty much all of PrestaShop’s PHP code is shared between those two environments. This code is split in four logical subsystems:
The Legacy subsystem contains the historical, non-namespaced code inherited from previous versions. It’s progressively being replaced since 1.6.1 by the Core subsystem, which uses namespaces and is based on SOLID principles.
The Adapter subsystem acts as a bridge to legacy classes, which are often static, in order to allow them to be injected in Core classes.
Finally, the Symfony-based subsystem called PrestaShop Bundle is a Symfony bundle that contains Symfony-specific functionality like controllers, forms, views, etc.
In the following figure, we can appreciate the four subsystems described above:
While this separation may seem excessively complex, it belongs to a transition phase that is necessary to allow the project to move forward progressively. Here’s how.
Notice the dotted yellow zone labeled temporary code. It means that code within that zone will sooner or later be moved to the Core or PrestaShop Bundle stack. Once the zone it’s empty, it will be deleted. Of course, such a change won’t be done in a minor version, so you can expect these four stacks to be present for the whole lifetime of PrestaShop 8.
If you look closely at the relationships between each stack, you’ll see that code outside the temporary code zone does not interact directly with legacy classes. As explained before, the Adapter layer sits between the Legacy and the “new” code to ease up the transition of code from the Legacy stack to the Core stack.
Whenever a Core (or PrestaShopBundle) class needs something provided by a Legacy class, instead of using the Legacy class directly, it delegates that task to an Adapter, which itself uses the Legacy class (see Adapter pattern).
Here’s where it gets interesting. Generally, these Adapters implement an interface declared in Core (even though it hasn’t always been the case, new classes do). Making consumers of that Adapter depend on the interface instead of the Adapter class itself (see Dependency Inversion principle) will allow to reimplement Adapter classes in Core progressively, without having to change the existing code that depends on them.
Why not use the Legacy class directly?
Most legacy classes are static, and since by definition they cannot be injected, it would result in coupled, untestable code. In addition, the ones that are not static generally still have too many responsibilities (see Single responsibility principle) and/or too many public methods or properties (see Open/closed principle), so they cannot be made to implement a proper interface.
PrestaShop is based on the Model-View-Controller (MVC) pattern, where Controllers are in charge of handling requests and returning responses, ideally delegating the hard work on dedicated services.
Controllers are divided in two big families: those that handle requests in FO, and those that handle requests in BO.
Controllers can belong to either the Legacy subsystem or to the PrestaShop Bundle. The first ones are referred to as “legacy controllers” and the latter as “Symfony controllers”. However, Symfony controllers are only available in BO.
FO controllers are all based on the
FrontController class. Modules can declare FO controllers of their own, which must extend the
ModuleFrontController class (which is based on
FrontController as well).
BO controllers are a little more complex, since they can be either legacy or Symfony based.
Legacy BO controllers are based on the
AdminController class, whereas Symfony controllers are based on the
FrameworkBundleAdminController class. Modules can declare BO controllers of their own, and they can be either legacy or symfony based as well. Legacy module controllers must extend the
ModuleAdminController class (which is based on
AdminController), and Symfony modules must simply extend
Finally, some BO pages like Stocks and Translation are API based. These controllers are based on the
As the migration to Symfony progresses, legacy BO controllers are being migrated from the legacy stack to the PrestaShop Bundle stack. Once the BO migration is complete, there will no longer be any legacy controller in the BO.
|Base controller class
ModuleFrontController (based on
ModuleAdminController (based on
|Native (BO API)
Legacy controllers use Smarty for templating, while Symfony controllers use Twig.
There are two kinds of themes in PrestaShop: FO themes and BO themes.
FO themes define the appearance of the Front Office.
PrestaShop comes bundled with a default FO theme, called “Classic”, but merchant can choose to use a different theme.
core.js, which has jQuery 3 bundled in.
Additionally, FO themes can redefine the layout of modules by overriding their templates.
BO themes define the appearance of the Back Office.
Even though BO themes are not interchangeable, PrestaShop comes bundled with two of them: default and new theme.
So why are there two? Legacy controllers are based on Smarty, like FO controller. Symfony controllers, conversely, are based on the Twig templating engine. As a result, there’s a theme for each one: legacy controllers use the default theme, while Symfony ones use the new theme. As controllers are progressively being migrated to Symfony, templates are moved from the default to the new theme and converted from Smarty to Twig.
Here’s what was said when announcing the architecture of 1.7:
Twig is Symfony’s templating language. In version 1.7, it will be used for all pages that are rewritten to use Symfony […], but NOT for the global interface (menu, header, etc.) nor the non-rewritten pages, which will still use Smarty. The two templating engines will be available, side by side, during the transition phase.
This means that the global interface is handled by the default theme, even in Symfony pages. Because they use both Twig and Smarty, this partially explains why some Symfony pages may sometimes be slower than legacy ones.
Rest assured, this is a temporary issue which will be solved when everything has been migrated to Twig and Symfony.
Finally, there’s Vue pages. Vue pages are hybrid: half-Symfony, half-API based BO pages. In those pages, the page’s skeleton is first rendered by a Symfony controller (therefore, based on the new theme), and then a VueJS application takes over in the browser and draws its content based on data sent by the BO API.
As stated before, currently only the Stock management and Translation pages are built on this technology. Even though we think that this is the way of the future, we find that going down this path in minor version releases would produce too many major extensibility and backwards incompatibility issues. Therefore, there will probably be no new Vue/BO API pages in PrestaShop 8.
The Modules system provides a plug-in approach to added functionality. As explained before, it mainly relies on specific extension points called “Hooks,” but their influence and deeply rooted relationships can go much further than that.
If you look at the Modules block at the center of the figure at the top of the page, you’ll notice that there are lots of arrows coming and going from it. Let’s explore these relationships.
Like we said, the main path for Module integration is Hooks, which are placed throughout the system. Modules can attach to them in order to provide or alter features.
There are two types of hooks:
The module system provides several other features:
Modules can also be used to customize PrestaShop:
In addition, modules can be customized by Themes. Themes supporting a given module can override the module’s own FO templates in order to improve their integration.
As you can see, the module system has many features, making modules very powerful. Modules have full access to the Core system, and the integration can go very deep into the Core. This power comes with a cost: the deeper the integration and customization, the more risk of upgrade and interoperability issues there is.
Now you know PrestaShop is much more complex than it can seem to be at first sight.
Remember the overview at the top of the article? Have a look at this more detailed version now: