0
votes

I want to display all categories and their subcategories in primary nav menu. When i hover on cateory, it should display its subcategories. I want to implement this functionality without loading Mage::getModel('catalog/category') in foreach loop.

3

3 Answers

1
votes

as you want to write code in phtml file use below code which create tree structure of category.

<ul>
    <?php
        $obj = new Mage_Catalog_Block_Navigation();
        $storeCategories = $obj->getStoreCategories();
        Mage::registry('current_category') ? $currentCategoryId = Mage::registry('current_category')->getId() : $currentCategoryId='';
        foreach ($storeCategories as $_category):
    ?>
            <li>
                <strong><?php echo $_category->getName(); ?></strong>
                <?php $categoryChildren = $_category->getChildren(); ?>
                <?php if($categoryChildren->count()) : ?>
                    <ul>

                        <?php foreach($categoryChildren as $_categoryChild) : ?>
                            <?php $_categoryChildModel = Mage::getModel('catalog/category')->load($_categoryChild->getId());?>
                            <?php $categoryGrandchildren=$_categoryChild->getChildren(); ?>
                            <li>
                                <?php
                                    $currentCategoryId===$_categoryChild->getId() ? $bold="style=\"font-weight:bold\"" : $bold='';
                                    echo '&emsp;' . '<a href="' . $_categoryChildModel->getUrl() . '"' . $bold . '>' .  $_categoryChild->getName() . '(' . $_categoryChildModel->getProductCollection()->count() . ')</a>';
                                ?>
                            </li>
                            <?php if($categoryGrandchildren->count()) : ?>
                                <?php foreach($categoryGrandchildren as $_categoryGrandchild) : ?>
                                    <?php $_categoryGrandchildModel = Mage::getModel('catalog/category')->load($_categoryGrandchild->getId());?>
                                    <li>
                                        <?php
                                            $currentCategoryId===$_categoryChild->getId() ? $bold="style=\"font-weight:bold\"" : $bold='';
                                            echo '&emsp;&emsp;' . '<a href="' . $_categoryGrandchildModel->getUrl() . '"' . $bold . '>' .  $_categoryGrandchild->getName() . '(' . $_categoryGrandchildModel->getProductCount() . ')</a>';
                                        ?>
                                    </li>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        <?php endforeach; ?>
                    </ul>
                <?php endif; ?>
            </li>
        <?php endforeach ?>
</ul>

and using css and HTML you can achieve your goal to display sub menu on hover of main menu.

Still let me if you need any other help.

Thanks

1
votes

ok so I just did this and I figured I'd search to see if anyone was wondering how to achieve this. The trick to this is

Mage::getResourceSingleton('catalog/category')->getAttributeRawValue($categoryEntityId,array('name','level','url_key','path','is_active'),Mage::app()->getStore());

This does not load the category model lets take a look at what it is actually doing.

Go to app/code/core/Mage/Catalog/Model/Resource/Abstract

public function getAttributeRawValue($entityId, $attribute, $store)
{
    if (!$entityId || empty($attribute)) {
        return false;
    }
    if (!is_array($attribute)) {
        $attribute = array($attribute);
    }

    $attributesData     = array();
    $staticAttributes   = array();
    $typedAttributes    = array();
    $staticTable        = null;
    $adapter            = $this->_getReadAdapter();

    foreach ($attribute as $_attribute) {
        /* @var $attribute Mage_Catalog_Model_Entity_Attribute */
        $_attribute = $this->getAttribute($_attribute);
        if (!$_attribute) {
            continue;
        }
        $attributeCode = $_attribute->getAttributeCode();
        $attrTable     = $_attribute->getBackend()->getTable();
        $isStatic      = $_attribute->getBackend()->isStatic();

        if ($isStatic) {
            $staticAttributes[] = $attributeCode;
            $staticTable = $attrTable;
        } else {
            /**
             * That structure needed to avoid farther sql joins for getting attribute's code by id
             */
            $typedAttributes[$attrTable][$_attribute->getId()] = $attributeCode;
        }
    }
    /**
     * Collecting static attributes
     */
    if ($staticAttributes) {
        $select = $adapter->select()->from($staticTable, $staticAttributes)
            ->where($this->getEntityIdField() . ' = :entity_id');
        $attributesData = $adapter->fetchRow($select, array('entity_id' => $entityId));
    }

    /**
     * Collecting typed attributes, performing separate SQL query for each attribute type table
     */
    if ($store instanceof Mage_Core_Model_Store) {
        $store = $store->getId();
    }

    $store = (int)$store;
    if ($typedAttributes) {
        foreach ($typedAttributes as $table => $_attributes) {
            $select = $adapter->select()
                ->from(array('default_value' => $table), array('attribute_id'))
                ->where('default_value.attribute_id IN (?)', array_keys($_attributes))
                ->where('default_value.entity_type_id = :entity_type_id')
                ->where('default_value.entity_id = :entity_id')
                ->where('default_value.store_id = ?', 0);
            $bind = array(
                'entity_type_id' => $this->getTypeId(),
                'entity_id'      => $entityId,
            );

            if ($store != $this->getDefaultStoreId()) {
                $valueExpr = $adapter->getCheckSql('store_value.value IS NULL',
                    'default_value.value', 'store_value.value');
                $joinCondition = array(
                    $adapter->quoteInto('store_value.attribute_id IN (?)', array_keys($_attributes)),
                    'store_value.entity_type_id = :entity_type_id',
                    'store_value.entity_id = :entity_id',
                    'store_value.store_id = :store_id',
                );

                $select->joinLeft(
                    array('store_value' => $table),
                    implode(' AND ', $joinCondition),
                    array('attr_value' => $valueExpr)
                );

                $bind['store_id'] = $store;

            } else {
                $select->columns(array('attr_value' => 'value'), 'default_value');
            }

            $result = $adapter->fetchPairs($select, $bind);
            foreach ($result as $attrId => $value) {
                $attrCode = $typedAttributes[$table][$attrId];
                $attributesData[$attrCode] = $value;
            }
        }
    }

    if (sizeof($attributesData) == 1) {
        $_data = each($attributesData);
        $attributesData = $_data[1];
    }

    return $attributesData ? $attributesData : false;
}

As you can see no model loading happening just retrieving specific pieces of info. Also being part of the resource abstract means all catalog resource models (I haven't checked other resource models but, I wouldn't be too surprised to find this in others) have this available.

If you use this in an override of Mage_Catalog_Block_Navigation you can then call all of the info you need about any category without loading the model. To traverse the tree however, you have to do terrible things.

You can use 'path'(explode on /) to easily retrieve parents but you will need to get dirty in order to retrieve children categories so something like this to get Children.

        $childrenQuery = "SELECT entity_id FROM catalog_category_entity WHERE path REGEXP '^.*\/" . $categoryId . "\/[[:digit:]]?[[:digit:]]?[[:digit:]]?[[:digit:]]?$'";
        $resource = Mage::getSingleton('core/resource');
        $readCxn = $resource->getConnection('core/read');
        $children = $readCxn->fetchAll($childrenQuery);
        if ($children[0]) {
            return $children;
        } else {
            return;
        }

The overall difficulty is that all model and resource model functions will expect an instance of a category object to make them all work with just the entity_id is definitely possible just a pain.

So I would not recommend doing this in general the only reason I did this is because the default magento root category in my case was not the actual functional root of categories (fun eh). If you are using a standard root category I would recommend using the Helper functions and retrieving the info from cache.

Either way from here all you have to do complete your functions in Mage_Catalog_Block_Navigation and assemble your menu in a template. And there you go; complete category menu without ever accessing a model->load.

0
votes

Try This Code

<?php
require_once("app/Mage.php");
Mage::app();

function getChildCategories($category, $First = false) {


        $sub = $First ? $category : $category->getChildren();


        foreach ($sub as $child) {
                $_categories[] = [ "name" => $child->getName(), "id" => $child->getId(), "children" => getChildCategories($child) ];
        }


        return $_categories;
};

function CategoriesTree($category, $First = false) {


        $sub = $First ? $category : $category->getChildren();
        echo "<pre>";

        foreach ($sub as $child) {
          echo $child->getName(); ;
                CategoriesTree($child);
        }

}

$_categories = Mage::helper('catalog/category')->getStoreCategories();


$categories = getChildCategories($_categories, true);

CategoriesTree($_categories, true);

?>