There is a feature in Magento where the category list can be sorted
based on how the administrator sees fit. The problem is that most stores
will want to sort the category list alphabetically. With a large
catalog, the list quickly becomes a hassle to navigate and becomes a
frustrating time sink.
The solution detailed here extends a Magento core file to sort the
category list. It will sort the categories for both the Category Edit
and the Product Edit Category tab.
Example of Alphabetically Sorted Magento Categories
The Quick Solution:
The quick solution is to use the PHP method usort() on the array of categories in a Magento core file.
overriding the magento core file: create the file Tree.php as following directory
app/code/local/Mage/Adminhtml/Block/Catalog/Category/Tree.php
Tree.php file:
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@magentocommerce.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magentocommerce.com for more information.
*
* @category Mage
* @package Mage_Adminhtml
* @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* Categories tree block
*
* @category Mage
* @package Mage_Adminhtml
* @author Magento Core Team <core@magentocommerce.com>
*/
class Mage_Adminhtml_Block_Catalog_Category_Tree extends Mage_Adminhtml_Block_Catalog_Category_Abstract
{
protected $_withProductCount;
public function __construct()
{
parent::__construct();
$this->setTemplate('catalog/category/tree.phtml');
$this->setUseAjax(true);
$this->_withProductCount = true;
}
protected function _prepareLayout()
{
$addUrl = $this->getUrl("*/*/add", array(
'_current'=>true,
'id'=>null,
'_query' => false
));
$this->setChild('add_sub_button',
$this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('catalog')->__('Add Subcategory'),
'onclick' => "addNew('".$addUrl."', false)",
'class' => 'add',
'id' => 'add_subcategory_button',
'style' => $this->canAddSubCategory() ? '' : 'display: none;'
))
);
if ($this->canAddRootCategory()) {
$this->setChild('add_root_button',
$this->getLayout()->createBlock('adminhtml/widget_button')
->setData(array(
'label' => Mage::helper('catalog')->__('Add Root Category'),
'onclick' => "addNew('".$addUrl."', true)",
'class' => 'add',
'id' => 'add_root_category_button'
))
);
}
$this->setChild('store_switcher',
$this->getLayout()->createBlock('adminhtml/store_switcher')
->setSwitchUrl($this->getUrl('*/*/*', array('_current'=>true, '_query'=>false, 'store'=>null)))
->setTemplate('store/switcher/enhanced.phtml')
);
return parent::_prepareLayout();
}
protected function _getDefaultStoreId()
{
return Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
}
public function getCategoryCollection()
{
$storeId = $this->getRequest()->getParam('store', $this->_getDefaultStoreId());
$collection = $this->getData('category_collection');
if (is_null($collection)) {
$collection = Mage::getModel('catalog/category')->getCollection();
/* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection */
$collection->addAttributeToSelect('name')
->addAttributeToSelect('is_active')
->setProductStoreId($storeId)
->setLoadProductCount($this->_withProductCount)
->setStoreId($storeId);
$this->setData('category_collection', $collection);
}
return $collection;
}
public function getAddRootButtonHtml()
{
return $this->getChildHtml('add_root_button');
}
public function getAddSubButtonHtml()
{
return $this->getChildHtml('add_sub_button');
}
public function getExpandButtonHtml()
{
return $this->getChildHtml('expand_button');
}
public function getCollapseButtonHtml()
{
return $this->getChildHtml('collapse_button');
}
public function getStoreSwitcherHtml()
{
return $this->getChildHtml('store_switcher');
}
public function getLoadTreeUrl($expanded=null)
{
$params = array('_current'=>true, 'id'=>null,'store'=>null);
if (
(is_null($expanded) && Mage::getSingleton('admin/session')->getIsTreeWasExpanded())
|| $expanded == true) {
$params['expand_all'] = true;
}
return $this->getUrl('*/*/categoriesJson', $params);
}
public function getNodesUrl()
{
return $this->getUrl('*/catalog_category/jsonTree');
}
public function getSwitchTreeUrl()
{
return $this->getUrl("*/catalog_category/tree", array('_current'=>true, 'store'=>null, '_query'=>false, 'id'=>null, 'parent'=>null));
}
public function getIsWasExpanded()
{
return Mage::getSingleton('admin/session')->getIsTreeWasExpanded();
}
public function getMoveUrl()
{
return $this->getUrl('*/catalog_category/move', array('store'=>$this->getRequest()->getParam('store')));
}
public function getTree($parenNodeCategory=null)
{
$rootArray = $this->_getNodeJson($this->getRoot($parenNodeCategory));
$tree = isset($rootArray['children']) ? $rootArray['children'] : array();
return $tree;
}
public function getTreeJson($parenNodeCategory=null)
{
$rootArray = $this->_getNodeJson($this->getRoot($parenNodeCategory));
$json = Mage::helper('core')->jsonEncode(isset($rootArray['children']) ? $rootArray['children'] : array());
return $json;
}
/**
* Get JSON of array of categories, that are breadcrumbs for specified category path
*
* @param string $path
* @param string $javascriptVarName
* @return string
*/
public function getBreadcrumbsJavascript($path, $javascriptVarName)
{
if (empty($path)) {
return '';
}
$categories = Mage::getResourceSingleton('catalog/category_tree')
->setStoreId($this->getStore()->getId())->loadBreadcrumbsArray($path);
if (empty($categories)) {
return '';
}
foreach ($categories as $key => $category) {
$categories[$key] = $this->_getNodeJson($category);
}
return
'<script type="text/javascript">'
. $javascriptVarName . ' = ' . Mage::helper('core')->jsonEncode($categories) . ';'
. ($this->canAddSubCategory() ? '$("add_subcategory_button").show();' : '$("add_subcategory_button").hide();')
. '</script>';
}
/**
* Get JSON of a tree node or an associative array
*
* @param Varien_Data_Tree_Node|array $node
* @param int $level
* @return string
*/
protected function _getNodeJson($node, $level = 0)
{
// create a node from data array
if (is_array($node)) {
$node = new Varien_Data_Tree_Node($node, 'entity_id', new Varien_Data_Tree);
}
$item = array();
$item['text'] = $this->buildNodeName($node);
/* $rootForStores = Mage::getModel('core/store')
->getCollection()
->loadByCategoryIds(array($node->getEntityId())); */
$rootForStores = in_array($node->getEntityId(), $this->getRootIds());
$item['id'] = $node->getId();
$item['store'] = (int) $this->getStore()->getId();
$item['path'] = $node->getData('path');
$item['cls'] = 'folder ' . ($node->getIsActive() ? 'active-category' : 'no-active-category');
//$item['allowDrop'] = ($level<3) ? true : false;
$allowMove = $this->_isCategoryMoveable($node);
$item['allowDrop'] = $allowMove;
// disallow drag if it's first level and category is root of a store
$item['allowDrag'] = $allowMove && (($node->getLevel()==1 && $rootForStores) ? false : true);
if ((int)$node->getChildrenCount()>0) {
$item['children'] = array();
}
$isParent = $this->_isParentSelectedCategory($node);
if ($node->hasChildren()) {
$item['children'] = array();
if (!($this->getUseAjax() && $node->getLevel() > 1 && !$isParent)) {
foreach ($node->getChildren() as $child) {
$item['children'][] = $this->_getNodeJson($child, $level+1);
}
}
// Sort the children category array
usort($item['children'], array(get_class($this), 'compareChildText'));
}
if ($isParent || $node->getLevel() < 2) {
$item['expanded'] = true;
}
return $item;
}
/**
* Helper comparison function to return the category children array, sorted alphabetically
* @param type $a
* @param type $b
* @return type
*/
static function compareChildText($a, $b)
{
return strnatcmp(strtolower($a['text']), strtolower($b['text']));
}
/**
* Get category name
*
* @param Varien_Object $node
* @return string
*/
public function buildNodeName($node)
{
$result = $this->htmlEscape($node->getName());
if ($this->_withProductCount) {
$result .= ' (' . $node->getProductCount() . ')';
}
return $result;
}
protected function _isCategoryMoveable($node)
{
$options = new Varien_Object(array(
'is_moveable' => true,
'category' => $node
));
Mage::dispatchEvent('adminhtml_catalog_category_tree_is_moveable',
array('options'=>$options)
);
return $options->getIsMoveable();
}
protected function _isParentSelectedCategory($node)
{
if ($node && $this->getCategory()) {
$pathIds = $this->getCategory()->getPathIds();
if (in_array($node->getId(), $pathIds)) {
return true;
}
}
return false;
}
/**
* Check if page loaded by outside link to category edit
*
* @return boolean
*/
public function isClearEdit()
{
return (bool) $this->getRequest()->getParam('clear');
}
/**
* Check availability of adding root category
*
* @return boolean
*/
public function canAddRootCategory()
{
$options = new Varien_Object(array('is_allow'=>true));
Mage::dispatchEvent(
'adminhtml_catalog_category_tree_can_add_root_category',
array(
'category' => $this->getCategory(),
'options' => $options,
'store' => $this->getStore()->getId()
)
);
return $options->getIsAllow();
}
/**
* Check availability of adding sub category
*
* @return boolean
*/
public function canAddSubCategory()
{
$options = new Varien_Object(array('is_allow'=>true));
Mage::dispatchEvent(
'adminhtml_catalog_category_tree_can_add_sub_category',
array(
'category' => $this->getCategory(),
'options' => $options,
'store' => $this->getStore()->getId()
)
);
return $options->getIsAllow();
}
}
Refersh the cache and recompile: