Back to Top

How to develop MultiStore compatible module in Prestashop?

Updated 10 December 2019

In this blog, we are going to learn how Prestashop manages Multistore and how we can develop PrestaShop module with multistore compatibility.

Let’s first understand the Prestashop multistore concept

The multi-store feature is an opportunity to set up and manage the unlimited number of sub-shops within single back-office. By default, a default shop gets create in a default group when we install the PrestaShop. Prestashop manages Shop group to categories and shares the data between the shops in that particular group.

Shop Group

Shop Group is a group of one or more than one shop. Multiple shops can share the customers, orders and available quantities to sell within the same group.
Share the customers – All shops in this group will share the customers’ data. That means if a customer registers in any one of these shops then that customer account will automatically be available in the other shops of this group.
Share available quantities to sell – Share available quantities between shops of this group. When changing this option, all available products quantities will be reset to 0.
Share the orders – When you enable the above two options, only then this is possible. In this, the customer’s cart will be shared by all the shops in this group. This means if any order starts in one shop then it will be able to be complete in another shop from the same group.

Shop

During a new shop creation, a shop can import the data form any listed shops except the customers and orders data.

Now let’s understand how the configuration works with multishop.

Configuration variable always works with multishop because when we call updateValue() and get() function automatically get the current context shop and shop group and save the data accordingly.
Configuration value works in the following hierarchy.

Searching for an experienced
Prestashop Company ?
Find out More

Shop Configuration -> Shop Group Configuration -> All Configuration

This means if shop wise configuration does not exist then only it will check the shop group configuration of that shop and will apply this value for that shop also. If the shop group configuration also not exist then only it will check for common configuration and that value will apply.

  • Suppose that we have selected the All shop in shop menu and saving the configuration of  “Number of decimals”
    The value in the configuration table will be like.
id_shop_group id_shop name value
NULL NULL PS_PRICE_DISPLAY_PRECISION 2

Here both id shop and shop group are null it means that if any shop or any shop group configuration is not saved then this will be applied to that shop groups and shops.

  • Suppose that we have selected the Default group(shop group id 1) in shop menu and saving the configuration of “Number of decimals”
    The value in the configuration table will be like
id_shop_group id_shop name value
1 NULL PS_PRICE_DISPLAY_PRECISION 2

Here both id shop is null it means that if any shop of this group configuration is not saved then this will be applied to that shop.

  • Suppose that we have selected the Demo shop(in shop group 1 and shop id is 1) in shop menu and saving the configuration of “Number of decimals”

The value in the configuration table will be like

id_shop_group id_shop name value
1 1 PS_PRICE_DISPLAY_PRECISION 2

Here both id shop and id shop group is mentioned it means that if this value is only for this particular shop.

To save and get the configuration we use get() and UpdateValue() function which is automatically managed multistore configuration.
Update function –https://github.com/PrestaShop/PrestaShop/blob/1.7.4.x/classes/Configuration.php#L422

Get Function –
https://github.com/PrestaShop/PrestaShop/blob/1.7.4.x/classes/Configuration.php#L198

If you want to apply a new value to the same Configuration variable for all existing stores then you can use theupdateGlobalValue() method.

Configuration::updateGlobalValue(‘myVariable’, ‘myValue’)

You can then retrieve that value for the current store by using the get() method.

Configuration::get(‘myVariable’) method.

What to do to make module multistore compatible?

Module Configuration
As we have read above the configuration of a module is already compatible with multistore if the data is stored in the configuration table.
If we want to manage the configuration in the separate table then it should be managed like the configuration table.

Classes in module

The classes which have a shop table needs to add some code update to work multishop as below.

  • The primary key of the table should be id_tablename in both tables(normal, shop table).
  • Need to update constructor in the class with the below code so that the shop table will be associated with that class object as lang table.
    public function __construct($id = null, $idLang = null, $idShop = null)
        {
            parent::__construct($id, null, $idShop);
            Shop::addTableAssociation('table_name', array('type' => 'shop'));
            // if there is also a lang table with shop id then add the below line of code for multilang shop dependency 
            Shop::addTableAssociation('table_name_lang', array('type' => 'fk_shop'));
        }

Admin controller in module

The admin controller which have a shop table needs to add some code to list the data properly through render list.

In constructor add the below code.

$this->lang = true;
Shop::addTableAssociation(‘wk_vacation_message’, array(‘type’ => ‘shop’));
$this->_where .= Shop::addSqlRestrictionOnLang(‘b’);
Module installation
  • Check that the Multistore feature is enabled, and if so, set the current context to all shops on this installation of Module.
    public function install()
    {
        if (Shop::isFeatureActive()) {
            Shop::setContext(Shop::CONTEXT_ALL);
        }
    
        if (!parent::install() ||
            !$this->registerHook('leftColumn') ||
            !$this->registerHook('header') ||
            !Configuration::updateValue('MYMODULE_NAME', 'my friend')
        ) {
            return false;
        }
    
        return true;
    }
    
    

Module Database tables

  • If there is a lang table in our module then it should have the id_shop column to manage the multistore functionality.
  • To manage the multistore functionality there should also be a shop table for the object or entity. For instance, suppose we are creating a testimonial module and we have created the table ps_testimonial to save the testimonial data, now to manage with multistore we have to create a shop table ps_testimonial_shop.

Now with the above table structure, we can use the shop class methods to manage the shop dependencies.

  • Shop::addSqlRestrictionOnLang($alias = null, $id_shop = null) – This function is used to add a restriction on id_shop for multishop lang table. Here $alias is a lang table alias/correlation name and id_shop is a shop id.
  • Shop::addSqlAssociation($table, $alias, $inner_join = true, $on = null, $force_not_default = false) – This function is used to add an SQL JOIN in query between a table and its associated table in multishop. Here $table is a table name, $alias is a table alias/correlation name, $inner_join is to ensure to use inner join or not and $on is to define an extra join condition.
  • For instance, suppose we are getting the product information then we can use these two functions in this way.
     $sql = 'SELECT p.*, product_shop.*, pl.* , m.`name` AS manufacturer_name, s.`name` AS supplier_name
                    FROM `' . _DB_PREFIX_ . 'product` p
                    ' . Shop::addSqlAssociation('product', 'p') . '
                    LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (p.`id_product` = pl.`id_product` ' . Shop::addSqlRestrictionOnLang('pl') . ')
                    LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
                    LEFT JOIN `' . _DB_PREFIX_ . 'supplier` s ON (s.`id_supplier` = p.`id_supplier`)' .
                    ($id_category ? 'LEFT JOIN `' . _DB_PREFIX_ . 'category_product` c ON (c.`id_product` = p.`id_product`)' : '') . '
                    WHERE pl.`id_lang` = ' . (int) $id_lang .
                        ($id_category ? ' AND c.`id_category` = ' . (int) $id_category : '') .
                        ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') .
                        ($only_active ? ' AND product_shop.`active` = 1' : '') . '
                    ORDER BY ' . (isset($order_by_prefix) ? pSQL($order_by_prefix) . '.' : '') . '`' . pSQL($order_by) . '` ' . pSQL($order_way) .
                    ($limit > 0 ? ' LIMIT ' . (int) $start . ',' . (int) $limit : '');
            $rq = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);

    In the above query we have used addSqlAssociation function to join the ps_product_shop table and Shop::addSqlRestrictionOnLang(‘pl’) to manage shop restriction with ps_product_lang table.

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home

Table of Content