Comment ajouter un champ personnalisé aux options dans l'extension Advanced Product Options
Publié: 2020-09-08À partir de cet article, vous apprendrez à créer un champ "GTIN" pour les options personnalisées du produit, à l'afficher sur le front-end de la page du produit et à l'afficher dans la commande.
Sans plus tarder, passons aux directives étape par étape.
Table des matières
- Étape 1. Créer un nouveau module
- Étape 2. Ajouter notre nouveau champ à la base de données
- Étape 3. Ajouter une logique pour travailler avec le backend
- Étape 4. Afficher notre champ sur le front-end de la page produit
- Étape #5. Ajouter nos données d'attribut aux détails de la commande dans la base de données
- Étape #6. Afficher les données sur la page des commandes dans le panneau d'administration
Étape 1. Créer un nouveau module
Nous avons décrit en détail comment créer un module dans cet article. Ainsi, sautons cette partie et passons directement au code dont vous aurez besoin pour créer un module complémentaire :
1.composer.json
{ "name": "mageworx/module-optiongtin", "description": "N/A", "require": { "magento/framework" : ">=100.1.0 <101", "magento/module-catalog": ">=101.0.0 <104" }, "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "autoload": { "files": [ "registration.php" ], "psr-4": { "VendorName\\OptionGtin\\": "" } } }
2.etc/module.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="VendorName_OptionGtin" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> <module name="MageWorx_OptionBase"/> </sequence> </module> </config>
3.inscription.php
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'VendorName_OptionGtin', __DIR__ );
Étape 2. Ajouter notre nouveau champ à la base de données
Après avoir construit un module vide, il est temps de créer le nouveau champ "GTIN" et de l'ajouter à la base de données dans la table correspondante. Comme nous ajoutons un champ pour les valeurs d'option, nous aurons besoin de la table "catalog_product_option".
Créons le fichier suivant :
app/code/VendorName/OptionGtin/Setup/InstallSchema.php
<?php namespace VendorName\OptionGtin\Setup; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\DB\Ddl\Table; class InstallSchema implements InstallSchemaInterface { public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $setup->getConnection()->addColumn( $setup->getTable('catalog_product_option'), 'gtin', [ 'type' => Table::TYPE_TEXT, 'nullable' => true, 'default' => null, 'comment' => 'Gtin (added by VendorName Option Gtin)', ] ); $setup->endSetup(); } }
Étape 3. Ajouter une logique pour travailler avec le backend
Nous allons utiliser le mécanisme de modificateur de pool pour ajouter notre nouveau champ.
Maintenant, ajoutez le fichier suivant :
app/code/VendorName/OptionGtin/etc/adminhtml/di.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <virtualType name="MageWorx\OptionBase\Ui\DataProvider\Product\Form\Modifier\Pool"> <arguments> <argument name="modifiers" xsi:type="array"> <item name="mageworx-option-gtin" xsi:type="array"> <item name="class" xsi:type="string">VendorName\OptionGtin\Ui\DataProvider\Product\Form\Modifier\OptionGtin</item> <item name="sortOrder" xsi:type="number">72</item> </item> </argument> </arguments> </virtualType> </config>
Ici, ajoutons notre modificateur au pool partagé de l'extension Advanced Product Options - "MageWorx\OptionBase\Ui\DataProvider\Product\Form\Modifier\Pool". "VendorName\OptionGtin\Ui\DataProvider\Product\Form\Modifier\OptionGtin" est notre modificateur de classe.
Le code qui permet d'ajouter notre champ au app/code/VendorName/OptionGtin/Ui/DataProvider/Product/Form/Modifier/OptionGtin.php
est fourni ci-dessous :
<?php namespace VendorName\OptionGtin\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions; use Magento\Ui\Component\Form\Element\Input; use Magento\Ui\Component\Form\Element\DataType\Number; use Magento\Ui\Component\Form\Field; use MageWorx\OptionBase\Ui\DataProvider\Product\Form\Modifier\ModifierInterface; class OptionGtin extends AbstractModifier implements ModifierInterface { /** * @var array */ protected $meta = []; /** * {@inheritdoc} */ public function modifyData(array $data) { return $data; } /** * {@inheritdoc} */ public function modifyMeta(array $meta) { $this->meta = $meta; $this->addFields(); return $this->meta; } /** * Adds fields to the meta-data */ protected function addFields() { $groupCustomOptionsName = CustomOptions::GROUP_CUSTOM_OPTIONS_NAME; $optionContainerName = CustomOptions::CONTAINER_OPTION; $commonOptionContainerName = CustomOptions::CONTAINER_COMMON_NAME; // Add fields to the option $optionFeaturesFields = $this->getOptionGtinFieldsConfig(); $this->meta[$groupCustomOptionsName]['children']['options']['children']['record']['children'] [$optionContainerName]['children'][$commonOptionContainerName]['children'] = array_replace_recursive( $this->meta[$groupCustomOptionsName]['children']['options']['children']['record']['children'] [$optionContainerName]['children'][$commonOptionContainerName]['children'], $optionFeaturesFields ); } /** * The custom option fields config * * @return array */ protected function getOptionGtinFieldsConfig() { $fields['gtin'] = $this->getGtinFieldConfig(); return $fields; } /** * Get gtin field config * * @return array */ protected function getGtinFieldConfig() { return [ 'arguments' => [ 'data' => [ 'config' => [ 'label' => __('GTIN'), 'componentType' => Field::NAME, 'formElement' => Input::NAME, 'dataType' => Number::NAME, 'dataScope' => 'gtin', 'sortOrder' => 65 ], ], ], ]; } /** * Check is current modifier for the product only * * @return bool */ public function isProductScopeOnly() { return false; } /** * Get sort order of modifier to load modifiers in the right order * * @return int */ public function getSortOrder() { return 32; } }
Essayons maintenant d'installer l'extension et de vérifier que tout s'affiche :
- module php bin/magento : activer VendorName_OptionGtin
- configuration de php bin/magento : mise à niveau
- php bin/cache magento : vider
Notre nouveau champ a été ajouté avec succès :
Étape 4. Afficher notre champ sur le front-end de la page produit
L'extension Mageworx Advanced Product Options a déjà tout pour afficher et travailler avec les attributs ajoutés par notre module. Tout ce que nous avons à faire est d'ajouter le nouvel attribut au jeu de données partagé.
Notre module MageWorx_OptionBase utilise déjà la méthode getExtendedOptionsConfig()
. Il collecte et affiche tous les attributs personnalisés dans un bloc sur le front-end. Ouvrez la classe app/code/MageWorx/OptionBase/Block/Product/View/Options.php
pour voir comment elle est implémentée.
Commençons par créer un modèle avec notre attribut :
app/code/VendorName/OptionGtin/Model/Attriburte/Option/Gtin.php
<?php namespace VendorName\OptionGtin\Model\Attribute\Option; use MageWorx\OptionBase\Model\Product\Option\AbstractAttribute; class Gtin extends AbstractAttribute { /** * @return string */ public function getName() { return 'gtin'; } }
Maintenant, utilisez le mécanisme "d'injection de dépendances" et ajoutez notre attribut au jeu de données d'attributs partagés de l'extension Advanced Product Options.
app/code/VendorName/OptionGtin/etc/di.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- Data --> <type name="MageWorx\OptionBase\Model\Product\Option\Attributes"> <arguments> <argument name="data" xsi:type="array"> <item name="gtin" xsi:type="object">VendorName\OptionGtin\Model\Attribute\Option\Gtin</item> </argument> </arguments> </type> </config>
En d'autres termes, en ouvrant la MageWorx\OptionBase\Model\Product\Option\Attributes
, vous verrez qu'elle collecte simplement tous les objets d'attribut dans le jeu de données partagé.
Pour afficher les données de notre nouvel attribut "GTIN", nous avons décidé d'utiliser la fonction firstrun()
de app/code/MageWorx/OptionFeatures/view/base/web/js/catalog/product/features.js
. Il a déjà toute l'implémentation requise qui correspond le mieux à notre exemple. Pour éviter d'écraser tout le fichier, nous appliquerons le mécanisme "JavaScript mixins", qui nous aidera à modifier uniquement la fonction nécessaire.
Créez le fichier suivant et définissez-y notre mixin : app/code/VendorName/OptionGtin/view/frontend/requirejs-config.js
var config = { config: { mixins: { 'MageWorx_OptionFeatures/js/catalog/product/features': { 'VendorName_OptionGtin/js/catalog/product/features-gtin-mixin' : true } } } };
Ici, MageWorx_OptionFeatures/js/catalog/product/features
est la racine de notre fichier, quelle méthode nous devons réécrire. VendorName_OptionGtin/js/catalog/product/features-gtin-mixin
est le fichier dans lequel nous allons réécrire la méthode.
Alors, créons-le : app/code/VendorName/OptionGtin/view/frontend/web/js/catalog/product/features-gtin-mixin.js
define([ 'jquery', 'jquery/ui', 'mage/utils/wrapper' ], function ($, wrapper) { 'use strict'; return function (widget) { $.widget('mageworx.optionFeatures', widget, { /** * Triggers one time at first run (from base.js) * @param optionConfig * @param productConfig * @param base * @param self */ firstRun: function firstRun(optionConfig, productConfig, base, self) { //shareable link $('#mageworx_shareable_hint_icon').qtip({ content: { text: this.options.shareable_link_hint_text }, style: { classes: 'qtip-light' }, position: { target: false } }); $('#mageworx_shareable_link').on('click', function () { try { self.copyTextToClipboard(self.getShareableLink(base)); $('.mageworx-shareable-link-container').hide(); $('.mageworx-shareable-link-success-container').show(); setTimeout(function () { $('.mageworx-shareable-link-container').show(); $('.mageworx-shareable-link-success-container').hide(); }, 2000); } catch (error) { console.log('Something goes wrong. Unable to copy'); } }); setTimeout(function () { // Qty input $('.mageworx-option-qty').each(function () { $(this).on('change', function () { var optionInput = $("[data-selector='" + $(this).attr('data-parent-selector') + "']"); optionInput.trigger('change'); }); }); }, 500); // Option\Value Description & tooltip var extendedOptionsConfig = typeof base.options.extendedOptionsConfig != 'undefined' ? base.options.extendedOptionsConfig : {}; for (var option_id in optionConfig) { if (!optionConfig.hasOwnProperty(option_id)) { continue; } var description = extendedOptionsConfig[option_id]['description'], gtin = extendedOptionsConfig[option_id]['gtin'], gtinTitle = "Global Trade Item Number: ", $option = base.getOptionHtmlById(option_id); if (1 > $option.length) { console.log('Empty option container for option with id: ' + option_id); continue; } var $label = $option.find('label'); if(gtin != null && gtin.length > 0) { if ($label.length > 0) { $label .first() .after($('<p class="option-gtin-text"><span>' + gtinTitle + '</span>' + gtin + '</p>')); } else { $label = $option.find('span'); $label .first() .parent() .after($('<p class="option-gtin-text"><span>' + gtinTitle + '</span>' + gtin + '</p>')); } } if (this.options.option_description_enabled && !_.isEmpty(extendedOptionsConfig[option_id]['description'])) { if (this.options.option_description_mode == this.options.option_description_modes.tooltip) { var $element = $option.find('label span') .first(); if ($element.length == 0) { $element = $option.find('fieldset legend span') .first(); } $element.css('border-bottom', '1px dotted black'); $element.qtip({ content: { text: description }, style: { classes: 'qtip-light' }, position: { target: false } }); } else if (this.options.option_description_mode == this.options.option_description_modes.text) { if ($label.length > 0) { $label .first() .after($('<p class="option-description-text">' + description + '</p>')); } else { $label = $option.find('span'); $label .first() .parent() .after($('<p class="option-description-text">' + description + '</p>')); } } else { console.log('Unknown option mode'); } } if (this.options.value_description_enabled) { this._addValueDescription($option, optionConfig, extendedOptionsConfig); } } } }); return $.mageworx.optionFeatures; }; });
Généralement, nous pouvons maintenant exécuter les commandes suivantes :
- php bin/cache magento : vider
- php bin/magento setup:static-content:deploy (uniquement pour le mode production)
et voir ce que nous avons. Mais d'abord, ajoutez quelques styles à notre nouvel attribut et donnez-lui une belle apparence sur le front-end.
Créez une mise en page et définissez-y notre nouveau fichier de styles : app/code/VendorName/OptionGtin/view/frontend/layout/catalog_product_view.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="VendorName_OptionGtin::css/gtin.css"/> </head> </page>
Il est temps de créer un fichier de styles : app/code/VendorName/OptionGtin/view/frontend/web/css/gtin.css
.option-gtin-text span { color: #6cc308; font-weight: 700; }
Maintenant, exécutons les commandes décrites précédemment et vérifions les résultats :
Étape #5. Ajouter nos données d'attribut aux détails de la commande dans la base de données
Lorsqu'un client effectue un achat, une commande est créée. Les détails sur les articles ajoutés sont inclus dans la table sales_order_item
. Cette table contient le champ product_options
qui contient des informations sur les paramètres sélectionnés d'un élément ajouté. C'est là que nous devrions ajouter les données de notre nouvel attribut.
Lorsqu'une commande est créée, l'événement sales_quote_address_collect_totals_before
est déclenché. Nous l'utiliserons pour ajouter nos données aux options du produit.
Définissons l'événement en créant : app/code/VendorName/OptionGtin/etc/events.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="sales_quote_address_collect_totals_before"> <observer name="mageworx_optiongtin_add_gtin_to_order" instance="VendorName\OptionGtin\Observer\AddGtinToOrder" /> </event> </config>
Ensuite, créez notre observateur : app/code/VendorName/OptionGtin/Observer/AddGtinToOrder.php
<?php namespace VendorName\OptionGtin\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Catalog\Model\ProductRepository as ProductRepository; use MageWorx\OptionBase\Helper\Data as BaseHelper; class AddGtinToOrder implements ObserverInterface { /** * @var BaseHelper */ protected $baseHelper; protected $productRepository; /** * AddGtinToOrder constructor. * @param BaseHelper $baseHelper * @param ProductRepository $productRepository */ public function __construct( BaseHelper $baseHelper, ProductRepository $productRepository ) { $this->baseHelper = $baseHelper; $this->productRepository = $productRepository; } /** * Add product to quote action * Processing: gtin * * @param Observer $observer * @return $this */ public function execute(Observer $observer) { $quoteItems = $observer->getQuote()->getAllItems(); /** @var \Magento\Quote\Model\Quote\Item $quoteItem */ foreach ($quoteItems as $quoteItem) { $buyRequest = $quoteItem->getBuyRequest(); $optionIds = array_keys($buyRequest->getOptions()); $productOptions = $this->productRepository->getById($buyRequest->getProduct())->getOptions(); $quoteItemOptionGtins = []; $optionGtins = []; foreach ($productOptions as $option) { if ($option->getGtin()) { $quoteItemOptionGtins[$option->getOptionId()] = $option->getGtin(); } } foreach ($optionIds as $optionId) { $optionGtins[$optionId] = $optionId; } $optionGtins = array_intersect_key($quoteItemOptionGtins, $optionGtins); $infoBuyRequest = $quoteItem->getOptionByCode('info_buyRequest'); $buyRequest->setData('gtin', $optionGtins); $infoBuyRequest->setValue($this->baseHelper->encodeBuyRequestValue($buyRequest->getData())); $quoteItem->addOption($infoBuyRequest); } } }
Ici, avec l'aide de l'observateur, nous obtenons la liste de tous les articles de la commande et ajoutons les données de notre attribut "GTIN" à ce que l'on appelle $infoBuyRequest
.
Pour vérifier que tout a été effectué correctement, créez une commande avec le produit, dont les options ont des données "GTIN". Vous pouvez vérifier que les données ont bien été ajoutées dans la sales_order_item table
-> champ product_options
:
Étape #6. Afficher les données sur la page des commandes dans le panneau d'administration
Il existe différents moyens d'afficher les informations requises dans le modèle prêt. Par exemple, en utilisant "js". Nous avons travaillé avec "js" dans cet article. Travaillons avec les modèles eux-mêmes pour changer et essayons de les réécrire !
Modifiez l' app/code/VendorName/OptionGtin/etc/adminhtml/di.xml
en y ajoutant le plugin :
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <virtualType name="MageWorx\OptionBase\Ui\DataProvider\Product\Form\Modifier\Pool"> <arguments> <argument name="modifiers" xsi:type="array"> <item name="mageworx-option-gtin" xsi:type="array"> <item name="class" xsi:type="string">VendorName\OptionGtin\Ui\DataProvider\Product\Form\Modifier\OptionGtin</item> <item name="sortOrder" xsi:type="number">72</item> </item> </argument> </arguments> </virtualType> <!-- Plugins--> <type name="Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn"> <plugin name="mageworx-optiongtin-add-default-column" type="VendorName\OptionGtin\Plugin\AddDefaultColumn" sortOrder="5" disabled="false" /> </type> </config>
Créez le plugin lui-même :
app/code/VendorName/OptionGtin/Plugin/AddDefaultColumn.php
<?php namespace VendorName\OptionGtin\Plugin; class AddDefaultColumn { /** * @param \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn $subject * @param $result * @return array */ public function afterGetOrderOptions(\Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn $subject, $result) { if ($options = $subject->getItem()->getProductOptions()) { if (isset($result)) { foreach ($result as &$option) { if (array_key_exists($option['option_id'], $options['info_buyRequest']['gtin'])) { $option['gtin'] = $options['info_buyRequest']['gtin'][$option['option_id']]; } } } } return $result; } }
Ce plugin ajoute des informations sur notre nouvel attribut pour les options de commande, pour lesquelles ces données existent.
vendor/magento/module-sales/view/adminhtml/templates/items/column/name.phtml
est responsable de l'affichage des informations sur les options du produit sur la page de commande dans le panneau d'administration.
Réécrivons-le pour afficher notre « GTIN ». Pour cela, nous devons réécrire le bloc "column_name", ou plutôt son modèle. Créez une mise en page et un modèle :
app/code/VendorName/OptionGtin/view/adminhtml/layout/sales_order_view.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"> <body> <referenceBlock name="column_name"> <action method="setTemplate"> <argument name="template" xsi:type="string">VendorName_OptionGtin::items/column/name.phtml</argument> </action> </referenceBlock> </body> </page>
app/code/VendorName/OptionGtin/view/adminhtml/templates/items/column/name.phtml
<?php /* @var $block \Magento\Sales\Block\Adminhtml\Items\Column\Name */ ?> <?php if ($_item = $block->getItem()) : ?> <div class="product-title"> <?= $block->escapeHtml($_item->getName()) ?> </div> <div class="product-sku-block"> <span><?= $block->escapeHtml(__('SKU'))?>:</span> <?= /* @noEscape */ implode('<br />', $this->helper(\Magento\Catalog\Helper\Data::class)->splitSku($block->escapeHtml($block->getSku()))) ?> </div> <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> <?php foreach ($block->getOrderOptions() as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?>:</dt> <dd> <?php if (isset($_option['custom_view']) && $_option['custom_view']) : ?> <?= /* @noEscape */ $block->getCustomizedOptionValue($_option) ?> <?php else : ?> <?php $optionValue = $block->getFormattedOption($_option['value']); ?> <?php $dots = 'dots' . uniqid(); ?> <?php $ . uniqid(); ?> <?= $block->escapeHtml($optionValue['value'], ['a', 'br']) ?><?php if (isset($optionValue['remainder']) && $optionValue['remainder']) : ?> <span> ...</span> <span><?= $block->escapeHtml($optionValue['remainder'], ['a']) ?></span> <script> require(['prototype'], function() { $('<?= /* @noEscape */ $id; ?>').hide(); $('<?= /* @noEscape */ $id; ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $id; ?>').show();}); $('<?= /* @noEscape */ $id; ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $dots; ?>').hide();}); $('<?= /* @noEscape */ $id; ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $id; ?>').hide();}); $('<?= /* @noEscape */ $id; ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $dots; ?>').show();}); }); </script> <?php endif; ?> <?php endif; ?> </dd> <dt> <?php if (isset($_option['gtin']) && $_option['gtin']) : ?> <span>GTIN:</span> <?php endif; ?> </dt> <dd> <?php if (isset($_option['gtin']) && $_option['gtin']) : ?> <span> <?= $block->escapeHtml($_option['gtin']) ?></span> <?php endif; ?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> <?php endif; ?>
Si tout a été exécuté correctement, effacé et compilé, vous verrez le résultat suivant :
Nous espérons que vous trouverez cet article utile. Si vous rencontrez des difficultés ou des problèmes, n'hésitez pas à nous en faire part dans le champ de commentaires ci-dessous.