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.
- Create the file
/app/code/DaanvdB/RedirectSimpleProducts/registration.phpand add the following snippet of code to it:This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'DaanvdB_RedirectSimpleProducts', __DIR__ ); - Create the file
/app/code/DaanvdB/RedirectSimpleProducts/etc/module.xmland add the following code to it:This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters<?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>
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); | |
| } | |
| } | |
| } |
The Observer we just created takes care of the entire process:
- At first it checks if we’re on a catalog_product_view-page,
- Then it checks if the current requests is a simple product,
- If so, it finds it’s corresponding configurable parent and loads all available configurable attributes,
- Then it takes the values for each configurable attribute from the simple product’s properties and builds an
optionsarray with them, - With this array it builds a query using Magento 2’s integrated URL parsing functionality.
- 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> |
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!
Hi Daan!
Thank you very much for this outstanding tutorial. It works like a charm.
But there is one question leaving. The url simply redirects than to the parent product. Isn’t it also possible to load the parent product by child url and preselect the options then?
So that the url would not be https://mywebsite.de/socks#123=1&456=2 but https://mywebsite.de/socks-red-size-45
Kind regards,
Alex
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.
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
Could be. I never thought about this, actually. Thanks for the heads up! Something to look into!
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.
Could be I’m not understanding you correctly, but loading the parent product using the simple (child) url is exactly what this extension does.
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
Hi Ram,
Have you tried applying the fix in this post?
It’s usually due to a bug in the swatch renderer.
Let me know if it worked out for you!
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
Are you by any chance having an error in your console, which looks like this?
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.
It’s strange. I didn’t have this issue, when I built it. It might be theme related.
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.
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
Really? Which version of Magento are you running? I last tested this on 2.2.6, I think. Perhaps the file changed since then.
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.
Hi Dug,
You’re right. I really should revise this post. I’ll add it to my to-do list!Done!
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.
Are you by any chance having an error in your console, which looks like this?
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.
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.
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.
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.
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
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
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.
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.
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.
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.
What if only params in the request are changed like id and options instead of redirecting
I’m not sure I understand what you mean. Can you clarify?
I mean to set request parameter in controller_action_predispatch_catalog_product_view for example if parent product id is 345.
$this->getRequest()->setParam(‘id’,345);
Then in product controller, it will automatically get parent product 345.
$productId = (int) $this->getRequest()->getParam(‘id’);
Hello
Can we get simple product id without using request object
$simpleProductId = $request->getParam(‘id’);
Thank You
My simple product urls are like
paardrijbroek-kingsland-katja-pull-on-full-grip+vendit_size-42+vendit_color-Grey~Forged~Iron.html
is there anyway to detect it as a simple product of config product an redirect to parent
Daan,
Very interesting, thanks for sharing. I am wondering if the redirect can also be achieved to bundle products?
Chris
Is there no magento2 equivalent to catalogSession->setSuperAttributes? Patching the the URL seems somewhat inelegant.
Not that I know of.
Hello Daan can you upload to Github for install directly via composer to magento 2.3.3 ??
I’ve been using your code for magento 1 for years
Would it be possible to do it with github?
That way we would have the code updated if you put in any updates or patches.
Hi Mariano,
I just did! You can find the repository here.
Hello,
Does this work for Magento 2.3.4 ? I have tried it over and over again, nothing changed !
Hi Mohammed,
I’m not sure. I haven’t tested or used it in a while. I should put the code on Github and allow people to make forks. To stay up-to-date.It works on M2.3.5-p1. You can find the Github Repository here.
Hi Mohammed,
Wanted to let you know that I just tested it with M2.3.5-p1 and it still works.
Hey Daan,
I just wanted to start off by thanking you for this tutorial, its definitely a lifesaver.
However, I am getting a similar issue as some of the other folks. Clicking on a child listing is linking correctly to the parent listing but it is not selecting the variation on load.
What I can do to get it to also select the variation that the customer clicks on.
I am running on CentOS 8 + Magento 2.3.5-P1
I am not receiving the error in my console: “Uncaught TypeError: Cannot read property ‘updateData’ of undefined”
The error that I am getting is: “JQMIGRATE: jQuery.attrFn is deprecated”
Thanks so much!
Jay
Hi Daan,
Nice tutorial!!, is the same method possible for configurable product listing to configurable product details page?
I don’t think so. From the logic in this article, there is a certain selection made. We can detect, from the simple product clicked in the product listing, which attributes to select. How would we do that if the configurable product is clicked?
if we add one of the attribute in the product url, when you click the url you can pass that attribute.
Hey Daan, I did some research and I think I may have found the issue causing the variations to not be selected when redirected on 2.3.5p. I think its the new CSP introduced to protect users from cross-site scripting. Here is the docs for it: https://devdocs.magento.com/guides/v2.3/extension-dev-guide/security/content-security-policies.html
Would you please kindly take a quick look for me? I would really appreciate it.
Best Regards,
Jay
Hi Jay,
I’ve tested it with M2.3.5-1 and with Magento_Csp disabled. It works fine. I don’t see why a Content Security Policy would interfere with its functioning, though.
Hi Daan
Trying your script as you advised, however i get an error: (running magento 2.3.5-p1)
ReflectionException: Class Magento\Framework\App\Http\Interceptor does not exist in /home/path/public_html/path/magento2/vendor/magento/framework/Code/Reader/ClassReader.php:26 Stack trace: #0 /home/path/public_html/path/magento2/vendor/magento/framework/Code/Reader/ClassReader.php(26): ReflectionClass->__construct(‘Magento\\Framewo…’) #1 /home/path/public_html/path/magento2/vendor/magento/framework/ObjectManager/Definition/Runtime.php(54): Magento\Framework\Code\Reader\ClassReader->getConstructor(‘Magento\\Framewo…’) #2 /home/path/public_html/path/magento2/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(48): Magento\Framework\ObjectManager\Definition\Runtime->getParameters(‘Magento\\Framewo…’) #3 /home/path/public_html/path/magento2/vendor/magento/framework/ObjectManager/ObjectManager.php(56): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create(‘Magento\\Framewo…’, Array) #4 /home/path/public_html/path/magento2/vendor/magento/framework/App/Bootstrap.php(235): Magento\Framework\ObjectManager\ObjectManager->create(‘Magento\\Framewo…’, Array) #5 /home/path/public_html/path/magento2/index.php(38): Magento\Framework\App\Bootstrap->createApplication(‘Magento\\Framewo…’) #6 {main}
Thanks in advance for any help.
I’ve also tried all the steps above in developer mode and production, i get error “This page isn’t working”
You did run setup:upgrade, etc. after creating the module? Because the class does exist in 2.3.5-p1. I just checked.
Did you run
bin/magento setup:upgrade?I tried this tutorial but how it works?
Hi, this is now working great on our Magento 2.3.3 site. However it gives new problem in tracking of google merchant. We provide utm for url of simple products to merchant by adding to url “utm_source=google&utm_medium=cpc&utm_campaign=example’
So example:
Normal url: https://oursite.com/tacx-shiva-bidon-wit-500-ml.html
To google: https://oursite.com/tacx-shiva-bidon-wit-500-ml.html?utm_source=google&utm_medium=cpc&utm_campaign=feedsimple
But with the extension it will create url: https://oursite.com/tacx-shiva-bidon.html#93=397&293=4099
So now problem with tracking of campaigns.
So we would like to keep the ‘utm’ extension we give. Would this be possible in any way?
Definitely. But it’ll need some adjustments in the code. Just keep in mind that you’ll need to modify the output url as follows: https://yoursite.com/product-url.html?utm_source=google&other_utm_params=something_else#93=397&293=4099.
I.e. the parameters after the hashtag (#) need to be added to the URL last, since everything after the # is client side, i.e. not accessible by the server.
Hi, works great. Thanks. However, it gives us an issue with adwords tracking. We add to the url utm_source=google&utm_medium=cpc&utm_campaign=aa
Now when redirect simple product it will redirect to something like #93=403&156=1604
and the utm code will be lost.
Is there any way to make sure we keep the utm code when redirecting with the extension?
Hi Emile,
You already left the same comment earlier. And I replied. 🙂
Ah, my bad. Sorry. Got a reminder from my adwords agency and kind of forgot
Hello Daan,
How same thing can be do in the search result page ?
We need to redirect same simple product to configurable product with pre selected option from search result page.
Let us know how can do it.
Thanks.
This extension has been a life saver and I can’t thank you enough for sharing it!
Unfortunately, I’m updating to M2.4.1 and the redirects are not triggered anymore.
Tested on a fresh install with Luma theme, the old link pattern is still working, it means the pre-selected options are being automatically selected when you add the options at the end of the urls, like those ones (#157=272&93=52)
But the only issue is the reductions are not working from simple products urls to their parent product.
Are you planning to update your code for Magento 2.4.1?
Thank you
Hi Dan,
Thanks for notifying me. I’ll put it on my todo list.
It’s working again but I cannot really explain how. I changed most of Magento default settings to make the new store ready to be used (canonical, flat catalog, .html etc…). Now it is working without any issue. It looks fully compatible with Magento 2.4.1.
Thanks again
Hello, very nice extension! I install it magento 2.4.1. the only thing is not working right is that open the configurable product page with preselected options but with Review tab open and not Description as it is default.
Hi guys,
For anyone still got the issue image not changed and no errors in console. Here is the fix I figured out:
Add these files to your module:
app/code/Vendor/Module/view/frontend/requirejs-config.js
********************************************************
var config = {
config: {
mixins: {
‘Magento_ConfigurableProduct/js/configurable’: {
‘Fgc_Simple2Configurable/js/configurable-mixin’: true
},
},
},
};
********************************************************
app/code/Vendor/Module/view/frontend/web/js/configurable-mixin.js
********************************************************
define([‘jquery’], function ($) {
‘use strict’;
return function (targetWidget) {
$.widget(‘mage.configurable’, targetWidget, {
_onGalleryLoaded: function (element) {
this._super(element);
this._changeProductImage();
},
_changeProductImage: function () {
this._super();
}
});
return $.mage.configurable;
};
});
********************************************************
Clear the cache, redeploy static files and check it out!
Thnx Hieu!
I’ll add this solution to the post! 🙂
Does this still work with Magento 2.4.1? I tried the tutorial and created the Module, but my simple Products still generates 404 Errors.
Hi Daan,
First thank you for this code. I tested and works fine on magento 2.3.6. The only problem that keep me to put it on production website is the next one:
I need to improve this in order to be able to show reviews from the parent product to all his child.
Because the way is made now I lose the possibility to show reviews of the simple product in category list.
Now is impossible to give reviews for simple products and only for his parent.
Because I use color variation the characteristics of the products are the same so if someone give 5 stars to one color I want to show that review to all the children that share the same parent.
How can I achieve that, can you help?
Thanks!
Is the this working working for magento opensource 2.4.2 please? We tried and had a couple of issues.
Hey Daan,
Nice tutorial!
I have installed the plugin in Magnto 2.4. It seems like not working. My simple products redirect to it’s unique page still. Not to the parent product.
No errors in the logs or console.
Any idea to fix this?
Hello Daan,
your module works perfectly wit Magento 2.4.1!
Thank you !!
Chiara
Hi
I have query here ..?
Please let me know how to come over this
EX: One simple product which is assigned to multiple configurable or bundle product ,So in this case i will get two parent product right then i can redirect …?
How would the module determine to which parent product it should redirect?
Daan
FYI we upgraded to 2.4.5-p1 and your module still functions as it should
Many thanks
Awesome! Good to hear, and thanks for the feedback!
Unfortunately this no longer seems to work as of 2023.
Sorry to hear that. A few weeks ago, someone else reported it still works in 2.4.5-p1. So, something else might be going on?