Paginação de grade não funciona quando usar cláusula de grupo na coleção

9

Estou trabalhando na grade do produto, mas sua paginação ou contagem de produtos não está funcionando (pois exibe contagem incorreta). Como minha função de bloco _preparecollection é a seguir, adicionei o código de filtro de categoria na coleção, portanto, tenho que usar a cláusula de grupo para evitar que já exista um erro para o mesmo 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;
    }

Eu tinha o google e obtive resposta e adicione-o a 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;
}

insira a descrição da imagem aqui Mas sem sorte, por favor, ajude a resolver isso

Zaheerabbas
fonte
Que classe você está estendendo? Mage_Adminhtml_Block_Widget_Grid?
B00MER
Sim i estendendoMage_Adminhtml_Block_Widget_Grid
Zaheerabbas
Que consulta retorna a chamada para getSelectCountSql?
Amasty 07/04/2015

Respostas:

17

Coleções e carregamento lento no Magento

A razão pela qual a paginação não funciona é devido à maneira como as coleções são contadas e como o carregamento lento funciona com as coleções.

Coleções no Magento implementam a classe Countable. Devido ao carregamento lento de coleções no Magento, sempre que o método count()é chamado, os dados precisam ser carregados. Como solução alternativa, as coleções implementam um método chamado getSize(). Ele irá clonar sua instrução SQL, envolvê-la em COUNT()e retornar o resultado. Isso permitiu que uma coleção obtivesse uma contagem total sem carregar todos os dados. Isso permite que itens como filtros sejam adicionados no último minuto.

É assim que é Varien_Data_Collection_Db::getSize()e é o parceiro 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;
    }

Basicamente, elimina limites, colunas, pedidos, etc. e deixa os filtros para trás. Em seguida, ele adiciona um MySQL COUNT()às colunas.

O problema

Normalmente, em uma tabela, isso retornaria uma linha com a contagem total. É por getSize()isso que faz fetchOne()contra a consulta. No entanto, ao fazer coisas como junções de tabelas, agrupamentos de itens e similares, você não retornará uma linha, retornará várias. É por isso que você precisa alterar o getSize()método em sua coleção.

A solução

É assim que seu método deve ser agora:

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 );
    }

Em vez de a fetchOne(), executamos um fetchAll()empacotado em uma count()função PHP. Agora seus totais retornarão adequadamente.

Ryan Street
fonte
2
É assim que desejo todas as respostas sobre o SE. Uma solução E alguma profundidade.
shampoo
4

Ótima solução. Talvez alguém tenha o mesmo problema que o nosso, por isso postarei outra solução possível. No nosso caso, tínhamos uma coleção, que algumas vezes incluía um grupo por declaração e outras vezes, dependendo da grade em que a coleção foi carregada. Usando a solução acima, encontramos dois problemas:

  1. Se a coleção estiver vazia, o tamanho será avaliado como 1, embora deva ser zero.
  2. Nos casos em que o método getSize foi chamado sem um grupo por instrução na coleção, o tamanho é avaliado como 1, não importa quantos itens estejam na coleção.

Depois de depurar um pouco, descobrimos que, no caso 1, a parte

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

retorna uma matriz que possui uma entrada com o valor 0. É por isso que a função count retorna 1, embora nenhuma entrada tenha sido encontrada.

No caso 2, a mesma parte retorna uma matriz com uma entrada, cujo valor é o tamanho real da coleção. A função count novamente retorna 1 e não o valor.

Buscando uma alternativa, descobrimos, que a coleção de produtos utiliza uma reescrita da função getSelectCountSql (). Nós adaptamos e modificamos um pouco, o que terminou nesta solução:

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;
}

Ele resolve os dois problemas que eu já mencionei e, tanto quanto posso ver, também funciona para os outros casos.

norgeindiano
fonte
Obrigado por fornecer a referência do modelo de coleção de produtos. Isso me ajudou.
Dinesh Yadav