0

I need to override this below controller :

Magento\Catalog\Controller\Adminhtml\Product\Attribute\Validate.php

But, I can't able to override. I need to customize checkUniqueOption() function.

di.xml

<preference for="Magento\Catalog\Controller\Adminhtml\Product\Attribute\Validate" type="Vendor\Magento\Controller\Adminhtml\Product\Attribute\Validate" />

How to do that?

Any help would be appreciated.

  • You could use preference for overiding.. – aravind Oct 14 '19 at 9:53
  • Yes. I already used. But, can't able to call my checkUniqueOption() code. – Niket Oct 14 '19 at 9:56
  • @aravind Can you please give me demo? – Niket Oct 14 '19 at 9:57
  • i have updated it..It's for Magento 2.3.. If you any other version replace with that code – aravind Oct 14 '19 at 10:01
4

You want to update checkUniqueOption() this function and because of that function is private you need to rename this as _checkUniqueOption() or something else, and in that file there are some other functions which are also private so you need to do same thing for that all functions as well. So your new Controller file's content will be :

Vendor/Module/Controller/Adminhtml/Product/Attribute/Validate.php

<?php

namespace Vendor\Module\Controller\Adminhtml\Product\Attribute;

use Magento\Framework\Serialize\Serializer\FormData;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
use Magento\Catalog\Controller\Adminhtml\Product\Attribute as AttributeAction;

class Validate extends \Magento\Catalog\Controller\Adminhtml\Product\Attribute\Validate
{
    const DEFAULT_MESSAGE_KEY = 'message';

    /**
     * @var \Magento\Framework\Controller\Result\JsonFactory
     */
    protected $resultJsonFactory;

    /**
     * @var \Magento\Framework\View\LayoutFactory
     */
    protected $layoutFactory;

    /**
     * @var array
     */
    private $multipleAttributeList;

    /**
     * @var FormData|null
     */
    private $formDataSerializer;

    /**
     * Constructor
     *
     * @param \Magento\Backend\App\Action\Context $context
     * @param \Magento\Framework\Cache\FrontendInterface $attributeLabelCache
     * @param \Magento\Framework\Registry $coreRegistry
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
     * @param \Magento\Framework\View\LayoutFactory $layoutFactory
     * @param array $multipleAttributeList
     * @param FormData|null $formDataSerializer
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\Cache\FrontendInterface $attributeLabelCache,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory,
        \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
        \Magento\Framework\View\LayoutFactory $layoutFactory,
        array $multipleAttributeList = [],
        FormData $formDataSerializer = null
    ) {
        parent::__construct($context, $attributeLabelCache, $coreRegistry, $resultPageFactory, $resultJsonFactory, $layoutFactory, $multipleAttributeList, $formDataSerializer);
        $this->multipleAttributeList = $multipleAttributeList;
        $this->formDataSerializer = $formDataSerializer ?: ObjectManager::getInstance()
            ->get(FormData::class);
    }

    public function execute()
    {
        $response = new DataObject();
        $response->setError(false);
        try {
            $optionsData = $this->formDataSerializer
                ->unserialize($this->getRequest()->getParam('serialized_options', '[]'));
        } catch (\InvalidArgumentException $e) {
            $message = __("The attribute couldn't be validated due to an error. Verify your information and try again. "
                . "If the error persists, please try again later.");
            $this->_setMessageToResponse($response, [$message]);
            $response->setError(true);
        }

        $attributeCode = $this->getRequest()->getParam('attribute_code');
        $frontendLabel = $this->getRequest()->getParam('frontend_label');
        $attributeCode = $attributeCode ?: $this->generateCode($frontendLabel[0]);
        $attributeId = $this->getRequest()->getParam('attribute_id');
        $attribute = $this->_objectManager->create(
            \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
        )->loadByCode(
            $this->_entityTypeId,
            $attributeCode
        );

        if ($attribute->getId() && !$attributeId || $attributeCode === 'product_type') {
            $message = strlen($this->getRequest()->getParam('attribute_code'))
                ? __('An attribute with this code already exists.')
                : __('An attribute with the same code (%1) already exists.', $attributeCode);

            $this->_setMessageToResponse($response, [$message]);

            $response->setError(true);
            $response->setProductAttribute($attribute->toArray());
        }
        if ($this->getRequest()->has('new_attribute_set_name')) {
            $setName = $this->getRequest()->getParam('new_attribute_set_name');
            /** @var $attributeSet \Magento\Eav\Model\Entity\Attribute\Set */
            $attributeSet = $this->_objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class);
            $attributeSet->setEntityTypeId($this->_entityTypeId)->load($setName, 'attribute_set_name');
            if ($attributeSet->getId()) {
                $setName = $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($setName);
                $this->messageManager->addErrorMessage(__('An attribute set named \'%1\' already exists.', $setName));

                $layout = $this->layoutFactory->create();
                $layout->initMessages();
                $response->setError(true);
                $response->setHtmlMessage($layout->getMessagesBlock()->getGroupedHtml());
            }
        }

        $multipleOption = $this->getRequest()->getParam("frontend_input");
        $multipleOption = (null === $multipleOption) ? 'select' : $multipleOption;

        if (isset($this->multipleAttributeList[$multipleOption])) {
            $options = $optionsData[$this->multipleAttributeList[$multipleOption]] ?? null;
            $this->_checkUniqueOption(
                $response,
                $options
            );
            $valueOptions = (isset($options['value']) && is_array($options['value'])) ? $options['value'] : [];
            foreach (array_keys($valueOptions) as $key) {
                if (!empty($options['delete'][$key])) {
                    unset($valueOptions[$key]);
                }
            }
            $this->_checkEmptyOption($response, $valueOptions);
        }

        return $this->resultJsonFactory->create()->setJsonData($response->toJson());
    }

    /**
     * Throws Exception if not unique values into options.
     *
     * @param array $optionsValues
     * @param array $deletedOptions
     * @return bool
     */
    private function _isUniqueAdminValues(array $optionsValues, array $deletedOptions)
    {
        $adminValues = [];
        foreach ($optionsValues as $optionKey => $values) {
            if (!(isset($deletedOptions[$optionKey]) && $deletedOptions[$optionKey] === '1')) {
                $adminValues[] = reset($values);
            }
        }
        $uniqueValues = array_unique($adminValues);
        return array_diff_assoc($adminValues, $uniqueValues);
    }

    /**
     * Set message to response object
     *
     * @param DataObject $response
     * @param string[] $messages
     * @return DataObject
     */
    private function _setMessageToResponse($response, $messages)
    {
        $messageKey = $this->getRequest()->getParam('message_key', static::DEFAULT_MESSAGE_KEY);
        if ($messageKey === static::DEFAULT_MESSAGE_KEY) {
            $messages = reset($messages);
        }
        return $response->setData($messageKey, $messages);
    }

    /**
     * Performs checking the uniqueness of the attribute options.
     *
     * @param DataObject $response
     * @param array|null $options
     * @return $this
     */
    private function _checkUniqueOption(DataObject $response, array $options = null)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/checkUniqueOption.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('checkUniqueOption Log'); // Simple Text Log

        if (is_array($options)
            && isset($options['value'])
            && isset($options['delete'])
            && !empty($options['value'])
            && !empty($options['delete'])
        ) {
            $duplicates = $this->_isUniqueAdminValues($options['value'], $options['delete']);
            if (!empty($duplicates)) {
                $this->_setMessageToResponse(
                    $response,
                    [__('The value of Admin must be unique. (%1)', implode(', ', $duplicates))]
                );
                $response->setError(true);
            }
        }
        return $this;
    }

    /**
     * Check that admin does not try to create option with empty admin scope option.
     *
     * @param DataObject $response
     * @param array $optionsForCheck
     * @return void
     */
    private function _checkEmptyOption(DataObject $response, array $optionsForCheck = null)
    {
        foreach ($optionsForCheck as $optionValues) {
            if (isset($optionValues[0]) && $optionValues[0] == '') {
                $this->_setMessageToResponse($response, [__("The value of Admin scope can't be empty.")]);
                $response->setError(true);
            }
        }
    }
}

Hope this will work for you.

  • Nope. not working. – Niket Oct 14 '19 at 10:24
  • There are no any class available : Magento\Eav\Model\Validator\Attribute\Code – Niket Oct 14 '19 at 10:26
  • Which version of Magento you're using ? – Kishan Savaliya Oct 14 '19 at 10:49
  • 2.3.1 ......... – Niket Oct 14 '19 at 10:50
  • I've updated code as per M2.3.1 version. – Kishan Savaliya Oct 14 '19 at 10:56
0

Vendor/Module/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\Controller\Adminhtml\Product\Attribute\Validate" type="Vendor\Module\Controller\Adminhtml\Product\Attribute\Validate" />
</config>

In Your Controller file copy paste the entire content from core file.

Vendor/Module/Controller/Adminhtml/Product/Attribute/Validate.php

<?php

namespace Vendor\Module\Controller\Adminhtml\Product\Attribute;

use Magento\Catalog\Controller\Adminhtml\Product\Attribute as AttributeAction;
use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
use Magento\Framework\Serialize\Serializer\FormData;

class Validate extends \Magento\Catalog\Controller\Adminhtml\Product\Attribute\Validate
{
   const DEFAULT_MESSAGE_KEY = 'message';

/**
 * @var \Magento\Framework\Controller\Result\JsonFactory
 */
protected $resultJsonFactory;

/**
 * @var \Magento\Framework\View\LayoutFactory
 */
protected $layoutFactory;

/**
 * @var array
 */
private $multipleAttributeList;

/**
 * @var FormData|null
 */
private $formDataSerializer;

/**
 * @var AttributeCodeValidator
 */
private $attributeCodeValidator;

/**
 * Constructor
 *
 * @param \Magento\Backend\App\Action\Context $context
 * @param \Magento\Framework\Cache\FrontendInterface $attributeLabelCache
 * @param \Magento\Framework\Registry $coreRegistry
 * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
 * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
 * @param \Magento\Framework\View\LayoutFactory $layoutFactory
 * @param array $multipleAttributeList
 * @param FormData|null $formDataSerializer
 * @param AttributeCodeValidator|null $attributeCodeValidator
 */
public function __construct(
    \Magento\Backend\App\Action\Context $context,
    \Magento\Framework\Cache\FrontendInterface $attributeLabelCache,
    \Magento\Framework\Registry $coreRegistry,
    \Magento\Framework\View\Result\PageFactory $resultPageFactory,
    \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
    \Magento\Framework\View\LayoutFactory $layoutFactory,
    array $multipleAttributeList = [],
    FormData $formDataSerializer = null,
    AttributeCodeValidator $attributeCodeValidator = null
) {
    parent::__construct($context, $attributeLabelCache, $coreRegistry, $resultPageFactory);
    $this->resultJsonFactory = $resultJsonFactory;
    $this->layoutFactory = $layoutFactory;
    $this->multipleAttributeList = $multipleAttributeList;
    $this->formDataSerializer = $formDataSerializer ?: ObjectManager::getInstance()
        ->get(FormData::class);
    $this->attributeCodeValidator = $attributeCodeValidator ?: ObjectManager::getInstance()->get(
        AttributeCodeValidator::class
    );
}

/**
 * @inheritdoc
 *
 * @return \Magento\Framework\Controller\ResultInterface
 * @SuppressWarnings(PHPMD.NPathComplexity)
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 */
public function execute()
{
    $response = new DataObject();
    $response->setError(false);
    try {
        $optionsData = $this->formDataSerializer
            ->unserialize($this->getRequest()->getParam('serialized_options', '[]'));
    } catch (\InvalidArgumentException $e) {
        $message = __("The attribute couldn't be validated due to an error. Verify your information and try again. "
            . "If the error persists, please try again later.");
        $this->setMessageToResponse($response, [$message]);
        $response->setError(true);
    }

    $attributeCode = $this->getRequest()->getParam('attribute_code');
    $frontendLabel = $this->getRequest()->getParam('frontend_label');
    $attributeCode = $attributeCode ?: $this->generateCode($frontendLabel[0]);
    $attributeId = $this->getRequest()->getParam('attribute_id');
    $attribute = $this->_objectManager->create(
        \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
    )->loadByCode(
        $this->_entityTypeId,
        $attributeCode
    );

    if ($attribute->getId() && !$attributeId || $attributeCode === 'product_type' || $attributeCode === 'type_id') {
        $message = strlen($this->getRequest()->getParam('attribute_code'))
            ? __('An attribute with this code already exists.')
            : __('An attribute with the same code (%1) already exists.', $attributeCode);

        $this->setMessageToResponse($response, [$message]);

        $response->setError(true);
        $response->setProductAttribute($attribute->toArray());
    }

    if (!$this->attributeCodeValidator->isValid($attributeCode)) {
        $this->setMessageToResponse($response, $this->attributeCodeValidator->getMessages());
        $response->setError(true);
    }

    if ($this->getRequest()->has('new_attribute_set_name')) {
        $setName = $this->getRequest()->getParam('new_attribute_set_name');
        /** @var $attributeSet \Magento\Eav\Model\Entity\Attribute\Set */
        $attributeSet = $this->_objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class);
        $attributeSet->setEntityTypeId($this->_entityTypeId)->load($setName, 'attribute_set_name');
        if ($attributeSet->getId()) {
            $setName = $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($setName);
            $this->messageManager->addErrorMessage(__('An attribute set named \'%1\' already exists.', $setName));

            $layout = $this->layoutFactory->create();
            $layout->initMessages();
            $response->setError(true);
            $response->setHtmlMessage($layout->getMessagesBlock()->getGroupedHtml());
        }
    }

    $multipleOption = $this->getRequest()->getParam("frontend_input");
    $multipleOption = (null === $multipleOption) ? 'select' : $multipleOption;

    if (isset($this->multipleAttributeList[$multipleOption])) {
        $options = $optionsData[$this->multipleAttributeList[$multipleOption]] ?? null;
        $this->checkUniqueOption(
            $response,
            $options
        );
        $valueOptions = (isset($options['value']) && is_array($options['value'])) ? $options['value'] : [];
        foreach (array_keys($valueOptions) as $key) {
            if (!empty($options['delete'][$key])) {
                unset($valueOptions[$key]);
            }
        }
        $this->checkEmptyOption($response, $valueOptions);
    }

    return $this->resultJsonFactory->create()->setJsonData($response->toJson());
}

/**
 * Throws Exception if not unique values into options.
 *
 * @param array $optionsValues
 * @param array $deletedOptions
 * @return bool
 */
private function isUniqueAdminValues(array $optionsValues, array $deletedOptions)
{
    $adminValues = [];
    foreach ($optionsValues as $optionKey => $values) {
        if (!(isset($deletedOptions[$optionKey]) && $deletedOptions[$optionKey] === '1')) {
            $adminValues[] = reset($values);
        }
    }
    $uniqueValues = array_unique($adminValues);
    return array_diff_assoc($adminValues, $uniqueValues);
}

/**
 * Set message to response object
 *
 * @param DataObject $response
 * @param string[] $messages
 * @return DataObject
 */
private function setMessageToResponse($response, $messages)
{
    $messageKey = $this->getRequest()->getParam('message_key', static::DEFAULT_MESSAGE_KEY);
    if ($messageKey === static::DEFAULT_MESSAGE_KEY) {
        $messages = reset($messages);
    }
    return $response->setData($messageKey, $messages);
}

/**
 * Performs checking the uniqueness of the attribute options.
 *
 * @param DataObject $response
 * @param array|null $options
 * @return $this
 */
private function checkUniqueOption(DataObject $response, array $options = null)
{
    if (is_array($options)
        && isset($options['value'])
        && isset($options['delete'])
        && !empty($options['value'])
        && !empty($options['delete'])
        && !empty($duplicates)
    ) {
        $duplicates = $this->isUniqueAdminValues($options['value'], $options['delete']);
        if (!empty($duplicates)) {
            $this->setMessageToResponse(
                $response,
                [__('The value of Admin must be unique. (%1)', implode(', ', $duplicates))]
            );
            $response->setError(true);
        }
    }
    return $this;
}

/**
 * Check that admin does not try to create option with empty admin scope option.
 *
 * @param DataObject $response
 * @param array $optionsForCheck
 * @return void
 */
private function checkEmptyOption(DataObject $response, array $optionsForCheck = null)
{
    foreach ($optionsForCheck as $optionValues) {
        if (isset($optionValues[0]) && $optionValues[0] == '') {
            $this->setMessageToResponse($response, [__("The value of Admin scope can't be empty.")]);
            $response->setError(true);
        }
    }
}
}
  • It's not working. Did you checked from your side? – Niket Oct 14 '19 at 10:04
  • What do you need to overwrite in checkUniqueOption() ? – aravind Oct 14 '19 at 10:11
  • Need to add one condition with !empty($duplicates) condition – Niket Oct 14 '19 at 10:12
  • How can i check whether it's working or not? – aravind Oct 14 '19 at 10:14
  • Could you explain the test case? – aravind Oct 14 '19 at 10:14

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.