1<?php
2/**
3 * Copyright (C) 2017-2019 thirty bees
4 * Copyright (C) 2007-2016 PrestaShop
5 *
6 * thirty bees is an extension to the PrestaShop software by PrestaShop SA.
7 *
8 * NOTICE OF LICENSE
9 *
10 * This source file is subject to the Open Software License (OSL 3.0)
11 * that is bundled with this package in the file LICENSE.md.
12 * It is also available through the world-wide-web at this URL:
13 * https://opensource.org/licenses/osl-3.0.php
14 * If you did not receive a copy of the license and are unable to
15 * obtain it through the world-wide-web, please send an email
16 * to license@thirtybees.com so we can send you a copy immediately.
17 *
18 * @author    thirty bees <contact@thirtybees.com>
19 * @author    PrestaShop SA <contact@prestashop.com>
20 * @copyright 2017-2019 thirty bees
21 * @copyright 2007-2016 PrestaShop SA
22 * @license   https://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
23 * PrestaShop is an internationally registered trademark of PrestaShop SA.
24 */
25
26namespace TbUpdaterModule;
27
28/**
29 * Class ShopCore
30 *
31 * @since 1.0.0
32 */
33class Shop extends ObjectModel
34{
35    // @codingStandardsIgnoreStart
36    /** @var int ID of shop group */
37    public $id_shop_group;
38
39    /** @var int ID of shop category */
40    public $id_category;
41
42    /** @var int ID of shop theme */
43    public $id_theme;
44
45    /** @var string Shop name */
46    public $name;
47
48    public $active = true;
49    public $deleted;
50
51    /** @var string Shop theme name (read only) */
52    public $theme_name;
53
54    /** @var string Shop theme directory (read only) */
55    public $theme_directory;
56
57    /** @var string Physical uri of main url (read only) */
58    public $physical_uri;
59
60    /** @var string Virtual uri of main url (read only) */
61    public $virtual_uri;
62
63    /** @var string Domain of main url (read only) */
64    public $domain;
65
66    /** @var string Domain SSL of main url (read only) */
67    public $domain_ssl;
68
69    /** @var \ShopGroup Shop group object */
70    protected $group;
71
72    /** @var array List of shops cached */
73    protected static $shops;
74
75    protected static $asso_tables = [];
76    protected static $id_shop_default_tables = [];
77    protected static $initialized = false;
78
79    /**
80     * Store the current context of shop (CONTEXT_ALL, CONTEXT_GROUP, CONTEXT_SHOP)
81     *
82     * @var int $context ;
83     */
84    protected static $context;
85
86    /**
87     * ID shop in the current context (will be empty if context is not CONTEXT_SHOP)
88     *
89     * @var int $context_id_shop
90     */
91    protected static $context_id_shop;
92
93    /**
94     * ID shop group in the current context (will be empty if context is CONTEXT_ALL)
95     *
96     * @var int $context_id_shop_group
97     */
98    protected static $context_id_shop_group;
99    // @codingStandardsIgnoreEnd
100
101    /**
102     * @see ObjectModel::$definition
103     */
104    public static $definition = [
105        'table'   => 'shop',
106        'primary' => 'id_shop',
107        'fields'  => [
108            'active'        => ['type' => self::TYPE_BOOL,   'validate' => 'isBool'                                         ],
109            'deleted'       => ['type' => self::TYPE_BOOL,   'validate' => 'isBool'                                         ],
110            'name'          => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 64],
111            'id_theme'      => ['type' => self::TYPE_INT,                                   'required' => true              ],
112            'id_category'   => ['type' => self::TYPE_INT,                                   'required' => true              ],
113            'id_shop_group' => ['type' => self::TYPE_INT,                                   'required' => true              ],
114        ],
115    ];
116
117    protected $webserviceParameters = [
118        'fields' => [
119            'id_shop_group' => ['xlink_resource' => 'shop_groups'],
120            'id_category'   => [],
121            'id_theme'      => [],
122        ],
123    ];
124
125    /**
126     * There are 3 kinds of shop context : shop, group shop and general
127     */
128    const CONTEXT_SHOP = 1;
129    const CONTEXT_GROUP = 2;
130    const CONTEXT_ALL = 4;
131
132    /**
133     * Some data can be shared between shops, like customers or orders
134     */
135    const SHARE_CUSTOMER = 'share_customer';
136    const SHARE_ORDER = 'share_order';
137    const SHARE_STOCK = 'share_stock';
138
139    /**
140     * On shop instance, get its theme and URL data too
141     *
142     * @param int $id
143     * @param int $idLang
144     * @param int $idShop
145     *
146     *
147     * @since   1.0.0
148     * @version 1.0.0 Initial version
149     */
150    public function __construct($id = null, $idLang = null, $idShop = null)
151    {
152        parent::__construct($id, $idLang, $idShop);
153        if ($this->id) {
154            $this->setUrl();
155        }
156    }
157
158    /**
159     * Initialize an array with all the multistore associations in the database
160     *
161     * @since   1.0.0
162     * @version 1.0.0 Initial version
163     */
164    protected static function init()
165    {
166        Shop::$id_shop_default_tables = ['product', 'category'];
167
168        $assoTables = [
169            'carrier'                      => ['type' => 'shop'],
170            'carrier_lang'                 => ['type' => 'fk_shop'],
171            'category'                     => ['type' => 'shop'],
172            'category_lang'                => ['type' => 'fk_shop'],
173            'cms'                          => ['type' => 'shop'],
174            'cms_lang'                     => ['type' => 'fk_shop'],
175            'cms_category'                 => ['type' => 'shop'],
176            'cms_category_lang'            => ['type' => 'fk_shop'],
177            'contact'                      => ['type' => 'shop'],
178            'country'                      => ['type' => 'shop'],
179            'currency'                     => ['type' => 'shop'],
180            'employee'                     => ['type' => 'shop'],
181            'hook_module'                  => ['type' => 'fk_shop'],
182            'hook_module_exceptions'       => ['type' => 'fk_shop', 'primary' => 'id_hook_module_exceptions'],
183            'image'                        => ['type' => 'shop'],
184            'lang'                         => ['type' => 'shop'],
185            'meta_lang'                    => ['type' => 'fk_shop'],
186            'module'                       => ['type' => 'shop'],
187            'module_currency'              => ['type' => 'fk_shop'],
188            'module_country'               => ['type' => 'fk_shop'],
189            'module_group'                 => ['type' => 'fk_shop'],
190            'product'                      => ['type' => 'shop'],
191            'product_attribute'            => ['type' => 'shop'],
192            'product_lang'                 => ['type' => 'fk_shop'],
193            'referrer'                     => ['type' => 'shop'],
194            'scene'                        => ['type' => 'shop'],
195            'store'                        => ['type' => 'shop'],
196            'webservice_account'           => ['type' => 'shop'],
197            'warehouse'                    => ['type' => 'shop'],
198            'stock_available'              => ['type' => 'fk_shop', 'primary' => 'id_stock_available'],
199            'carrier_tax_rules_group_shop' => ['type' => 'fk_shop'],
200            'attribute'                    => ['type' => 'shop'],
201            'feature'                      => ['type' => 'shop'],
202            'group'                        => ['type' => 'shop'],
203            'attribute_group'              => ['type' => 'shop'],
204            'tax_rules_group'              => ['type' => 'shop'],
205            'zone'                         => ['type' => 'shop'],
206            'manufacturer'                 => ['type' => 'shop'],
207            'supplier'                     => ['type' => 'shop'],
208        ];
209
210        foreach ($assoTables as $tableName => $tableDetails) {
211            Shop::addTableAssociation($tableName, $tableDetails);
212        }
213
214        Shop::$initialized = true;
215    }
216
217    /**
218     * @return bool
219     *
220     * @since   1.0.0
221     * @version 1.0.0 Initial version
222     */
223    public function setUrl()
224    {
225        static $row = NULL; // Trivial caching.
226
227        if (!$row) {
228            $query = '
229                SELECT t.id_theme, t.name, t.directory
230			FROM '._DB_PREFIX_.'shop s
231			LEFT JOIN '._DB_PREFIX_.'theme t ON (t.id_theme = s.id_theme)
232			WHERE s.id_shop = '.(int) $this->id.'
233                AND s.active = 1 AND s.deleted = 0
234            ';
235            $row = Db::getInstance()->getRow($query);
236        }
237        if (!$row) {
238            return false;
239        }
240
241        // Find matching shop URL.
242        foreach (ShopUrl::getShopUrls() as $url) {
243            if ($url['id_shop'] == $this->id && $url['main']) {
244                break;
245            }
246            unset($url);
247        }
248        if (!isset($url)) {
249            return false;
250        }
251
252        $this->theme_id = $row['id_theme'];
253        $this->theme_name = $row['name'];
254        $this->theme_directory = $row['directory'];
255        $this->physical_uri = $url['physical_uri'];
256        $this->virtual_uri = $url['virtual_uri'];
257        if ($url['domain'] === '*automatic*') {
258            $this->domain = $_SERVER['HTTP_HOST'];
259        } else {
260            $this->domain = $url['domain'];
261        }
262        if ($url['domain_ssl'] === '*automatic*') {
263            $this->domain_ssl = $_SERVER['HTTP_HOST'];
264        } else {
265            $this->domain_ssl = $url['domain'];
266        }
267
268        return true;
269    }
270
271    /**
272     * Add a shop, and clear the cache
273     *
274     * @param bool $autodate
275     * @param bool $nullValues
276     *
277     * @return bool
278     */
279    public function add($autodate = true, $nullValues = false)
280    {
281        $res = parent::add($autodate, $nullValues);
282        // Set default language routes
283        $langs = Language::getLanguages(false, $this->id, true);
284        // @codingStandardsIgnoreStart
285        Configuration::updateValue('PS_ROUTE_product_rule', array_map(function() {return '{categories:/}{rewrite}';}, $langs));
286        Configuration::updateValue('PS_ROUTE_category_rule', array_map(function() {return '{rewrite}';}, $langs));
287        Configuration::updateValue('PS_ROUTE_supplier_rule', array_map(function() {return '{rewrite}';}, $langs));
288        Configuration::updateValue('PS_ROUTE_manufacturer_rule', array_map(function() {return '{rewrite}';}, $langs));
289        Configuration::updateValue('PS_ROUTE_cms_rule', array_map(function() {return '{categories:/}{rewrite}';}, $langs));
290        Configuration::updateValue('PS_ROUTE_cms_category_rule', array_map(function() {return '{categories:/}{rewrite}';}, $langs));
291        // @codingStandardsIgnoreEnd
292
293        Shop::cacheShops(true);
294
295        return $res;
296    }
297
298    /**
299     * @since   1.0.0
300     * @version 1.0.0 Initial version
301     */
302    public function associateSuperAdmins()
303    {
304        $superAdmins = Employee::getEmployeesByProfile(_PS_ADMIN_PROFILE_);
305        foreach ($superAdmins as $superAdmin) {
306            $employee = new Employee((int) $superAdmin['id_employee']);
307            $employee->associateTo((int) $this->id);
308        }
309    }
310
311    /**
312     * Remove a shop only if it has no dependencies, and remove its associations
313     *
314     * @return bool
315     *
316     * @since   1.0.0
317     * @version 1.0.0 Initial version
318     */
319    public function delete()
320    {
321        if (Shop::hasDependency($this->id) || !$res = parent::delete()) {
322            return false;
323        }
324
325        foreach (Shop::getAssoTables() as $tableName => $row) {
326            $id = 'id_'.$row['type'];
327            if ($row['type'] == 'fk_shop') {
328                $id = 'id_shop';
329            } else {
330                $tableName .= '_'.$row['type'];
331            }
332            $res &= Db::getInstance()->execute(
333                '
334				DELETE FROM `'.bqSQL(_DB_PREFIX_.$tableName).'`
335				WHERE `'.bqSQL($id).'`='.(int) $this->id
336            );
337        }
338
339        // removes stock available
340        $res &= Db::getInstance()->delete('stock_available', '`id_shop` = '.(int) $this->id);
341
342        // Remove urls
343        ShopUrl::deleteShopUrls($this->id);
344
345        // Remove currency restrictions
346        $res &= Db::getInstance()->delete('module_currency', '`id_shop` = '.(int) $this->id);
347
348        // Remove group restrictions
349        $res &= Db::getInstance()->delete('module_group', '`id_shop` = '.(int) $this->id);
350
351        // Remove country restrictions
352        $res &= Db::getInstance()->delete('module_country', '`id_shop` = '.(int) $this->id);
353
354        // Remove carrier restrictions
355        $res &= Db::getInstance()->delete('module_carrier', '`id_shop` = '.(int) $this->id);
356
357        Shop::cacheShops(true);
358
359        return $res;
360    }
361
362    /**
363     * Detect dependency with customer or orders
364     *
365     * @param int $idShop
366     *
367     * @return bool
368     *
369     * @since   1.0.0
370     * @version 1.0.0 Initial version
371     */
372    public static function hasDependency($idShop)
373    {
374        $hasDependency = false;
375        $nbrCustomer = (int) Db::getInstance()->getValue(
376            '
377			SELECT count(*)
378			FROM `'._DB_PREFIX_.'customer`
379			WHERE `id_shop`='.(int) $idShop
380        );
381        if ($nbrCustomer) {
382            $hasDependency = true;
383        } else {
384            $nbrOrder = (int) Db::getInstance()->getValue(
385                '
386				SELECT count(*)
387				FROM `'._DB_PREFIX_.'orders`
388				WHERE `id_shop`='.(int) $idShop
389            );
390            if ($nbrOrder) {
391                $hasDependency = true;
392            }
393        }
394
395        return $hasDependency;
396    }
397
398    /**
399     * Find the shop from current domain / uri and get an instance of this shop
400     * if INSTALL_VERSION is defined, will return an empty shop object
401     *
402     * @return Shop
403     *
404     * @since   1.0.0
405     * @version 1.0.0 Initial version
406     */
407    public static function initialize()
408    {
409        // Find current shop from URL
410        if (!($idShop = Tools::getValue('id_shop')) || defined('_PS_ADMIN_DIR_')) {
411            $host = Tools::getHttpHost();
412            $requestUri = rawurldecode($_SERVER['REQUEST_URI']);
413
414            $allUrls = ShopUrl::get();
415            $sql = 'SELECT id_shop, active, deleted
416                    FROM '._DB_PREFIX_.'shop';
417            $result = Db::getInstance()->executeS($sql);
418
419            // Search for a shop matching this host.
420            $idShop = false;
421            $isMainUri = false;
422            $foundUri = false;
423            foreach ($allUrls as $url) {
424                if ($host === $url['domain'] || $host === $url['domain_ssl']) {
425                    $uri = $url['physical_uri'].$url['virtual_uri'];
426                    foreach ($result as $row) {
427                        if ($row['id_shop'] == $url['id_shop'] &&
428                            $row['active'] && !$row['deleted'] &&
429                            preg_match('#^'.preg_quote($uri, '#').'#i', $requestUri)) {
430
431                            $idShop = $row['id_shop'];
432                            $foundUri = $uri;
433                            if ($url['main']) {
434                                $isMainUri = true;
435                            }
436                            break;
437                        }
438                    }
439                    if ($idShop) {
440                        break;
441                    }
442                }
443            }
444
445            // If an URL was found but is not the main URL, redirect to main URL
446            if ($idShop && !$isMainUri) {
447                foreach ($allUrls as $url) {
448                    if ($url['id_shop'] == $idShop && $url['main']) {
449                        $requestUri = substr($requestUri, strlen($foundUri));
450                        $uri = str_replace('//', '/', $url['domain'].$foundUri.$requestUri);
451                        $redirectType = Configuration::get('PS_CANONICAL_REDIRECT');
452                        $redirectCode = ($redirectType == 1 ? '302' : '301');
453                        $redirectHeader = ($redirectType == 1 ? 'Found' : 'Moved Permanently');
454                        header('HTTP/1.0 '.$redirectCode.' '.$redirectHeader);
455                        header('Cache-Control: no-cache');
456                        header('Location: http://'.$uri);
457                        exit;
458                    }
459                }
460            }
461        }
462
463        $httpHost = Tools::getHttpHost();
464        $allMedia = array_merge(Configuration::getMultiShopValues('PS_MEDIA_SERVER_1'), Configuration::getMultiShopValues('PS_MEDIA_SERVER_2'), Configuration::getMultiShopValues('PS_MEDIA_SERVER_3'));
465
466        if ((!$idShop && defined('_PS_ADMIN_DIR_')) || Tools::isPHPCLI() || in_array($httpHost, $allMedia)) {
467            // If in admin, we can access to the shop without right URL
468            if ((!$idShop && Tools::isPHPCLI()) || defined('_PS_ADMIN_DIR_')) {
469                $idShop = (int) Configuration::get('PS_SHOP_DEFAULT');
470            }
471
472            $shop = new Shop((int) $idShop);
473            if (!Validate::isLoadedObject($shop)) {
474                $shop = new Shop((int) Configuration::get('PS_SHOP_DEFAULT'));
475            }
476
477            $shop->virtual_uri = '';
478
479            // Define some $_SERVER variables like HTTP_HOST if PHP is launched with php-cli
480            if (Tools::isPHPCLI()) {
481                if (!isset($_SERVER['HTTP_HOST']) || empty($_SERVER['HTTP_HOST'])) {
482                    $_SERVER['HTTP_HOST'] = $shop->domain;
483                }
484                if (!isset($_SERVER['SERVER_NAME']) || empty($_SERVER['SERVER_NAME'])) {
485                    $_SERVER['SERVER_NAME'] = $shop->domain;
486                }
487                if (!isset($_SERVER['REMOTE_ADDR']) || empty($_SERVER['REMOTE_ADDR'])) {
488                    $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
489                }
490            }
491        } else {
492            // Handle automatic shop URLs.
493            if (!$idShop) {
494                // The whole purpose of this block is to find out wether
495                // we should load the default shop with or without redirection.
496                // Keeping $idShop at false means with redirection.
497                $idDefaultShop = Configuration::get('PS_SHOP_DEFAULT');
498
499                foreach (ShopUrl::get() as $url) {
500                    if ($url['id_shop'] == $idDefaultShop && $url['main']) {
501                        if (Configuration::get('PS_SSL_ENABLED')) {
502                            if ($url['domain_ssl'] === '*automatic*') {
503                                $idShop = $idDefaultShop;
504                            }
505                        } else {
506                            if ($url['domain'] === '*automatic*') {
507                                $idShop = $idDefaultShop;
508                            }
509                        }
510                    }
511                }
512            }
513
514            $shop = new Shop($idShop);
515            if (!Validate::isLoadedObject($shop) || !$shop->active) {
516                // No shop found ... too bad, let's redirect to default shop
517                $defaultShop = new Shop(Configuration::get('PS_SHOP_DEFAULT'));
518
519                // Hmm there is something really bad in your Prestashop !
520                if (!Validate::isLoadedObject($defaultShop)) {
521                    throw new \Exception('Shop not found');
522                }
523
524                $params = $_GET;
525                unset($params['id_shop']);
526                $url = $defaultShop->domain;
527                if (!Configuration::get('PS_REWRITING_SETTINGS')) {
528                    $url .= $defaultShop->getBaseURI().'index.php?'.http_build_query($params);
529                } else {
530                    // Catch url with subdomain "www"
531                    if (strpos($url, 'www.') === 0 && 'www.'.$_SERVER['HTTP_HOST'] === $url || $_SERVER['HTTP_HOST'] === 'www.'.$url) {
532                        $url .= $_SERVER['REQUEST_URI'];
533                    } else {
534                        $url .= $defaultShop->getBaseURI();
535                    }
536
537                    if (count($params)) {
538                        $url .= '?'.http_build_query($params);
539                    }
540                }
541
542                $redirectType = Configuration::get('PS_CANONICAL_REDIRECT');
543                $redirectCode = ($redirectType == 1 ? '302' : '301');
544                $redirectHeader = ($redirectType == 1 ? 'Found' : 'Moved Permanently');
545                header('HTTP/1.0 '.$redirectCode.' '.$redirectHeader);
546                header('Location: http://'.$url);
547                exit;
548            } elseif (defined('_PS_ADMIN_DIR_') && empty($shop->physical_uri)) {
549                $shopDefault = new Shop((int) Configuration::get('PS_SHOP_DEFAULT'));
550                $shop->physical_uri = $shopDefault->physical_uri;
551                $shop->virtual_uri = $shopDefault->virtual_uri;
552            }
553        }
554
555        static::$context_id_shop = $shop->id;
556        static::$context_id_shop_group = $shop->id_shop_group;
557        static::$context = static::CONTEXT_SHOP;
558
559        return $shop;
560    }
561
562    /**
563     * @return Address the current shop address
564     *
565     * @since   1.0.0
566     * @version 1.0.0 Initial version
567     */
568    public function getAddress()
569    {
570        if (!isset($this->address)) {
571            $address = new Address();
572            $address->company = Configuration::get('PS_SHOP_NAME');
573            $address->id_country = Configuration::get('PS_SHOP_COUNTRY_ID') ? Configuration::get('PS_SHOP_COUNTRY_ID') : Configuration::get('PS_COUNTRY_DEFAULT');
574            $address->id_state = Configuration::get('PS_SHOP_STATE_ID');
575            $address->address1 = Configuration::get('PS_SHOP_ADDR1');
576            $address->address2 = Configuration::get('PS_SHOP_ADDR2');
577            $address->postcode = Configuration::get('PS_SHOP_CODE');
578            $address->city = Configuration::get('PS_SHOP_CITY');
579
580            $this->address = $address;
581        }
582
583        return $this->address;
584    }
585
586    /**
587     * Get shop theme name
588     *
589     * @return string
590     *
591     * @since   1.0.0
592     * @version 1.0.0 Initial version
593     */
594    public function getTheme()
595    {
596        return $this->theme_directory;
597    }
598
599    /**
600     * Get shop URI
601     *
602     * @return string
603     *
604     * @since   1.0.0
605     * @version 1.0.0 Initial version
606     */
607    public function getBaseURI()
608    {
609        return $this->physical_uri.$this->virtual_uri;
610    }
611
612    /**
613     * Get shop URL
614     *
615     * @param string $autoSecureMode if set to true, secure mode will be checked
616     * @param string $addBaseUri     if set to true, shop base uri will be added
617     *
618     * @return string complete base url of current shop
619     *
620     * @since   1.0.0
621     * @version 1.0.0 Initial version
622     */
623    public function getBaseURL($autoSecureMode = false, $addBaseUri = true)
624    {
625        if (($autoSecureMode && Tools::usingSecureMode() && !$this->domain_ssl) || !$this->domain) {
626            return false;
627        }
628
629        $url = [];
630        $url['protocol'] = $autoSecureMode && Tools::usingSecureMode() ? 'https://' : 'http://';
631        $url['domain'] = $autoSecureMode && Tools::usingSecureMode() ? $this->domain_ssl : $this->domain;
632
633        if ($addBaseUri) {
634            $url['base_uri'] = $this->getBaseURI();
635        }
636
637        return implode('', $url);
638    }
639
640    /**
641     * Get group of current shop
642     *
643     * @return ShopGroup
644     *
645     * @since   1.0.0
646     * @version 1.0.0 Initial version
647     */
648    public function getGroup()
649    {
650        if (!$this->group) {
651            $this->group = new ShopGroup($this->id_shop_group);
652        }
653
654        return $this->group;
655    }
656
657    /**
658     * Get root category of current shop
659     *
660     * @return int
661     *
662     * @since   1.0.0
663     * @version 1.0.0 Initial version
664     */
665    public function getCategory()
666    {
667        return (int) ($this->id_category ? $this->id_category : Configuration::get('PS_ROOT_CATEGORY'));
668    }
669
670    /**
671     * Get list of shop's urls
672     *
673     * @return array
674     *
675     * @since   1.0.0
676     * @version 1.0.0 Initial version
677     */
678    public function getUrls()
679    {
680        $result = ShopUrl::get();
681        foreach ($result as $id => &$url) {
682            // Remove the ones we don't need.
683            if ($url['id_shop'] != $this->id || !$url['active']) {
684                unset($result[$id]);
685            } else {
686                $url['id_shop_url'] = $id;
687            }
688        }
689        unset($url);
690
691        return $result;
692    }
693
694    /**
695     * Check if current shop ID is the same as default shop in configuration
696     *
697     * @return bool
698     *
699     * @since   1.0.0
700     * @version 1.0.0 Initial version
701     */
702    public function isDefaultShop()
703    {
704        return $this->id == Configuration::get('PS_SHOP_DEFAULT');
705    }
706
707    /**
708     * Get the associated table if available
709     *
710     * @return array
711     *
712     * @since   1.0.0
713     * @version 1.0.0 Initial version
714     */
715    public static function getAssoTable($table)
716    {
717        if (!Shop::$initialized) {
718            Shop::init();
719        }
720
721        return (isset(Shop::$asso_tables[$table]) ? Shop::$asso_tables[$table] : false);
722    }
723
724    /**
725     * check if the table has an id_shop_default
726     *
727     * @return bool
728     *
729     * @since   1.0.0
730     * @version 1.0.0 Initial version
731     */
732    public static function checkIdShopDefault($table)
733    {
734        if (!Shop::$initialized) {
735            Shop::init();
736        }
737
738        return in_array($table, static::$id_shop_default_tables);
739    }
740
741    /**
742     * Get list of associated tables to shop
743     *
744     * @return array
745     *
746     * @since   1.0.0
747     * @version 1.0.0 Initial version
748     */
749    public static function getAssoTables()
750    {
751        if (!Shop::$initialized) {
752            Shop::init();
753        }
754
755        return Shop::$asso_tables;
756    }
757
758    /**
759     * Add table associated to shop
760     *
761     * @param string $table_name
762     * @param array  $table_details
763     *
764     * @return bool
765     *
766     * @since   1.0.0
767     * @version 1.0.0 Initial version
768     */
769    public static function addTableAssociation($table_name, $table_details)
770    {
771        if (!isset(Shop::$asso_tables[$table_name])) {
772            Shop::$asso_tables[$table_name] = $table_details;
773        } else {
774            return false;
775        }
776
777        return true;
778    }
779
780    /**
781     * Check if given table is associated to shop
782     *
783     * @param string $table
784     *
785     * @return bool
786     *
787     * @since   1.0.0
788     * @version 1.0.0 Initial version
789     */
790    public static function isTableAssociated($table)
791    {
792        if (!Shop::$initialized) {
793            Shop::init();
794        }
795
796        return isset(Shop::$asso_tables[$table]) && Shop::$asso_tables[$table]['type'] == 'shop';
797    }
798
799    /**
800     * Load list of groups and shops, and cache it
801     *
802     * @param bool $refresh
803     *
804     * @since   1.0.0
805     * @version 1.0.0 Initial version
806     */
807    public static function cacheShops($refresh = false)
808    {
809        if (!is_null(static::$shops) && !$refresh) {
810            return;
811        }
812
813        static::$shops = [];
814
815        $from = '';
816        $where = '';
817
818        $employee = Context::getContext()->employee;
819
820        // If the profile isn't a superAdmin
821        if (Validate::isLoadedObject($employee) && $employee->id_profile != _PS_ADMIN_PROFILE_) {
822            $from .= 'LEFT JOIN '._DB_PREFIX_.'employee_shop es ON es.id_shop = s.id_shop';
823            $where .= 'AND es.id_employee = '.(int) $employee->id;
824        }
825
826        $sql = 'SELECT gs.*, s.*, gs.name AS group_name, s.name AS shop_name, s.active
827				FROM '._DB_PREFIX_.'shop_group gs
828				LEFT JOIN '._DB_PREFIX_.'shop s
829					ON s.id_shop_group = gs.id_shop_group
830				'.$from.'
831				WHERE s.deleted = 0
832					AND gs.deleted = 0
833					'.$where.'
834				ORDER BY gs.name, s.name';
835
836        if ($results = Db::getInstance()->executeS($sql)) {
837            foreach ($results as $row) {
838                if (!isset(static::$shops[$row['id_shop_group']])) {
839                    static::$shops[$row['id_shop_group']] = [
840                        'id'             => $row['id_shop_group'],
841                        'name'           => $row['group_name'],
842                        'share_customer' => $row['share_customer'],
843                        'share_order'    => $row['share_order'],
844                        'share_stock'    => $row['share_stock'],
845                        'shops'          => [],
846                    ];
847                }
848
849                // Find matching main URL.
850                foreach (ShopUrl::get() as $url) {
851                    if ($url['id_shop'] == $row['id_shop'] && $url['main']) {
852                        break;
853                    }
854                    unset($url);
855                }
856
857                $dest = &static::$shops[$row['id_shop_group']]['shops'][$row['id_shop']];
858                $dest = [
859                    'id_shop'       => $row['id_shop'],
860                    'id_shop_group' => $row['id_shop_group'],
861                    'name'          => $row['shop_name'],
862                    'id_theme'      => $row['id_theme'],
863                    'id_category'   => $row['id_category'],
864                    'active'        => $row['active'],
865                ];
866                if (isset($url)) {
867                    $dest += [
868                        'domain'     => $url['domain'],
869                        'domain_ssl' => $url['domain_ssl'],
870                        'uri'        => $url['physical_uri'].$url['virtual_uri'],
871                    ];
872                }
873            }
874        }
875    }
876
877    /**
878     * @return array|null
879     *
880     * @since   1.0.0
881     * @version 1.0.0 Initial version
882     */
883    public static function getCompleteListOfShopsID()
884    {
885        $cacheId = 'Shop::getCompleteListOfShopsID';
886        if (!Cache::isStored($cacheId)) {
887            $list = [];
888            $sql = 'SELECT id_shop FROM '._DB_PREFIX_.'shop';
889            foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql) as $row) {
890                $list[] = $row['id_shop'];
891            }
892
893            Cache::store($cacheId, $list);
894
895            return $list;
896        }
897
898        return Cache::retrieve($cacheId);
899    }
900
901    /**
902     * Get shops list
903     *
904     * @param bool $active
905     * @param int  $idShopGroup
906     * @param bool $getAsListId
907     *
908     * @return array
909     *
910     * @since   1.0.0
911     * @version 1.0.0 Initial version
912     */
913    public static function getShops($active = true, $idShopGroup = null, $getAsListId = false)
914    {
915        Shop::cacheShops();
916
917        $results = [];
918        foreach (static::$shops as $group_id => $group_data) {
919            foreach ($group_data['shops'] as $id => $shop_data) {
920                if ((!$active || $shop_data['active']) && (!$idShopGroup || $idShopGroup == $group_id)) {
921                    if ($getAsListId) {
922                        $results[$id] = $id;
923                    } else {
924                        $results[$id] = $shop_data;
925                    }
926                }
927            }
928        }
929
930        return $results;
931    }
932
933    /**
934     * @return array|bool
935     *
936     * @since   1.0.0
937     * @version 1.0.0 Initial version
938     */
939    public function getUrlsSharedCart()
940    {
941        if (!$this->getGroup()->share_order) {
942            return false;
943        }
944
945        $domains = [];
946        foreach (ShopUrl::get() as $url) {
947            if ($url['main'] && $url['active']) {
948                $domains[] = $url['domain'];
949            }
950        }
951
952        return $domains;
953    }
954
955    /**
956     * Get a collection of shops
957     *
958     * @param bool $active
959     * @param int  $idShopGroup
960     *
961     * @return PrestaShopCollection Collection of Shop
962     *
963     * @since   1.0.0
964     * @version 1.0.0 Initial version
965     */
966    public static function getShopsCollection($active = true, $idShopGroup = null)
967    {
968        $shops = new PrestaShopCollection('Shop');
969        if ($active) {
970            $shops->where('active', '=', 1);
971        }
972
973        if ($idShopGroup) {
974            $shops->where('id_shop_group', '=', (int) $idShopGroup);
975        }
976
977        return $shops;
978    }
979
980    /**
981     * Return some informations cached for one shop
982     *
983     * @param int $shopId
984     *
985     * @return array
986     *
987     * @since   1.0.0
988     * @version 1.0.0 Initial version
989     */
990    public static function getShop($shopId)
991    {
992        Shop::cacheShops();
993        foreach (static::$shops as $group_id => $groupData) {
994            if (array_key_exists($shopId, $groupData['shops'])) {
995                return $groupData['shops'][$shopId];
996            }
997        }
998
999        return false;
1000    }
1001
1002    /**
1003     * Return a shop ID from shop name
1004     *
1005     * @param string $name
1006     *
1007     * @return int
1008     *
1009     * @since   1.0.0
1010     * @version 1.0.0 Initial version
1011     */
1012    public static function getIdByName($name)
1013    {
1014        Shop::cacheShops();
1015        foreach (static::$shops as $groupData) {
1016            foreach ($groupData['shops'] as $shop_id => $shopData) {
1017                if (Tools::strtolower($shopData['name']) == Tools::strtolower($name)) {
1018                    return $shop_id;
1019                }
1020            }
1021        }
1022
1023        return false;
1024    }
1025
1026    /**
1027     * @param bool $active
1028     * @param int  $idShopGroup
1029     *
1030     * @return int Total of shops
1031     *
1032     * @since   1.0.0
1033     * @version 1.0.0 Initial version
1034     */
1035    public static function getTotalShops($active = true, $idShopGroup = null)
1036    {
1037        return count(Shop::getShops($active, $idShopGroup));
1038    }
1039
1040    /**
1041     * Retrieve group ID of a shop
1042     *
1043     * @param int $shopId Shop ID
1044     *
1045     * @return int Group ID
1046     *
1047     * @since   1.0.0
1048     * @version 1.0.0 Initial version
1049     */
1050    public static function getGroupFromShop($shopId, $asId = true)
1051    {
1052        Shop::cacheShops();
1053        foreach (static::$shops as $groupId => $groupData) {
1054            if (array_key_exists($shopId, $groupData['shops'])) {
1055                return ($asId) ? $groupId : $groupData;
1056            }
1057        }
1058
1059        return false;
1060    }
1061
1062    /**
1063     * If the shop group has the option $type activated, get all shops ID of this group, else get current shop ID
1064     *
1065     * @param int $shopId
1066     * @param int $type Shop::SHARE_CUSTOMER | Shop::SHARE_ORDER
1067     *
1068     * @return array
1069     *
1070     * @since   1.0.0
1071     * @version 1.0.0 Initial version
1072     */
1073    public static function getSharedShops($shopId, $type)
1074    {
1075        if (!in_array($type, [Shop::SHARE_CUSTOMER, Shop::SHARE_ORDER, SHOP::SHARE_STOCK])) {
1076            die('Wrong argument ($type) in Shop::getSharedShops() method');
1077        }
1078
1079        Shop::cacheShops();
1080        foreach (static::$shops as $groupData) {
1081            if (array_key_exists($shopId, $groupData['shops']) && $groupData[$type]) {
1082                return array_keys($groupData['shops']);
1083            }
1084        }
1085
1086        return [$shopId];
1087    }
1088
1089    /**
1090     * Get a list of ID concerned by the shop context (E.g. if context is shop group, get list of children shop ID)
1091     *
1092     * @param string $share If false, dont check share datas from group. Else can take a Shop::SHARE_* constant value
1093     *
1094     * @return array
1095     *
1096     * @since   1.0.0
1097     * @version 1.0.0 Initial version
1098     */
1099    public static function getContextListShopID($share = false)
1100    {
1101        if (Shop::getContext() == Shop::CONTEXT_SHOP) {
1102            $list = ($share) ? Shop::getSharedShops(Shop::getContextShopID(), $share) : [Shop::getContextShopID()];
1103        } elseif (Shop::getContext() == Shop::CONTEXT_GROUP) {
1104            $list = Shop::getShops(true, Shop::getContextShopGroupID(), true);
1105        } else {
1106            $list = Shop::getShops(true, null, true);
1107        }
1108
1109        return $list;
1110    }
1111
1112    /**
1113     * Return the list of shop by id
1114     *
1115     * @param int    $id
1116     * @param string $identifier
1117     * @param string $table
1118     *
1119     * @return array
1120     *
1121     * @since   1.0.0
1122     * @version 1.0.0 Initial version
1123     */
1124    public static function getShopById($id, $identifier, $table)
1125    {
1126        return Db::getInstance()->executeS(
1127            '
1128			SELECT `id_shop`, `'.bqSQL($identifier).'`
1129			FROM `'._DB_PREFIX_.bqSQL($table).'_shop`
1130			WHERE `'.bqSQL($identifier).'` = '.(int) $id
1131        );
1132    }
1133
1134    /**
1135     * Change the current shop context
1136     *
1137     * @param int $type Shop::CONTEXT_ALL | Shop::CONTEXT_GROUP | Shop::CONTEXT_SHOP
1138     * @param int $id   ID shop if CONTEXT_SHOP or id shop group if CONTEXT_GROUP
1139     *
1140     * @throws \Exception
1141     * @since   1.0.0
1142     * @version 1.0.0 Initial version
1143     */
1144    public static function setContext($type, $id = null)
1145    {
1146        // @codingStandardsIgnoreStart
1147        switch ($type) {
1148            case static::CONTEXT_ALL :
1149                static::$context_id_shop = null;
1150                static::$context_id_shop_group = null;
1151                break;
1152
1153            case static::CONTEXT_GROUP :
1154                static::$context_id_shop = null;
1155                static::$context_id_shop_group = (int) $id;
1156                break;
1157
1158            case static::CONTEXT_SHOP :
1159                static::$context_id_shop = (int) $id;
1160                static::$context_id_shop_group = Shop::getGroupFromShop($id);
1161                break;
1162
1163            default :
1164                throw new \Exception('Unknown context for shop');
1165        }
1166        // @codingStandardsIgnoreEnd
1167
1168        static::$context = $type;
1169    }
1170
1171    /**
1172     * Get current context of shop
1173     *
1174     * @return int
1175     *
1176     * @since   1.0.0
1177     * @version 1.0.0 Initial version
1178     */
1179    public static function getContext()
1180    {
1181        return static::$context;
1182    }
1183
1184    /**
1185     * Get current ID of shop if context is CONTEXT_SHOP
1186     *
1187     * @return int
1188     *
1189     * @since   1.0.0
1190     * @version 1.0.0 Initial version
1191     */
1192    public static function getContextShopID($nullValueWithoutMultishop = false)
1193    {
1194        if ($nullValueWithoutMultishop && !Shop::isFeatureActive()) {
1195            return null;
1196        }
1197
1198        // @codingStandardsIgnoreStart
1199        return static::$context_id_shop;
1200        // @codingStandardsIgnoreEnd
1201    }
1202
1203    /**
1204     * Get current ID of shop group if context is CONTEXT_SHOP or CONTEXT_GROUP
1205     *
1206     * @return int
1207     *
1208     * @since   1.0.0
1209     * @version 1.0.0 Initial version
1210     */
1211    public static function getContextShopGroupID($nullValueWithoutMultishop = false)
1212    {
1213        if ($nullValueWithoutMultishop && !Shop::isFeatureActive()) {
1214            return null;
1215        }
1216
1217        // @codingStandardsIgnoreStart
1218        return static::$context_id_shop_group;
1219        // @codingStandardsIgnoreEnd
1220    }
1221
1222    public static function getContextShopGroup()
1223    {
1224        static $contextShopGroup = null;
1225        if ($contextShopGroup === null) {
1226            $contextShopGroup = new ShopGroup((int) static::$context_id_shop_group);
1227        }
1228
1229        return $contextShopGroup;
1230    }
1231
1232    /**
1233     * Add an sql restriction for shops fields
1234     *
1235     * @param int    $share If false, dont check share datas from group. Else can take a Shop::SHARE_* constant value
1236     * @param string $alias
1237     *
1238     * @since   1.0.0
1239     * @version 1.0.0 Initial version
1240     */
1241    public static function addSqlRestriction($share = false, $alias = null)
1242    {
1243        if ($alias) {
1244            $alias .= '.';
1245        }
1246
1247        $group = Shop::getGroupFromShop(Shop::getContextShopID(), false);
1248        if ($share == Shop::SHARE_CUSTOMER && Shop::getContext() == Shop::CONTEXT_SHOP && $group['share_customer']) {
1249            $restriction = ' AND '.$alias.'id_shop_group = '.(int) Shop::getContextShopGroupID().' ';
1250        } else {
1251            $restriction = ' AND '.$alias.'id_shop IN ('.implode(', ', Shop::getContextListShopID($share)).') ';
1252        }
1253
1254        return $restriction;
1255    }
1256
1257    /**
1258     * Add an SQL JOIN in query between a table and its associated table in multishop
1259     *
1260     * @param string $table     Table name (E.g. product, module, etc.)
1261     * @param string $alias     Alias of table
1262     * @param bool   $innerJoin Use or not INNER JOIN
1263     * @param string $on
1264     *
1265     * @return string
1266     *
1267     * @since   1.0.0
1268     * @version 1.0.0 Initial version
1269     */
1270    public static function addSqlAssociation($table, $alias, $innerJoin = true, $on = null, $forceNotDefault = false)
1271    {
1272        $table_alias = $table.'_shop';
1273        if (strpos($table, '.') !== false) {
1274            list($table_alias, $table) = explode('.', $table);
1275        }
1276
1277        $asso_table = Shop::getAssoTable($table);
1278        if ($asso_table === false || $asso_table['type'] != 'shop') {
1279            return;
1280        }
1281        $sql = (($innerJoin) ? ' INNER' : ' LEFT').' JOIN '._DB_PREFIX_.$table.'_shop '.$table_alias.'
1282		ON ('.$table_alias.'.id_'.$table.' = '.$alias.'.id_'.$table;
1283        if ((int) static::$context_id_shop) {
1284            $sql .= ' AND '.$table_alias.'.id_shop = '.(int) static::$context_id_shop;
1285        } elseif (Shop::checkIdShopDefault($table) && !$forceNotDefault) {
1286            $sql .= ' AND '.$table_alias.'.id_shop = '.$alias.'.id_shop_default';
1287        } else {
1288            $sql .= ' AND '.$table_alias.'.id_shop IN ('.implode(', ', Shop::getContextListShopID()).')';
1289        }
1290        $sql .= (($on) ? ' AND '.$on : '').')';
1291
1292        return $sql;
1293    }
1294
1295    /**
1296     * Add a restriction on id_shop for multishop lang table
1297     *
1298     * @param string  $alias
1299     * @param Context $context
1300     *
1301     * @return string
1302     *
1303     * @since   1.0.0
1304     * @version 1.0.0 Initial version
1305     */
1306    public static function addSqlRestrictionOnLang($alias = null, $idShop = null)
1307    {
1308        if (isset(Context::getContext()->shop) && is_null($idShop)) {
1309            $idShop = (int) Context::getContext()->shop->id;
1310        }
1311        if (!$idShop) {
1312            $idShop = (int) Configuration::get('PS_SHOP_DEFAULT');
1313        }
1314
1315        return ' AND '.(($alias) ? $alias.'.' : '').'id_shop = '.$idShop.' ';
1316    }
1317
1318    /**
1319     * Get all groups and associated shops as subarrays
1320     *
1321     * @return array
1322     *
1323     * @since   1.0.0
1324     * @version 1.0.0 Initial version
1325     */
1326    public static function getTree()
1327    {
1328        Shop::cacheShops();
1329
1330        return static::$shops;
1331    }
1332
1333    /**
1334     * @return bool Return true if multishop feature is active and at last 2 shops have been created
1335     *
1336     * @since   1.0.0
1337     * @version 1.0.0 Initial version
1338     */
1339    public static function isFeatureActive()
1340    {
1341        static $featureActive = null;
1342
1343        if ($featureActive === null) {
1344            $featureActive = (bool) Db::getInstance()->getValue('SELECT value FROM `'._DB_PREFIX_.'configuration` WHERE `name` = "PS_MULTISHOP_FEATURE_ACTIVE"')
1345                && (Db::getInstance()->getValue('SELECT COUNT(*) FROM '._DB_PREFIX_.'shop') > 1);
1346        }
1347
1348        return $featureActive;
1349    }
1350
1351    /**
1352     * @param      $oldId
1353     * @param bool $tablesImport
1354     * @param bool $deleted
1355     *
1356     * @since   1.0.0
1357     * @version 1.0.0 Initial version
1358     */
1359    public function copyShopData($oldId, $tablesImport = false, $deleted = false)
1360    {
1361        // If we duplicate some specific data, automatically duplicate other data linked to the first
1362        // E.g. if carriers are duplicated for the shop, duplicate carriers langs too
1363
1364        if (!$oldId) {
1365            $oldId = Configuration::get('PS_SHOP_DEFAULT');
1366        }
1367
1368        if (isset($tablesImport['carrier'])) {
1369            $tablesImport['carrier_tax_rules_group_shop'] = true;
1370            $tablesImport['carrier_lang'] = true;
1371        }
1372
1373        if (isset($tablesImport['cms'])) {
1374            $tablesImport['cms_lang'] = true;
1375            $tablesImport['cms_category'] = true;
1376            $tablesImport['cms_category_lang'] = true;
1377        }
1378
1379        $tablesImport['category_lang'] = true;
1380        if (isset($tablesImport['product'])) {
1381            $tablesImport['product_lang'] = true;
1382        }
1383
1384        if (isset($tablesImport['module'])) {
1385            $tablesImport['module_currency'] = true;
1386            $tablesImport['module_country'] = true;
1387            $tablesImport['module_group'] = true;
1388        }
1389
1390        if (isset($tablesImport['hook_module'])) {
1391            $tablesImport['hook_module_exceptions'] = true;
1392        }
1393
1394        if (isset($tablesImport['attribute_group'])) {
1395            $tablesImport['attribute'] = true;
1396        }
1397
1398        // Browse and duplicate data
1399        foreach (Shop::getAssoTables() as $tableName => $row) {
1400            if ($tablesImport && !isset($tablesImport[$tableName])) {
1401                continue;
1402            }
1403
1404            // Special case for stock_available if current shop is in a share stock group
1405            if ($tableName == 'stock_available') {
1406                $group = new ShopGroup($this->id_shop_group);
1407                if ($group->share_stock && $group->haveShops()) {
1408                    continue;
1409                }
1410            }
1411
1412            $id = 'id_'.$row['type'];
1413            if ($row['type'] == 'fk_shop') {
1414                $id = 'id_shop';
1415            } else {
1416                $tableName .= '_'.$row['type'];
1417            }
1418
1419            if (!$deleted) {
1420                $res = Db::getInstance()->getRow('SELECT * FROM `'._DB_PREFIX_.$tableName.'` WHERE `'.$id.'` = '.(int) $oldId);
1421                if ($res) {
1422                    unset($res[$id]);
1423                    if (isset($row['primary'])) {
1424                        unset($res[$row['primary']]);
1425                    }
1426
1427                    $categories = Tools::getValue('categoryBox');
1428                    if ($tableName == 'product_shop' && count($categories) == 1) {
1429                        unset($res['id_category_default']);
1430                        $keys = implode('`, `', array_keys($res));
1431                        $sql = 'INSERT IGNORE INTO `'._DB_PREFIX_.$tableName.'` (`'.$keys.'`, `id_category_default`, '.$id.')
1432								(SELECT `'.$keys.'`, '.(int) $categories[0].', '.(int) $this->id.' FROM '._DB_PREFIX_.$tableName.'
1433								WHERE `'.$id.'` = '.(int) $oldId.')';
1434                    } else {
1435                        $keys = implode('`, `', array_keys($res));
1436                        $sql = 'INSERT IGNORE INTO `'._DB_PREFIX_.$tableName.'` (`'.$keys.'`, '.$id.')
1437								(SELECT `'.$keys.'`, '.(int) $this->id.' FROM '._DB_PREFIX_.$tableName.'
1438								WHERE `'.$id.'` = '.(int) $oldId.')';
1439                    }
1440                    Db::getInstance()->execute($sql);
1441                }
1442            }
1443        }
1444
1445        // Hook for duplication of shop data
1446        $modulesList = Hook::getHookModuleExecList('actionShopDataDuplication');
1447        if (is_array($modulesList) && count($modulesList) > 0) {
1448            foreach ($modulesList as $m) {
1449                if (!$tablesImport || isset($tablesImport['Module'.ucfirst($m['module'])])) {
1450                    Hook::exec(
1451                        'actionShopDataDuplication',
1452                        [
1453                            'old_id_shop' => (int) $oldId,
1454                            'new_id_shop' => (int) $this->id,
1455                        ],
1456                        $m['id_module']
1457                    );
1458                }
1459            }
1460        }
1461    }
1462
1463    /**
1464     * @param int $id
1465     *
1466     * @return array
1467     *
1468     * @since   1.0.0
1469     * @version 1.0.0 Initial version
1470     */
1471    public static function getCategories($id = 0, $onlyId = true)
1472    {
1473        // build query
1474        $query = new DbQuery();
1475        if ($onlyId) {
1476            $query->select('cs.`id_category`');
1477        } else {
1478            $query->select('DISTINCT cs.`id_category`, cl.`name`, cl.`link_rewrite`');
1479        }
1480        $query->from('category_shop', 'cs');
1481        $query->leftJoin('category_lang', 'cl', 'cl.`id_category` = cs.`id_category` AND cl.`id_lang` = '.(int) Context::getContext()->language->id);
1482        $query->where('cs.`id_shop` = '.(int) $id);
1483        $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
1484
1485        if ($onlyId) {
1486            $array = [];
1487            foreach ($result as $row) {
1488                $array[] = $row['id_category'];
1489            }
1490            $array = array_unique($array);
1491        } else {
1492            return $result;
1493        }
1494
1495        return $array;
1496    }
1497
1498    /**
1499     * @deprecated 2.0.0 Use shop->id
1500     */
1501    public static function getCurrentShop()
1502    {
1503        Tools::displayAsDeprecated();
1504
1505        return Context::getContext()->shop->id;
1506    }
1507
1508    /**
1509     * @param string $entity
1510     * @param int    $idShop
1511     *
1512     * @return array|bool
1513     *
1514     * @since   1.0.0
1515     * @version 1.0.0 Initial version
1516     */
1517    public static function getEntityIds($entity, $idShop, $active = false, $delete = false)
1518    {
1519        if (!Shop::isTableAssociated($entity)) {
1520            return false;
1521        }
1522
1523        return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
1524            '
1525			SELECT entity.`id_'.pSQL($entity).'`
1526			FROM `'._DB_PREFIX_.pSQL($entity).'_shop`es
1527			LEFT JOIN '._DB_PREFIX_.pSQL($entity).' entity
1528				ON (entity.`id_'.pSQL($entity).'` = es.`id_'.pSQL($entity).'`)
1529			WHERE es.`id_shop` = '.(int) $idShop.
1530            ($active ? ' AND entity.`active` = 1' : '').
1531            ($delete ? ' AND entity.deleted = 0' : '')
1532        );
1533    }
1534}
1535