Implementing a workflow in Magento

Last week we completed and went live with a client's revamped website. The site was based on Mangento, but has a big twist to it: users needed to follow specific workflow.

The usual Magento workflow looks something like this:

  1. Browse the catalog / search for products.
  2. When you find something you like, click Add to Cart. Goto 1.
  3. When you have found everything you like, click Checkout.
  4. Punch your credit card details in.

It's a pretty free workflow when you're choosing products.

But this client wanted a stricter workflow. Something more like this:

  1. Login / register.
  2. Browse category A. You must purchase something before you continue.
  3. Browse category B. You must purchase at least one product before you continue (you can add more later if you want).
  4. Browse category C. You must purchase 3 or more products before you continue.
  5. (Repeat for a few more categories)
  6. Proceed with the normal Magento checkout process.

Steps 2 through 4 are the interesting ones. How can you convince Magento to restrict its usual "browse catalog" function?

Well, we didn't strictly enforce the workflow. Rather, we encouraged users to use that workflow as much as possible. That means a) making it hard to discover and navigate to categories and b) making it really easy to find and follow the workflow. (If you try hard enough, you'll find alternate links to the categories and can thus break out of the workflow. And if that's you, give yourself a little pat yourself on the back! Now move along and do something more useful.)

A: Take away the normal catalog browse functionality

Disable the normal catalog drop down in Mage_Catalog_Block_Navigation._renderCategoryMenuItemHtml(). (See Alan Storm's helpful posts on how to override existing Magento classes.)

The usual mouse over "Holiday Planner" no longer works, although there are categories under there. Without those usual links, most users will just click "Holiday Planner". ("Packages" and "Location Info" are still browsable in the usual way, however.)

B: Guide users through the desired workflow

When you click "Holiday Planner" you get something like this.

There's your easy to follow workflow! As you complete steps, you get redirected back to that page. More ticks appear and there's always a next step to move to. Pretty simple. But how does it work?

Technical details

There are 3 Magento widgets for each step:

  1. A green tickbox image which appears when you complete the step.
  2. The "Click here for the next step" link to guide you to the next step.
  3. An optional "Add More...." link to browse back to previous categories.

The workflow boils down to whether particular items are in the user's shopping cart. So you end up with a bunch of functions which check for each discrete state:

	public function isCustomerLoggedIn()
	{
		return Mage::getSingleton('customer/session')->isLoggedIn();
	}
	public function isProductABCInCart()
	{
		foreach ($this->_quote->getAllItems() as $item)
		{
			$sku = $item->getProduct()->getSku();
			if ($sku == 'ABC')
				return true;
		}
		return false;
	}	
	public function isProductTypeXYZInCart()
	{
		foreach ($this->_quote->getAllItems() as $item)
		{
			$attributeSetModel = Mage::getModel('eav/entity_attribute_set');
			$attributeSetModel->load($item->getProduct()->getAttributeSetId());	
			if ($attributeSetModel->getAttributeSetName() == 'XYZ')
				return true;
		}
		return false;
	}

Then you end up with a really ugly looking state machine to identify the state you're currently in:

	public function getCurrentStatus()
	{
		if ( !$this->isCustomerLoggedIn() ) {
			return 'registration';
		} else if ( $this->isCustomerLoggedIn() 
						&& !$this->isProductABCInCart())
							) {
			return 'step2';
		} else if ( $this->isCustomerLoggedIn() 
						&& $this->isProductABCInCart())
						&& !$this->isProtuctTypeXYZInCart()
							) {
			return 'step3';			
		} else if ( $this->isCustomerLoggedIn() 
						&& $this->isProductABCInCart() 
						&& $this->isProtuctTypeXYZInCart()
						...
						&& !$this->isProductForStepNInCart()) {
			return 'stepN';
		} else if ( $this->isCustomerLoggedIn() 
						&& $this->isProductABCInCart() 
						&& $this->isProtuctTypeXYZInCart()
						&& $this->isProductForStepNInCart() 
						...) {
			return 'stepN+1';
		} else {
			// Does not match any status.
			throw new Exception("Unknown workflow state.");
		}		
	}

(Yes, I'm relying alot on Magento's EAV / database components to do caching for me!)

Then you turn each discrete state into a numeric one:

	public function getCurrentStatusNumeric($status = '')
	{
		if ( $status == '') {
			$status = $this->getCurrentStatus();
		}
		
		if ( $status == 'registration' ) {
			return 1;
		} else if ( $status == 'step2'  ) {
			return 2;
		} else if ( $status == 'step3' ) {
			return 3;
		...
		} else if ( $status == 'stepN' ) {
			return 999;
		} else if ( $status == 'stepN+1' ) {
			return 1000;
		} else {
			// Does not match any status.
			return -1;
		}	
	}

The numeric order allows for some smarts in some cases, eg: when you want to identify if you're past a step rather than at a step.

So, based on the step you're up to, and the steps you've completed, emit HTML from a custom Magento widget (how to create widgets part 1, part 2) to generate the links / images required:

class Faredge_Workflow_Block_Links
    extends Mage_Core_Block_Abstract
    implements Mage_Widget_Block_Interface
{

    protected function _toHtml()
    {
		$status = $this->getData('link_status');
		$currentStatus = Mage::helper('holidayplanner/workflow')->getCurrentStatus();
		$numericStatus = Mage::helper('holidayplanner/workflow')->getCurrentStatusNumeric($currentStatus);
		$html = $this->_getCompleteImgHtml($status, $numericStatus);
        return $html;
    }

	protected function _getCompleteImgHtml($status, $numericStatus)
	{
		$html = '<img src="' . $this->getSkinUrl() . '/images/success-tick.gif" alt="Complete" height="16" width="16"/> ';
	
		if ( $status == 'registration' && $numericStatus >= 1 ) {
			return $html;
		} else if ( $status == 'step2' && $numericStatus >= 2 ) {
			return $html;
		} else if ( $status == 'step3' && $numericStatus >= 3 ) {
			return $html;
		...
		} else if ( $status == 'stepN' && $numericStatus >= 999 ) {
			return $html;
		} else if ( $status == 'stepN+1' && $numericStatus > 1000 ) {
			return $html;
		} else {
			// Does not match this status.
			return '';
		}
	}
}

Finally, we have a static block which contains the descriptive HTML and the three widgets for each workflow step.

And there you have it! A simple way to guide users through a more restrictive workflow in Magento.

0
Windows cannot complete the password change error ...
File Synchronisation with Unison

Related Posts

Comments

 
No comments made yet. Be the first to submit a comment
Mobile Version | Desktop Version