5

In my project, by default, I have some categories which are visible in frontend as shown below:

 category-1
     sub-category-1
     sub-category-2
 category-2
     sub-category-1
 category-3
 category-4
 category-5

The above categories should be sorted based on a attribute value item_branch which will be assigned to each product (not category or sub-category). This attribute can have following values:

  • X
  • Y
  • Z

So, the sorted product list should look like this: (many to many relationship)

X
  category-1
     sub-category-2
  category-2
     sub-category-1
  category-3
Y
  category-1
     sub-category-1
     sub-category-2
Z
  category-2
     sub-category-1
  category-3
  category-4
  category-5

To achieve the above sorting, I have two approaches in my mind.

  • First one is to create a dropdown attribute in admin panel (with possible values as X,Y and Z) and assign to a group in attribute sets.

  • Second is to add a new Catalog as item_branch with a categories X, Y and Z and while creating a product, I will assign both categories. i.e category-1 and Y (for example).

In the above approaches, which one is better one? or is there any more better approach?

Please explain it with some php code.. as I am new to php.

  • Do you want to sort products in category or you want to sort category in navigation? Please clarify which you want to sort and where will be shown sorting result? – mageUz Oct 24 '13 at 11:13
  • @mageUz I want to sort categories in vertical navigation menu.. – Mr_Green Oct 24 '13 at 11:24
  • So what happens if a category has products with attribute values of both X and Y? – Sander Mangel Oct 28 '13 at 11:01
  • @SanderMangel The categories will be categorized in both item_branch values but the item lists will be different. – Mr_Green Oct 28 '13 at 11:17
7
+50

Ok, I try to explain my solution, I give here most important points, but you should have some experiences like magento layouts, blocks, create custom module.
1. Create attribute which you assumed in your question.
2. Configure your products attaching them to particular category and attribute value created above.
3. Create custom block given in example (/app/code/local/Some/Module/Block/Catalog/Navigation.php):

class Some_Module_Block_Catalog_Navigation extends Mage_Core_Block_Template
{
    protected $_attributeCode = 'item_branch';

    /**
     * @return Mage_Catalog_Model_Resource_Eav_Attribute
     */
    public function getAttribute()
    {
        $attribute = Mage::getModel('catalog/resource_eav_attribute')
            ->loadByCode(Mage_Catalog_Model_Product::ENTITY, $this->_attributeCode);
        return $attribute;
    }

    protected function _construct()
    {
        $this->addData(array(
            'cache_lifetime' => false,
            'cache_tags'     => array(Mage_Catalog_Model_Category::CACHE_TAG),
        ));
    }

    public function getCacheKeyInfo()
    {
        $shortCacheId = array(
            'CATALOG_NAVIGATION',
            Mage::app()->getStore()->getId(),
            Mage::getDesign()->getPackageName(),
            Mage::getDesign()->getTheme('template'),
            'template' => $this->getTemplate(),
            'name'     => $this->getNameInLayout(),
            $this->_attributeCode
        );

        return $shortCacheId;
    }

    public function getCategoryUrl($category)
    {
        if ($category instanceof Mage_Catalog_Model_Category) {
            $url = $category->getUrl();
        } else {
            $url = $this->_getCategoryInstance()
                ->setData($category->getData())
                ->getUrl();
        }

        return $url;
    }

    public function getTree()
    {
        $attributeId = $this->getAttribute()->getId();
        $websiteId   = Mage::app()->getWebsite()->getId();

        $resource   = new Mage_Catalog_Model_Resource_Setup('core_setup');

        $categories = Mage::getModel('catalog/category')->getCollection();

        $categories
            ->addFieldToSelect(array('category_id' => 'entity_id', 'parent_id'))
            ->getSelect()
            ->join(array('cp' => $resource->getTable('catalog_category_product')), 'cp.category_id=main_table.entity_id', 'product_id')
            ->join(array('pw' => $resource->getTable('catalog_product_website')), "pw.product_id=cp.product_id AND  pw.website_id = $websiteId", 'website_id')
            ->join(array('av' => $resource->getTable('catalog_product_entity_int')), "av.entity_id=pw.product_id AND  av.attribute_id = $attributeId", array('option_id' => 'value'))
            ->join(array('ov' => $resource->getTable('eav_attribute_option_value')), 'ov.option_id=av.`value` AND  ov.store_id = 0', array('option_value' => 'value'))
            ->order(array('option_value', 'category_id'));

        $query = $categories->getSelectSql(true);

        $data = $resource->getConnection()->fetchAll($query);
        $tree = array();

        foreach ($data as $row) {
            $optionValue = $row['option_value'];
            if (!isset($tree[$optionValue])) {
                $tree[$optionValue] = array(
                    'label'      => $optionValue,
                    'categories' => array()
                );
            }
            $parentCategory = $row['parent_id'];
            if (!isset($tree[$optionValue]['categories'][$parentCategory])) {
                $tree[$optionValue]['categories'][$parentCategory] = array(
                    'category_id'    => $parentCategory,
                    'sub_categories' => array(),
                );
            }
            $categoryId = $row['category_id'];
            $tree[$optionValue]['categories'][$parentCategory]['sub_categories'][$categoryId] = $categoryId;
        }

        return $tree;
    }
} 

I created this block to achieve caching of my custom category navigation, because of to build this navigation it is required to load extra data from db. But you can use tree build logic in code given above without using block.

4.Add this navigation to frontend using local.xml layout (/app/design/frontend/default/default/layout/local.xml):

<?xml version="1.0"?>
<layout version="0.1.0">
    <catalog_category_default translate="label">
        <reference name="left">
            <block type="some_module/catalog_navigation" name="catalog.leftnav" before="currency"
                   template="catalog/navigation/myleft.phtml"/>
        </reference>
    </catalog_category_default>

    <catalog_category_layered translate="label">
        <reference name="left">
            <block type="some_module/catalog_navigation" name="catalog.myleft"
                   template="catalog/navigation/myleft.phtml"/>
        </reference>
    </catalog_category_layered>
</layout> 

5.Create navigation block template (/app/design/frontend/default/default/template/catalog/navigation/myleft.phtml):

<?php /* @var $this Some_Module_Block_Catalog_Navigation */ ?>
<?php $tree = $this->getTree(); ?>
<?php $categories = Mage::getModel('catalog/category')->getCollection()->addAttributeToSelect('name'); ?>
<?php $_count = count($tree) ?>
<?php if ($_count): ?>
    <div class="block block-layered-nav">
        <div class="block-title">
            <strong><span><?php echo $this->__('Browse By ') . $this->getAttribute()->getFrontendLabel() ?></span></strong>
        </div>
        <div class="block-content">
            <?php foreach ($tree as $label => $optionCategory): ?>
                <div>
                    <h5><?php echo $label ?></h5>
                    <ul>
                        <?php foreach ($optionCategory['categories'] as $parent): ?>
                            <?php $_category = $categories->getItemById($parent['category_id']) ?>
                            <li>
                                <a href="<?php echo $this->getCategoryUrl($_category) ?>"><?php echo $this->htmlEscape($_category->getName()) ?></a>
                                <ul>
                                    <?php foreach ($parent['sub_categories'] as $categoryId): ?>
                                        <?php $_category = $categories->getItemById($categoryId) ?>
                                        <li>
                                            --<a href="<?php echo $this->getCategoryUrl($_category) ?>"><?php echo $this->htmlEscape($_category->getName()) ?></a>
                                        </li>
                                    <?php endforeach ?>
                                </ul>
                            </li>
                        <?php endforeach ?>
                    </ul>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
<?php endif; ?>

6.You can fix design of this menu adding some css classes and rules as you want.

By the way your attribute type should be select. Try to understand the logic and adjust it to your store, this gives me perfect result, maybe it not works via copy/paste without understanding and adjusting the code. See my result:
category navigation

P.S: maybe this solution has some bugs or issues which I didn't sense, comments will be appreciated.

  • 4
    Tsk, tsk...queries in a block class? What if the table names are prefixed? Other than that seams ok at a first glance. – Marius Oct 28 '13 at 12:21
  • 1
    :). yeah...but still...queries in a block? Try to use a Magento collection or at least a Zend_Select object and you have my +1. – Marius Oct 28 '13 at 12:39
  • Ok, query generated via magento friendly way, check if is it satisfied. – mageUz Oct 28 '13 at 13:43
  • 1
    now I'm satisfied. +1. :) – Marius Oct 28 '13 at 13:55
  • Thank you I have still not checked your solution.. I will soon :) – Mr_Green Nov 1 '13 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.