Based on the solution provided by Marius and some code I had locally i put the below together.
Since the categories are added via an observer we simply disable the default observer and create own own version based on the original that adds the new filters in and pops the default categories under a "By Category" node when the Category level is 2.
<page_block_html_topmenu_gethtml_before>
<observers>
<catalog_add_topmenu_items>
<type>disabled</type>
</catalog_add_topmenu_items>
<neo_topnav>
<class>neo_topnav/observer</class>
<method>pageBlockHtmlTopmenuGethtmlBefore</method>
</neo_topnav>
</observers>
</page_block_html_topmenu_gethtml_before>
/**
* Adds catalog categories to top menu
*
* @param Varien_Event_Observer $observer
*/
public function pageBlockHtmlTopmenuGethtmlBefore(Varien_Event_Observer $observer)
{
$block = $observer->getEvent()->getBlock();
$block->addCacheTag(Mage_Catalog_Model_Category::CACHE_TAG);
$this->_addCategoriesToMenu(
Mage::helper('catalog/category')->getStoreCategories(), $observer->getMenu(), $block, true
);
}
/**
* Recursively adds categories to top menu
*
* @param Varien_Data_Tree_Node_Collection|array $categories
* @param Varien_Data_Tree_Node $parentCategoryNode
* @param Mage_Page_Block_Html_Topmenu $menuBlock
* @param bool $addTags
*/
protected function _addCategoriesToMenu($categories, $parentCategoryNode, $menuBlock, $addTags = false)
{
$categoryModel = Mage::getModel('catalog/category');
foreach ($categories as $category) {
if (!$category->getIsActive()) {
continue;
}
$nodeId = 'category-node-' . $category->getId();
$categoryModel->setId($category->getId());
if ($addTags) {
$menuBlock->addModelTags($categoryModel);
}
$tree = $parentCategoryNode->getTree();
$categoryData = array(
'name' => $category->getName(),
'id' => $nodeId,
'url' => Mage::helper('catalog/category')->getCategoryUrl($category),
'is_active' => $this->_isActiveMenuCategory($category)
);
$categoryNode = new Varien_Data_Tree_Node($categoryData, 'id', $tree, $parentCategoryNode);
$parentCategoryNode->addChild($categoryNode);
$flatHelper = Mage::helper('catalog/category_flat');
if ($flatHelper->isEnabled() && $flatHelper->isBuilt(true)) {
$subcategories = (array)$category->getChildrenNodes();
} else {
$subcategories = $category->getChildren();
}
switch($category->getLevel()){
case 2:
$byCategoryNode = $this->_addShopByCategory($parentCategoryNode, $category, $tree, $categoryNode);
$this->_addCategoriesToMenu($subcategories, $byCategoryNode, $menuBlock, $addTags);
$this->_addFiltersToMenu($category->getId(),$categoryNode,$tree,$parentCategoryNode);
break;
default:
$this->_addCategoriesToMenu($subcategories, $categoryNode, $menuBlock, $addTags);
break;
}
}
}
/**
* @param $parentCategoryNode
* @param $category
* @param $tree
* @param $categoryNode
* @return Varien_Data_Tree_Node
*/
protected function _addShopByCategory($parentCategoryNode, $category, $tree, $categoryNode)
{
$byCategoryNodeId = 'by-category-node-' . $category->getId();
$byCategoryData = array(
'name' => Mage::helper('neo_topnav')->__('By Category'),
'id' => $byCategoryNodeId,
'url' => '#',
'is_active' => false
);
$byCategoryNode = new Varien_Data_Tree_Node($byCategoryData, 'id', $tree, $parentCategoryNode);
$categoryNode->addChild($byCategoryNode);
return $byCategoryNode;
}
/**
* @param $id
* @param $category
* @param $tree
* @param $parentCategoryNode
*/
function _addFiltersToMenu($id,$category,$tree,$parentCategoryNode){
$attributes = $this->getAttributes($id);
$mainUrl = $category->getUrl();
foreach ($attributes as $attribute) {
//get the options for each attribute
$options = $attribute->getSource()->getAllOptions();
if (count($options) > 0) {
//add "By ..." menu item - non clickable
$attrNodeId = 'attribute-'.$id.'-'.$attribute->getId();
$data = array(
'name' => Mage::helper('neo_topnav')->__('By %s', $attribute->getFrontendLabel()),
'id' => $attrNodeId,
'url' => '#',
'is_active' => false
);
$attrNode = new Varien_Data_Tree_Node($data, 'id', $tree,$parentCategoryNode);
//for each option add a new sub menu
foreach ($options as $option) {
if ($option['value']) {
$optionNodeId = 'attribute-'.$id.'-'.$attribute->getId().'-'.$option['value'];
$data = array(
'name' => $option['label'],
'id' => $optionNodeId,
'url' => $mainUrl.'?'.$attribute->getAttributeCode().'='.$option['value'],
'is_active' => false
);
$optionNode = new Varien_Data_Tree_Node($data, 'id', $tree,$parentCategoryNode);
$attrNode->addChild($optionNode);
}
}
$category->addChild($attrNode);
}
}
}
//get allowed attributes in that category
public function getAttributes($categoryId) {
$layer = Mage::getModel("catalog/layer");
$layer->setCurrentCategory(Mage::getModel('catalog/category')->load($categoryId));
$validAttributes = array();
foreach ($layer->getFilterableAttributes() as $attribute) {
//allow only select attributes - you can implement your additional filters here
if ($attribute->getFrontendInput() == 'select'){
$validAttributes[] = $attribute;
}
}
return $validAttributes;
}
/**
* Checks whether category belongs to active category's path
*
* @param Varien_Data_Tree_Node $category
* @return bool
*/
protected function _isActiveMenuCategory($category)
{
$catalogLayer = Mage::getSingleton('catalog/layer');
if (!$catalogLayer) {
return false;
}
$currentCategory = $catalogLayer->getCurrentCategory();
if (!$currentCategory) {
return false;
}
$categoryPathIds = explode(',', $currentCategory->getPathInStore());
return in_array($category->getId(), $categoryPathIds);
}
}
Figured it out by myself.
Above code returns nothing because attribute (size) is assigned only to simple products (associated products) and all simple product's visibility is set to 1 i.e, not visible individually.
Step 1 : Remove visibility to 1, now i can get list of all simple products (associated products) which is assigned to size 3.
Step 2 : Join table catalog_product_super_link to get parent product associated with it.
Complete code is,
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToFilter(
array(
array('attribute'=> 'color','like' => '12')
)
)
// multiple filters can be achieved by adding another filterattribute
->addAttributeToFilter(
array(
array('attribute'=> 'size','like' => '3')
)
);
$collection->getSelect()->joinLeft(array('link_table' => 'catalog_product_super_link'),'link_table.product_id = e.entity_id',
array('product_id','parent_id')
);
$collection->getSelect()->group('link_table.parent_id');
foreach($collection as $product) {
$ids[] = $product->getParentId();
}
$collection->load();
echo json_encode($ids);
Hope this may help to someone.
Best Answer
There are plenty extensions that do this.
My favourite is ManaDev Multiple Select In Layered Navigation (Filters) But there are others.
There is a similar question here
Both answers can provide info on your problem.