Redirect Simple Products to their Configurable Parent with Attributes Pre-selected in Magento 2

About two years ago I wrote a tutorial on how to redirect simple products to their configurable parents with pre-selected attributes in Magento 1.9.x. Shortly after that Magento 2 was released and I’ve received some requests on how to do the same in the new version.

In this post I will explain to you how to build a module that will redirect simple products to their configurable parent product with automatically pre-selected configurable attributes.

[Update June 18th, 2020] Due to many comments that the code in this tutorial doesn’t work anymore, I’ve tested it with Magento 2.3.5-p1 and can verify that it still works.

If it doesn’t work, then another module must be messing with your configuration.

Don’t feel like doing the work? Download the extension from Github. Feel free to fork its repository and add your own features!

Building a Basic Magento 2 Module

First we need to create the basic files and folders necessary for the module to be recognized by Magento 2.

  1. Create the file /app/code/DaanvdB/RedirectSimpleProducts/registration.php and add the following snippet of code to it:
    <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'DaanvdB_RedirectSimpleProducts',
    __DIR__
    );
  2. Create the file /app/code/DaanvdB/RedirectSimpleProducts/etc/module.xml and add the following code to it:
    <?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="DaanvdB_RedirectSimpleProducts" setup_version="1.0.0" />
    </config>
    view raw module.xml hosted with ❤ by GitHub

Run php bin/magento setup:upgrade from your terminal so Magento 2 will update and add your new module to its configuration.

Redirecting Simple Products to their Configurable Parent using an Observer

In order to redirect simple products to their configurable parent, we need to create an observer that hooks into an event which is triggered before the catalog_product_view is requested. The best event for this observer is controller_action_predispatch_catalog_product_view.

First we create our custom Observer-class in /app/code/DaanvdB/RedirectSimpleProducts/Observer/Predispatch.php and add the following code (inspired by StackOverflow):

<?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);
}
}
}
view raw Predispatch.php hosted with ❤ by GitHub

The Observer we just created takes care of the entire process:

  1. At first it checks if we’re on a catalog_product_view-page,
  2. Then it checks if the current requests is a simple product,
  3. If so, it finds it’s corresponding configurable parent and loads all available configurable attributes,
  4. Then it takes the values for each configurable attribute from the simple product’s properties and builds an options array with them,
  5. With this array it builds a query using Magento 2’s integrated URL parsing functionality.
  6. The created query is appended to the configurable products’ URL-key and a Redirect is set.

Adding the Observer-class to an event

To trigger our custom Observer when the earlier mentioned event is triggered, we need to create a file called events.xml.

Create the file /app/code/DaanvdB/RedirectSimpleProducts/etc/events.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="controller_action_predispatch_catalog_product_view">
<observer name="daanvdb_redirectsimple_products_observer_predispatch" instance="DaanvdB\RedirectSimpleProducts\Observer\Predispatch"/>
</event>
</config>
view raw events.xml hosted with ❤ by GitHub

The events.xml takes care of triggering our custom observer whenever the controller_action_predispatch_catalog_product_view is called.

That’s it! It is this simple to create a module that redirects simple products to their configurable parent product with pre-selected configurable attributes. Make sure you empty your cache (php bin/magento cache:flush) after you’ve copied the files to your site. Enjoy!

❤️ it? Share it!

76 thoughts on “Redirect Simple Products to their Configurable Parent with Attributes Pre-selected in Magento 2”

    1. Thank you for your donation, Alex! 🙂

      That’s an interesting question! However, I do think this will make the module much more extensive. If it’s for SEO purpose, I’d suggest you rewrite the canonical URL to the single product’s URL.

      If you’re interested, I could write a tutorial soon on how to do this.

      1. Hi,

        First, Great tutorial ! It will certainly help managing interaction between simple product and configurable.

        Can you please explain why rewriting canonical URL in configurable will help for SEO?

        Don’t you think it will create duplicate contents?

        Best regards,

        Fx

      2. Thank you very much for this outstanding tutorial. It works like a charm.
        Isn’t it also possible to load the parent product by child url and preselect the options then?
        If possible then send me details how can do this.

  1. Hello Daan,
    Your code works great but it did not update the image of selected options ,it always loads
    main configurable product image.
    Can you help me in this.

    Thanks

    Ram Singh

  2. Hi Daan,

    This is great! One thing though is the image of the pre-selected swatch doesn’t always show as the main image, it often shows as the image of the configurable product.
    Do you know how I can make sure the simple product image is shown?

    Thanks

      1. Hi Daan,

        Great post and works very well too. As few others have mentioned the product image always defaults to the main image and doesn’t show the image of the selection option. There are no errors in the console I can confirm.

        I’m on Magento ver. 2.3.0

        Thanks.

          1. Hi Daan, thank you for this tutorial.
            I have the same issue too. No errors in JS console, the image selected is the configurable image’s.

      2. Hi Daan,

        Thanks for getting back to me! I have been through that but it has stopped the functionality of the pre-selected swatch and gives this error in the console:

        this.processUpdateBaseImage is not a function

        Thanks

          1. Hi Daan,

            I can confirm both Dave and Attique’s issue: image isn’t updating, no error in console and the processUpdateBaseImage-error after using your tip.

            Have you ever considered looking into this issue? I’m currently on 2.3.1.

  3. Awesome working almost fine. but I am getting the issue with multiple attribute option with swatch. When we have multiple attribute options that time only first attribute value is selected. But I want all the options should be selected as pre-selected.

      1. No. But I am getting “TypeError: gallery is undefined” error in swatch-renderer.js file.
        When I checked the error then I get the error in gallery.updateData(imagesToUpdate); line.

        1. Try adding the following above that line:


          if (gallery === 'undefined) {
          return;
          }

          If that fixes it, then follow the guide I told you, but instead add this code to your override.

          1. Now it works. But now I have found out a new issue. When the child product’s visibility set as “Not Visible Individually” then the event not able to call and its redirect to 404 pages not found page.

            But if I save the visibility as Catalog, Catalog Search then its work fine. Even after save the child product as Catalog Search and then set it Not Visible Individually then also its work.

          2. Awesome!

            Best thing to do is to set visibility to ‘catalog’ and not add the configurable product to any categories. This way only your simple products are visible in your frontend and they still redirect to the configurable’s page.

  4. Hi Dan,

    Firstly thanks for this great post, it’s what I am looking for.
    Unfortunately for me I’m getting this error

    Fatal error: Uncaught Error: Class ‘DaanvdB\RedirectSimpleProducts\Observer\Predispatch’ not found in /home1/x81jbbzn/public_html/beta/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php:93 Stack trace: #0

    Here’s my site

    http://idealsupps.com/beta/optimum-nutrition-zma.html

    Any idea how I can fix this.

    Kind Regards

    1. Have you flushed your cache? Is your store in Developer mode or Production mode?

      In developer mode:
      – run bin/magento setup:upgrade
      – run bin/magento ca:fl
      – empty generated-folder

      In production mode:
      – run bin/magento setup:upgrade
      – run bin/magento setup:di:compile
      – run bin/magento setup:static-content-deploy (do not forget to add your storecodes seperately)
      – run bin/magento ca:fl

      1. Thanks for your quick reply.

        Yes, I tried to empty the cash.

        I was on default mode, I switched it to developer mode and followed your steps, except this “empty generated-folder” not sure which folder.

        Still getting the same error.

        1. You must’ve made a mistake somewhere. Check if you entered the namespace correctly in the Observer/Predispatch.php. Check if you entered the path to the file correctly in the etc/events.xml. Check if the folder structure is correct.

          1. This is not my proudest moment but I had a typo, Daanvdb instead of DaanvdB.
            It loads now but it doesn’t have the attribute pre-selected on the product page.
            I will check this link that you posted – https://daan.dev/how-to/uncaught-typeerror-updatedata-undefined/ and let you know how I get one.

            The console error:
            *******************************************
            Uncaught DOMException: Blocked a frame with origin “http://idealsupps.com” from accessing a cross-origin frame.
            at contents (http://idealsupps.com/beta/pub/static/version1562149260/frontend/TemplateMonster/theme063/en_US/jquery.js:3123:47)

            *******************************************

            Thanks for all the help, I was spending hours trying to figure it out last night.

          2. Haha, that’s fine. It happens to the best of us!

            As you can see in the console error, another resource is trying to make a call towards another origin. (cross-origin). This means the exception is probably caused by something than the module you just created. This module doesn’t make any cross-origin calls.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Shopping Cart
  • Your cart is empty.