1

I am working on a module to add a product custom option input type (Derivated from file type).

In the backend under catalog / product / custom options, my new option is present in the "input type" list but when I select it, the associated fields are not displayed (Price, price type, sky, compatible file extension, etc).

I do not manage to find what is going on...

Thank you for your help,

My files :

app/code/A/Custoptiontype/etc/module.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="A_Custoptiontype" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Catalog"/>
        </sequence>
    </module>
</config>

app/code/A/Custoptiontype/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">
    <preference for="Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option" type="A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Option"/>
    <preference for="Magento\Catalog\Model\Product\Option" type="A\Custoptiontype\Model\Catalog\Product\Option"/>
</config>

app/code/A/Custoptiontype/etc/product_options.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_options.xsd">
    <option name="xfile" label="X File" renderer="A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type\Xfile">
        <inputType name="xfile" label="X File" />
    </option>
</config>

app/code/A/Custoptiontype/Block/Adminhtml/Product/Edit/Tab/Options/Option.php

<?php
namespace A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options;

class Option extends \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option
{
    protected $_template = 'A_Custoptiontype::catalog/product/edit/options/option.phtml';

    /**
    * Class constructor
    */  
  public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Config\Model\Config\Source\Yesno $configYesNo,
        \Magento\Catalog\Model\Config\Source\Product\Options\Type $optionType,
        \Magento\Catalog\Model\Product $product,
        \Magento\Framework\Registry $registry,
        \Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig,
        array $data = []
    )
    {
        parent::__construct(
            $context,
            $configYesNo,
            $optionType,
            $product,
            $registry,
            $productOptionConfig,
            $data
        );
    }

    /**
     * Retrieve html templates for different types of product custom options
     *
     * @return string
     */
    public function getTemplatesHtml()
    {
        $canEditPrice = $this->getCanEditPrice();
        $canReadPrice = $this->getCanReadPrice();

        $this->getChildBlock('xfile_option_type')
            ->setCanReadPrice($canReadPrice)
            ->setCanEditPrice($canEditPrice);
        $templates = parent::getTemplatesHtml() . "\n" .
            $this->getChildHtml('xfile_option_type');

        return $templates;    
    }          
}

app/code/A/Custoptiontype/Block/Adminhtml/Product/Edit/Tab/Options/Type/Xfile.php

<?php
namespace A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type;

class Xfile extends \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\AbstractType
{
    /**
     * @var string
     */
    protected $_template = 'A_Custoptiontype::catalog/product/edit/options/type/xfile.phtml';


    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Catalog\Model\Config\Source\Product\Options\Price $optionPrice,
        array $data = []
    ) {
        $this->_optionPrice = $optionPrice;
        parent::__construct($context, $optionPrice, $data);
    }

}

app/code/A/Custoptiontype/view/adminhtml/templates/catalog/product/edit/options/option.phtml

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
// @codingStandardsIgnoreFile
?>
<?php /** @var $block \A\Custoptiontype\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option */ ?>
<?php echo $block->getTemplatesHtml() ?>
<script id="custom-option-base-template" type="text/x-magento-template">
    <div class="fieldset-wrapper admin__collapsible-block-wrapper opened" id="option_<%- data.id %>">
        <div class="fieldset-wrapper-title">
            <strong
                class="admin__collapsible-title"
                data-toggle="collapse"
                data-target="#<%- data.id %>-content">
                <span id="option_<%- data.id %>_header_title"><%- data.title %></span>
            </strong>
            <div class="actions">
                <button type="button" title="<?php /* @escapeNotVerified */ echo __('Delete Custom Option'); ?>" class="action-delete" id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_delete">
                    <span><?php /* @escapeNotVerified */ echo __('Delete Custom Option'); ?></span>
                </button>
            </div>
            <div id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_move" data-role="draggable-handle" class="draggable-handle"
                 title="<?php /* @escapeNotVerified */ echo __('Sort Custom Options'); ?>"></div>
        </div>
        <div class="fieldset-wrapper-content in collapse" id="<%- data.id %>-content">
            <fieldset class="fieldset">
                <fieldset class="fieldset-alt" id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>">
                    <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_is_delete" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][is_delete]" type="hidden" value=""/>
                    <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_previous_type" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][previous_type]" type="hidden" value="<%- data.type %>"/>
                    <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_previous_group" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][previous_group]" type="hidden" value=""/>
                    <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_id" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][id]" type="hidden" value="<%- data.id %>"/>
                    <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_option_id" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][option_id]" type="hidden" value="<%- data.option_id %>"/>
                    <input name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][sort_order]" type="hidden" value="<%- data.sort_order %>"/>

                    <div class="field field-option-title required">
                        <label class="label" for="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title">
                            <?php /* @escapeNotVerified */ echo __('Option Title') ?>
                        </label>
                        <div class="control">
                            <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title"
                                   name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][title]"
                                   class="required-entry input-text"
                                   type="text"
                                   value="<%- data.title %>"
                                   data-store-label="<%- data.title %>"
                                   <% if (typeof data.scopeTitleDisabled != 'undefined' && data.scopeTitleDisabled != null) { %> disabled="disabled" <% } %>
                                   >
                            <%- data.checkboxScopeTitle %>
                        </div>
                    </div>

                    <div class="field field-option-input-type required">
                        <label class="label" for="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title">
                            <?php /* @escapeNotVerified */ echo __('Input Type') ?>
                        </label>
                        <div class="control opt-type">
                            <?php echo $block->getTypeSelectHtml() ?>
                        </div>
                    </div>

                    <div class="field field-option-req">
                        <div class="control">
                            <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_required" class="is-required" type="checkbox" checked="checked"/>
                            <label for="field-option-req">
                                <?php /* @escapeNotVerified */ echo __('Required')?>
                            </label>
                            <span style="display:none"><?php echo $block->getRequireSelectHtml() ?></span>
                        </div>
                    </div>
                </fieldset>
            </fieldset>
        </div>
    </div>
</script>

<div id="import-container" style="display: none;"></div>
<?php if (!$block->isReadonly()): ?>
<div><input type="hidden" name="affect_product_custom_options" value="1"/></div>
<?php endif; ?>
<script>
require([
    "jquery",
    "Magento_Catalog/js/custom-options"
], function(jQuery){

jQuery(function ($) {
    var fieldSet = $('[data-block=product-custom-options]');
    fieldSet.customOptions(<?php /* @escapeNotVerified */ echo $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode(
        [
            'fieldId' => $block->getFieldId(),
            'productGridUrl' => $block->getProductGridUrl(),
            'formKey' => $block->getFormKey(),
            'customOptionsUrl' => $block->getCustomOptionsUrl(),
            'isReadonly' => $block->isReadonly(),
            'itemCount' => $block->getItemCount(),
            'currentProductId' => $block->getCurrentProductId(),
        ]
    )?>);
    //adding data to templates
    <?php /** @var $_value \Magento\Framework\DataObject */ ?>
    <?php foreach ($block->getOptionValues() as $_value): ?>
        fieldSet.customOptions('addOption', <?php /* @escapeNotVerified */ echo $_value->toJson() ?>);
    <?php endforeach; ?>
});

});
</script>

app/code/A/Custoptiontype/view/adminhtml/templates/catalog/product/edit/options/type/xfile.phtml

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
// @codingStandardsIgnoreFile
?>
<?php /** @var $block \A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type\Xfile */ ?>
<h1>TOTOTOTOTO</h1>
<script id="custom-option-xfile-type-template" type="text/x-magento-template">
    <div id="product_option_<%- data.option_id %>_type_<%- data.group %>" class="fieldset">
        <table class="data-table">
            <thead>
            <tr>
                <?php if ($block->getCanReadPrice() !== false) : ?>
                <th><?php /* @escapeNotVerified */ echo __('Price'); ?></th>
                <th><?php /* @escapeNotVerified */ echo __('Price Type'); ?></th>
                <?php endif; ?>
                <th><?php /* @escapeNotVerified */ echo __('SKU'); ?></th>
                <th><?php /* @escapeNotVerified */ echo __('Compatible File Extensions'); ?></th>
                <th><?php /* @escapeNotVerified */ echo __('Maximum Image Size'); ?></th>
            </tr>
            </thead>
            <tr>
                <?php if ($block->getCanReadPrice() !== false) : ?>
                <td class="opt-price">
                    <input name="product[options][<%- data.option_id %>][price]" data-store-label="<%- data.price %>"
                           class="input-text validate-zero-or-greater" type="text" value="<%- data.price %>"
                        <?php if ($block->getCanEditPrice() === false) : ?>
                           disabled="disabled"
                        <?php endif; ?>>
                </td>
                <td class="opt-price-type"><?php echo $block->getPriceTypeSelectHtml('data-attr="price-type"') ?><%- data.checkboxScopePrice %></td>
                <?php else : ?>
                <input name="product[options][<%- data.option_id %>][price]" type="hidden">
                <input id="product_option_<%- data.option_id %>_price_type" name="product[options][<%- data.option_id %>][price_type]" type="hidden">
                <?php endif; ?>
                <td>
                    <input name="product[options][<%- data.option_id %>][sku]" class="input-text" type="text" value="<%- data.sku %>">
                </td>
                <td>
                    <input name="product[options][<%- data.option_id %>][file_extension]" class="input-text" type="text" value="<%- data.file_extension %>">
                </td>
                <td class="col-file"><?php /* @escapeNotVerified */ echo __('%1 <span>x</span> %2 <span>px.</span>',
                    '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_x]" value="<%- data.image_size_x %>">',
                    '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_y]" value="<%- data.image_size_y %>">') ?>
                    <div class="note"><?php /* @escapeNotVerified */ echo __('Please leave blank if it is not an image.') ?></div>
                </td>
            </tr>
        </table>
    </div>
</script>
  • Are u solve this? can u help me – Mano M Dec 28 '18 at 2:09
0

This is what I did to create a custom option type in Magento 2. For the new option type, I retained using the sku field but renamed it.

app/code/Testvendor/CustomOptions/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">
<preference for="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions" type="Testvendor\CustomOptions\Ui\DataProvider\Catalog\Product\Form\Modifier\CustomOptions" /></config>

app/code/Testvendor/CustomOptions/etc/product_options.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_options.xsd">
   <option name="testvendor" label="Test Vendor" renderer="Testvendor\CustomOptions\Block\Adminhtml\Catalog\Product\Edit\Tab\Options\Type\Testvendor">
    <inputType name="testoption" label="Test Option" />
</option>

app/code/Testvendor/CustomOptions/Ui/DataProvider/Catalog/Product/Form/Modifier/CustomOptions.php

<?php
namespace Testvendor\CustomOptions\Ui\DataProvider\Catalog\Product\Form\Modifier;


use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Catalog\Model\ProductOptions\ConfigInterface;
use Magento\Catalog\Model\Config\Source\Product\Options\Price as ProductOptionsPrice;
use Magento\Framework\UrlInterface;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Ui\Component\Modal;
use Magento\Ui\Component\Container;
use Magento\Ui\Component\DynamicRows;
use Magento\Ui\Component\Form\Fieldset;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Element\Select;
use Magento\Ui\Component\Form\Element\Checkbox;
use Magento\Ui\Component\Form\Element\ActionDelete;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\DataType\Number;
use Magento\Framework\Locale\CurrencyInterface;



class CustomOptions extends \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions
{


    const FIELD_TESTOPTION_NAME = 'testoption';

    protected function getStaticTypeContainerConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'componentType' => Container::NAME,
                        'formElement' => Container::NAME,
                        'component' => 'Magento_Ui/js/form/components/group',
                        'breakLine' => false,
                        'showLabel' => false,
                        'additionalClasses' => 'admin__field-group-columns admin__control-group-equal',
                        'sortOrder' => $sortOrder,
                    ],
                ],
            ],
            'children' => [
                static::FIELD_PRICE_NAME => $this->getPriceFieldConfig(10),
                static::FIELD_PRICE_TYPE_NAME => $this->getPriceTypeFieldConfig(20),
                static::FIELD_SKU_NAME => $this->getSkuFieldConfig(30),
                static::FIELD_TESTOPTION_NAME => $this->getTestoptionFieldConfig(30),
                static::FIELD_MAX_CHARACTERS_NAME => $this->getMaxCharactersFieldConfig(40),
                static::FIELD_FILE_EXTENSION_NAME => $this->getFileExtensionFieldConfig(50),
                static::FIELD_IMAGE_SIZE_X_NAME => $this->getImageSizeXFieldConfig(60),
                static::FIELD_IMAGE_SIZE_Y_NAME => $this->getImageSizeYFieldConfig(70)
            ]
        ];
    }

    /**
     * Get config for grid for "select" types
     *
     * @param int $sortOrder
     * @return array
     */
    protected function getSelectTypeGridConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'addButtonLabel' => __('Add Value'),
                        'componentType' => DynamicRows::NAME,
                        'component' => 'Magento_Ui/js/dynamic-rows/dynamic-rows',
                        'additionalClasses' => 'admin__field-wide',
                        'deleteProperty' => static::FIELD_IS_DELETE,
                        'deleteValue' => '1',
                        'renderDefaultRecord' => false,
                        'sortOrder' => $sortOrder,
                    ],
                ],
            ],
            'children' => [
                'record' => [
                    'arguments' => [
                        'data' => [
                            'config' => [
                                'componentType' => Container::NAME,
                                'component' => 'Magento_Ui/js/dynamic-rows/record',
                                'positionProvider' => static::FIELD_SORT_ORDER_NAME,
                                'isTemplate' => true,
                                'is_collection' => true,
                            ],
                        ],
                    ],
                    'children' => [
                        static::FIELD_TITLE_NAME => $this->getTitleFieldConfig(10),
                        static::FIELD_PRICE_NAME => $this->getPriceFieldConfig(20),
                        static::FIELD_PRICE_TYPE_NAME => $this->getPriceTypeFieldConfig(30, ['fit' => true]),
                        static::FIELD_SKU_NAME => $this->getSkuFieldConfig(40),
                        static::FIELD_SORT_ORDER_NAME => $this->getPositionFieldConfig(50),
                        static::FIELD_IS_DELETE => $this->getIsDeleteFieldConfig(60)
                    ]
                ]
            ]
        ];
    }



       protected function getTypeFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Option Type'),
                        'componentType' => Field::NAME,
                        'formElement' => Select::NAME,
                        'component' => 'Magento_Catalog/js/custom-options-type',
                        'elementTmpl' => 'ui/grid/filters/elements/ui-select',
                        'selectType' => 'optgroup',
                        'dataScope' => static::FIELD_TYPE_NAME,
                        'dataType' => Text::NAME,
                        'sortOrder' => $sortOrder,
                        'options' => $this->getProductOptionTypes(),
                        'disableLabel' => true,
                        'multiple' => false,
                        'selectedPlaceholders' => [
                            'defaultPlaceholder' => __('-- Please select --'),
                        ],
                        'validation' => [
                            'required-entry' => true
                        ],
                        'groupsConfig' => [
                            'text' => [
                                'values' => ['field', 'area'],
                                'indexes' => [
                                    static::CONTAINER_TYPE_STATIC_NAME,
                                    static::FIELD_PRICE_NAME,
                                    static::FIELD_PRICE_TYPE_NAME,
                                    static::FIELD_SKU_NAME,
                                    static::FIELD_MAX_CHARACTERS_NAME
                                ]
                            ],
                            'Testvendor' => [
                                'values' => ['testoption'],
                                'indexes' => [
                                    static::CONTAINER_TYPE_STATIC_NAME,
                                    #static::FIELD_SKU_NAME,
                                    static::FIELD_TESTOPTION_NAME,
                                ]
                            ],

                            'file' => [
                                'values' => ['file'],
                                'indexes' => [
                                    static::CONTAINER_TYPE_STATIC_NAME,
                                    static::FIELD_PRICE_NAME,
                                    static::FIELD_PRICE_TYPE_NAME,
                                    static::FIELD_SKU_NAME,
                                    static::FIELD_FILE_EXTENSION_NAME,
                                    static::FIELD_IMAGE_SIZE_X_NAME,
                                    static::FIELD_IMAGE_SIZE_Y_NAME
                                ]
                            ],
                            'select' => [
                                'values' => ['drop_down', 'radio', 'checkbox', 'multiple'],
                                'indexes' => [
                                    static::GRID_TYPE_SELECT_NAME
                                ]
                            ],
                            'data' => [
                                'values' => ['date', 'date_time', 'time'],
                                'indexes' => [
                                    static::CONTAINER_TYPE_STATIC_NAME,
                                    static::FIELD_PRICE_NAME,
                                    static::FIELD_PRICE_TYPE_NAME,
                                    static::FIELD_SKU_NAME
                                ]
                            ]
                        ],
                    ],
                ],
            ],
        ];
    }

protected function getTestoptionFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Test Option'),
                        'componentType' => Field::NAME,
                        'formElement' => Input::NAME,
                        'dataScope' => static::FIELD_SKU_NAME,
                        'dataType' => Text::NAME,
                        'sortOrder' => $sortOrder,
                    ],
                ],
            ],
        ];
    }

       }

After this... I went in the bin directory and ran

./magento setup:upgrade ./magento setup:di:compile ./magento cache:flush ./magento cache:clean

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.