In this article, we will talk about a module that has been used for a long time by more than a thousand online stores — Pre-order and Waiting List Module. With the release of CMS PrestaShop 1.7, we started rewriting the modules for this version of the platform. Today I’ll tell you about the difficulties I encountered when developing the module for this version of PrestaShop.
Difficulty #1 was the fact that the product page on the administrative part of the platform was rewritten for Symfony. And now for the purpose of modules’ data preservation, we will have to create a separate entity in which we will store the data of the module and its controller, which will process this data and store it in the entity.
I started with defining the values that should be stored in the database and got down to creating the entity:
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 |
class PreorderProduct extends PPModel { public $id_preorder_product; public $active_preorder; public $active_countdown; public $id_product; public $id_product_attribute; public $date_available; public $qty; public $date_add; public $date_upd; public static $definition = array( 'table' => 'preorder_product', 'primary' => 'id_preorder_product', 'multilang' => true, 'multilang_shop' => true, 'multishop' => true, 'fields' => array( 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'id_preorder_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), //shop fields 'id_product' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'), 'id_product_attribute' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'), 'qty' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isInt'), 'active_preorder' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'active_countdown' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'date_available' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDate'), ), ); public function __construct($id = null, $id_lang = null, $id_shop = null) { Shop::addTableAssociation(self::$definition['table'], array('type' => 'shop')); Shop::addTableAssociation(self::$definition['table'] . '_lang', array('type' => 'fk_shop')); parent::__construct($id, $id_lang, $id_shop); } } |
Here everything remains unchanged compared to the 1.6.x version. The first two lines in the constructor are needed for the operation of an entity with a multistore.
Now we move on and create a controller:
class AdminPreorderProductController extends ModuleAdminController
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 |
{ public function ajaxProcessDelete() { try { $id = PreorderProduct::getIdByIdProductAndIdAttr( Tools::getValue('id_product'), Tools::getValue('id_product_attribute') ); $pp = new PreorderProduct($id); $pp->delete(); $this->ajaxProcessGetList(); } catch (Exception $e) { throw new PrestaShopException($e->getMessage()); } } public function ajaxProcessGetList() { try { $id_product = Tools::getValue('id_product'); $product = new Product($id_product, false, $this->context->language->id); $combinations = $product->getAttributeCombinations(); $groups = array(); if (empty($combinations)) { $qty = StockAvailable::getQuantityAvailableByProduct($id_product); if ($qty <= 0) { $groups[] = array( 'name' => $product->name, 'id_product_attribute' => 0, 'active_preorder' => false, ); } } foreach ($combinations as $c) { if ($c['quantity'] <= 0) { $id_p_attr = $c['id_product_attribute']; if (isset($groups[$id_p_attr])) { $groups[$id_p_attr]['name'] .= $c['group_name'] . ': ' . $c['attribute_name']; } else { $groups[$id_p_attr] = array( 'name' => $c['group_name'] . ': ' . $c['attribute_name'] . ', ', 'id_product_attribute' => $c['id_product_attribute'], 'active_preorder' => false, ); } } } if ($preorders = PreorderProduct::getAllByIdProduct($id_product)) { foreach ($preorders as $p) { $id_p_attr = $p['id_product_attribute']; if (isset($groups[$id_p_attr])) { $groups[$id_p_attr] = array_merge($groups[$id_p_attr], $p); } } } $list = $this->context->smarty->createTemplate( $this->module->getLocalPath() . 'views/templates/admin/list.tpl' ); $list->assign(array( 'preorder_products' => $groups, )); die($list->fetch()); } catch (Exception $e) { throw new PrestaShopException($e->getMessage()); } } } |
This is the beginning of the Ajax territory. Now developers have to use various handlers for each operation on the admin side, which is a little more complicated than it used to be in PrestaShop 1.6. The main techniques while working with product settings are try catch and json constructions. But you are free to go your own path. May the force be with you!
The registration of the administrative controller is carried out using the Tab class, just like it used to be earlier:
1 2 3 4 5 6 7 |
$tab = new Tab(); $tab->active = $active; $tab->class_name = $class_name; $tab->name = array(); foreach (Language::getLanguages(true) as $lang) { $tab->name[$lang['id_lang']] = $tab_name; } |
1 2 3 4 |
$tab->id_parent = $id_parent; $tab->position = $position; $tab->module = $this->name; return $tab→save(); |
Here is what we have as a result of all these manipulations:
The advantage of this approach is the speed of work with the product. The disadvantage — an increase in time spent to develop this functionality.
Unfortunately, the creation of custom pages remained unchanged, CMS developers promise to gradually move the rest of the functionality to Symfony. That is why the creation of the module settings page remained the same as in the 1.6.x version.
The module is fairly simple to use. As soon as a product is out of stock, it becomes possible to activate preorder and configure countdown. Here is what will be displayed on the frontend.:
You will see a “Pre-order” button, a “Notify” button (subscription to email notifications on the product availability) and the time when the product is due to be back in stock.
The administrator will be able to control the lists of subscribers and the products subscribed to, but the mailing and stock updating are automatic, the administrator will only have to control the supplies to the warehouse:
The main feature of the module for this PrestaShop version is the ability to create your own email templates for the notifications on the received products:
The functionality allows you to create templates for different product categories and for different groups of customers. It’s quite useful if you have a group of premium customers.
The Pre-order and Waiting List module are available in our module store.