在集合中使用group子句时,网格分页不起作用


9

我在产品网格上工作,但是其分页或产品计数不起作用(因为它显示的计数错误)。由于我的_preparecollection块的功能如下。我在集合中添加了类别过滤器代码,因此我必须使用group子句来防止相同ID已存在的错误。

    protected function _prepareCollection()
    {
        $store = $this->_getStore();
        $collection = Mage::getModel('catalog/product')->getCollection()
            ->addAttributeToSelect('sku')
            ->addAttributeToSelect('name')
            ->addAttributeToSelect('attribute_set_id')
            ->addAttributeToSelect('type_id')
            ->joinField('category_id',
                'catalog/category_product',
                'category_id',
                'product_id=entity_id',
                null,
                'left');
$collection->addAttributeToFilter('category_id', array('in' => array(4,10)))
            ->distinct(true);
            $collection->getSelect()->group('e.entity_id');


        if (Mage::helper('catalog')->isModuleEnabled('Mage_CatalogInventory')) {
            $collection->joinField('qty',
                'cataloginventory/stock_item',
                'qty',
                'product_id=entity_id',
                '{{table}}.stock_id=1',
                'left');
        }
        $collection->joinField('position',
                'catalog/category_product',
                'position',
                'product_id=entity_id',
                null,
                'left');
        $collection->joinField('websites',
            'catalog/product_website',
            'website_id',
            'product_id=entity_id',
            null,
            'left');
        if ($store->getId()) {
            //$collection->setStoreId($store->getId());
            $adminStore = Mage_Core_Model_App::ADMIN_STORE_ID;
            $collection->addStoreFilter($store);
            $collection->joinAttribute(
                'name',
                'catalog_product/name',
                'entity_id',
                null,
                'inner',
                $adminStore
            );

            $collection->joinAttribute(
                'custom_name',
                'catalog_product/name',
                'entity_id',
                null,
                'inner',
                $store->getId()
            );
            $collection->joinAttribute(
                'status',
                'catalog_product/status',
                'entity_id',
                null,
                'inner',
                $store->getId()
            );
            $collection->joinAttribute(
                'visibility',
                'catalog_product/visibility',
                'entity_id',
                null,
                'inner',
                $store->getId()
            );
            $collection->joinAttribute(
                'price',
                'catalog_product/price',
                'entity_id',
                null,
                'left',
                $store->getId()
            );
        }
        else {
            $collection->addAttributeToSelect('price');
            $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner');
            $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner');
        }

        $this->setCollection($collection);

        parent::_prepareCollection();
        $this->getCollection()->addWebsiteNamesToResult();
        return $this;
    }

我有谷歌并得到答案并将其添加到 lib/varian/data/collection/db.php

    public function 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);

        if(count($this->getSelect()->getPart(Zend_Db_Select::GROUP)) > 0) {
            $countSelect->reset(Zend_Db_Select::GROUP);
            $countSelect->distinct(true);
            $group = $this->getSelect()->getPart(Zend_Db_Select::GROUP);
            $countSelect->columns("COUNT(DISTINCT ".implode(", ", $group).")");
        } else {
            $countSelect->columns('COUNT(*)');
        }
        return $countSelect;
}

在此处输入图片说明 但没有运气请帮助解决这个问题


你要上什么课?Mage_Adminhtml_Block_Widget_Grid
B00MER 2015年

是的,我延伸Mage_Adminhtml_Block_Widget_Grid
Zaheerabbas 2015年

什么查询返回对getSelectCountSql的调用?
Amasty 2015年

Answers:


17

Magento中的集合和延迟加载

分页不起作用的原因是因为如何计算集合以及集合的延迟加载方式。

Magento中的collection实现该类Countable。由于Magento中的集合的延迟加载,每当count()调用该方法时,都必须加载数据。解决此问题的方法是,集合会实现一种称为的方法getSize()。它将克隆您的SQL语句,将其包装在中COUNT()并返回结果。这样一来,集合就可以在不加载所有数据的情况下获得总数。这允许在最后一分钟添加诸如过滤器之类的内容。

这就是Varien_Data_Collection_Db::getSize()它的伙伴,它getSelectCountSql()看起来像:

/**
     * Get collection size
     *
     * @return int
     */
    public function getSize()
    {
        if (is_null($this->_totalRecords)) {
            $sql = $this->getSelectCountSql();
            $this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams);
        }
        return intval($this->_totalRecords);
    }

    /**
     * Get SQL for get record count
     *
     * @return Varien_Db_Select
     */
    public function 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->columns('COUNT(*)');

        return $countSelect;
    }

基本上,它会删除限制,列,订单等,并将过滤器留在后面。然后,它将MySQL添加COUNT()到列中。

问题

通常,在一张表上,这将返回总计数的一行。这就是为什么getSize()a fetchOne()对查询。但是,当执行诸如表联接,分组依据之类的操作时,您将不会返回一行,而将返回多行。因此,您需要更改getSize()集合中的方法。

解决方案

这是您的方法现在应如下所示:

public function getSize() {

        if ( is_null( $this->_totalRecords ) ) {
            $sql = $this->getSelectCountSql();
            // fetch all rows since it's a joined table and run a count against it.
            $this->_totalRecords = count( $this->getConnection()->fetchall( $sql, $this->_bindParams ) );
        }

        return intval( $this->_totalRecords );
    }

而不是fetchOne(),我们运行了一个fetchAll()包装在count()PHP函数中的包装。现在您的总数将适当地返回。


2
我希望SE上的所有答案都是这样。一个解决方案和一些深度。
洗发水

4

很好的解决方案。也许有人遇到和我们一样的问题,所以我将发布另一个可能的解决方案。在我们的例子中,我们有一个集合,该集合有时包括一个group by语句,有时不包括group语句,具体取决于加载集合的网格。使用上述解决方案,我们发现了两个问题:

  1. 如果集合为空,则大小将为1,尽管应为零。
  2. 如果在集合上没有使用group by语句的情况下调用getSize方法,则无论集合中有多少个项目,大小都将被计算为1。

经过调试一段时间后,我们发现情况1

$this->getConnection()->fetchall( $sql, $this->_bindParams ) 

返回一个数组,该数组具有一个值为0的条目。这就是为什么count函数返回1的原因,尽管未找到任何条目。

在情况2中,同一部分返回一个具有一个条目的数组,其值是集合的实际大小。计数功能再次返回1,而不是值。

在寻找替代方案时,我们发现产品集合使用getSelectCountSql()函数的重写。我们对此进行了调整,并对其进行了一些更改,从而在此解决方案中结束:

public function getSelectCountSql()
{
    $countSelect = parent::getSelectCountSql();
    $countSelect->reset(Zend_Db_Select::COLUMNS);
    $countSelect->reset(Zend_Db_Select::GROUP);
    $countSelect->columns('COUNT(DISTINCT item_id)');

    return $countSelect;
}

它解决了我已经提到的两个问题,据我所知,它也适用于其他情况。


感谢您提供产品收集模型的参考。它帮助了我。
Dinesh Yadav
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.