Я работал над этой проблемой несколько дней и наконец решил ее.Поскольку мое решение является лишь одной из нескольких разработок, которые я разработал, я не могу показать вам простое решение для вырезания и вставки.Вместо этого я сосредоточусь на , что сделать вместо , как , чтобы сделать это.Конечно, я предоставлю как можно больше фрагментов кода, но я не могу гарантировать, что они будут работать самостоятельно.Также обратите внимание, что решение, которое я описал, было протестировано только с 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
_prepareCollection()
.Как всегда, лучший способ сделать это - создать свой собственный модуль и добавить класс, который расширяет класс Magento.Поскольку нет способа аккуратно подключиться к исходному методу _prepareCollection()
, вам придется скопировать весь метод в ваш переопределяющий класс и добавить туда код.(Не забудьте проверить наличие изменений кода в оригинале при обновлении Magento.)
Мы используем здесь левое соединение, которое приведет к возвращению нескольких строк для продуктов, имеющих несколько категорий.Это в основном то, что мы хотим, так как мы можем затем взять идентификаторы одной категории (поэтому я назвал поле single_category_id
) и преобразовать их в название категории.Однако коллекция не может обрабатывать несколько строк для одного и того же объекта (т. Е. Одного и того же продукта).Это, возможно, то, откуда приходит ваше сообщение об ошибке.
Теперь получить имя категории немного сложно, поскольку мы не можем объединять другие типы сущностей в нашу коллекцию.Поэтому нам придется сделать это грязным путем и напрямую убрать имена из данных базы данных EAV для сущностей категорий.Я старался оставаться максимально чистым и не кодировать жестко идентификаторы типа атрибута или тому подобное в запросе.Вам понадобятся некоторые знания о структуре EAV Magento , чтобы понять, что происходит ниже.
Вот как это работает:
$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
будет содержать числовой идентификатор атрибута для «имени».
Имея эту информацию, мы теперь можем вручную присоединить правильную таблицу 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
блок <models>
:
<?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()
включит наш обходной путь для сетки администратора, и все в порядке.