{"id":12730,"date":"2020-09-08T11:24:51","date_gmt":"2020-09-08T11:24:51","guid":{"rendered":"https:\/\/www.mageworx.com\/blog\/?p=12730"},"modified":"2021-10-25T10:56:41","modified_gmt":"2021-10-25T10:56:41","slug":"how-to-add-custom-field-to-advanced-product-options-extension","status":"publish","type":"post","link":"https:\/\/www.mageworx.com\/blog\/how-to-add-custom-field-to-advanced-product-options-extension","title":{"rendered":"How to Add Custom Field to Options in Advanced Product Options Extension"},"content":{"rendered":"\n<!-- SEO Ultimate (http:\/\/www.seodesignsolutions.com\/wordpress-seo\/) - Code Inserter module -->\n<!-- Google Tag Manager (noscript) -->\r\n<noscript><iframe src=\"https:\/\/www.googletagmanager.com\/ns.html?id=GTM-5DTCW7B8\"\r\nheight=\"0\" width=\"0\" style=\"display:none;visibility:hidden\"><\/iframe><\/noscript>\r\n<!-- End Google Tag Manager (noscript) -->\n<!-- \/SEO Ultimate -->\n\n<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>\n<p>From this article, you will learn how to create a \u201cGTIN\u201d field for <a href=\"https:\/\/www.mageworx.com\/magento-2-advanced-product-options-suite.html\">product custom options<\/a>, show it on the product page front-end, and display it in the order.<\/p>\n\n\n\n<p>Without further ado, let\u2019s proceed to the step-by-step guidelines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step #1. Create New Module<\/h2>\n\n\n\n<p>We described in detail how to create a module <a href=\"https:\/\/www.mageworx.com\/blog\/advanced-product-options-customization\/\">in this article<\/a>. Thus, let\u2019s skip this part, and move straight to the code you\u2019ll need to create an add-on:<\/p>\n\n\n\n<p>1.composer.json<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n    \"name\": \"mageworx\/module-optiongtin\",\n    \"description\": \"N\/A\",\n    \"require\": {\n        \"magento\/framework\"     :     \"&gt;=100.1.0 &lt;101\",\n        \"magento\/module-catalog\":     \"&gt;=101.0.0 &lt;104\"\n    },\n    \"type\": \"magento2-module\",\n    \"version\": \"1.0.0\",\n    \"license\": &#91;\n        \"OSL-3.0\",\n        \"AFL-3.0\"\n    ],\n    \"autoload\": {\n        \"files\": &#91;\n            \"registration.php\"\n        ],\n        \"psr-4\": {\n            \"VendorName\\\\OptionGtin\\\\\": \"\"\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>2.etc\/module.xml<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:Module\/etc\/module.xsd\"&gt;\n    &lt;module name=\"VendorName_OptionGtin\" setup_version=\"1.0.0\"&gt;\n        &lt;sequence&gt;\n            &lt;module name=\"Magento_Catalog\"\/&gt;\n            &lt;module name=\"MageWorx_OptionBase\"\/&gt;\n        &lt;\/sequence&gt;\n    &lt;\/module&gt;\n&lt;\/config&gt;<\/code><\/pre>\n\n\n\n<p>3.registration.php<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\\Magento\\Framework\\Component\\ComponentRegistrar::register(\n    \\Magento\\Framework\\Component\\ComponentRegistrar::MODULE,\n    'VendorName_OptionGtin',\n    __DIR__\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step #2. Add Our New Field to Database<\/h2>\n\n\n\n<p>After we\u2019ve built an empty module, it\u2019s time to create the new \u201cGTIN\u201d field and add it to the database within the corresponding table. As we add a field for option values, we\u2019ll need the &#8220;catalog_product_option&#8221; table.<\/p>\n\n\n\n<p>Let\u2019s create the following file:<\/p>\n\n\n\n<p> <code>app\/code\/VendorName\/OptionGtin\/Setup\/InstallSchema.php<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\nnamespace VendorName\\OptionGtin\\Setup;\n\nuse Magento\\Framework\\Setup\\InstallSchemaInterface;\nuse Magento\\Framework\\Setup\\ModuleContextInterface;\nuse Magento\\Framework\\Setup\\SchemaSetupInterface;\nuse Magento\\Framework\\DB\\Ddl\\Table;\n\nclass InstallSchema implements InstallSchemaInterface\n{\n    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)\n    {\n        $setup-&gt;startSetup();\n\n        $setup-&gt;getConnection()-&gt;addColumn(\n            $setup-&gt;getTable('catalog_product_option'),\n            'gtin',\n            &#91;\n                'type'     =&gt; Table::TYPE_TEXT,\n                'nullable' =&gt; true,\n                'default'  =&gt; null,\n                'comment'  =&gt; 'Gtin (added by VendorName Option Gtin)',\n            ]\n        );\n\n        $setup-&gt;endSetup();\n\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step #3. Add Logic to Work with Backend<\/h2>\n\n\n\n<p>We\u2019ll use the pool-modifier mechanism to add our new field.<\/p>\n\n\n\n<p>Now, add the following file:<\/p>\n\n\n\n<p> <code>app\/code\/VendorName\/OptionGtin\/etc\/adminhtml\/di.xml<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:ObjectManager\/etc\/config.xsd\"&gt;\n    &lt;virtualType name=\"MageWorx\\OptionBase\\Ui\\DataProvider\\Product\\Form\\Modifier\\Pool\"&gt;\n        &lt;arguments&gt;\n            &lt;argument name=\"modifiers\" xsi:type=\"array\"&gt;\n                &lt;item name=\"mageworx-option-gtin\" xsi:type=\"array\"&gt;\n                    &lt;item name=\"class\" xsi:type=\"string\"&gt;VendorName\\OptionGtin\\Ui\\DataProvider\\Product\\Form\\Modifier\\OptionGtin&lt;\/item&gt;\n                    &lt;item name=\"sortOrder\" xsi:type=\"number\"&gt;72&lt;\/item&gt;\n                &lt;\/item&gt;\n            &lt;\/argument&gt;\n        &lt;\/arguments&gt;\n    &lt;\/virtualType&gt;\n&lt;\/config&gt;<\/code><\/pre>\n\n\n\n<p>Here, let\u2019s add our modifier to the shared pool of the Advanced Product Options extension\u2015&#8221;MageWorx\\OptionBase\\Ui\\DataProvider\\Product\\Form\\Modifier\\Pool&#8221;. &#8220;VendorName\\OptionGtin\\Ui\\DataProvider\\Product\\Form\\Modifier\\OptionGtin&#8221; is our class modifier.&nbsp;<\/p>\n\n\n\n<p>The code that allows adding our field to the <code>app\/code\/VendorName\/OptionGtin\/Ui\/DataProvider\/Product\/Form\/Modifier\/OptionGtin.php<\/code> form is provided below:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\nnamespace VendorName\\OptionGtin\\Ui\\DataProvider\\Product\\Form\\Modifier;\n\nuse Magento\\Catalog\\Ui\\DataProvider\\Product\\Form\\Modifier\\AbstractModifier;\nuse Magento\\Catalog\\Ui\\DataProvider\\Product\\Form\\Modifier\\CustomOptions;\nuse Magento\\Ui\\Component\\Form\\Element\\Input;\nuse Magento\\Ui\\Component\\Form\\Element\\DataType\\Number;\nuse Magento\\Ui\\Component\\Form\\Field;\nuse MageWorx\\OptionBase\\Ui\\DataProvider\\Product\\Form\\Modifier\\ModifierInterface;\n\nclass OptionGtin extends AbstractModifier implements ModifierInterface\n{\n    \/**\n     * @var array\n     *\/\n    protected $meta = &#91;];\n\n    \/**\n     * {@inheritdoc}\n     *\/\n    public function modifyData(array $data)\n    {\n        return $data;\n    }\n\n    \/**\n     * {@inheritdoc}\n     *\/\n    public function modifyMeta(array $meta)\n    {\n        $this-&gt;meta = $meta;\n\n        $this-&gt;addFields();\n\n        return $this-&gt;meta;\n    }\n\n    \/**\n     * Adds fields to the meta-data\n     *\/\n    protected function addFields()\n    {\n\n        $groupCustomOptionsName    = CustomOptions::GROUP_CUSTOM_OPTIONS_NAME;\n        $optionContainerName       = CustomOptions::CONTAINER_OPTION;\n        $commonOptionContainerName = CustomOptions::CONTAINER_COMMON_NAME;\n\n        \/\/ Add fields to the option\n        $optionFeaturesFields  = $this-&gt;getOptionGtinFieldsConfig();\n\n        $this-&gt;meta&#91;$groupCustomOptionsName]&#91;'children']&#91;'options']&#91;'children']&#91;'record']&#91;'children']\n        &#91;$optionContainerName]&#91;'children']&#91;$commonOptionContainerName]&#91;'children'] = array_replace_recursive(\n            $this-&gt;meta&#91;$groupCustomOptionsName]&#91;'children']&#91;'options']&#91;'children']&#91;'record']&#91;'children']\n            &#91;$optionContainerName]&#91;'children']&#91;$commonOptionContainerName]&#91;'children'],\n            $optionFeaturesFields\n        );\n\n    }\n\n    \/**\n     * The custom option fields config\n     *\n     * @return array\n     *\/\n    protected function getOptionGtinFieldsConfig()\n    {\n        $fields&#91;'gtin'] = $this-&gt;getGtinFieldConfig();\n\n        return $fields;\n    }\n\n    \/**\n     * Get gtin field config\n     *\n     * @return array\n     *\/\n    protected function getGtinFieldConfig()\n    {\n        return &#91;\n            'arguments' =&gt; &#91;\n                'data' =&gt; &#91;\n                    'config' =&gt; &#91;\n                        'label'         =&gt; __('GTIN'),\n                        'componentType' =&gt; Field::NAME,\n                        'formElement'   =&gt; Input::NAME,\n                        'dataType'      =&gt; Number::NAME,\n                        'dataScope'     =&gt; 'gtin',\n                        'sortOrder'     =&gt; 65\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    \/**\n     * Check is current modifier for the product only\n     *\n     * @return bool\n     *\/\n    public function isProductScopeOnly()\n    {\n        return false;\n    }\n\n    \/**\n     * Get sort order of modifier to load modifiers in the right order\n     *\n     * @return int\n     *\/\n    public function getSortOrder()\n    {\n        return 32;\n    }\n}<\/code><\/pre>\n\n\n\n<p>Now, let\u2019s try to install the extension and check that everything gets displayed:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>php bin\/magento module:enable VendorName_OptionGtin<\/li><li>php bin\/magento setup:upgrade<\/li><li>php bin\/magento cache:flush<\/li><\/ul>\n\n\n\n<p>Our new field has been added successfully:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/ebaE0oUQqSGP3HZ5_HHmB1IHqwpU7m3mHfOW8oTi2zXJifu_TAjECmZ5zyRnEvAs4RgSpAvMJcRhNHC6vLisRKTl3flSVNzxiKHrBaS1yfJXfN9y05Q_xlEyDInqWZwSNzUak6DL\" alt=\"How to Add Custom Field to Advanced Product Options | Mageworx Blog\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step #4. Display our Field on Product Page Front-End<\/h2>\n\n\n\n<p>The Mageworx Advanced Product Options extension already has it all to display and work with attributes that our module adds. All we need to do is add the new attribute to the shared dataset.<\/p>\n\n\n\n<p>Our MageWorx_OptionBase module already uses the <code>getExtendedOptionsConfig()<\/code>method. It collects and displays all the custom attributes in a block on the front-end. Open the <code>app\/code\/MageWorx\/OptionBase\/Block\/Product\/View\/Options.php<\/code> class to see how it gets implemented.&nbsp;&nbsp; <\/p>\n\n\n\n<p>Let\u2019s start with creating a model with our attribute:<\/p>\n\n\n\n<p> <code>app\/code\/VendorName\/OptionGtin\/Model\/Attriburte\/Option\/Gtin.php<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nnamespace VendorName\\OptionGtin\\Model\\Attribute\\Option;\n\n\nuse MageWorx\\OptionBase\\Model\\Product\\Option\\AbstractAttribute;\n\nclass Gtin extends AbstractAttribute\n{\n    \/**\n     * @return string\n     *\/\n    public function getName()\n    {\n        return 'gtin';\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>Now, use the &#8220;dependency injection&#8221; mechanism and add our attribute to the shared attributes dataset of the Advanced Product Options extension.<\/p>\n\n\n\n<p><code>app\/code\/VendorName\/OptionGtin\/etc\/di.xml<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:ObjectManager\/etc\/config.xsd\"&gt;\n    &lt;!-- Data --&gt;\n    &lt;type name=\"MageWorx\\OptionBase\\Model\\Product\\Option\\Attributes\"&gt;\n        &lt;arguments&gt;\n            &lt;argument name=\"data\" xsi:type=\"array\"&gt;\n                &lt;item name=\"gtin\" xsi:type=\"object\"&gt;VendorName\\OptionGtin\\Model\\Attribute\\Option\\Gtin&lt;\/item&gt;\n            &lt;\/argument&gt;\n        &lt;\/arguments&gt;\n    &lt;\/type&gt;\n&lt;\/config&gt;<\/code><\/pre>\n\n\n\n<p>In other words, by opening the <code>MageWorx\\OptionBase\\Model\\Product\\Option\\Attributes<\/code> class, you will see that it simply collects all attribute objects to the shared dataset.<\/p>\n\n\n\n<p>To display data of our new \u201cGTIN\u201d attribute, we\u2019ve decided to use the <code>firstrun()<\/code> function from <code>app\/code\/MageWorx\/OptionFeatures\/view\/base\/web\/js\/catalog\/product\/features.js<\/code>. It already has all the required implementation that fits our example the best. To avoid overwriting the whole file, we will apply the &#8220;JavaScript mixins&#8221; mechanism, which will help us change the necessary function only.<\/p>\n\n\n\n<p>Create the following file, and define our mixin there: <code>app\/code\/VendorName\/OptionGtin\/view\/frontend\/requirejs-config.js<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>var config = {\n    config: {\n        mixins: {\n            'MageWorx_OptionFeatures\/js\/catalog\/product\/features': {\n                'VendorName_OptionGtin\/js\/catalog\/product\/features-gtin-mixin' : true\n            }\n        }\n    }\n};<\/code><\/pre>\n\n\n\n<p>Here, <code>MageWorx_OptionFeatures\/js\/catalog\/product\/features<\/code> is the root to our file, which method we need to rewrite. <code>VendorName_OptionGtin\/js\/catalog\/product\/features-gtin-mixin<\/code> is the file, where we will rewrite the method. <\/p>\n\n\n\n<p>So, let\u2019s create it: <code>app\/code\/VendorName\/OptionGtin\/view\/frontend\/web\/js\/catalog\/product\/features-gtin-mixin.js<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define(&#91;\n    'jquery',\n    'jquery\/ui',\n    'mage\/utils\/wrapper'\n], function ($, wrapper) {\n    'use strict';\n\n    return function (widget) {\n        $.widget('mageworx.optionFeatures', widget, {\n\n            \/**\n             * Triggers one time at first run (from base.js)\n             * @param optionConfig\n             * @param productConfig\n             * @param base\n             * @param self\n             *\/\n            firstRun: function firstRun(optionConfig, productConfig, base, self) {\n\n                \/\/shareable link\n                $('#mageworx_shareable_hint_icon').qtip({\n                    content: {\n                        text: this.options.shareable_link_hint_text\n                    },\n                    style: {\n                        classes: 'qtip-light'\n                    },\n                    position: {\n                        target: false\n                    }\n                });\n\n                $('#mageworx_shareable_link').on('click', function () {\n                    try {\n                        self.copyTextToClipboard(self.getShareableLink(base));\n                        $('.mageworx-shareable-link-container').hide();\n                        $('.mageworx-shareable-link-success-container').show();\n\n                        setTimeout(function () {\n                            $('.mageworx-shareable-link-container').show();\n                            $('.mageworx-shareable-link-success-container').hide();\n                        }, 2000);\n                    } catch (error) {\n                        console.log('Something goes wrong. Unable to copy');\n                    }\n                });\n\n                setTimeout(function () {\n\n                    \/\/ Qty input\n                    $('.mageworx-option-qty').each(function () {\n\n                        $(this).on('change', function () {\n\n                            var optionInput = $(\"&#91;data-selector='\" + $(this).attr('data-parent-selector') + \"']\");\n                            optionInput.trigger('change');\n                        });\n                    });\n                }, 500);\n\n                \/\/ Option\\Value Description &amp; tooltip\n                var extendedOptionsConfig = typeof base.options.extendedOptionsConfig != 'undefined' ?\n                    base.options.extendedOptionsConfig : {};\n\n                for (var option_id in optionConfig) {\n                    if (!optionConfig.hasOwnProperty(option_id)) {\n                        continue;\n                    }\n\n                    var description = extendedOptionsConfig&#91;option_id]&#91;'description'],\n                        gtin = extendedOptionsConfig&#91;option_id]&#91;'gtin'],\n                        gtinTitle = \"Global Trade Item Number: \",\n                        $option = base.getOptionHtmlById(option_id);\n                    if (1 &gt; $option.length) {\n                        console.log('Empty option container for option with id: ' + option_id);\n                        continue;\n                    }\n\n                    var $label = $option.find('label');\n\n                    if(gtin != null &amp;&amp; gtin.length &gt; 0) {\n                        if ($label.length &gt; 0) {\n                            $label\n                                .first()\n                                .after($('&lt;p class=\"option-gtin-text\"&gt;&lt;span&gt;' + gtinTitle + '&lt;\/span&gt;' + gtin + '&lt;\/p&gt;'));\n                        } else {\n                            $label = $option.find('span');\n                            $label\n                                .first()\n                                .parent()\n                                .after($('&lt;p class=\"option-gtin-text\"&gt;&lt;span&gt;' + gtinTitle + '&lt;\/span&gt;' + gtin + '&lt;\/p&gt;'));\n                        }\n                    }\n\n                    if (this.options.option_description_enabled &amp;&amp; !_.isEmpty(extendedOptionsConfig&#91;option_id]&#91;'description'])) {\n                        if (this.options.option_description_mode == this.options.option_description_modes.tooltip) {\n                            var $element = $option.find('label span')\n                                .first();\n                            if ($element.length == 0) {\n                                $element = $option.find('fieldset legend span')\n                                    .first();\n                            }\n                            $element.css('border-bottom', '1px dotted black');\n                            $element.qtip({\n                                content: {\n                                    text: description\n                                },\n                                style: {\n                                    classes: 'qtip-light'\n                                },\n                                position: {\n                                    target: false\n                                }\n                            });\n                        } else if (this.options.option_description_mode == this.options.option_description_modes.text) {\n\n                            if ($label.length &gt; 0) {\n                                $label\n                                    .first()\n                                    .after($('&lt;p class=\"option-description-text\"&gt;' + description + '&lt;\/p&gt;'));\n                            } else {\n                                $label = $option.find('span');\n                                $label\n                                    .first()\n                                    .parent()\n                                    .after($('&lt;p class=\"option-description-text\"&gt;' + description + '&lt;\/p&gt;'));\n                            }\n                        } else {\n                            console.log('Unknown option mode');\n                        }\n                    }\n\n                    if (this.options.value_description_enabled) {\n                        this._addValueDescription($option, optionConfig, extendedOptionsConfig);\n                    }\n                }\n            }\n\n        });\n        return $.mageworx.optionFeatures;\n    };\n\n});<\/code><\/pre>\n\n\n\n<p>Generally, we can run the following commands now:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>php bin\/magento cache:flush<\/li><li>php bin\/magento setup:static-content:deploy (only for production mode)<\/li><\/ul>\n\n\n\n<p>and see what we\u2019ve got. But first, add some styles to our new attribute and make it look nice on the front-end.<\/p>\n\n\n\n<p>Create a layout and define our new styles file there: <code>app\/code\/VendorName\/OptionGtin\/view\/frontend\/layout\/catalog_product_view.xml<\/code> <br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n&lt;page xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:View\/Layout\/etc\/page_configuration.xsd\"&gt;\n    &lt;head&gt;\n        &lt;css src=\"VendorName_OptionGtin::css\/gtin.css\"\/&gt;\n    &lt;\/head&gt;\n&lt;\/page&gt;<\/code><\/pre>\n\n\n\n<p>It\u2019s time to create a styles file: <code>app\/code\/VendorName\/OptionGtin\/view\/frontend\/web\/css\/gtin.css<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.option-gtin-text span {\n    color: #6cc308;\n    font-weight: 700;\n}<\/code><\/pre>\n\n\n\n<p>Now, let\u2019s run the previously described commands and check the results:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/f5MrcOXyrwarDq8FHqgLQOPMCJl3BzP4zda48ZsdMgWpsbQSinpTxh6Axatqfcs0PAAqbSiPCv8h18HQUVZ6QzD1Dbo2O7EUYB9K-UNMeyRYM5aiMMiGJI_gTQPnHLo_sWHYtLVw\" alt=\"How to Add Custom Field to Advanced Product Options | Mageworx Blog\"\/><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Step #5. Add our Attribute Data to Order Details in Database<\/h2>\n\n\n\n<p>When a customer makes a purchase, an order gets created. Details about the added items get included in the <code>sales_order_item<\/code> table. This table has the <code>product_options<\/code> field that contains information about the selected parameters of an added item. That\u2019s where we should add our new attribute\u2019s data.  <\/p>\n\n\n\n<p>As an order gets created, the <code>sales_quote_address_collect_totals_before<\/code> event gets triggered. We will use it to add our data to product options.<\/p>\n\n\n\n<p>Let\u2019s define the event by creating: <code>app\/code\/VendorName\/OptionGtin\/etc\/events.xml<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:Event\/etc\/events.xsd\"&gt;\n    &lt;event name=\"sales_quote_address_collect_totals_before\"&gt;\n        &lt;observer name=\"mageworx_optiongtin_add_gtin_to_order\"\n                  instance=\"VendorName\\OptionGtin\\Observer\\AddGtinToOrder\"\n        \/&gt;\n    &lt;\/event&gt;\n&lt;\/config&gt;<\/code><\/pre>\n\n\n\n<p>Then, create our observer: <code>app\/code\/VendorName\/OptionGtin\/Observer\/AddGtinToOrder.php<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\nnamespace VendorName\\OptionGtin\\Observer;\n\nuse Magento\\Framework\\Event\\Observer;\nuse Magento\\Framework\\Event\\ObserverInterface;\nuse Magento\\Catalog\\Model\\ProductRepository as ProductRepository;\nuse MageWorx\\OptionBase\\Helper\\Data as BaseHelper;\n\nclass AddGtinToOrder implements ObserverInterface\n{\n    \/**\n     * @var BaseHelper\n     *\/\n    protected $baseHelper;\n\n    protected $productRepository;\n\n    \/**\n     * AddGtinToOrder constructor.\n     * @param BaseHelper $baseHelper\n     * @param ProductRepository $productRepository\n     *\/\n    public function __construct(\n        BaseHelper $baseHelper,\n        ProductRepository $productRepository\n    ) {\n        $this-&gt;baseHelper        = $baseHelper;\n        $this-&gt;productRepository = $productRepository;\n    }\n\n    \/**\n     * Add product to quote action\n     * Processing: gtin\n     *\n     * @param Observer $observer\n     * @return $this\n     *\/\n    public function execute(Observer $observer)\n    {\n        $quoteItems = $observer-&gt;getQuote()-&gt;getAllItems();\n\n        \/** @var \\Magento\\Quote\\Model\\Quote\\Item $quoteItem *\/\n        foreach ($quoteItems as $quoteItem) {\n\n            $buyRequest           = $quoteItem-&gt;getBuyRequest();\n            $optionIds            = array_keys($buyRequest-&gt;getOptions());\n            $productOptions       = $this-&gt;productRepository-&gt;getById($buyRequest-&gt;getProduct())-&gt;getOptions();\n            $quoteItemOptionGtins = &#91;];\n            $optionGtins          = &#91;];\n\n            foreach  ($productOptions as $option) {\n                if ($option-&gt;getGtin()) {\n                    $quoteItemOptionGtins&#91;$option-&gt;getOptionId()] = $option-&gt;getGtin();\n                }\n            }\n            foreach ($optionIds as $optionId) {\n                $optionGtins&#91;$optionId] = $optionId;\n            }\n\n            $optionGtins = array_intersect_key($quoteItemOptionGtins, $optionGtins);\n\n            $infoBuyRequest = $quoteItem-&gt;getOptionByCode('info_buyRequest');\n            $buyRequest-&gt;setData('gtin', $optionGtins);\n            $infoBuyRequest-&gt;setValue($this-&gt;baseHelper-&gt;encodeBuyRequestValue($buyRequest-&gt;getData()));\n            $quoteItem-&gt;addOption($infoBuyRequest);\n\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Here, with the help of the observer, we get the list of all items in the order and add the data of our \u201cGTIN\u201d attribute to the so-called <code>$infoBuyRequest<\/code>.<\/p>\n\n\n\n<p>To check that everything has been performed correctly, create an order with the product, which options have \u201cGTIN\u201d data. You can check that the data has been added in the Database <code>sales_order_item table<\/code> -&gt; <code>product_options<\/code> field:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/NrFw2tce1Mx1Xtj94Yco-UlljPpcsC53368oUw-gfIgXEPlnjJMkpw0AT4oFZqLHt-ZSLngpwbs32h5jvh8r2BRcTWKNw9aQ1Xiwskcs7-xIwmHrhE4kMFQcjhTO4FhNy3xr4lxk\" alt=\"How to Add Custom Field to Advanced Product Options | Mageworx Blog\"\/><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step #6. Display Data on Orders Page in Admin Panel<\/strong><\/h2>\n\n\n\n<p>There are different means to display the required information in the ready template. For example, using &#8220;js&#8221;. We worked with &#8220;js&#8221; in this article. Let\u2019s work with the templates themselves for a change, and try to rewrite them!&nbsp;<\/p>\n\n\n\n<p>Change the previously created <code>app\/code\/VendorName\/OptionGtin\/etc\/adminhtml\/di.xml<\/code> by adding the plugin there:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:ObjectManager\/etc\/config.xsd\"&gt;\n    &lt;virtualType name=\"MageWorx\\OptionBase\\Ui\\DataProvider\\Product\\Form\\Modifier\\Pool\"&gt;\n        &lt;arguments&gt;\n            &lt;argument name=\"modifiers\" xsi:type=\"array\"&gt;\n                &lt;item name=\"mageworx-option-gtin\" xsi:type=\"array\"&gt;\n                    &lt;item name=\"class\" xsi:type=\"string\"&gt;VendorName\\OptionGtin\\Ui\\DataProvider\\Product\\Form\\Modifier\\OptionGtin&lt;\/item&gt;\n                    &lt;item name=\"sortOrder\" xsi:type=\"number\"&gt;72&lt;\/item&gt;\n                &lt;\/item&gt;\n            &lt;\/argument&gt;\n        &lt;\/arguments&gt;\n    &lt;\/virtualType&gt;\n\n    &lt;!-- Plugins--&gt;\n    &lt;type name=\"Magento\\Sales\\Block\\Adminhtml\\Items\\Column\\DefaultColumn\"&gt;\n        &lt;plugin name=\"mageworx-optiongtin-add-default-column\"\n                type=\"VendorName\\OptionGtin\\Plugin\\AddDefaultColumn\"\n                sortOrder=\"5\"\n                disabled=\"false\"\n        \/&gt;\n    &lt;\/type&gt;\n&lt;\/config&gt;<\/code><\/pre>\n\n\n\n<p>Create the plugin itself:<\/p>\n\n\n\n<p> <code>app\/code\/VendorName\/OptionGtin\/Plugin\/AddDefaultColumn.php<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\nnamespace VendorName\\OptionGtin\\Plugin;\n\nclass AddDefaultColumn\n{\n\n    \/**\n     * @param \\Magento\\Sales\\Block\\Adminhtml\\Items\\Column\\DefaultColumn $subject\n     * @param $result\n     * @return array\n     *\/\n    public function afterGetOrderOptions(\\Magento\\Sales\\Block\\Adminhtml\\Items\\Column\\DefaultColumn $subject, $result)\n    {\n        if ($options = $subject-&gt;getItem()-&gt;getProductOptions()) {\n            if (isset($result)) {\n\n                foreach ($result as &amp;$option) {\n                    if (array_key_exists($option&#91;'option_id'], $options&#91;'info_buyRequest']&#91;'gtin'])) {\n                        $option&#91;'gtin'] = $options&#91;'info_buyRequest']&#91;'gtin']&#91;$option&#91;'option_id']];\n                    }\n                }\n            }\n        }\n        return $result;\n    }\n}<\/code><\/pre>\n\n\n\n<p>This plugin adds information about our new attribute for order options, for which these data exist.<\/p>\n\n\n\n<p><code>vendor\/magento\/module-sales\/view\/adminhtml\/templates\/items\/column\/name.phtml<\/code> is responsible for displaying information about product options on the order page in the admin panel.<\/p>\n\n\n\n<p>Let\u2019s rewrite it to display our \u201cGTIN\u201d. For that, we need to rewrite the &#8220;column_name&#8221; block, or rather its template. Create a layout and a template:&nbsp; <\/p>\n\n\n\n<p><code>app\/code\/VendorName\/OptionGtin\/view\/adminhtml\/layout\/sales_order_view.xml<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\"?&gt;\n&lt;page xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:View\/Layout\/etc\/page_configuration.xsd\"&gt;\n    &lt;body&gt;\n        &lt;referenceBlock name=\"column_name\"&gt;\n            &lt;action method=\"setTemplate\"&gt;\n                &lt;argument name=\"template\" xsi:type=\"string\"&gt;VendorName_OptionGtin::items\/column\/name.phtml&lt;\/argument&gt;\n            &lt;\/action&gt;\n        &lt;\/referenceBlock&gt;\n    &lt;\/body&gt;\n&lt;\/page&gt;<\/code><\/pre>\n\n\n\n<p><code>app\/code\/VendorName\/OptionGtin\/view\/adminhtml\/templates\/items\/column\/name.phtml<\/code><br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/* @var $block \\Magento\\Sales\\Block\\Adminhtml\\Items\\Column\\Name *\/\n?&gt;\n&lt;?php if ($_item = $block-&gt;getItem()) : ?&gt;\n    &lt;div id=\"order_item_&lt;?= (int) $_item-&gt;getId() ?&gt;_title\"\n         class=\"product-title\"&gt;\n        &lt;?= $block-&gt;escapeHtml($_item-&gt;getName()) ?&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"product-sku-block\"&gt;\n        &lt;span&gt;&lt;?= $block-&gt;escapeHtml(__('SKU'))?&gt;:&lt;\/span&gt; &lt;?= \/* @noEscape *\/ implode('&lt;br \/&gt;', $this-&gt;helper(\\Magento\\Catalog\\Helper\\Data::class)-&gt;splitSku($block-&gt;escapeHtml($block-&gt;getSku()))) ?&gt;\n    &lt;\/div&gt;\n    &lt;?php if ($block-&gt;getOrderOptions()) : ?&gt;\n        &lt;dl class=\"item-options\"&gt;\n            &lt;?php foreach ($block-&gt;getOrderOptions() as $_option) : ?&gt;\n                &lt;dt&gt;&lt;?= $block-&gt;escapeHtml($_option&#91;'label']) ?&gt;:&lt;\/dt&gt;\n                &lt;dd&gt;\n                    &lt;?php if (isset($_option&#91;'custom_view']) &amp;&amp; $_option&#91;'custom_view']) : ?&gt;\n                        &lt;?= \/* @noEscape *\/ $block-&gt;getCustomizedOptionValue($_option) ?&gt;\n                    &lt;?php else : ?&gt;\n                        &lt;?php $optionValue = $block-&gt;getFormattedOption($_option&#91;'value']); ?&gt;\n                        &lt;?php $dots = 'dots' . uniqid(); ?&gt;\n                        &lt;?php $id = 'id' . uniqid(); ?&gt;\n                        &lt;?= $block-&gt;escapeHtml($optionValue&#91;'value'], &#91;'a', 'br']) ?&gt;&lt;?php if (isset($optionValue&#91;'remainder']) &amp;&amp; $optionValue&#91;'remainder']) : ?&gt;\n                            &lt;span id=\"&lt;?= \/* @noEscape *\/ $dots; ?&gt;\"&gt; ...&lt;\/span&gt;\n                            &lt;span id=\"&lt;?= \/* @noEscape *\/ $id; ?&gt;\"&gt;&lt;?= $block-&gt;escapeHtml($optionValue&#91;'remainder'], &#91;'a']) ?&gt;&lt;\/span&gt;\n                            &lt;script&gt;\n                                require(&#91;'prototype'], function() {\n                                    $('&lt;?= \/* @noEscape *\/ $id; ?&gt;').hide();\n                                    $('&lt;?= \/* @noEscape *\/ $id; ?&gt;').up().observe('mouseover', function(){$('&lt;?= \/* @noEscape *\/ $id; ?&gt;').show();});\n                                    $('&lt;?= \/* @noEscape *\/ $id; ?&gt;').up().observe('mouseover', function(){$('&lt;?= \/* @noEscape *\/ $dots; ?&gt;').hide();});\n                                    $('&lt;?= \/* @noEscape *\/ $id; ?&gt;').up().observe('mouseout',  function(){$('&lt;?= \/* @noEscape *\/ $id; ?&gt;').hide();});\n                                    $('&lt;?= \/* @noEscape *\/ $id; ?&gt;').up().observe('mouseout',  function(){$('&lt;?= \/* @noEscape *\/ $dots; ?&gt;').show();});\n                                });\n                            &lt;\/script&gt;\n                        &lt;?php endif; ?&gt;\n                    &lt;?php endif; ?&gt;\n                &lt;\/dd&gt;\n                &lt;dt&gt;\n                    &lt;?php if (isset($_option&#91;'gtin']) &amp;&amp; $_option&#91;'gtin']) : ?&gt;\n                        &lt;span&gt;GTIN:&lt;\/span&gt;\n                    &lt;?php endif; ?&gt;\n                &lt;\/dt&gt;\n                &lt;dd&gt;\n                    &lt;?php if (isset($_option&#91;'gtin']) &amp;&amp; $_option&#91;'gtin']) : ?&gt;\n                        &lt;span&gt; &lt;?= $block-&gt;escapeHtml($_option&#91;'gtin']) ?&gt;&lt;\/span&gt;\n                    &lt;?php endif; ?&gt;\n                &lt;\/dd&gt;\n\n            &lt;?php endforeach; ?&gt;\n        &lt;\/dl&gt;\n    &lt;?php endif; ?&gt;\n    &lt;?= $block-&gt;escapeHtml($_item-&gt;getDescription()) ?&gt;\n&lt;?php endif; ?&gt;<\/code><\/pre>\n\n\n\n<p>If everything has been performed correctly, cleared, and compiled, then you will see the following result:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/zd4TtimTLXJ2QqjnjP0nYqgIMQhvZtUOtg7OewhyGidzt9P_77dAxhq2ftxMBV6GEN8H33yLgCUcwM7XJ7ft7sgbOau-5ou39w4PRnb52YNNaqn8RS1jogT8x0fFS5OnnbC9E09b\" alt=\"How to Add Custom Field to Advanced Product Options | Mageworx Blog\"\/><\/figure>\n\n\n\n<p>We hope you find this article helpful. Should you have any difficulties or issues, feel free to let us know in the comments field below.<br><\/p>\n\n\n<p><a href=\"https:\/\/calendly.com\/kate-volchock\/demo\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15059 size-full\" src=\"https:\/\/www.mageworx.com\/blog\/wp-content\/uploads\/2021\/02\/live-demo-1.png\" alt=\"Book a Live Demo with Mageworx\" width=\"690\" height=\"260\" srcset=\"https:\/\/www.mageworx.com\/blog\/wp-content\/uploads\/2021\/02\/live-demo-1.png 690w, https:\/\/www.mageworx.com\/blog\/wp-content\/uploads\/2021\/02\/live-demo-1-600x226.png 600w, https:\/\/www.mageworx.com\/blog\/wp-content\/uploads\/2021\/02\/live-demo-1-250x94.png 250w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/a><\/p>","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span>From this article, you will learn how to create a \u201cGTIN\u201d field for product custom options, show it on the product page front-end, and display it in the order. Without further ado, let\u2019s proceed to the step-by-step guidelines. Step #1. Create New Module We described in detail how to create a module in this article. [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":12737,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[255,425,426],"tags":[379,436],"class_list":{"0":"post-12730","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-magento-2","8":"category-magento-how-tos","9":"category-extensions-tips-and-tricks","10":"tag-apo","11":"tag-developer-diaries"},"_links":{"self":[{"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/posts\/12730","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/comments?post=12730"}],"version-history":[{"count":31,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/posts\/12730\/revisions"}],"predecessor-version":[{"id":15089,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/posts\/12730\/revisions\/15089"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/media\/12737"}],"wp:attachment":[{"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/media?parent=12730"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/categories?post=12730"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mageworx.com\/blog\/wp-json\/wp\/v2\/tags?post=12730"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}