How to Add Custom Field for Option Values in Advanced Product Options

How to add custom fields for products on Magento 2 | Mageworx Blog

Reading Time: 6 minutes
Save the product.
Create a model with our attribute:
As you can see, the newly added field gets displayed now:

var config = { map: { '*': { optionValueGtin: 'VendorName_OptionValueGtin/js/option-value-gtin' } }
};

`MageWorxOptionBaseUiDataProviderProductFormModifierPool`.
`app/code/VendorName/OptionValueGtin/view/frontend/layout/catalog_product_view.xml`

1. composer.json

From the previous article, you’ve learned how to create Magento custom option fields. We also have found out how to display the field data on both the product page front-end and the order page in the admin panel.
`app/code/VendorName/OptionValueGtin/etc/adminhtml/system.xml`
Let’s create the following file:
In our example, I added dropdown, swatch, radio, and check box.
`app/code/VendorName/OptionValueGtin/etc/di.xml`
`app/code/VendorName/OptionValueGtin/Model/Attribute/OptionValue/Gtin.php`
Next:
<?xml version="1.0"?>
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> <css src="VendorName_OptionValueGtin::css/valueGtin.css"/> </head> <body> <referenceBlock name="product.info.options.wrapper"> <container name="vendorname.option.value.gtin.container" after="product.info.options"> <block class="VendorNameOptionValueGtinBlockValueGtin" name="vendorname.option.value.gtin" template="VendorName_OptionValueGtin::config.phtml"/> </container> </referenceBlock> </body>
</page>

Using the `getJsonData()` method, let’s render data to the front-end using the template that we are about to create:
<?php namespace VendorNameOptionValueGtinModelAttributeOptionValue; use MageWorxOptionBaseModelProductOptionAbstractAttribute; class Gtin extends AbstractAttribute
{ /** * @return string */ public function getName() { return 'gtin'; } }

Before we run the final check, don’t forget to clear the cache and deploy the static content again:
Below:
Thus, without further ado, here is the code that we will need:
`app/code/VendorName/OptionValueGtin/view/frontend/requirejs-config.js`

  • php bin/magento module:enable VendorName_OptionValueGtin
  • php bin/magento setup:upgrade
  • php bin/magento cache:flush

Please share your feedback about the article in the comments field below. How easy was it to Magento add field to custom options?

How to add custom fields for products on Magento 2 | Mageworx Blog

Step #4. Adding Setting to Disable GTIN Field Display for Advanced Product Options Config

Create our new block and a template for it:
And finally:
<?php
namespace VendorNameOptionValueGtinHelper; use MagentoFrameworkAppHelperAbstractHelper;
use MagentoStoreModelScopeInterface;
use MagentoFrameworkAppHelperContext; class Data extends AbstractHelper
{ const XML_PATH_DEFAULT_OPTION_VALUE_GTIN = 'mageworx_apo/optionvaluegtin/use_optionvaluegtin'; /** * Additional product attributes for product_attributes table * * @var array */ protected $additionalProductAttributes; /** * @param Context $context */ public function __construct( Context $context ) { parent::__construct($context); } /** * Check if option value GTIN enabled * * @param int|null $storeId * @return string */ public function isOptionValueGtinEnabled($storeId = null) { return $this->scopeConfig->getValue( self::XML_PATH_DEFAULT_OPTION_VALUE_GTIN, ScopeInterface::SCOPE_STORE, $storeId ); } }

Let’s create the following file:
<?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="MageWorxOptionBaseModelProductOptionValueAttributes"> <arguments> <argument name="data" xsi:type="array"> <item name="gtin" xsi:type="object">VendorNameOptionValueGtinModelAttributeOptionValueGtin</item> </argument> </arguments> </type>
</config>

`app/code/VendorName/OptionValueGtin/view/frontend/web/js/option-value-gtin.js`
What’s now?
We add our modifier to the general pool of the Advanced Product Options extension for Magento 2―
In the example file, let’s implement the display logic of GTIN for the select options, and for the radio and check box options separately.
{ "name": "mageworx/module-optionvaluegtin", "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\OptionValueGtin\": "" } }
}

2. etc/module.xml

Now:

How to add custom fields for products on Magento 2 | Mageworx Blog

Step #5. Displaying New Field on Product Page Front-End

Log in to the admin panel.
`VendorNameOptionValueGtinUiDataProviderProductFormModifierOptionValueGtin` is the modifier class.
Define the new js:
See the code that allows adding our field to the `app/code/VendorName/OptionValueGtin/Ui/DataProvider/Product/Form/Modifier/OptionValueGtin.php` form:
<?php namespace MageWorxOptionValueGtinBlock; use MagentoFrameworkJsonEncoderInterface;
use MagentoFrameworkViewElementTemplate;
use MagentoFrameworkViewElementTemplateContext;
use MageWorxOptionValueGtinHelperData as Helper; class ValueGtin extends Template
{ /** * @var EncoderInterface */ protected $jsonEncoder; /** * @var Helper */ protected $helper; /** * @param Context $context * @param EncoderInterface $jsonEncoder * @param Helper $helper * @param array $data */ public function __construct( Context $context, EncoderInterface $jsonEncoder, Helper $helper, array $data = [] ) { parent::__construct( $context, $data ); $this->jsonEncoder = $jsonEncoder; $this->helper = $helper; } /** * @return string */ public function getJsonData() { $data = [ 'isOptionValueGtinEnabled' => $this->helper->isOptionValueGtinEnabled($this->_storeManager->getStore()) ]; return $this->jsonEncoder->encode($data); }
}

<?php
namespace VendorNameOptionValueGtinUiDataProviderProductFormModifier; use MagentoCatalogUiDataProviderProductFormModifierAbstractModifier;
use MagentoCatalogUiDataProviderProductFormModifierCustomOptions;
use MagentoUiComponentFormElementInput;
use MagentoUiComponentFormElementDataTypeNumber;
use MagentoUiComponentFormField;
use MageWorxOptionBaseUiDataProviderProductFormModifierModifierInterface; class OptionValueGtin 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; // Add fields to the values $valueFeaturesFields = $this->getValueFieldsConfig(); $this->meta[$groupCustomOptionsName]['children']['options']['children']['record']['children'] [$optionContainerName]['children']['values']['children']['record']['children'] = array_replace_recursive( $this->meta[$groupCustomOptionsName]['children']['options']['children']['record']['children'] [$optionContainerName]['children']['values']['children']['record']['children'], $valueFeaturesFields ); } /** * The custom option fields config * * @return array */ protected function getValueFieldsConfig() { $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' => 92 ], ], ], ]; } /** * 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; }
}

I offer to add some new functionality―the ability to enable/disable the GTIN field display for option values on the product page front-end. 
<?php
namespace VendorNameOptionValueGtinSetup; use MagentoFrameworkSetupInstallSchemaInterface;
use MagentoFrameworkSetupModuleContextInterface;
use MagentoFrameworkSetupSchemaSetupInterface;
use MagentoFrameworkDBDdlTable; class InstallSchema implements InstallSchemaInterface
{ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $setup->getConnection()->addColumn( $setup->getTable('catalog_product_option_type_value'), 'gtin', [ 'type' => Table::TYPE_TEXT, 'nullable' => true, 'default' => null, 'comment' => 'Gtin (added by MageWorx Option Value Gtin)', ] ); $setup->endSetup(); }
}

Step #3. Adding Logic to Work with Backend

`app/code/VendorName/OptionValueGtin/view/frontend/templates/config.phtml`
`app/code/VendorName/OptionValueGtin/Helper/Data.php`
Let’s take a different approach and create a js widget, which will be used to display the new data on the product page.
Book a Live Demo with Mageworx
Create a product with custom options in Magento product.
Table of Contents
`app/code/VendorName/OptionValueGtin/Setup/InstallSchema.php`
For that, create the following file:
<?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="MageWorxOptionBaseUiDataProviderProductFormModifierPool"> <arguments> <argument name="modifiers" xsi:type="array"> <item name="mageworx-option-value-gtin" xsi:type="array"> <item name="class" xsi:type="string">MageWorxOptionValueGtinUiDataProviderProductFormModifierOptionValueGtin</item> <item name="sortOrder" xsi:type="number">72</item> </item> </argument> </arguments> </virtualType>
</config>

As we are adding a field for option values, the `catalog_product_option_type_value` table will be required.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <tab id="mageworx" sortOrder="2001"> <label>MageWorx</label> </tab> <section id="mageworx_apo" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label><![CDATA[Advanced Product Options]]></label> <tab>mageworx</tab> <resource>VendorName_OptionValueGtin::config_optionvaluegtin</resource> <group id="optionvaluegtin" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label><![CDATA[Option Value GTIN]]></label> <field id="use_optionvaluegtin" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> <label><![CDATA[Enable Option's Value 'GTIN']]></label> <source_model>MagentoConfigModelConfigSourceYesno</source_model> </field> </group> </section> </system>
</config>

The setting should get displayed in the admin panel.
define([ 'jquery', 'Magento_Catalog/js/price-utils', 'underscore', 'jquery/ui'
], function ($, utils, _) { 'use strict'; $.widget('mageworx.optionValueGtin', { options: { optionConfig: {} }, /** * * @param optionConfig * @param productConfig * @param base * @param self */ firstRun: function firstRun(optionConfig, productConfig, base, self) { if (parseFloat(this.options.isOptionValueGtinEnabled)) { var extendedOptionsConfig = typeof base.options.extendedOptionsConfig != 'undefined' ? base.options.extendedOptionsConfig : {}; for (var option_id in optionConfig) { if (!optionConfig.hasOwnProperty(option_id)) { continue; } var $option = base.getOptionHtmlById(option_id); this._addValueGtin($option, optionConfig, extendedOptionsConfig); } } }, /** * Add description to the values * @param $option * @param optionConfig * @param extendedOptionsConfig * @private */ _addValueGtin: function _addValueGtin($option, optionConfig, extendedOptionsConfig) { var self = this, $options = $option.find('.product-custom-option'); //selectable options $options.filter('select').each(function (index, element) { var $element = $(element), optionId = utils.findOptionId($element), value = extendedOptionsConfig[optionId]['values']; if ($element.attr('multiple') && !$element.hasClass('mageworx-swatch')) { return; } if (typeof value == 'undefined' || _.isEmpty(value)) { return; } var gtinTitle = 'GTIN: '; var $gtin = $('<div class="option-value-gtin"></div>', { style: 'display: none' }); var $label = $option.find('.control'); $element.parent().prepend($gtin); $element.on('change', function (e) { var valueId = $element.val(); if (!_.isUndefined(value[valueId]) && !_.isEmpty(value[valueId]['gtin']) ) { if ($label.length > 0) { $label .first() .after($gtin.text(gtinTitle + value[valueId]['gtin'])); } $gtin.show(); } else { $gtin.hide(); } }); if ($element.val()) { $element.trigger('change'); } }); $options.filter('input[type="radio"], input[type="checkbox"]').each(function (index, element) { var $element = $(element), optionId = utils.findOptionId($element), value = extendedOptionsConfig[optionId]['values']; if ($element.attr('multiple') && !$element.hasClass('mageworx-swatch')) { return; } if (typeof value == 'undefined' || _.isEmpty(value)) { return; } var gtinTitle = 'GTIN: '; var $gtin = $('<div class="option-value-gtin-redio-check"></div>'); var $label = $option.find('.control'); $element.parent().append($gtin); var valueId = $element.val(); if (!_.isUndefined(value[valueId]) && !_.isEmpty(value[valueId]['gtin'])) { $gtin.text(gtinTitle + value[valueId]['gtin']); } if ($element.val()) { $element.trigger('change'); } }); }, }); return $.mageworx.optionValueGtin; });

You will need to create the file:
The new Helper Class should be created. There, we will obtain data about the setting.
Remember what we talked about in the previous article?
`app/code/MageWorx/OptionBase/Block/Product/View/Options.php`
Time to see how it all looks like on the front-end:
We mentioned that our MageWorx_OptionBase module already has the `getExtendedOptionsConfig()` method that collects and displays all our custom attributes on the front-end via blocks.
What about mixing up our article a little?
What’s next?
So, let’s create and fill in the following class:
`app/code/VendorName/OptionValueGtin/Block/ValueGtin.php`
<?php
/** @var VendorNameOptionValueGtinBlockValueGtin $block */
?>
<script> require([ 'jquery', 'optionValueGtin', 'uiRegistry' ], function ($, optionValueGtin, registry) { var optionBase = registry.get('mageworxOptionBase'); if (optionBase) { optionBase.addUpdater(7, optionValueGtin(<?= /* @noEscape */ $block->getJsonData() ?>)); } else { var updaters = registry.get('mageworxOptionUpdaters'); if (!updaters) { updaters = {}; } updaters[7] = optionValueGtin(<?= /* @noEscape */ $block->getJsonData() ?>); registry.set('mageworxOptionUpdaters', updaters); } });
</script>

 These are going to be two different logics as the logic in the work and markup of such options differ from one another:
Let’s start with the new module creation, which process was covered in detail in this Mageworx blog post.
What do you think about the result?
`app/code/VendorName/OptionValueGtin/view/frontend/web/css/valueGtin.css`
Don’t forget to fill in our new GTIN fields for the corresponding option values.
Let’s learn how to add the same custom GTIN field for the option values and display it on the product page front-end.
To see how it gets implemented, open the following class:

How to add custom fields for products on Magento 2 | Mageworx Blog

We will add the new Option Value GTIN tab to the config of our Advanced Product Options extension. You should be able to create a tab in your module too.


`app/code/VendorName/OptionValueGtin/etc/adminhtml/di.xml`
.option-value-gtin, .option-value-gtin-redio-check { color: #1da0e0; font-weight: 700; margin-top: 5px;
} .option-value-gtin-redio-check { display: contents;
}

It only remains to connect our block and the styles.