Magento – Override Magento\Catalog\Block\Product\View\Options\Type\Select

magento-catalogmagento2override-block

SOLVED :: 15.9.17 (the following code works on a fresh Magento 2.1.7 installation; tested on Luma theme)

I am struggling with the override of this particular core block

Magento\Catalog\Block\Product\View\Options\Type\Select

Here is my module structure:

├── <VendorName>
|   ├── <ModuleName>
|   |   ├── Block/
|   |   |   ├── Product/
|   |   |   |   ├── View/
|   |   |   |   |   ├── Options/
|   |   |   |   |   |   ├── Type/
|   |   |   |   |   |   |   └── Select.php
|   |   ├── etc/ 
|   |   |   └── di.xml
|   |   |   └── module.xml
|   |   ├── view/
|   |   |   ├── frontend/
|   |   |   |   ├── templates/
|   |   |   |   |   ├── product/
|   |   |   |   |   |   ├── view/
|   |   |   |   |   |   |   ├── options/
|   |   |   |   |   |   |   |   ├── type/
|   |   |   |   |   |   |   |   |   └── select.phtml
|   |   └── registration.php

The registration.php

    <?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'VendorName_ModuleName',
    __DIR__
);

The 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_ModuleName" setup_version="1.0.0">
            <sequence>
                <module name="Magento_Catalog"/>
            </sequence>
        </module>
</config>

The 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\Product\View\Options\Type\Select" type="VendorName\ModuleName\Block\Product\View\Options\Type\Select" />
</config>

The select.phtml (copied as it is from vendor/magento folder)

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

?>

<?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Select */ ?>
<?php
$_option = $block->getOption();
$class = ($_option->getIsRequire()) ? ' required' : '';
?>
<div class="field<?php /* @escapeNotVerified */ echo $class; ?>">
    <label class="label" for="select_<?php /* @escapeNotVerified */ echo $_option->getId() ?>">
        <span><?php echo  $block->escapeHtml($_option->getTitle()) ?></span>
    </label>
    <div class="control">
        <?php echo $block->getValuesHtml() ?>
        <?php if ($_option->getIsRequire()): ?>
            <?php if ($_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_RADIO || $_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_CHECKBOX): ?>
                <span id="options-<?php /* @escapeNotVerified */ echo $_option->getId() ?>-container"></span>
            <?php endif; ?>
        <?php endif;?>
    </div>
</div>

The Select.php (I removed the custom logic here, as I think not relevant in this context)

<?php
namespace VendorName\ModuleName\Block\Product\View\Options\Type;

class Select extends \Magento\Catalog\Block\Product\View\Options\AbstractOptions 

{

   public function getValuesHtml()
    {
    // My Custom Code
    }
}

According to https://magento.stackexchange.com/a/112847/51071 I was expecting to succesfully override the block after having activated the module and run setup:di:compile, but nothing happened.

Does anyone has a clue what am I missing here in order to make the module working properly? Any help is much appreciated.

Best Answer


Above answers helped me but it did not work for me as it is.
This is what I did to override block \Magento\Catalog\Block\Product\View\Options\Type\Select.php

File - [Vendor][Module]\etc\di.xml

<type name="Magento\Catalog\Block\Product\View\Options\Type\Select">
    <plugin name="[Vendor]_[Module]_select" sortOrder="10"
            type="[Vendor]\[Module]\Block\Product\View\Options\Type\Select"/>
</type>

File - [Vendor][Module]\Block\Product\View\Options\Type\Select.php

<?php

namespace [Vendor]\[Module]\Block\Product\View\Options\Type;

class Select extends \Magento\Catalog\Block\Product\View\Options\AbstractOptions
{

 public function afterGetValuesHtml(\Magento\Catalog\Block\Product\View\Options\Type\Select $subject, $result)
 {
    $_option = $subject->getOption();
    $configValue = $subject->getProduct()->getPreconfiguredValues()->getData('options/' . $_option->getId());
    $store = $subject->getProduct()->getStore();

    $subject->setSkipJsReloadPrice(1);
    // Remove inline prototype onclick and onchange events

    if ($_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_DROP_DOWN ||
        $_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_MULTIPLE
    ) {
        $require = $_option->getIsRequire() ? ' required' : '';

        $extraParams = '';
        $select = $subject->getLayout()->createBlock(
            'Magento\Framework\View\Element\Html\Select'
        )->setData(
            [
                'id' => 'select_' . $_option->getId(),
                'class' => $require . ' product-custom-option admin__control-select'
            ]
        );
        if ($_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_DROP_DOWN) {
            // Code changes start
            if($_option->getIsSize()){
                $select->setName('options[' . $_option->getid() . ']')->addOption('', __('Select a Size'));
            }
            else if($_option->getIsCut()){
                $select->setName('options[' . $_option->getid() . ']')->addOption('', __('Select a Cut'));
            }
            else{
                $select->setName('options[' . $_option->getid() . ']')->addOption('', __('Select an Option'));
            }
            // Code changes end
        } else {
            $select->setName('options[' . $_option->getid() . '][]');
            $select->setClass('multiselect admin__control-multiselect' . $require . ' product-custom-option');
        }
        foreach ($_option->getValues() as $_value) {
            $priceStr = $subject->_formatPrice(
                [
                    'is_percent' => $_value->getPriceType() == 'percent',
                    'pricing_value' => $_value->getPrice($_value->getPriceType() == 'percent'),
                ],
                false
            );
            $select->addOption(
                $_value->getOptionTypeId(),
                $_value->getTitle() . ' ' . strip_tags($priceStr) . '',
                ['price' => $subject->pricingHelper->currencyByStore($_value->getPrice(true), $store, false)]
            );
        }
        if ($_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_MULTIPLE) {
            $extraParams = ' multiple="multiple"';
        }
        if (!$subject->getSkipJsReloadPrice()) {
            $extraParams .= ' onchange="opConfig.reloadPrice()"';
        }
        $extraParams .= ' data-selector="' . $select->getName() . '"';
        $select->setExtraParams($extraParams);

        if ($configValue) {
            $select->setValue($configValue);
        }

        return $select->getHtml();
    }

    if ($_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_RADIO ||
        $_option->getType() == \Magento\Catalog\Model\Product\Option::OPTION_TYPE_CHECKBOX
    ) {
        $selectHtml = '<div class="options-list nested" id="options-' . $_option->getId() . '-list">';
        $require = $_option->getIsRequire() ? ' required' : '';
        $arraySign = '';
        switch ($_option->getType()) {
            case \Magento\Catalog\Model\Product\Option::OPTION_TYPE_RADIO:
                $type = 'radio';
                $class = 'radio admin__control-radio';
                if (!$_option->getIsRequire()) {
                    $selectHtml .= '<div class="field choice admin__field admin__field-option">' .
                        '<input type="radio" id="options_' .
                        $_option->getId() .
                        '" class="' .
                        $class .
                        ' product-custom-option" name="options[' .
                        $_option->getId() .
                        ']"' .
                        ' data-selector="options[' . $_option->getId() . ']"' .
                        ($subject->getSkipJsReloadPrice() ? '' : ' onclick="opConfig.reloadPrice()"') .
                        ' value="" checked="checked" /><label class="label admin__field-label" for="options_' .
                        $_option->getId() .
                        '"><span>' .
                        __('None') . '</span></label></div>';
                }
                break;
            case \Magento\Catalog\Model\Product\Option::OPTION_TYPE_CHECKBOX:
                $type = 'checkbox';
                $class = 'checkbox admin__control-checkbox';
                $arraySign = '[]';
                break;
        }
        $count = 1;
        foreach ($_option->getValues() as $_value) {
            $count++;

            $priceStr = $subject->_formatPrice(
                [
                    'is_percent' => $_value->getPriceType() == 'percent',
                    'pricing_value' => $_value->getPrice($_value->getPriceType() == 'percent'),
                ]
            );

            $htmlValue = $_value->getOptionTypeId();
            if ($arraySign) {
                $checked = is_array($configValue) && in_array($htmlValue, $configValue) ? 'checked' : '';
            } else {
                $checked = $configValue == $htmlValue ? 'checked' : '';
            }

            $dataSelector = 'options[' . $_option->getId() . ']';
            if ($arraySign) {
                $dataSelector .= '[' . $htmlValue . ']';
            }

            $selectHtml .= '<div class="field choice admin__field admin__field-option' .
                $require .
                '">' .
                '<input type="' .
                $type .
                '" class="' .
                $class .
                ' ' .
                $require .
                ' product-custom-option"' .
                ($subject->getSkipJsReloadPrice() ? '' : ' onclick="opConfig.reloadPrice()"') .
                ' name="options[' .
                $_option->getId() .
                ']' .
                $arraySign .
                '" id="options_' .
                $_option->getId() .
                '_' .
                $count .
                '" value="' .
                $htmlValue .
                '" ' .
                $checked .
                ' data-selector="' . $dataSelector . '"' .
                ' price="' .
                $subject->pricingHelper->currencyByStore($_value->getPrice(true), $store, false) .
                '" />' .
                '<label class="label admin__field-label" for="options_' .
                $_option->getId() .
                '_' .
                $count .
                '"><span>' .
                $_value->getTitle() .
                '</span> ' .
                $priceStr .
                '</label>';
            $selectHtml .= '</div>';
        }
        $selectHtml .= '</div>';

        return $selectHtml;
    }
 }
}

Important things to note here -
After copying \Magento\Catalog\Block\Product\View\Options\Type\Select.php to [Vendor][Module]\Block\Product\View\Options\Type\Select.php, you must do following.

  1. Change the namespace to [Vendor]\[Module]\Block\Product\View\Options\Type;
  2. Replace public function getValuesHtml() to public function afterGetValuesHtml(\Magento\Catalog\Block\Product\View\Options\Type\Select $subject, $result)
  3. Replace $this to $subject

Hope this helps.

Related Topic