Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Giorgio Bignozzi - How to develop a Sticker plug-in for Magento 2: best practice

373 views

Published on

Case history: how to create a plugin that adds the classical label on product image in the different layouts concerning products (product page, category page, related products, upsell, cross-sell).

Aims:

1. To create two labels with different shape and position,
2. To have a divided graphic management of these labels,
3. To avoid linking labels to single products, but working on categories

They focused on some issues that highlight the new mechanisms in Magento 2, compared to the usual procedures in Magento 1:

– Structure of folds and plugin files, different from Magento 1,
– Plugin registration, different from Magento 1,
– Use of dependency injection in M2,
– Core rewriting through ‘plugin’ in M2,
– Use of ‘RequireJS’ to load javascript in M2,
– Adding an ACL, different from Magento 1,
– Adding setting parameters and source models, similar to M1.

They shared technical choices, the problems we faced, the status of the documentation available online and the interaction with Magento community.

  • Be the first to comment

Giorgio Bignozzi - How to develop a Sticker plug-in for Magento 2: best practice

  1. 1. Developing a Sticker plug-in Sticker plug-in for Magento 2 Best practice
  2. 2. Giorgio Bignozzi Technical Manager | Marketing Informatico https://www.marketinginformatico.it @GiorgioBignozzi Michele Fantetti Magento Developer at Maggioli Editore http://www.fantetti.net @WaP_oNe Who we are
  3. 3. Abstract: Our aim was to create an extension that adds the classical label to the product image in the different layouts concerning products (product page, category page, related products, upsell products, cross-sell products, widget, search results). Goals: • To create two labels (“Discount” and “New”) with different shapes and positions • To have a separate graphic management of these labels (custom label or image). If you choose a custom label, you can set two labels with a configurable text, text colour and background colour • To display the label on sale products or products associated to certain categories (“Discount”) • To display the label on products associated to certain categories (“New”) • To enable/disable automatically (with programmable date and time) one or both the stickers. Our project: Stickers
  4. 4. Desiderata: category page
  5. 5. Desiderata: product page
  6. 6. Desiderata: widget
  7. 7. Desiderata: related / cross-sell / upsell
  8. 8. Desiderata: search results
  9. 9. • ISSUE #1: how to add the label (HTML code) without modifying layout files in multiple pages and making the plugin independent • SOLUTION #1: when rendering product attributes, upload HTML code that will be completed lately via script JS. • ISSUE #2: how and where to insert this HTML code • SOLUTION #2: thanks to Magento 2 PLUGIN, beforeGetProductPrice() and afterGetProductPrice(). Development: problems and solutions (1)
  10. 10. • ISSUE #3: how to insert extra code to this HTML code • SOLUTION #3: requireJS • ISSUE #4: enable/disable some configurable fields depending on which fields you have valued before • SOLUTION #4: upload a JS in the layout of the configuration page of the plugin Development: problems and solutions (2)
  11. 11. File System: M1 VS M2 Differences in file system organization in M1 e M2 (Vanilla installation)
  12. 12. • registration.php • etc/module.xml Development: M1 vs M2 structure for plug-in registration In M2 we need to modify only one folder (app/code)
  13. 13. app/code/Mainf/Stickers/Model/Stickers.php It manages every feature of the sticker (isInCategory(), isDiscounted(), getDiscountAmount() etc.) in addition to inserting HTML code: protected function _getHTML($stickerLayoutType, $class, $firstLabel = '', $secondLabel = '') { switch ($stickerLayoutType) { case self::STICKER_IMAGE: $html = "<input type='hidden' class='".$class."' value='1' />"; break; case self::STICKER_AREA: $html = "<input type='hidden' class='".$class."' value='" . $this->getConfigValue($firstLabel) . "<br />" . $this->getConfigValue($secondLabel) . "' />"; break; case self::STICKER_CALCULATED: $html = "<input type='hidden' class='".$class."' value='" . $this->getDiscountAmount() . "' />"; break; default: $html = "<input type='hidden' class='".$class."' value='1' />"; break; } return $html; } Development : model class Stickers
  14. 14. app/code/Mainf/Stickers/Plugin/ProductStickersPlugin.php namespace MainfStickersPlugin; class ProductStickersPlugin extends MainfStickersModelStickers { const DISCOUNT_ACTIVATION = "stickers/stickers_discount_page/discount_activation"; const LATEST_ACTIVATION = "stickers/stickers_latest_page/latest_activation"; public function beforeGetProductPrice( MagentoCatalogBlockProductAbstractProduct $abstractProduct, MagentoCatalogModelProduct $product ) { $this->_product = $product; } public function afterGetProductPrice(MagentoCatalogBlockProductAbstractProduct $product, $result) { if($this->isStickerActive(self::DISCOUNT_ACTIVATION)) { $result .= $this->_setDiscountStickerHTML(); } if($this->isStickerActive(self::LATEST_ACTIVATION)) { $result .= $this->_setLatestStickerHTML(); } return $result; } } Development : PLUGIN (Dependency Injection)
  15. 15. app/code/Mainf/Stickers/view/frontend/layout/default.xml <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> <css src="Mainf_Stickers::css/mainf-stickers.css"/> <link src="https://fonts.googleapis.com/css?family=Oswald:400,700,300" rel="stylesheet" type="text/css" src_type="url" /> </head> <body> <referenceContainer name="content"> <block name="category.discount.sticker" class="MainfStickersBlockDiscount" template="Mainf_Stickers::categoryDiscount.phtml" cacheable="false" after="-" /> <block name="category.latest.sticker" class="MainfStickersBlockLatest" template="Mainf_Stickers::categoryLatest.phtml" cacheable="false" after="-" /> </referenceContainer> </body> </page> Other layout files: catalog_product_view.xml e checkout_cart_index.xml Development: layout
  16. 16. app/code/Mainf/Stickers/view/frontend/templates/categoryDiscount.phtml if($block->isDiscountActive()): $discountImage = "<div class='mainf-sticker-wrapper top-right'>"; $discountImage .= "<img class='categoryDiscountImage' alt='".__('Discount')."' src='".$this- >getUrl('pub/media')."mainf/stickers/images/".$block->getStickerImage()."' />"; $discountImage .= "</div>"; $discountArea = "<div class='mainf-sticker-wrapper top-right'>"; $discountArea .= "<div class='mainf-sticker discount-product' style='background-color: #".$block->getStickerBackground()."; color: #".$block- >getStickerText().";'></div>"; $discountArea .= "</div>"; ?> Development: template <script type="text/x-magento-init"> { "*":{ "categoryPageDiscount":{ "imageTag": { "discountImage": "<?php /* @escapeNotVerified */ echo $discountImage; ?>", "discountArea": "<?php /* @escapeNotVerified */ echo $discountArea; ?>" }}}} </script> <?php endif;
  17. 17. app/code/Mainf/Stickers/view/frontend/requirejs-config.js var config = { map: { '*': { categoryPageDiscount: 'Mainf_Stickers/js/categoryPageDiscount', categoryPageLatest: 'Mainf_Stickers/js/categoryPageLatest', viewPageDiscount: 'Mainf_Stickers/js/viewPageDiscount', viewPageLatest: 'Mainf_Stickers/js/viewPageLatest' } } }; Development: RequireJS (1)
  18. 18. app/code/Mainf/Stickers/view/frontend/web/js/categoryPageDiscount.js define( ['jquery'], function ($) { $.widget( 'mainf.categoryPageStickers', { _create: function () { var self = this; $(".categoryPageDiscount").each(function () { if ($(this).val() == 1) { $($(this).parent().parent().find("a").find("span").find("span")) .prepend(self.options.imageTag.discountImage); } else { var discountAmount = $(this).val(); $($(this).parent().parent().find("a").find("span").find("span")) .prepend(self.options.imageTag.discountArea); $(".discount-product").html(discountAmount); } }); } } ); return $.mainf.categoryPageStickers; } ); Development: RequireJS (2)
  19. 19. Backend: module configuration & customization (1)
  20. 20. Backend: module configuration & customization (2)
  21. 21. app/code/Mainf/Stickers/etc/adminhtml/system.xml <system> <tab>..</tab> <section><group><field>..</field></ group></section> </system> <source_model>MainfStickersModelConfigBackendStickerType</source_model> app/code/Mainf/Stickers/Model/Config/Backend/StickerType.php <?php namespace MainfStickersModelConfigBackend; use MagentoFrameworkOptionArrayInterface; class StickerType implements ArrayInterface { public function toOptionArray() { $stickerTypes = array(); $stickerTypes[] = ['value' => 'image','label' => __('Image')]; $stickerTypes[] = ['value' => 'custom','label' => __('Custom Label')]; return $stickerTypes; } } Development: module configuration & source model
  22. 22. Issue: to create a hierarchy when enabling/disabling fields Solution: to upload a template in the layout of the configuration page (adminhtml_system_config_edit.xml): <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="footer"> <block class="MainfStickersBlockConfig" template="Mainf_Stickers::js.phtml"/> </referenceContainer> </body> </page> In js.phtml template insert the JS code: <script type="text/javascript"> require( ['jquery'], function($) { $(function() { .. }); } ); </script> Development: module configuration & customization
  23. 23. • crontab.xml file creation (new!): <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd"> <group id="default"> <job name="mainf_stickers_activation" instance="MainfStickersModelObserver” method="checkStickersActivation"> <schedule>*/5 * * * *</schedule> </job> </group> </config> • Observer.php file creation: control of the status of the sticker (enabled/disabled) comparison of date enabling/disabling the sticker flush of date field Development: Observer
  24. 24. Basic CSS: .product.media .mainf-sticker-wrapper.top-right { top: 13px; right: 13px; } .product.media .mainf-sticker-wrapper.top-left { bottom: 200px; } .mainf-sticker.discount-product { width: 66px; border: #FFF 2px solid; border-radius: 50%; box-shadow: 2px 2px 2px #CCC; text-align: center; height: 60px; padding: 10px 5px; } .mainf-sticker.latest-product { min-width: 66px; max-width: 130px; min-height: 76px; padding: 5px; border: #FFF 2px solid; box-shadow: 2px 2px 2px #CCC; text-align:left; } Graphic changes
  25. 25. Basic CSS 1° option: .product.media .mainf-sticker-wrapper.top-right { top: 150px; right: 150px; } Graphic changes
  26. 26. Basic CSS 2° option: .product.media .mainf-sticker-wrapper.top-right { top: 400px; right: 150px; } .product.media .mainf-sticker-wrapper.top-left { bottom: 600px; } .mainf-sticker.latest-product { min-width: 66px; max-width: 130px; width: 150px; min-height: 76px; padding: 5px; border: #FFF 2px solid; box-shadow: 2px 2px 2px #CCC; text-align:left; } Graphic changes
  27. 27. • Structural improvements for the development of extensions: all files for each extension in the same folder; substitution of controllers/ folder with Controller/ ; use of XML Style Definition (XSD) within xml file; split of config.xml in multiple xml files, each with a precise task (module.xml, routes.xml, crontab.xml etc.) ; one file for each action; bin/magento • Lots of bugs still open (1695 in GitHub, our is #8370), community not ready as it is for M1 and a lot of doubts. Conclusions
  28. 28. You can download the extension “Stickers” here: https://github.com/WaPoNe/module-stickers Or you can buy the extension “Stickers Pro” here: http://www.magentostoremanager.it/module-stickers- pro.html Discount coupon for you: MMIT17 Reference
  29. 29. Write us at: giorgio.bignozzi@marketinginformatico.it michele.fantetti@maggioli.it Marketing Informatico Milano | Bologna | Rimini | Bari Any question?

×