Diving into PrestaShop Core development

Fundamentals

Concepts

 

You should be familiar with PHP and Object-Oriented Programming before attempting to write your own module.

PrestaShop was conceived so that third-party modules could easily build upon its foundations, making it an extremely customizable e-commerce software.

A module is an extension to PrestaShop that enables any developer to add the following:

The company behind PrestaShop provides more than 100 modules for free with the tool itself, enabling you to launch your business quickly and for free.

More than 1600 add-ons are also available at the official add-ons site.
These additional modules were built by the PrestaShop company or members of the PrestaShop community, and are sold at affordable prices.
As a developer, you can also share your modules on this site, and receive 70% of the amounts associated with the sale of your creations. Sign up now!

PrestaShop's technical architecture

PrestaShop is based on a 3-tier architecture:

This is the same principle as the Model–View–Controller (MVC) architecture, only in a simpler and more accessible way.

Our developer team chose not to use a PHP framework, such as Zend Framework, Symfony or CakePHP, so as to allow for better readability, and thus faster editing.

This also makes for better performances, since the software is only made of the lines of code it requires, and does not contain a bunch of supplemental generic libraries.

A 3-tier architecture has many advantages:

Model

A model represents the application's behavior: data processing, database interaction, etc.

It describes or contains the data that have been processed by the application. It manages this data and guarantees its integrity.

View

A view is the interface with which the user interacts.

Its first role is to display the data that is been provided by the model. Its second role is to handle all the actions from the user (mouse click, element selection, buttons, etc.), and send these events to the controller.

The view does not do any processing; it only displays the result of the processing performed by the model, and interacts with the user.

Controller

The Controller manages synchronization events between the Model and the View, and updates both as needed. It receives all the user events and triggers the actions to perform.

If an action needs data to be changed, the Controller will "ask" the Model to change the data, and in turn the Model will notify the View that the data has been changed, so that the View can update itself.

The PrestaShop file structure

The PrestaShop developers have done their best to clearly and intuitively separate the various parts of the software.

Here is how the files are organized:

About the cache

The /cache/Class_index.php file contains the link between the class and the declaration file. It can be safely deleted.

The /cache/xml folder contains the list of all the base modules.

When the store's front-end doesn't quite reflect your changes and emptying the browser's cache is not effective, you should try emptying the following folders:

PrestaShop's context

The Context is a technical feature introduced with version 1.5 of PrestaShop. Its two goals are:

The Context is a light implementation of the Registry design pattern: it is a class that stores the main PrestaShop information, such as the current cookie, the customer, the employee, the cart, Smarty, etc.

Before version 1.5, you had to rely on the cookie in order to access this data:

$cookie->id_lang;

Now that the Context is available, the same data can be accessed more cleanly:

$this->context->language->id;

You can read more about the Context in the "Using the Context Object" page of the Developer Guide.

Accessing the database

The database structure

PrestaShop's database tables start with the ps_ prefix. Note that this can be customized during installation

All table names are in lowercase, and words are separated with an underscore character ("_").

When a table establishes the links between two entities, the names of both entities are mentioned in the table's name. For instance, ps_category_product links products to their category.

A few details to note:

The ObjectModel class

This is the main object in PrestaShop object model. It can be overridden with precaution.

Defining the model

You must use the $definition static variable in order to define the model.

For instance:

/**
* Example from the CMS model (CMSCore) 
*/
public static $definition = array(
  'table' => 'cms',
  'primary' => 'id_cms',
  'multilang' => true,
  'fields' => array(
    'id_cms_category'  => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
    'position'         => array('type' => self::TYPE_INT),
    'active'           => array('type' => self::TYPE_BOOL),
    // Lang fields
    'meta_description' => 
        array('type'=>self::TYPE_STRING, 'lang'=>true, 'validate'=>'isGenericName', 'size'=>255),
    'meta_keywords'    => 
        array('type'=>self::TYPE_STRING, 'lang'=>true, 'validate'=>'isGenericName', 'size'=>255),
    'meta_title'       => 
        array('type' =>self::TYPE_STRING, 'lang'=>true, 'validate'=>'isGenericName', 'required'=>true, 'size'=>128),
    'link_rewrite'     =>
        array('type' => self::TYPE_STRING, 'lang'=>true, 'validate'=>'isLinkRewrite', 'required'=>true, 'size'=>128),
    'content'          => 
        array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isString', 'size' => 3999999999999),
  ),
);

A model for many stores and/or languages

In order to have an object in many languages:

'multilang' => true

In order to have an object depending on the current store

'multishop' => true

In order to have an object which depend on the current store, and in many languages:

'multilang_shop' => true

The main methods

Any overriding of its methods is bound to influence how all the other classes and methods act. Use with care.

Method name and parameters

Description

getValidationRules($className = _CLASS_)

Return object validation rules (fields validity).

getFields()

Prepare fields for ObjectModel class (add, update).

__construct($id = NULL, $id_lang = NULL)

Build object.

save($nullValues = false, $autodate = true)

Save current object to database (add or update).

add($autodate = true, $nullValues = false)

Save current object to database (add or update).

add($autodate = true, $nullValues = false)

Add current object to database.

update($nullValues = false)

Update current object to database.

delete()

Delete current object from database.

deleteSelection($selection)

Delete several objects from database.

toggleStatus()

Toggle object's status in database.

validateFields($die = true, $errorReturn = false)

Check for fields validity before database interaction.

The DBQuery class

The DBQuery class is a query builder, which helps you creation SQL queries. For instance:

$sql = new DbQuery();
$sql->select('*');
$sql->from('cms', 'c');
$sql->innerJoin('cms_lang', 'l', 'c.id_cms = l.id_cms AND l.id_lang = '.(int)$id_lang);
$sql->where('c.active = 1');
$sql->orderBy('position');
return Db::getInstance()->executeS($sql

The Dispatcher

The Dispatcher is one of the main technical features of v1.5. It handles URL redirections. Instead of using multiple files on the root folder like product.php, order.php or category.php, only one file is used: index.php. From now on, internal URL will look like index.php?controller=category, index.php?controller=product, etc.

Additionally, the Dispatcher is built to support URL rewriting. Therefore, when URL-rewriting is off, PrestaShop will use the following URL form:

http://myprestashop.com/index.php?controller=category&id_category=3&id_lang=1
http://myprestashop.com/index.php?controller=product&id_product=1&id_lang=1

...and URL-rewriting is on (or "Friendly URLs"), PrestaShop's Dispatcher will correctly support this URL form:

http://myprestashop.com/en/3-music-ipods
http://myprestashop.com/en/1-ipod-nano.html

There are several advantages for this system:

The Dispatcher makes use of three new 1.5 abstract classes: Controller, FrontController and AdminController (the last two inheriting from the first one).

New routes can be created by overriding the loadRoutes() method.
The store administrator can change a controller's URL using the "SEO & URLs" page in the back-office's "Preferences" menu.

Controllers

In the MVC architecture, a controller manages the synchronization events between the View and the Model, and keeps them up to date. It receives all the user events and triggers the actions to perform.
If an action needs data to be changed, the Controller will "ask" the Model to change the data, and in turn the Model will notify the View that the data has been changed, so that the View can update itself.

All of PrestaShop's controllers actually override the Controller class through another inheriting class, such as AdminController, ModuleAdminController, FrontController or ModuleFrontController.

The FrontController class

Some of the class' properties

PropertyDescription
$templateTemplate name for page content.
$css_filesArray list of CSS files.
$js_filesArray list of JavaScript files.
$errorsArray of errors that have occurred.
$guestAllowedWhether a customer who has signed out can access the page.
$initializedWhether the init() function has been called.
$isoThe ISO code of the currently selected language.
$nThe number of items per page.
$orderByThe field used to sort.
$orderWayWhether to sort is ascending or descending ("ASC" or "DESC").
$pThe current page number.
$ajaxIf the ajax parameter is detected in request, set this flag to true.

Execution order of the controller's functions

  1. __contruct(): Sets all the controller's member variables.
  2. init(): Initializes the controller.
  3. setMedia() or setMobileMedia(): Adds all JavaScript and CSS specifics to the page so that they can be combined, compressed and cached.
  4. postProcess(): Handles ajaxProcess.
  5. initHeader(): Called before initContent().
  6. initContent(): Initialize the content.
  7. initFooter(): Called after initContent().
  8. display() or displayAjax(): Displays the content.

Existing controllers

Controller's filenameDescription
AddressController.phpUsed by address.php to edit a customer's address.
AddressesController.phpUsed by addresses.php to get customer's addresses.
AuthController.phpUsed by authentication.php for customer login.
BestSalesController.phpUsed by best-sales.php to get best-sellers.
CartController.phpUsed by cart.php to manage the customer's cart.
CategoryControllerUsed by category.php to get product categories.
CMSController.phpUsed by cms.php to get a CMS page.
CompareController.phpUsed by products-comparison.php to compare products.
ContactController.phpUsed by contact-form.php to send messages.
DiscountController.phpUsed by discount.php to get a customer's vouchers.
GuestTrackingController.phpUsed by guest-tracking.php to manage guest orders.
HistoryController.phpUsed by history.php to get a customer's orders.
IdentityController.phpUsed by identity.php for customer's personal info.
IndexController.phpUsed by index.php to display the homepage.
ManufacturerController.phpUsed by manufacturer.php to get manufacturers.
MyAccountController.phpUsed by my-account.php to manage customer account.
NewProductsController.phpUsed by new-products.php to get new products.
OrderConfirmationController.phpUsed by order-confirmation.php for order confirmation.
OrderController.phpUsed by order.php to manage the five-step checkout.
OrderDetailController.phpUsed by order-detail.php to get a customer order.
OrderFollowController.phpUsed by order-follow.php to get a customer's returns.
OrderOpcController.phpUsed by order-opc.php to manage one-page checkout.
OrderReturnController.phpUsed by order-return.php to get a merchandise return.
OrderSlipController.phpUsed by order-slip.php to get a customer's credit slips.
PageNotFoundController.phpUsed by 404.php to manage the "Page not found" page.
ParentOrderController.phpManages shared order code.
PasswordController.phpUsed by password.php to reset a lost password.
PricesDropController.phpUsed by prices-drop.php to get discounted products.
ProductController.phpUsed by product.php to get a product.
SearchController.phpUsed by search.php to get search results.
SitemapController.phpUsed by sitemap.php to get the sitemap.
StoresController.phpUsed by stores.php to get store information.
StoresController.phpUsed by supplier.php to get suppliers.

Overriding a controller

Thanks to inheritance, you can change a controller's behaviors, or add new ones.

PrestaShop's controllers are all stored in the /controllers folder, and use the Core suffix.

For instance, when working with the category controller:

In order to override a controller, you must first create a new class without the Core suffix, and place its file in the /override/controllers folder.

For instance, when overriding the category controller:

Views

PrestaShop uses the Smarty template engine to generate its views: http://www.smarty.net/

The views are stored in .tpl files.

A view name is generally the same as the name for the code using it. For instance, 404.php uses 404.tpl.

As there is no inheritance, there is no way to override a view.

In order to change a view, you must rewrite the template file, and place it in your theme's folder.

Cookies

PrestaShop uses encrypted cookies to store all the session information, for visitors/clients as well as for employees/administrators.

The Cookie class (/classes/Cookie.php) is used to read and write cookies.

In order to access the cookies from within PrestaShop code, you can use this:

$this->context->cookie;

All the information stored within a cookie is available using this code:

$this->context->cookie->variable;

If you need to access the PrestaShop cookie from non-PrestaShop code, you can use this code:

include_once('path_to_prestashop/config/config.inc.php');
include_once('path_to_prestashop/config/settings.inc.php');
include_once('path_to_prestashop/classes/Cookie.php');
$cookie = new Cookie('ps'); // Use "psAdmin" to read an employee's cookie.

Data stored in a visitor's cookie

 

TokenDescription
date_addThe date and time the cookie was created (in YYYY-MM-DD HH:MM:SS format).
id_langThe ID of the selected language.
id_currencyThe ID of the selected currency.
last_visited_categoryThe ID of the last visited category of product listings.
ajax_blockcart_displayWhether the cart block is "expanded" or "collapsed".
viewedThe IDs of recently viewed products as a comma-separated list.
id_wishlistThe ID of the current wishlist displayed in the wishlist block.
checkedTOSWhether the "Terms of service" checkbox has been ticked (1 if it has and 0 if it hasn't)
id_guestThe guest ID of the visitor when not logged in.
id_connectionsThe connection ID of the visitor's current session.
id_customerThe customer ID of the visitor when logged in.
customer_lastnameThe last name of the customer.
customer_firstnameThe first name of the customer.
loggedWhether the customer is logged in.
passwdThe MD5 hash of the _COOKIE_KEY_ in config/settings.inc.php and the password the customer used to log in.
emailThe email address that the customer used to log in.
id_cartThe ID of the current cart displayed in the cart block.
checksumThe Blowfish checksum used to determine whether the cookie has been modified by a third party.
The customer will be logged out and the cookie deleted if the checksum doesn't match.

Data stored in an employee's cookie

 

TokenDescription
date_addThe date and time the cookie was created (in YYYY-MM-DD HH:MM:SS format).
id_langThe ID of the selected language.
id_employeeThe ID of the employee.
lastnameThe last name of the employee.
firstnameThe first name of the employee.
emailThe email address the employee used to log in.
profileThe ID of the profile that determines which tabs the employee can access.
passwdThe MD5 hash of the _COOKIE_KEY_ in config/settings.inc.php and the password the employee used to log in.
checksumThe Blowfish checksum used to determine whether the cookie has been modified by a third party.
The customer will be logged out and the cookie deleted if the checksum doesn't match.

Hooks

Hooks are a way to associate your code to some specific PrestaShop events.

Most of the time, they are used to insert content in a page.

For instance, the PrestaShop default theme's home page has the following hooks:

Hook nameDescription
displayHeaderDisplays the content in the page's header area.
displayTopDisplays the content in the page's top area.
displayLeftColumnDisplays the content in the page's left column.
displayHomeDisplays the content in the page's central area.
displayRightColumnDisplays the content in the page's right column.
displayFooter

Displays the content in the page's footer area.

Hooks can also be used to perform specific actions under certain circumstances (i.e. sending an e-mail to the client).

Using hooks

...in a controller

It is easy to call a hook from within a controller: you simply have to use its name with the hookExec() method: Module::hookExec('NameOfHook');

For instance:

$this->context->smarty->assign('HOOK_LEFT_COLUMN', Module::hookExec('leftColumn'));

...in a module

In order to attach your code to a hook, you must create a non-static public method, starting with the "hook" keyword followed by either "display" or "action", and the name of the hook you want to use.

This method receives one (and only one) argument: a table of the contextual information sent to the hook.

 

public function hookDisplayNameOfHook($params)
{
    // Your code.
}

In order for a module to respond to a hook call, the hook must be registered within PrestaShop. Hook registration is done using the registerHook() method. Registration is usually done during the module's installation.

public function install()
{
    return parent::install() && $this->registerHook('NameOfHook');
}

Existing hooks: front-office

Home page and general site pages

Hook nameDescription
displayHeaderCalled between the HEAD tags. Ideal location for adding JavaScript and CSS files.
displayTopCalled in the page's header.
displayLeftColumnCalled when loading the left column.
displayRightColumnCalled when loading the right column.
displayFooterCalled in the page's footer.
displayHomeCalled at the center of the homepage.

Product page

Hook nameDescription
displayLeftColumnProductCalled right before the "Print" link, under the picture.
displayRightColumnProductCalled right after the block for the "Add to Cart" button.
displayProductButtonsCalled inside the block for the "Add to Cart" button, right after that button.
actionProductOutOfStockCalled inside the block for the "Add to Cart" button, right after the "Availability" information.
displayFooterProductCalled right before the tabs.
displayProductTabCalled in tabs list, such as "More info", "Data sheet", "Accessories"...
displayProductTabContentCalled when a tab is clicked.

Cart page

Hook nameDescription
actionCartSaveCalled right after a cart creation or update.
displayShoppingCartFooterCalled right below the cart items table.
displayShoppingCartCalled after the cart's table of items, right above the navigation buttons.
displayCustomerAccountFormTopCalled within the client account creation form, right above the "Your personal information" block.
displayCustomerAccountFormCalled within the client account creation form, right before the "Register" button.
actionCustomerAccountAddCalled right after the client account creation.
displayCustomerAccountCalled on the client account homepage, after the list of available links. Ideal location to add a link to this list.
displayMyAccountBlockCalled within the "My account" block, in the left column, below the list of available links. This is the ideal location to add a link to this list.
displayMyAccountBlockfooterDisplays extra information inside the "My account" block.
actionAuthenticationCalled right after the client identification, only if the authentication is valid (e-mail address and password are both OK).
actionBeforeAuthenticationCalled right before authentication.

Search page

Hook nameDescription
actionSearchCalled after a search is performed. Ideal location to parse and/or handle the search query and results.

Carrier choice page

Hook name

Description

displayBeforeCarrierDisplayed before the carrier list on front-office.
displayCarrierListCalled after the list of available carriers, during the order process. Ideal location to add a carrier, as added by a module.

Payment page

Hook nameDescription
displayPaymentTopTop of payment page.
displayPaymentCalled when needing to build a list of the available payment solutions, during the order process. Ideal location to enable the choice of a payment module that you have developed.
displayPaymentReturnCalled when the user is sent back to the store after having paid on the 3rd-party website. Ideal location to display a confirmation message or to give some details on the payment.
displayOrderConfirmationA duplicate of paymentReturn.
displayBeforePaymentCalled when displaying the list of available payment solutions. Ideal location to redirect the user instead of displaying said list (i.e., 1-click PayPal checkout).

Order page

Hook nameDescription
actionOrderReturnCalled when the customer request to send his merchandise back to the store, and if now error occurs.
displayPDFInvoiceCalled when displaying the invoice in PDF format. Ideal location to display content within the invoice.

Existing hooks: back-office

General hooks

Hook nameDescription
displayBackOfficeTopCalled within the header, above the tabs.
displayBackOfficeHeaderCalled between the HEAD tags. Ideal location for adding JavaScript and CSS files.
displayBackOfficeFooterCalled within the page footer, above the "Power By PrestaShop" line.
displayBackOfficeHomeCalled at the center of the homepage.

Orders and order details

Hook nameDescription
actionValidateOrderCalled during the new order creation process, right after it has been created.
actionPaymentConfirmationCalled when an order's status becomes "Payment accepted".
actionOrderStatusUpdateCalled when an order's status is changed, right before it is actually changed.
actionOrderStatusPostUpdateCalled when an order's status is changed, right after it is actually changed.
actionProductCancelCalled when an item is deleted from an order, right after the deletion.
displayInvoiceCalled when the order's details are displayed, above the Client Information block.
displayAdminOrderCalled when the order's details are displayed, below the Client Information block.
actionOrderSlipAddCalled during the creation of a credit note, right after it has been created.

Products

Hook nameDescription
actionProductSaveCalled when saving products.
actionUpdateQuantityCalled during an the validation of an order, the status of which being something other than "canceled" or "Payment error", for each of the order's items.
actionProductAttributeUpdateCalled when a product declination is updated, right after said update.
actionProductAttributeDeleteCalled when a product declination is deleted.
actionWatermarkCalled when an image is added to a product, right after said addition.
displayAttributeFormAdd fields to the form "attribute value".
displayAttributeGroupFormAdd fields to the form "attribute group".
displayAttributeGroupPostProcessCalled when post-process in admin attribute group.
displayFeatureFormAdd fields to the form "feature".
displayFeaturePostProcessCalled when post-process in admin feature.
displayFeatureValueFormAdd fields to the form "feature value".
displayFeatureValuePostProcessCalled when post-process in admin feature value.

Statistics

Hook nameDescription
displayAdminStatsGraphEngineCalled when a stats graph is displayed.
displayAdminStatsGridEngineCalled when the grid of stats is displayed.
displayAdminStatsModulesCalled when the list of stats modules is displayed.

Clients

Hook nameDescription
displayAdminCustomersCalled when a client's details are displayed, right after the list of the clients groups the current client belongs to.

Carriers

Hook nameDescription
actionCarrierUpdateCalled during a carrier's update, right after said update.

Creating your own hook

You can create new PrestaShop hooks by adding a new record in the ps_hook table in your MySQL database.

INSERT INTO `ps_hook` (`name`, `title`, `description`) VALUES ('nameOfHook', 'The name of your hook', 'This is a custom hook!');

 

 

 

$template

template name for page content.

$css_files

array list of css files

$js_files

array list of javascript files

$errors

An array of errors that have occurred.

$guestAllowed

Whether a customer who has signed out can access the page

$initialized

Whether the init() function has been called.

$iso

The iso code of the currently selected language.

$n

The number of items per page.

$orderBy

The field used to sort.

$orderWay

Whether to sort ascending or descending ("ASC" or "DESC").

$p

The current page number.