Create a product-filtering AJAX "Wizard" in Magento

October 4, 2010
Development

As New Media Campaigns has begun working more in the e-commerce space, we have primarily been using Magento as our platform. Although Magento is very powerful and easy for clients to maintain, its complex code-base can make customization quite difficult. We thought we would share the code for one recent feature we created for a client of ours.

Hy-Tech Roof Drains wanted to feature a "Drain Wizard" prominently on their new website. This would be a step-by-step process to narrow down the compatible parts based on whatever information someone knows about their roof drains. We built this for them as an AJAX script that operates within a single page. Go check it out, and then I'll explain how it works.

Back? Good. As I was saying, the Wizard is a single page in the CMS portion of Magento. There is very little content entered on that page—just an introductory paragraph—but it pulls in a custom block, where I built all the HTML and JavaScript needed to make it work.

Including the Wizard block

In the custom Wizard template, I have a minimal amount of HTML and lots of JavaScript. The majority of the markup is generated on the fly, based on a big array of JS objects specifying each step of the process. As you can see in the example below, each step includes text, the step to go to when a visitor either selects an option or decides to skip the step, and the various options.

'attachement': {
	title: "Do you know how your ring is attached to the base?",
	skip: "No, I don’t know. Skip this step.",
	skipTo: 'bolt_type',
	continueTo: 'bolt_type',
	options: [
		{
			title: 'Bolts',
			value: 28,
			image: 'bolts.jpg'
		},
		{
			title: 'Post Clamps',
			value: 27,
			image: 'clamps.jpg'
		}
	]
}

As each step is needed, a JavaScript function sends an AJAX request to a special template that lists of all the available options for a given step. Why check with the server, when we already have the options hardcoded on the page? Because many of the possible combinations of options don't actually exist. That is, just because there are green drains, drains with three holes, and 9" drains, doesn't mean there is a 9" green drain with three holes. So after each step we check in with the parameters we have collected so far to see what options are still relevant. (I am going to leave out a lot of the code. Feel free to view source if you want to see the everything.)

// Get the available options for a step
function checkOptions(attribute){
	new Ajax.Request('/wizard/list', {
		method:'get',
		parameters: $('current_options').serialize(true),
		onSuccess: function(transport){
			var products = transport.responseText.evalJSON();
			var options = Array();
			
			for (i in products) {
				if (products[i][attribute]) {
					if (options.indexOf(products[i][attribute]) == -1) {
						options.push(products[i][attribute]);
					}
				}
			}
			
			// Pass it on to the drawing function
			drawStep(attribute, options);
		},
		onFailure: function() {
			alert('The Wizard seems to be sleeping right now. Please try again later.')
		}
	});
}

Finally, the list of available options is passed off to the drawStep() function, which matches them up with the data in our array of steps to generate the HTML. Now, the really interesting part here is the template that generates a JSON object of all the projects that meet a set of criteria. We can use Magento's product_collection model to get and filter all the products in our catalog. In our case, we are filtering by both category (since we only want to get drains, not accessories or other types of products), and by the attributes that we pass in as a query string. Enter the following PHP code in a new file in your templates folder. I chose wizard/list.phtml.

<?php
// Get only products from category "11" - Drains
$category = Mage::getModel('catalog/category')->load(11);
$products = Mage::getResourceModel('catalog/product_collection')->addCategoryFilter($category);

// Add a filter for each parameter
foreach ( $_GET as $attribute=>$value )
{
	$products->addAttributeToFilter($attribute, $value);
}

// Construct an array of the parts we care about
$productList = array();
foreach ( $products as $productModel )
{
	$product = Mage::getModel('catalog/product')->load($productModel->getId());
	
	$attributes = array();
	$attributes['manufacturer'] = $product->getAttributeText('manufacturer');
	$attributes['color'] = $product->getAttributeText('color');
	$attributes['attachement'] = $product->getAttributeText('attachement');
	$attributes['hole_number'] = $product->getAttributeText('hole_number');
	$attributes['hole_type'] = $product->getAttributeText('hole_type');
	$attributes['bolt_type'] = $product->getAttributeText('bolt_type');
	$attributes['inner_diameter'] = array($product->getAttributeText('inner_diameter'), $product->getInner_diameter());
	$attributes['outer_diameter'] = array($product->getAttributeText('outer_diameter'), $product->getOuter_diameter());
	$attributes['bolt_diameter'] = array($product->getAttributeText('bolt_diameter'), $product->getBolt_diameter());

	$productList[$product->getName()] = $attributes;
}

// Return the JSON string
echo json_encode($productList);

Obviously, if you are using this code in your own site, you will need to change the category number (or remove it entirely, if you want to search all the products) and change the list of attributes that are retrieved. You also need to create a CMS page and layout to generate this page. I set up a new tempate in my app/design/frontend/default/theme_name/template/page folder called "data.phtml". The contents of this template are very simple:

<?php
echo $this->getChildHtml('data');

All it does is spit out whatever content it is given, no formatting or other gunk. In order for this new layout to show up in the CMS page editor, you also will need to tell Magento that it exists. Open up app/code/local/Mage/Page/etc/config.xml (you can copy the file from app/code/core/Mage/Page/etc/ if it doesn't already exist) and find the section where your other page layouts are listed. Add this block to that list:

<data module="page" translate="label">
	<label>Data</label>
	<template>page/data.phtml</template>
	<layout_handle>data</layout_handle>
</data>

Finally, go into your CMS section and create a new page. I called mine "Wizard List". In the "Design" tab, set the layout to "Data" and enter the following XML in the "Layout Update XML" area:

<reference name="data">
	<block type="page/html" name="product_json" template="wizard/list.phtml"/>
</reference>

Change "wizard/list.phtml" to whatever folder and filename you saved the PHP code as earlier. You can test it out by visiting the url of the page you just created. For instance, if the page's url key was wizard/list, you would point your browser to http://domain.com/wizard/list?color=green. You should see a JSON string of the green products in your catalog. That url should also be set as the end point of your AJAX request.

The completed Wizard

There are several other pieces that go into the Drain Wizard, but they are more closely tied to our specific implementation. Check the source of http://www.hy-techroofdrains.com/drain-wizard for details or, better yet, contact New Media Campaigns for your Magento project. We would love to work with you!

Comments

Bryan Veloso's avatar
Bryan Veloso
Quite confusing at first but I was able to make it work. Thanks for the tips. :D
Perry's avatar
Perry
Thanks for the code! I tried it on my site but it wouldn't work for my Magento 1.8.1. I then made some research and found that one of the modules installed in my store can create ajax filters (improved layered navigation by Amasty).
I wouldn't have found that feature if I didn't read your post. Thanks!
Carol Willkins's avatar
Carol Willkins
Hi! Thanks for helpfull information! I saw an interesting solution recently - a private sale script from Plumrocket. A number of magento extensions and magento private sales theme. And there you can find an extension Product Filter, that is a dynamic Magento ajax product filter that allows customers to sort products according to different attributes. I think you can find it on their site, maybe you'll find it valuable for yourself.
Kamal Singhania's avatar
Kamal Singhania
Thanks for providing the information about setting up the wizard on Magento. However I dont see the content loading up on my page. Where do we put the javascript code.
BigBridge's avatar
BigBridge
Thanks for this one! Also nice to see you use a less HTML.
nz warrior's avatar
nz warrior
having trouple getting this to work!

- the new layout template throws blank page (even added the layoutupdate to page.xml)

- were do you place the script (at the top of wizard-list.phtml?...in the cms page?...)

- the setup just spits out full array of products

please help!!!

Leave a comment