3

How can I redirect a SEO URL Key of a Simple product of which the visibility is set to "Not Individually Visible" to the parent Configurable Product SEO URL Key?

There are two important conditions that needs to be met:

  • the options on the Configurable Product page should be correctly preselected to display the attributes of the Simple product.

  • the price for the Single Product also needs to be displayed correctly.

Example

Configurable Product P1 has two attributes. Each attribute has two options:

Attribute A1 has option O1 and O2

Attribute A2 has option O3 and O4

So Configurable Product P1 has 4 Configurations represented by 4 Simple Products with each a different price:

P2: O1 and O3 -> EUR 1

P3: O1 and O4 -> EUR 2

P4: O2 and O3 -> EUR 3

P5: O2 and O4 -> EUR 4

The problem that needs to be solved is that for each Configuration the parent Configurable Product URL Key is sent to Google Merchant Shopping as an XML feed.

But when Google is verifying the prices of the Configurations on the parent Configurable Product URL page the prices won't match for P3, P4 and P5 as by default the lowest price is shown by Magento. And this in turn will result in the following warning in Google Merchant:

"Insufficient match of microdata price information". In the above example 75% of the data isn't matching.

I already took a look at How redirect simple products of Configurable product but it is referring to an event which to my understanding does not exists in Magento 2: controller_action_predispatch_catalog_product_view.

I also tried the suggestions at Magento2 Event Observer Redirect Simple Product to Configurable Product but it seems that the Plugin can only work if the product visibility is not set to "Not Individually Visible".

So I suspect that the canShow method defined in Magento/Catalog/Helper/Product.php is called before the Magento/Catalog/Controller/Product/View is executed but I have an hard time figuring out where this happens.

How does Magento determine that an URL key is representing a product?

And where are the product id, category id and options parameters derived from the URL key to be passed on to the execute method in Magento/Catalog/Controller/Product/View.php?

Any help would be much appreciated.

1

You can use a plugin to achieve that. In your module's etc/di.xml file write the plugin declaration

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Product\Url">
        <plugin name="My_Extension_Product_Redirect" type="Vendor\Module\Plugins\Url" />
    </type>
</config>

And then in the Vendor/Module/Plugins/Url.php file you can use a before plugin like so

<?php

namespace Vendor\Module\Plugins;
use Magento\Catalog\Model\ProductRepository;
use \Magento\ConfigurableProduct\Model\Product\Type\Configurable;

class Url{

    protected $_configurableProductInstance;
    protected $_productRepository;

    function __construct(
        Configurable $configurable,
        ProductRepository $productRepository,
    )
    {
        $this->_configurableProductInstance = $configurable;
        $this->_productRepository = $productRepository;
    }

    public function beforegetProductUrl(\Magento\Catalog\Model\Product\Url $subject, \Magento\Catalog\Model\Product $product, $useSid)
    {
        $parentId = $this->_configurableProductInstance->getParentIdsByChild($product->getId());
        if(count($parentId)){
            return [$this->_productRepository->getById($parentId[0]), $useSid];
        }
        return null;
    }
}
  • "How does Magento determine that an URL key is representing a product?" This link may be helpful to understand URL rewrites in Magento – Jay Ghosh Sep 12 '17 at 5:26
  • 1
    I believe plugin method name should be beforeGetProductUrl, not beforegetProductUrl (G letter needs to be Capitalised) – Macas Feb 1 '18 at 9:33
  • 1
    @Macas At the time of answering this, both beforegetProductUrl and beforeGetProductUrl seemed to work. But in Magento 2.2.2, it seems only beforeGetProductUrl would work – Jay Ghosh Feb 2 '18 at 9:45
  • You will not be happy with that solution... Cause Google will reject your products because the price in source code will not match with the price in your feed. – roman204 Feb 14 '18 at 10:20
0

Came across this question and see you haven't accepted an answer yet. I wrote a tutorial on this a while back: https://dev.daanvandenbergh.com/how-to/redirect-simple-products-configurable-parent-attributes-pre-selected/

I'm not going to copy-paste the entire tutorial here, but basically you can take care of it using only an observer:

<?php
namespace DaanvdB\RedirectSimpleProducts\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class Predispatch implements ObserverInterface {

    protected $_redirect;

    protected $_productTypeConfigurable;

    protected $_productRepository;

    protected $_storeManager;

    public function __construct (
        \Magento\Framework\App\Response\Http $redirect,
        \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $productTypeConfigurable,
        \Magento\Catalog\Model\ProductRepository $productRepository,
        \Magento\Store\Model\StoreManagerInterface $storeManager
    ) {
        $this->_redirect = $redirect;
        $this->_productTypeConfigurable = $productTypeConfigurable;
        $this->_productRepository = $productRepository;
        $this->_storeManager = $storeManager;
    }

    public function execute(Observer $observer)
    {
        $pathInfo = $observer->getEvent()->getRequest()->getPathInfo();
        /** If it's not a product view we don't need to do anything. */
        if (strpos($pathInfo, 'product') === false) {
            return;
        }
        $request = $observer->getEvent()->getRequest();
        $simpleProductId = $request->getParam('id');
        if (!$simpleProductId) {
            return;
        }
        $simpleProduct = $this->_productRepository->getById($simpleProductId, false, $this->_storeManager->getStore()->getId());
        if (!$simpleProduct || $simpleProduct->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) {
            return;
        }
        $configProductId = $this->_productTypeConfigurable->getParentIdsByChild($simpleProductId);
        if (isset($configProductId[0])) {
            $configProduct = $this->_productRepository->getById($configProductId[0], false, $this->_storeManager->getStore()->getId());
            $configType = $configProduct->getTypeInstance();
            $attributes = $configType->getConfigurableAttributesAsArray($configProduct);
            $options = [];
            foreach ($attributes as $attribute) {
                $id = $attribute['attribute_id'];
                $value = $simpleProduct->getData($attribute['attribute_code']);
                $options[$id] = $value;
            }
            $options = http_build_query($options);
            $hash = $options ? '#' . $options : '';
            $configProductUrl = $configProduct->getUrlModel()
                    ->getUrl($configProduct) . $hash;
            $this->_redirect->setRedirect($configProductUrl, 301);
        }
    }
}

Hope it'll help you or anyone out.

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.