我正在尝试将类别列添加到产品网格中.我修改了Mage_Adminhtml_Block_Catalog_Product_Grid.在_prepareCollection中添加了以下内容:
->joinField('category_ids',
'catalog/category_product_index',
'category_id',
'product_id=entity_id',
null,
'left')
Run Code Online (Sandbox Code Playgroud)
这给了我一个错误:a:5:{i:0; s:72:"具有相同id"16243"的物品(Mage_Catalog_Model_Product)已经存在"
在prepareColumns我添加:
$this->addColumn('category_ids',
array(
'header'=> Mage::helper('catalog')->__('Categories'),
'index' => 'category_ids',
'width' => '150px'
));
Run Code Online (Sandbox Code Playgroud)
如何修复查询以便我不会收到错误?是否可以按类别名称而不是ID显示和过滤?
一个论坛帖子显示类似的代码,但我无法使其与类别 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');
Run Code Online (Sandbox Code Playgroud)
scy*_*scy 19
我已经在这个问题上工作了好几天,终于解决了它.由于我的解决方案只是我开发的几个产品管理网格增强功能的一部分,我无法向您展示简单的剪切和粘贴解决方案.相反,我将重点放在什么做的,而不是如何去做.当然,我会提供尽可能多的代码片段,但我不能保证他们会自己工作.另请注意,我描述的解决方案仅使用Magento 1.3.2.4进行了测试.
首先,category_ids
您的产品属性很可能对您没用.这是以逗号分隔的类别ID列表(例如206,208,231
).我假设大多数人不需要那种形式的类别.(如果你这样做,你很幸运,只需将包含该category_ids
属性的列添加到你的网格中即可完成.)另外,据我所知,这个属性在Magento 1.4中不再存在.
此属性的问题在于它只是实际类别分配的冗余副本.权威类别信息存储在表中catalog_category_product
,每个产品/类别对一行.
由于类别是Magento 中的实体,并且不是通过产品属性直接引用,因此您不能使用joinAttribute()
或joinField()
使用它们.据我所知,你不能加入其他类型的所有实体,而不是加入其中的集合.
但是,您可以使用joinTable()
将类别ID放入结果集中,如下所示:
$collection->joinTable(
'catalog/category_product',
'product_id=entity_id',
array('single_category_id' => 'category_id'),
null,
'left'
);
Run Code Online (Sandbox Code Playgroud)
As you’ve already found out, this has to be added to Mage_Adminhtml_Block_Catalog_Product_Grid
’s _prepareCollection()
function. The best way to do this, as always, is to create your own module and add a class that extends the Magento class. Since there is no way to cleanly hook into the original _prepareCollection()
method, you will have to copy the whole method over to your overriding class and add the code there. (Remember to check for code changes in the original when updating Magento.)
We’re using a left join here, which will lead to multiple rows being returned for products having multiple categories. This is basically what we want, since we can then take the single category IDs (that’s why I called the field single_category_id
) and translate them to the category name. However, the collection cannot handle multiple rows for the same entity (i.e. the same product). That’s possibly where your error message comes from.
Now, getting the category name is a bit complicated, since we cannot join other entity types into our collection. Therefore we’ll have to do it the dirty way and directly take the names out of the EAV database data for category entities. I’ve tried to stay as clean as possible and not hard-code any attribute type IDs or the like into the query. You will need some knowledge about Magento’s EAV structure to understand what’s going on below.
Here’s how it works:
$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();
Run Code Online (Sandbox Code Playgroud)
After this, $nametable
will contain the name of the database table that contains the category’s name, $nameattrid
will contain the numeric attribute ID for "name".
Having this information, we can now join the correct EAV table manually into the query:
$collection->joinTable(
$nametable,
'entity_id=single_category_id',
array('single_category_name' => 'value'),
"attribute_id=$nameattrid",
'left'
);
Run Code Online (Sandbox Code Playgroud)
This will add a column single_category_name
to our result rows.
Remember that we still have one row per category for multiple-category products. That’s what we’re going to fix next. To do that, we’ll have to group the resulting rows by the product ID and simultaneously concatenate all those single_category_name
columns.
Grouping is relatively easy:
$collection->groupByAttribute('entity_id');
Run Code Online (Sandbox Code Playgroud)
However, you should insert this before the code that joins the category name table. Don’t worry, I’ll show you a correctly sorted chunk of code at the bottom.
Concatenating the category names is somewhat harder. Since we manually brought in an EAV table, we cannot use addExpressionAttributeToSelect()
on the category name attribute. Instead, we have to go down all the way to the Zend Framework database classes and manipulate the query there:
$collection->getSelect()->columns(
array('category_names' => new Zend_Db_Expr(
"IFNULL(GROUP_CONCAT(`$nametable`.`value` SEPARATOR '; '), '')"
)));
Run Code Online (Sandbox Code Playgroud)
This retrieves the underlying Zend_Db_Select
and adds a new expression column to it, which will concatenate the category names, separated by semicolon-space. Additionally, the IFNULL
will take care of products not having any category at all, setting the category_names
column to the empty string instead of a MySQL NULL
value.
Let’s summarize it up to here:
$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 '; '), '')")));
Run Code Online (Sandbox Code Playgroud)
In order to show the column, you have to add something like this to prepareColumns()
:
$this->addColumn('category_ids',
array(
'header' => Mage::helper('catalog')->__('Categories'),
'index' => 'category_names',
'width' => '150px',
'filter' => false,
'sortable' => false,
));
Run Code Online (Sandbox Code Playgroud)
The filter
and sortable
flags will prevent the column header to be clickable and also remove the filter text box for that column. Since we have done some heavy workarounding to get the categories column into the grid, these features won’t work anyway. I don’t need them, therefore I have not looked into how hard it is to make them work.
Now, if you have copied these two chunks of code into your installation, you’ll note that while the grid will correctly show the first page of results, above the table it will say that only one product has been returned and you won’t be able to paginate through the results. That’s because Magento uses a separate, automatically generated SQL query to count the number of results, and this method does not work with GROUP BY clauses. To fix that, we’ll have to override the collection class and add a workaround to it.
This is a class with that workaround:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
The getSelectCountSql()
method is based on the original one (and even calls it if $calculateSizeWithoutGroupClause
is not set), but additionally resets the GROUP BY
clause.
Save this new class as app/code/local/Our/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php
(or replace Our
by whatever your module name is) and enable the rewrite by changing your app/code/local/Our/Catalog/etc/config.xml
to contain a <models>
block:
<?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>
Run Code Online (Sandbox Code Playgroud)
Finally, setting
$collection->calculateSizeWithoutGroupClause = true;
Run Code Online (Sandbox Code Playgroud)
in _prepareCollection()
will enable our workaround for the admin grid and all is fine.
归档时间: |
|
查看次数: |
23594 次 |
最近记录: |