This tutorial will demonstrate some functionality specific to the Configurable product type. This post complements the overview of this product type, which you can find in my previous posts: Magento Configurable Product Type (Part 1) and Magento Configurable Product Type (Part 2).
I will lead you through developing a simple Magento extension that will make a small change in the way configurable products are presented in the front-end. Normally, when you open a product detail page of a configurable product its options are not selected. Because of this a customer is unable to add the product to cart immediately. We are going to load configurable products with their options already pre-selected. The test shop the extension is developed for runs sample product data, which you can download here.
Before we start, take a look at the screenshot of a product page. This T-shirt has an option “size” and after the page is loaded the drop-down list has no selected value:
First step is to create a basic directory structure for our extension. I will put it into the local code pool, package “Solvingmagento”. The name of the extension is “ConfigurablePreSelect”.
app -code -local -Solvingmagento -ConfigurablePreSelect -Model -etc
We also have to add an XML file Solvingmagento_ConfigurablePreSelect.xml to the /app/etc/modules directory. Its content is standard:
<?xml version="1.0"?> <config> <modules> <Solvingmagento_ConfigurablePreSelect> <active>true</active> <codePool>local</codePool> </Solvingmagento_ConfigurablePreSelect> </modules> </config>
Listing 1. Module registration file
How are we going to pre-select configurable attributes? The selection of the attributes is controlled by the spConfig Javascript object. If you supply it with property defaultValues then the script will automatically select respective items in the option drop-down lists.
The spConfig uses configuration data generated by block class Mage_Catalog_Block_Product_View_Type_Configurable. Its function getJsonConfig goes through all the configurable settings of a product and compiles an object that is then passed to the front-end Javascript. This process has been discussed in more details in the first part of the Configurable product type overview. In the first lines of this function the system checks if the current product has pre-configured values:
$preconfiguredFlag = $currentProduct->hasPreconfiguredValues(); if ($preconfiguredFlag) { $preconfiguredValues = $currentProduct->getPreconfiguredValues(); $defaultValues = array(); }
Listing 2. Checking product for preconfigured values, /app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php, line 136.
Pre-configured values can be an instance of the Varien_Object class with a super_attribute property. This property should contain an array with keys corresponding to configurable attribute IDs and elements containing values for these attributes.
We are going to inject this preconfigured_values property into every product object of configurable type whenever a product detail page is displayed. For this purpose we are going to listen to an event dispatched after a product object is initiated. This event is catalog_controller_product_init_after and it is dispatched by function Mage_Catalog_Helper_Product::initProduct, which itself is called when a product detail view is generated. In our observer we will have to check if we are on a detail page, because this event can also be dispatched from pages generated by Mage_Wishlist and Mage_Checkout modules.
Let’s add the observer configuration to the config.xml file of our module:
<config> ... <frontend> ... <events> <catalog_controller_product_init_after> <observers> <solvingmagento_configurablepreselect> <class>solvingmagento_configurablepreselect/observer</class> <method>preSelectConfigurable</method> </solvingmagento_configurablepreselect> </observers> </catalog_controller_product_init_after> </events> ... </frontend> ... </config>
Listing 3. Event-observer configuration in file config.xml.
Note that the event configuration is within the <frontend> node. This will restrict our observer to listening only to the front-end event. If this event is dispatched from the back-end, our observer will ignore it.
Since we are addressing the observer class by a configuration path solvingmagento_configurablepreselect/observer we must define it as well:
<config> ... <global> ... <models> <solvingmagento_configurablepreselect> <class>Solvingmagento_ConfigurablePreSelect_Model</class> </solvingmagento_configurablepreselect> </models> ... </global> ... </config>
Listing 4. Model configuration in file config.xml.
Now it is time to create the observer class. As it is often with observers, it does not need to extend any parent class. Important is, however, that our observer has a public function preSelectConfigurable, which we’ve defined earlier in the event-observer configuration.
Here are the first lines of this function:
class Solvingmagento_ConfigurablePreSelect_Model_Observer { public function preSelectConfigurable(Varien_Event_Observer $observer) { $product = $observer->getEvent()->getProduct(); $request = Mage::app()->getRequest(); $controller = $request->getControllerName(); $action = $request->getActionName(); $candidates = array(); } }
Listing 5. Getting product object.
First we get the product object passed by the event. We also take a request object and get the names of the current controller and action. These we will check to find out if we are indeed on a product detail page of a configurable product. We add this check after initializing an empty array $candidates, which we are going to use to fill with possible configurable options:
if (($action === 'view') && ($controller === 'product') && ($product->getTypeId() === 'configurable')) { }
Listing 6. Checking if the event is dispatched from a detail page of a configurable product.
Now what we are going to do is:
- get a list of the configurable attributes of the configurable product (it ca be only one attribute or many);
- get the first associated product, which is saleable;
- retrieve the associated product’s values for the configurable attributes and pack these attribute-values combinations into the $candidates array;
- set the $candidates array as pre-configured values for our configurable product.
We can get the list of configurable attributes by fetching a type instance of a configurable product and calling the getConfigurableAttributes method. The associated products are also returned by calling the type instance’s method getUsedProducts:
$configurableAttributes = $product->getTypeInstance(true) ->getConfigurableAttributes($product); $usedProducts = $product->getTypeInstance(true) ->getUsedProducts(null, $product);
Listing 7. Getting configurable attributes and associated products.
You may wonder, why in the call to the getUsedProducts function the first parameter is null. After all, shouldn’t we pass a list of configurable attributes there so that only associated products with attributes’ valid values are returned? Well, in this case it is unnecessary because the earlier call to method getConfigurableAttributes has already generated a list of child products. And these products are already filtered by the attributes required by our configurable. This list is now can be retrieved from the $product object’s property _cache_instance_products, which the product type instance does without querying the database again.
With attributes and products ready we can start a loop through associated products to find one that is saleable and get its values for configurable attributes:
foreach ($usedProducts as $childProduct) { if (!$childProduct->isSaleable()) { continue; } foreach ($configurableAttributes as $attribute) { $productAttribute = $attribute->getProductAttribute(); $productAttributeId = $productAttribute->getId(); $attributeValue = $childProduct->getData($productAttribute->getAttributeCode()); $candidates[$childProduct->getId()] = array($productAttributeId => $attributeValue); } break; }
Listing 8. Collecting values for every configurable attributes from the associated products.
The elements of the configurable attribute list are of type Mage_Catalog_Model_Product_Type_Configurable_Attribute, which is not the same as the class representing the product attribute, Mage_Catalog_Model_Resource_Eav_Attribute. We get attribute instances from the items:
$productAttribute = $attribute->getProductAttribute();
The ID and name of the configurable attribute we get from the $productAttribute object. We use the name, or attribute_code, to get the attribute’s values from an associated products At the end of the loop, the $candidates array is in a form that can be passed on as pre-configured values. The object to hold these values can be an instance of the Varien_Object class. We create such an instance and assign the $candidates array to its property super_attribute:
$preconfiguredValues = new Varien_Object(); $preconfiguredValues->setData('super_attribute', $candidates); $product->setPreconfiguredValues($preconfiguredValues);
Listing 9. Assigning pre-configured values to the product object.
Our function does not need to return anything. The product object is accessed by reference and the changes we mage are available to the code that does further processing.
Since the product now has pre-configured values the “preconfiguredFlag” from Listing 2 is set to true. Further in the getJsonConfig function these values a passed to the $defaultValues array, which is later converted into a property under the same name in the spConfig Javascript object in the front-end:
if ($preconfiguredFlag) { $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId); if ($configValue) { $defaultValues[$attributeId] = $configValue; } }
Listing 10. Assigning pre-configured values to the defaultValues array, /app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php, line 220.
That is it, the extension is ready. Clear the shop cache and open a configurable product. Its configurable options must now come pre-selected.
Incidentally, this would also work for products with more than one configurable attribute. Create such a product and give it a try.
Conclusion
While developing this simple extension we’ve tackled the topic of configurable product type and employed some of the methods provided by class Mage_Catalog_Model_Product_Type_Configurable to alter the front-end behavior of configurable products. With this practical demonstration we’ve gained insight into the internal works of the Configurable type. Get the Magento Configurable type extension code from GitHub.
Hi,
Very useful post thank you!
Trying to pass an attribute value with a controller…
Meaning if the user clicks on the red shirt image he will get color red selected. is using a controller the right way to go? Or is there a better way to do this ?
Cheers
Alexandre
Just to be more precise….
I have a view of my catalog, the user clicks on the red shirt…
In the product page I want to pre-select the red attribute.
If I understand you correctly, you have a view (that is not a product details page) where users can select a color. This selection is then used to display the color attribute pre-selected in the product detail page.
I see no problem using a controller here – it receives the user input from the view, writes the selection to the session, and later the system uses this session value to set the default values in the Configurable.php block in a way similar to the one in this tutorial.
That’s exactly what I meant…thanks for the reply!
Hello again,
Sorry but I’m just a bit stuck on one more point…
How do I call the controller and also go to the products page.
So on my index page I have a grid with all my products appearing as small images.
to call the controller ill have to link an image to the corresponding action url. But I also want to display the product page after setting my session, is there a way to go to the product page from the controller…Or maybe there is another observer usable when the user clicks on a product, in the index page.
Regards
Alexandre
Figured it out!
If anyone ever has the same question…
Use url to pass product id or whatever you want to the controller’action.
http://www.mysite.com/index/yourmodule‘sfrontname/index/youraction/$your_value’s_name/yourvalue
then in controller set your session variable to preselect the right attribute, you can use :
Mage::getSingleton(‘core/session’)->setYourValue’sName($YourValue’sName);
then redirect to your product page.
$prdurl=$product->getProductUrl();
$url=Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK);
$_prdurl=explode($url,$prdurl);
$this->_redirect($_prdurl[1]);
Finally follow this great tutorial 🙂
Cheers
Alexandre
Great! I’m glad that you made it work and happy my tutorial was of help 🙂
Hi,
I have read your blogs about configurable products and bundle products.
Those are very useful to me.
Now, i am developing an a store for jewelry shop with two configurable products put in to cart as a bundle. like:
1. Ring => Metal [Yello Gold, White Gold, Rose Gold, Platinum]
2. Diamond => Color [D,E,F,G,H,I] , Shape [Circle, Square, Oval]
is it possible to do that?
Hi Oleg! Thanks very much for a great write-up. I used your ideas to help complete my first module which modifies the product URL of configurables’ simple products to all point to their parent configurable product, but with a SKU parameter in the query string. Then, with your article’s help, the configurables’ product details page is now pre-configured to the specified simple product. It’s awesome.
Just wanted to point out an error that tripped me up a bit. In the last line within the loop going through the configurable’s configurable attributes, the code in the article is:
$candidates[$childProduct->getId()] =
array($productAttributeId => $attributeValue);
This did not work for me. However I checked your source code on GitHub, which had a different line:
$candidates[$productAttributeId] = $attributeValue;
That line did work for me.
Thanks again for a great write-up! It was much appreciated and helped me accomplish something much faster than I could have alone!