Я хочу, чтобы в моем главном меню не было никаких категорий, которые пусты. Я сделал это для многоуровневой навигации очень легко в соответствующем файле phtml, используя
$_category->getProductCount()
Однако для навигационного меню я считаю невозможным сделать это так же легко (я видел пример Prattski, но это похоже на OTT).
Главное меню, по-видимому, построено в Mage_Page_Block_Html_Topmenu.php
, особенно в функции _getHtml
. Это $child->getId()
всех детей в меню, и если я попробую что-то вроде $child->getId()
, я получаю что-то вроде "category-node-36"
.
Не похоже, что я слишком далек от использования getProductCount()
и поэтому проверяю, больше ли он.
Можно ли сделать это? Может ли кто-нибудь указать мне, как?
Если смогу, я расширю класс своей версией.
Для этого перейдите по ссылке:
app/code/core/Mage/Catalog/Block
Folder и скопируйте Navigation.php и переопределите его в локальном пакете.
Откройте Navigation.php вашего пакета и вставьте ниже кода в этот файл:
if ($category->getIsActive()) { $cat = Mage::getModel('catalog/category')->load($category->getId()); $products = Mage::getResourceModel('catalog/product_collection')->addCategoryFilter($cat); Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products); Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products); Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($products); if(count($products)==0) return; }
Надеюсь, мой код вам поможет.
Я, наконец, взломал его, хотя я далеко не убежден, что это оптимальное решение. Во всяком случае, я расскажу, что я здесь сделал, и, надеюсь, кто-то может сделать его более эффективным. Я дам описание «удар по удару», поскольку я был довольно многочисленным. Так что извиняйтесь за длину.
Как я уже сказал, в моем случае, по крайней мере, главное меню создается через Topmenu.php в приложении / коде / ядре / Маге / Page / Block / Html, в частности методе _getHtml. Я очень определенно не хочу изменять основной файл, поэтому я узнал, как расширить этот метод с помощью нового модуля. (Вы можете пропустить этот бит, если вы знакомы с созданием новых модулей.)
Мне нужно было создать новый модуль (я буду называть его MYMOD ниже). Поскольку я перезаписываю блок-страницу основного блока magento, мне приходилось создавать новые папки: app / code / local / MYMOD / Page и там две подпапки, Block и т. Д. (Я считаю, что они чувствительны к регистру). И внутри Block еще одна подпапка Html. Вы можете видеть, что это точно отражает структуру папок с помощью приложения / кода / ядра / Мага.
Папка etc содержит спецификацию для нового модуля в файле config.xml. Это выглядит так:
<?xml version="1.0" encoding="UTF-8"?> <!-- The root node for Magento module configuration --> <config> <!-- The module's node contains basic information about each Magento module --> <modules> <!-- This must exactly match the namespace and module's folder names, with directory separators replaced by underscores --> <MYMOD_Page> <!-- The version of our module, starting at 0.0.1 --> <version>0.0.1</version> </MYMOD_Page> </modules> <global> <blocks> <page> <rewrite> <html_topmenu>MYMOD_Page_Block_Html_Topmenu</html_topmenu> </rewrite> </page> </blocks> </global> </config>
Вы можете узнать о whys и wherefors этого в другом месте.
К сожалению (на мой взгляд!), Это еще не все, что вам нужно сделать, чтобы указать новый модуль в Magento. Вам также необходимо создать файл под названием «MYMOD_Page.xml» в приложении / etc / modules. Это просто говорит Magento о вашем модуле и где его искать. Моя выглядит так:
<?xml version="1.0" encoding="UTF-8"?> <config> <modules> <MYMOD_Page> <!-- Whether our module is active: true or false --> <active>true</active> <!-- Which code pool to use: core, community or local --> <codePool>local</codePool> </MYMOD_Page> </modules> </config>
Хорошо, извините за неуместные инструкции по модулям, но мне нравятся самодостаточные объяснения.
Теперь я могу создать новый файл в моем модуле с подклассом, в котором у меня могут быть методы (функции), которые будут использоваться вместо основных Magento. Имя файла должно быть таким же, как исходный файл, Topmenu.php, и он идет в app / code / local / MYMOD / Page / Block / Html.
Помните, что объектно-ориентированная структура означает, что все функции исходной основной версии Topmenu.php доступны мне, мне не нужно копировать их в моей новой версии (отлично подходит для удобства обслуживания). Моя версия Topmenu.php должна содержать только любые новые функции, которые я мог бы захотеть (в данном случае мне они не нужны) и переопределить любые функции, которые я хочу переписать с помощью своей собственной версии. В моем случае мне нужно было изменить две функции: _getHtml и _getMenuItemClasses.
_getHtml требует дополнительных проверок, поэтому любые пустые категории не включаются.
_getMenuItemClasses нуждается в дополнительных проверках, чтобы класс «родитель» не был добавлен в категории, чьи дети пустые.
Вот как я это сделал (с комментариями). Я уверен, что есть лучшие способы, но я до сих пор довольно новичок в Magento.
class MYMOD_Page_Block_Html_Topmenu extends Mage_Page_Block_Html_Topmenu // Create my subclass, in accordance with how I've defined the new module { /** * Recursively generates top menu html from data that is specified in $menuTree * * @param Varien_Data_Tree_Node $menuTree * @param string $childrenWrapClass * @return string */ protected function _getHtml(Varien_Data_Tree_Node $menuTree, $childrenWrapClass) { $html = ''; $children = $menuTree->getChildren(); $parentLevel = $menuTree->getLevel(); $childLevel = is_null($parentLevel) ? 0 : $parentLevel + 1; $counter = 1; $childrenCount = $children->count(); $parentPositionClass = $menuTree->getPositionClass(); $itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-'; foreach ($children as $child) { $child->setLevel($childLevel); $child->setIsFirst($counter == 1); $child->setIsLast($counter == $childrenCount); $child->setPositionClass($itemPositionClassPrefix . $counter); $outermostClassCode = ''; $outermostClass = $menuTree->getOutermostClass(); if ($childLevel == 0 && $outermostClass) { $outermostClassCode = ' class="' . $outermostClass . '" '; $child->setClass($outermostClass); } /* * Find out if this category has any products. I don't know an easier way. * The id of every child returned by getID is of the form "category-node-nnn" * where nnn is the id that can be used to select the category details. * substr strips everything leaving just the nnn. * Then use getModel-> getCollection with a filter on the id. Although this looks * like it will return many, obviously category ids are unique so in fact it only * returns the category we're currently looking at. */ $_gcategoryId = substr($child->getId(), 14, 6); $_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId)); foreach ($_gcategories as $_gcategory) { $_gcategoryCount = $_gcategory->getProductCount(); } /* * Now only include those categories that have products. * In my case I also wanted to include the top level categories come what may. */ if (($childLevel == 0) || ($_gcategoryCount > 0)) { $html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>'; $html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml($child->getName()) . '</span></a>'; if ($child->hasChildren()) { if (!empty($childrenWrapClass)) { $html .= '<div class="' . $childrenWrapClass . '">'; } $html .= '<ul class="level' . $childLevel . '">'; $html .= $this->_getHtml($child, $childrenWrapClass); $html .= '</ul>'; if (!empty($childrenWrapClass)) { $html .= '</div>'; } } $html .= '</li>'; } $counter++; } return $html; } /** * Returns array of menu item's classes * * @param Varien_Data_Tree_Node $item * @return array */ protected function _getMenuItemClasses(Varien_Data_Tree_Node $item) { $classes = array(); $classes[] = 'level' . $item->getLevel(); $classes[] = $item->getPositionClass(); if ($item->getIsFirst()) { $classes[] = 'first'; } if ($item->getIsActive()) { $classes[] = 'active'; } if ($item->getIsLast()) { $classes[] = 'last'; } if ($item->getClass()) { $classes[] = $item->getClass(); } if ($item->hasChildren()) { /* * Don't just check if there are children but, if there are, are they all empty? * If so, then the changes in _getHtml will mean none of them will be included * and so this one has no children displayed and so the "parent" class is not appropriate. */ $children = $item->getChildren(); // Get all the children from this menu category foreach ($children as $child) { // Loop over each child and find out how many products (see _getHtml) $_gcategoryId = substr($child->getId(), 14, 6); $_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId)); foreach ($_gcategories as $_gcategory) { // Remember, there's actually only one category that will match the child's id $_gcategoryCount = $_gcategory->getProductCount(); } if ($_gcategoryCount > 0) { // As soon as one child has products, then we have a parent and can stop looking $classes[] = 'parent'; break; } } } return $classes; } }
Надеюсь, это ясно. Он делает то, что я хочу (мой магазин небольшой), но любые предложения по улучшению приветствуются.
путь : app / design / frontend / rwd / default / template / page / html / topmenu / renderer.phtml
Сделайте этот запрос (он равен 0,0004 с), под foreach ($children as $child) {
$mageconnection = Mage::getSingleton("core/resource")->getConnection("core_read"); $query="select count(cataloginventory_stock_item.is_in_stock) as subcount,catalog_category_flat_store_1.`name` from catalog_category_flat_store_1 INNER JOIN catalog_category_product_index on catalog_category_product_index.category_id=catalog_category_flat_store_1.entity_id INNER JOIN cataloginventory_stock_item on cataloginventory_stock_item.product_id=catalog_category_product_index.product_id where cataloginventory_stock_item.is_in_stock=1 and catalog_category_product_index.category_id="; $subCatqueryId = str_replace('category-node-', '', $child->getId()); $prodCollection = $mageconnection->fetchAll("$query'{$subCatqueryId}'"); if($prodCollection[0]["subcount"] > 0) { $child->setLevel($childLevel); $child->setIsFirst($counter == 1); // these are the existing code .. ... ... $counter++; } }
Это очень быстрый и безопасный способ контроля количества продуктов.