1

I'm trying to override a function in the Helper of the Swatches module of Magento 2. The function resides in the Data.php file. As some of you may know in specific versions of Magento 2 the text swatch values fall back to the default Admin value due to a bug.

A fix was issued here: https://github.com/magento/magento2/pull/15960

I am trying to implement this fix by overriding the function in Data.php using preference.

Since the Porto theme that I'm using is already overriding some other functions I decided to add my override there.

app/code/Smartwave/Porto/etc/di.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Smartwave\Porto\Block\Template">
    <arguments>
        <argument name="customTemplate" xsi:type="array">
            <item name="context" xsi:type="string">\Magento\Framework\View\Element\Template\Context</item>
            <item name="registry" xsi:type="string">\Magento\Framework\Registry</item>
        </argument>
    </arguments>
</type>
<type name="Smartwave\Porto\Block\CategoryCollection">
    <arguments>
        <argument name="categoryCollection" xsi:type="array">
            <item name="context" xsi:type="string">\Magento\Framework\View\Element\Template\Context</item>
            <item name="helper" xsi:type="string">\Magento\Catalog\Helper\Category</item>
            <item name="flatstate" xsi:type="string">\Magento\Catalog\Model\Indexer\Category\Flat\State</item>
            <item name="menu" xsi:type="string">\Magento\Theme\Block\Html\Topmenu</item>
        </argument>
    </arguments>
</type>
<preference for="Magento\Catalog\Controller\Category\View" type="Smartwave\Porto\Controller\Category\View" />
<preference for="Magento\CatalogSearch\Controller\Result\Index" type="Smartwave\Porto\Controller\CatalogSearch\Result\Index" />
<preference for="Magento\Catalog\Helper\Product\View" type="Smartwave\Porto\Helper\Product\View" />
<preference for="Magento\Swatches\Helper\Data" type="Smartwave\Porto\Helper\Swatches\Data" />

As you can see I added the preference on the last line.

app/code/Smartwave/Porto/Helper/Swatches/Data.php

namespace Smartwave\Porto\Helper\Swatches;

class Data extends \Magento\Swatches\Helper\Data
{

/**
 * @param array $fallbackValues
 * @param array $swatches
 * @return array
 */
private function addFallbackOptions(array $fallbackValues, array $swatches)
{
    $currentStoreId = $this->storeManager->getStore()->getId();
    foreach ($fallbackValues as $optionId => $optionsArray) {
        if (isset($optionsArray[$currentStoreId]['type'], $swatches[$optionId]['type'])
            && $swatches[$optionId]['type'] === $optionsArray[$currentStoreId]['type']
        ) {
            $swatches[$optionId] = $optionsArray[$currentStoreId];
        } elseif (isset($optionsArray[$currentStoreId])) {
            $swatches[$optionId] = $optionsArray[$currentStoreId];
        } elseif (isset($optionsArray[self::DEFAULT_STORE_ID])) {
            $swatches[$optionId] = $optionsArray[self::DEFAULT_STORE_ID];
        }
    }

    return $swatches;
}

}

However this doesn't seem to work. Is there anyone that can tell me why this is the case.

5
  • Check first any module already overwrite this class or not. Also, this is a public method, so try to use plugin instead of preference – Sohel Rana Jan 9 '20 at 17:42
  • This wouldn't be the best approach. You should look into using a composer patch to apply the commit from the pull request you referenced. devdocs.magento.com/guides/v2.3/comp-mgr/… – wesleywmd Jan 9 '20 at 20:05
  • @SohelRana Thank you for your reply. The function is actually private, I missed that and have now corrected it. However it still does not work. What are the conditions which influence the use of either a plugin or preference? – Hendrik M. Jan 10 '20 at 15:29
  • @wesleywmd Thanks for your reply. The PR was actually proposed for M2.2 but it seems that it wasn't accepted even up to 2.3.0 because that's what I'm running. I won't be able to use this PR, I'll see if I can find when they actually accepted it. – Hendrik M. Jan 10 '20 at 15:39
  • You can't do this way. Check my answer, that should work for you. – Sohel Rana Jan 10 '20 at 15:41
0

Your class should be:

use Magento\Swatches\Model\Swatch;

class Data extends \Magento\Swatches\Helper\Data
{
    /**
     * @var array
     */
    private $swatchesCache = [];

    /**
     * @inheritDoc
     */
    public function getSwatchesByOptionsId(array $optionIds)
    {
        $swatches = $this->getCachedSwatches($optionIds);

        if (count($swatches) !== count($optionIds)) {
            $swatchOptionIds = array_diff($optionIds, array_keys($swatches));
            /** @var \Magento\Swatches\Model\ResourceModel\Swatch\Collection $swatchCollection */
            $swatchCollection = $this->swatchCollectionFactory->create();
            $swatchCollection->addFilterByOptionsIds($swatchOptionIds);

            $swatches = [];
            $fallbackValues = [];
            $currentStoreId = $this->storeManager->getStore()->getId();
            foreach ($swatchCollection as $item) {
                if ($item['type'] != Swatch::SWATCH_TYPE_TEXTUAL) {
                    $swatches[$item['option_id']] = $item->getData();
                } elseif ($item['store_id'] == $currentStoreId && $item['value'] != '') {
                    $fallbackValues[$item['option_id']][$currentStoreId] = $item->getData();
                } elseif ($item['store_id'] == self::DEFAULT_STORE_ID) {
                    $fallbackValues[$item['option_id']][self::DEFAULT_STORE_ID] = $item->getData();
                }
            }

            if (!empty($fallbackValues)) {
                $swatches = $this->addFallbackOptions($fallbackValues, $swatches);
            }
            $this->setCachedSwatches($swatchOptionIds, $swatches);
        }

        return array_filter($this->getCachedSwatches($optionIds));
    }

    /**
     * @inheritDoc
     */
    private function addFallbackOptions(array $fallbackValues, array $swatches)
    {
        $currentStoreId = $this->storeManager->getStore()->getId();
        foreach ($fallbackValues as $optionId => $optionsArray) {
            if (isset($optionsArray[$currentStoreId]['type'], $swatches[$optionId]['type'])
                && $swatches[$optionId]['type'] === $optionsArray[$currentStoreId]['type']
            ) {
                $swatches[$optionId] = $optionsArray[$currentStoreId];
            } elseif (isset($optionsArray[$currentStoreId])) {
                $swatches[$optionId] = $optionsArray[$currentStoreId];
            } elseif (isset($optionsArray[self::DEFAULT_STORE_ID])) {
                $swatches[$optionId] = $optionsArray[self::DEFAULT_STORE_ID];
            }
        }

        return $swatches;
    }

    /**
     * @inheritDoc
     */
    private function getCachedSwatches(array $optionIds)
    {
        return array_intersect_key($this->swatchesCache, array_combine($optionIds, $optionIds));
    }

    /**
     * @inheritDoc
     */
    private function setCachedSwatches(array $optionIds, array $swatches)
    {
        foreach ($optionIds as $optionId) {
            $this->swatchesCache[$optionId] = isset($swatches[$optionId]) ? $swatches[$optionId] : null;
        }
    }
}
2
  • Becareful with this syntax isset($swatches[$optionId]) ? $swatches[$optionId] : null. This can cause performance impacts in certain cases because you are evaluating the array twice. The performance isn't impacted as much here since its only pulling an id from the array but in cases where you pull an object from an array can be a much larger impact. You should use $swatches[$optionId] ?? null instead. Plus its a bit easier to read. – wesleywmd Jan 11 '20 at 21:04
  • @wesleywmd, guys I just copied this private method from original class. I know that, thanks. – Sohel Rana Jan 12 '20 at 5:53

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.