Prestashop 1.4 has pleased developers with a possibility to change standard functions behavior without core modification. New Prestashop 1.5 went even further by presenting the possibility to rewrite files while installing a module.
Here is what the install function looked like in Prestashop 1.4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * Insert module into datable */ public function install() { if (!Validate::isModuleName($this->name)) die(Tools::displayError()); $result = Db::getInstance()->getRow(' SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($this->name).'\''); if ($result) return false; $result = Db::getInstance()->AutoExecute(_DB_PREFIX_.$this->table, array('name' => $this->name, 'active' => 1), 'INSERT'); if (!$result) return false; $this->id = Db::getInstance()->Insert_ID(); return true; } |
I.e., the system checked the module table for records about the module installed and if there were no any, it made one and returned its ID. In new Prestashop 1.5 this function looks like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
/** * Insert module into datable */ public function install() { // Check module name validation if (!Validate::isModuleName($this->name)) die(Tools::displayError()); // Check PS version compliancy if (version_compare(_PS_VERSION_, $this->ps_versions_compliancy['min']) < 0 || version_compare(_PS_VERSION_, $this->ps_versions_compliancy['max']) >= 0) { $this->_errors[] = $this->l('The version of your module is not compliant with your PrestaShop version.'); return false; } // Check module dependencies if (count($this->dependencies) > 0) foreach ($this->dependencies as $dependency) if (!Db::getInstance()->getRow('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($dependency).'\'')) { $error = $this->l('Before installing this module, you have to installed these/this module(s) first :').'<br />'; foreach ($this->dependencies as $d) $error .= '- '.$d.'<br />'; $this->_errors[] = $error; return false; } // Check if module is installed $result = Db::getInstance()->getRow('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($this->name).'\''); if ($result) { $this->_errors[] = $this->l('This module has already been installed.'); return false; } // Install overrides try { $this->installOverrides(); } catch (Exception $e) { $this->_errors[] = sprintf(Tools::displayError('Unable to install override: %s'), $e->getMessage()); $this->uninstallOverrides(); return false; } // Install module and retrieve the installation id $result = Db::getInstance()->insert($this->table, array('name' => $this->name, 'active' => 1, 'version' => $this->version)); if (!$result) { $this->_errors[] = $this->l('Technical error : PrestaShop could not installed this module.'); return false; } $this->id = Db::getInstance()->Insert_ID(); Cache::clean('Module::isInstalled'.$this->name); // Enable the module for current shops in context $this->enable(); // Permissions management Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'module_access` (`id_profile`, `id_module`, `view`, `configure`) ( SELECT id_profile, '.(int)$this->id.', 1, 1 FROM '._DB_PREFIX_.'access a WHERE id_tab = ( SELECT `id_tab` FROM '._DB_PREFIX_.'tab WHERE class_name = \'AdminModules\' LIMIT 1) AND a.`view` = 1)'); Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'module_access` (`id_profile`, `id_module`, `view`, `configure`) ( SELECT id_profile, '.(int)$this->id.', 1, 0 FROM '._DB_PREFIX_.'access a WHERE id_tab = ( SELECT `id_tab` FROM '._DB_PREFIX_.'tab WHERE class_name = \'AdminModules\' LIMIT 1) AND a.`view` = 0)'); // Adding Restrictions for client groups Group::addRestrictionsForModule($this->id, Shop::getShops(true, null, true)); return true; } |
New Prestashop version provides a possibility to specify the system versions your module is compatible with. $this->ps_versions_compliancy[‘min’] and $this->ps_versions_compliancy[‘max’] are used for it; if client’s system version is not compatible with your module, the following message will be displayed for a user: The version of your module is not compliant with your PrestaShop version. installOverrides() и addOverride() functions are responsible for controllers and classes redefining while module installation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
/** * Install overrides files for the module * * @return bool */ public function installOverrides() { // Get local path for module if (!is_dir($this->getLocalPath().'override')) return true; $result = true; foreach (Tools::scandir($this->getLocalPath().'override', 'php', '', true) as $file) { $class = basename($file, '.php'); // Add all methods in a module override to the override class if (Autoload::getInstance()->getClassPath($class.'Core')) $result &= $this->addOverride($class); } return $result; } /** * Add all methods in a module override to the override class * * @param string $classname * @return bool */ public function addOverride($classname) { $path = Autoload::getInstance()->getClassPath($classname.'Core'); // Check if there is already an override file, if not, we just need to copy the file if (!($classpath = Autoload::getInstance()->getClassPath($classname))) { $override_src = $this->getLocalPath().'override'.DIRECTORY_SEPARATOR.$path; $override_dest = _PS_ROOT_DIR_.DIRECTORY_SEPARATOR.'override'.DIRECTORY_SEPARATOR.$path; if (!is_writable(dirname($override_dest))) throw new Exception(sprintf(Tools::displayError('directory (%s) not writable'), dirname($override_dest))); copy($override_src, $override_dest); return true; } // Check if override file is writable $override_path = _PS_ROOT_DIR_.'/'.Autoload::getInstance()->getClassPath($classname); if (!is_writable($override_path)) throw new Exception(sprintf(Tools::displayError('file (%s) not writable'), $override_path)); // Make a reflection of the override class and the module override class $override_file = file($override_path); eval(preg_replace(array('#^\s*<\?php#', '#class\s+'.$classname.'\s+extends\s+([a-z0-9_]+)(\s+implements\s+([a-z0-9_]+))?#i'), array('', 'class '.$classname.'OverrideOriginal'), implode('', $override_file))); $override_class = new ReflectionClass($classname.'OverrideOriginal'); $module_file = file($this->getLocalPath().'override'.DIRECTORY_SEPARATOR.$path); eval(preg_replace(array('#^\s*<\?php#', '#class\s+'.$classname.'(\s+extends\s+([a-z0-9_]+)(\s+implements\s+([a-z0-9_]+))?)?#i'), array('', 'class '.$classname.'Override'), implode('', $module_file))); $module_class = new ReflectionClass($classname.'Override'); // Check if none of the methods already exists in the override class foreach ($module_class->getMethods() as $method) if ($override_class->hasMethod($method->getName())) throw new Exception(sprintf(Tools::displayError('The method %1$s in the class %2$s is already overriden.'), $method->getName(), $classname)); // Check if none of the properties already exists in the override class foreach ($module_class->getProperties() as $property) if ($override_class->hasProperty($property->getName())) throw new Exception(sprintf(Tools::displayError('The property %1$s in the class %2$s is already defined.'), $property->getName(), $classname)); // Insert the methods from module override in override $copy_from = array_slice($module_file, $module_class->getStartLine() + 1, $module_class->getEndLine() - $module_class->getStartLine() - 2); array_splice($override_file, $override_class->getEndLine() - 1, 0, $copy_from); $code = implode('', $override_file); file_put_contents($override_path, $code); return true; } |
In order to make an override in the module, one should create the “override” folder in it and place the file with logic changed. Folder structure should be saved. For instance, to overload CmsController.php, you need to the following folders structure override/controllers/front/CmsController.php with the following content in the folder
1 2 3 4 5 6 7 8 9 |
class CmsController extends CmsControllerCore { public function setMedia(){ parent::setMedia(); die('Test override function'); } } |
When your module is installed, the system will find the file it needs to rewrite. Things are not that neat and tidy though. Prestashop will not simply replace the override/controllers/front/CmsController.php file with the one located in the same folder with your module. What if the main file contains already some kind of a changed logic? Protection mechanism launches here: the system compares its main file with overrides (ex. public_html/override/controllers/front/CmsController.php) and file in you module. If functions names are not the same in these files, there will be an installation failure and a user will see, for instance, the following message: The method setMedia in the class CmsController is already overridden.
I would like to warn you about the harm of overrides after all. They stir up troubles for developers: if user’s store is strongly customized and same class (or function, what is worse) is overridden by several modules at once, logic should be combined into a single file. I.e., if Prestashop displays “this function is already overridden” error, there will be nothing to do except for editing the main file with the rewrite. So, if you are able to use Hook in your module – do it and you’ll avoid the rewrite.
Yassine,
You can find an example of the module for the 1.4 version in this article: http://blog.belvg.com/developer-tips-how-to-enable-prestashop-cms-search-part-2.html. As of today, we do not have examples for the 1.5 version. However, follow our blog and we’ll publish the article with the 1.5 example next week.
Hi
Can u make an example of this module
i would view source code of this module
Kasper,
It will be removed itself. When the module is being deleted, overridden methods will be deleted as well. (see Module::uninstallOverrides())
Great tutorial about how to add an override in a module. Any word un how to remove the override when you uninstall the module?