Добавьте столбец категорий в таблицу продуктов в Magento admin
Я пытаюсь добавить столбец категории в таблицу продуктов. Я изменил Mage_Adminhtml_Block_Catalog_Product_Grid
. В _prepareCollection
добавлено следующее:
->joinField('category_ids',
'catalog/category_product_index',
'category_id',
'product_id=entity_id',
null,
'left')
что выдает ошибку: a:5:{i:0;s:72:"Item (Mage_Catalog_Model_Product) with the same id "16243" already exist"
.
В prepareColumns я добавляю:
$this->addColumn('category_ids',
array(
'header'=> Mage::helper('catalog')->__('Categories'),
'index' => 'category_ids',
'width' => '150px'
));
Как я могу исправить свой запрос, чтобы я не получил ошибку? Можно ли показывать и фильтровать по именам категорий вместо идентификаторов?
В сообщении на форуме показан похожий код, но я не смог заставить его работать с категориями http://www.magentocommerce.com/boards/viewthread/44534/
static protected $COLUMN_ID_TRADE_REFERENCES = 'ref_text';
protected function _prepareCollection()
{
$store = $this->_getStore();
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('name')
->addAttributeToSelect('attribute_set_id')
->addAttributeToSelect('type_id')
->addAttributeToSelect('ref_text')
->joinTable('productreferences/reference',
'product_id=entity_id',
array('ref_text'),
null,
'left')
->joinField('qty',
'cataloginventory/stock_item',
'qty',
'product_id=entity_id',
'{{table}}.stock_id=1',
'left')
->addStaticField('ref_text')
->addExpressionAttributeToSelect(self::$COLUMN_ID_TRADE_REFERENCES,
'GROUP_CONCAT(ref_text SEPARATOR " ; ")',
'ref_text')
->groupByAttribute('entity_id');
Ответы
Ответ 1
Я работал над этой проблемой несколько дней и окончательно решил ее. Поскольку мое решение является лишь частью нескольких усовершенствований сетки админов, которые я разработал, я не могу показать вам простое решение для вырезания и вставки. Вместо этого я сосредоточусь на , что делать вместо , как. Конечно, я предоставляю как можно больше фрагментов кода, но я не могу гарантировать, что они будут работать самостоятельно. Также обратите внимание, что описанное решение было протестировано только с Magento 1.3.2.4.
Прежде всего, атрибут category_ids
ваших продуктов, скорее всего, будет бесполезен для вас. Это список идентификаторов категорий, разделенных запятыми (например, 206,208,231
). Я предполагаю, что большинству людей не нужны категории в этой форме. (Если вы это делаете, вам повезло, просто добавьте столбец, содержащий атрибут category_ids
, в вашу сетку и сделайте это.) Кроме того, насколько я знаю, этот атрибут больше не существует в Magento 1.4.
Проблема с этим атрибутом заключается в том, что это просто избыточная копия фактического присвоения категории. Информация об авторитетной категории хранится в таблице catalog_category_product
, по одной строке на пару товаров/категорий.
Так как категории являются объектами в Magento и напрямую не связаны через атрибут продукта, вы не можете использовать с ними joinAttribute()
или joinField()
. Насколько я знаю, вы не можете присоединиться ко всем сущностям другого типа, чем к коллекции в нем.
Однако вы можете использовать joinTable()
, чтобы поместить идентификаторы категорий в набор результатов следующим образом:
$collection->joinTable(
'catalog/category_product',
'product_id=entity_id',
array('single_category_id' => 'category_id'),
null,
'left'
);
Как вы уже узнали, это нужно добавить в функцию Mage_Adminhtml_Block_Catalog_Product_Grid
s _prepareCollection()
. Лучший способ сделать это, как всегда, - создать свой собственный модуль и добавить класс, расширяющий класс Magento. Поскольку нет способа вставить оригинальный метод _prepareCollection()
, вы должны будете скопировать весь метод в свой переопределяющий класс и добавить туда код. (Не забудьте проверить изменения кода в оригинале при обновлении Magento.)
Здесь использовалось левое соединение, что приведет к возврату нескольких строк для продуктов, имеющих несколько категорий. Это в основном то, что мы хотим, так как мы можем взять идентификаторы одной категории (поэтому я назвал поле single_category_id
) и перевести их в название категории. Тем не менее, коллекция не может обрабатывать несколько строк для одного и того же объекта (т.е. Того же продукта). Возможно, откуда появляется сообщение об ошибке.
Теперь получение названия категории немного сложно, поскольку мы не можем присоединиться к другим типам сущностей в нашей коллекции. Поэтому необходимо сделать это грязным способом и напрямую вывести имена из данных базы данных EAV для объектов категории. Ive старался оставаться как можно чище, а не жестко кодировать любые идентификаторы типа атрибута или тому подобное в запросе. Вам понадобятся знания о структуре Magentos EAV, чтобы понять, что происходит дальше.
Вот как это работает:
$res = Mage::getSingleton('core/resource');
$eav = Mage::getModel('eav/config');
$nameattr = $eav->getAttribute('catalog_category', 'name');
$nametable = $res->getTableName('catalog/category') . '_' . $nameattr->getBackendType();
$nameattrid = $nameattr->getAttributeId();
После этого $nametable
будет содержать имя таблицы базы данных, которая содержит имя категории, $nameattrid
будет содержать идентификатор числового атрибута для "name".
Имея эту информацию, мы можем теперь вручную присоединить таблицу EAV к запросу:
$collection->joinTable(
$nametable,
'entity_id=single_category_id',
array('single_category_name' => 'value'),
"attribute_id=$nameattrid",
'left'
);
Это добавит столбец single_category_name
к нашим результирующим строкам.
Помните, что мы по-прежнему имеем одну строку для каждой категории для продуктов с несколькими категориями. Вот что должно было исправить дальше. Для этого достаточно сгруппировать полученные строки с идентификатором продукта и одновременно объединить все те столбцы single_category_name
.
Группирование относительно просто:
$collection->groupByAttribute('entity_id');
Однако вы должны вставить это перед кодом, который присоединяется к таблице имен категорий. Не волнуйтесь, я покажу вам правильно отсортированный фрагмент кода внизу.
Конкатенация названий категорий несколько сложнее. Поскольку мы вручную приводили таблицу EAV, мы не можем использовать addExpressionAttributeToSelect()
для атрибута названия категории. Вместо этого мы должны пройти весь путь до классов базы данных Zend Framework и манипулировать запросом там:
$collection->getSelect()->columns(
array('category_names' => new Zend_Db_Expr(
"IFNULL(GROUP_CONCAT(`$nametable`.`value` SEPARATOR '; '), '')"
)));
Это возвращает базовый Zend_Db_Select
и добавляет к нему новый столбец выражения, который объединяет имена категорий, разделенные точкой с запятой. Кроме того, IFNULL
позаботится о продуктах, не имеющих какой-либо категории вообще, установив столбец category_names
в пустую строку вместо значения MySQL NULL
.
Подведем итог:
$collection->joinTable('catalog/category_product',
'product_id=entity_id', array('single_category_id' => 'category_id'),
null, 'left')
->groupByAttribute('entity_id')
->joinTable($nametable,
"entity_id=single_category_id", array('single_category_name' => 'value'),
"attribute_id=$nameattrid", 'left')
->getSelect()->columns(array('category_names' => new Zend_Db_Expr("IFNULL(GROUP_CONCAT(`$nametable`.`value` SEPARATOR '; '), '')")));
Чтобы показать столбец, вы должны добавить что-то вроде этого в prepareColumns()
:
$this->addColumn('category_ids',
array(
'header' => Mage::helper('catalog')->__('Categories'),
'index' => 'category_names',
'width' => '150px',
'filter' => false,
'sortable' => false,
));
Флаги filter
и sortable
предотвращают кликабельность заголовка столбца, а также удаляют текстовое поле фильтра для этого столбца. Поскольку мы сделали некоторые тяжелые попытки обхода, чтобы получить столбцы категорий в сетке, эти функции вообще не будут работать. Мне они не нужны, поэтому я не смотрел, как трудно заставить их работать.
Теперь, если вы скопировали эти два фрагмента кода в свою установку, вы заметите, что, пока сетка будет правильно показывать первую страницу результатов, над таблицей будет указано, что был возвращен только один продукт, и вы не будете способный разбивать страницы на результаты. Это потому, что Magento использует отдельный, автоматически сгенерированный SQL-запрос для подсчета количества результатов, и этот метод не работает с предложениями GROUP BY. Чтобы исправить это, нужно переопределить класс коллекции и добавить к нему обходной путь.
Это класс с таким обходным путем:
class Our_Catalog_Model_Resource_Eav_Mysql4_Product_Collection extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection {
public $calculateSizeWithoutGroupClause = false;
public function getSelectCountSql()
{
if (!$this->calculateSizeWithoutGroupClause) {
return parent::getSelectCountSql();
}
$this->_renderFilters();
$countSelect = clone $this->getSelect();
$countSelect->reset(Zend_Db_Select::ORDER);
$countSelect->reset(Zend_Db_Select::LIMIT_COUNT);
$countSelect->reset(Zend_Db_Select::LIMIT_OFFSET);
$countSelect->reset(Zend_Db_Select::COLUMNS);
$countSelect->reset(Zend_Db_Select::GROUP);
$countSelect->from('', 'COUNT(DISTINCT `e`.`entity_id`)');
return $countSelect;
}
}
Метод getSelectCountSql()
основан на исходном (и даже вызывает его, если $calculateSizeWithoutGroupClause
не установлен), но дополнительно сбрасывает предложение GROUP BY
.
Сохраните этот новый класс как app/code/local/Our/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php
(или замените Our
на любое имя вашего модуля) и включите переписывание, изменив app/code/local/Our/Catalog/etc/config.xml
на :
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Our_Catalog>
<version>1.2.3</version>
</Our_Catalog>
</modules>
<global>
<models>
<catalog_resource_eav_mysql4>
<rewrite>
<product_collection>Our_Catalog_Model_Resource_Eav_Mysql4_Product_Collection</product_collection>
</rewrite>
</catalog_resource_eav_mysql4>
</models>
</global>
</config>
Наконец, установка
$collection->calculateSizeWithoutGroupClause = true;
в _prepareCollection()
будет включать наше обходное решение для сетки администратора и все в порядке.
Ответ 2
В приведенной выше цитате.
Фильтр и сортируемые флаги предотвращают доступ к заголовку столбца, а также удаляют текстовое поле фильтра для этого столбца. Поскольку мы сделали некоторые тяжелые попытки обхода, чтобы получить столбцы категорий в сетке, эти функции вообще не будут работать. Мне они не нужны, поэтому я не смотрел, как трудно заставить их работать.
Все, что вам нужно сделать, это вставить код ниже
$collection = Mage::getModel('catalog/category')->getCollection()->addAttributeToSelect('name');
$options = array();
foreach ($collection as $item){
if($item->getId() != ''){
$options[$item->getId()] = $item->getName();
}
}
$this->addColumn('category_ids',
array(
'header' => Mage::helper('catalog')->__('Categories'),
'index' => 'single_category_id',
'width' => '150px',
'type' => 'options',
'options' => $options
));
вместо
$this->addColumn('category_ids',
array(
'header' => Mage::helper('catalog')->__('Categories'),
'index' => 'category_names',
'width' => '150px',
'filter' => false,
'sortable' => false,
));
Ответ 3
если вы просто хотите добавить категорию (не путь к категории), и если у продукта есть только одна категория, добавьте это в сборку:
$collection->joinAttribute('catname','catalog_category/name','category_ids',null,'left');
Ответ 4
чтобы отобразить имя o всех категорий, которые вы можете использовать
$this->addColumn('category_ids',
array(
'header'=> Mage::helper('catalog')->__('Category'),
'type' => 'options',
'index' => 'category_ids',
'options' => $this->catOptions,
'renderer' => 'Your_Module_Block_Adminhtml_List_Cat',
));
в вашем классе
class Your_Module_Block_Adminhtml_List_Cat extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{
public function render(Varien_Object $row)
{
$product = Mage::getModel('catalog/product')->load($row->getEntityId());
$cats = $product->getCategoryIds();
$allCats = '';
foreach($cats as $key => $cat)
{
$_category = Mage::getModel('catalog/category')->load($cat);
$allCats.= $_category->getName();
if($key < count($cats)-1)
$allCats.= ' ,';
}
return $allCats;
}
}
Ответ 5
Ответ здесь, я тестировал лично, и он работает: http://kathytong.blogspot.it/2010/09/magento-how-to-add-category-filter-to.html
Ответ 6
вот два уже выполненных и протестированных решения, которые дают вам возможность искать/фильтровать продукты по категориям или добавлять эти продукты в любую другую категорию, чтобы увидеть эти 2 расширения
http://www.jtechextensions.com/magento-extensions/category-filter.html
http://www.jtechextensions.com/magento-extensions/mass-category-management-for-products-grid.html
Ответ 7
Попробуйте это расширение, вам очень поможет.
http://codecanyon.net/item/magento-admin-catalog-product-filter/4669822
Ответ 8
Я знаю, что уже поздно отвечать, но недавно я увидел этот вопрос, поэтому я отвечаю, так как он может помочь и другим.
Ссылка здесь: here
Вам необходимо создать расширение для отображения категории в сетке продукта. Создайте следующие файлы, и он будет работать для вас:
Создайте новый файл в app/code/local/SoftProdigy/AdminGridCategoryFilter/Block/Catalog/Product/Grid/Render/Category.php
и добавьте следующий код:
<?php
class SoftProdigy_AdminGridCategoryFilter_Block_Catalog_Product_Grid_Render_Category extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{
public function render(Varien_Object $row)
{
$product = Mage::getModel('catalog/product')->load($row->getEntityId());
$cats = $product->getCategoryIds();
$allCats = '';
foreach($cats as $key => $cat)
{
$_category = Mage::getModel('catalog/category')->load($cat);
$allCats.= $_category->getName();
if($key < count($cats)-1)
$allCats.= ',<br />';
}
return $allCats;
}
}
Создайте новый файл в app/code/local/SoftProdigy/AdminGridCategoryFilter/etc/config.xml
и добавьте следующий код:
<?xml version="1.0"?>
<config>
<modules>
<SoftProdigy_AdminGridCategoryFilter>
<version>0.0.0.1</version>
</SoftProdigy_AdminGridCategoryFilter>
</modules>
<global>
<models>
<admingridcategoryfilter>
<class>SoftProdigy_AdminGridCategoryFilter_Model</class>
</admingridcategoryfilter>
</models>
<helpers>
<admingridcategoryfilter>
<class>SoftProdigy_AdminGridCategoryFilter_Helper</class>
</admingridcategoryfilter>
</helpers>
<blocks>
<admingridcategoryfilter>
<class>SoftProdigy_AdminGridCategoryFilter_Block</class>
</admingridcategoryfilter>
</blocks>
</global>
<adminhtml>
<events>
<core_block_abstract_prepare_layout_before>
<observers>
<admingridcategoryfilter>
<class>admingridcategoryfilter/observer</class>
<method>addCategoryFilterToProductGrid</method>
</admingridcategoryfilter>
</observers>
</core_block_abstract_prepare_layout_before>
</events>
</adminhtml>
</config>
Создайте новый файл в app/code/local/SoftProdigy/AdminGridCategoryFilter/Helper/Data.php
и добавьте следующий код:
<?php
class SoftProdigy_AdminGridCategoryFilter_Helper_Data extends Mage_Core_Helper_Abstract
{
}
Создайте новый файл в app/code/local/SoftProdigy/AdminGridCategoryFilter/Model/Observer.php
и добавьте следующий код:
<?php
class SoftProdigy_AdminGridCategoryFilter_Model_Observer
{
public function addCategoryFilterToProductGrid(Varien_Event_Observer $observer)
{
$block = $observer->getEvent()->getBlock();
if( ($block instanceof Mage_Adminhtml_Block_Catalog_Product_Grid) ) {
$block->addColumnAfter('softprodigy_category_list', array(
'header' => Mage::helper('admingridcategoryfilter')->__('Category'),
'index' => 'softprodigy_category_list',
'sortable' => false,
'width' => '250px',
'type' => 'options',
'options' => Mage::getSingleton('admingridcategoryfilter/system_config_source_category')->toOptionArray(),
'renderer' => 'admingridcategoryfilter/catalog_product_grid_render_category',
'filter_condition_callback' => array($this, 'filterCallback'),
),'name');
}
}
public function filterCallback($collection, $column)
{
$value = $column->getFilter()->getValue();
$_category = Mage::getModel('catalog/category')->load($value);
$collection->addCategoryFilter($_category);
return $collection;
}
}
Создайте новый файл в app/code/local/SoftProdigy/AdminGridCategoryFilter/Model/System/Config/Source/Category.php
и добавьте следующий код:
<?php
class SoftProdigy_AdminGridCategoryFilter_Model_System_Config_Source_Category
{
public function toOptionArray($addEmpty = true)
{
$options = array();
foreach ($this->load_tree() as $category) {
$options[$category['value']] = $category['label'];
}
return $options;
}
public function buildCategoriesMultiselectValues(Varien_Data_Tree_Node $node, $values, $level = 0)
{
$level++;
$values[$node->getId()]['value'] = $node->getId();
$values[$node->getId()]['label'] = str_repeat("--", $level) . $node->getName();
foreach ($node->getChildren() as $child)
{
$values = $this->buildCategoriesMultiselectValues($child, $values, $level);
}
return $values;
}
public function load_tree()
{
$store = Mage::app()->getFrontController()->getRequest()->getParam('store', 0);
$parentId = $store ? Mage::app()->getStore($store)->getRootCategoryId() : 1; // Current store root category
$tree = Mage::getResourceSingleton('catalog/category_tree')->load();
$root = $tree->getNodeById($parentId);
if($root && $root->getId() == 1)
{
$root->setName(Mage::helper('catalog')->__('Root'));
}
$collection = Mage::getModel('catalog/category')->getCollection()
->setStoreId($store)
->addAttributeToSelect('name')
->addAttributeToSelect('is_active');
$tree->addCollectionData($collection, true);
return $this->buildCategoriesMultiselectValues($root, array());
}
}
Создайте новый файл в app/etc/modules/SoftProdigy_AdminGridCategoryFilter.xml
и добавьте следующий код:
<?xml version="1.0"?>
<config>
<modules>
<SoftProdigy_AdminGridCategoryFilter>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_Catalog />
<Mage_Adminhtml />
</depends>
</SoftProdigy_AdminGridCategoryFilter>
</modules>
</config>
Теперь очистите кеш от управления кешем, и вы увидите столбец категории в сетке продукта.