La paginación de cuadrícula no funciona cuando se usa la cláusula group en la colección

9

Estoy trabajando en la cuadrícula del producto, pero su paginación o recuento de productos no funciona (ya que muestra un recuento incorrecto). como mi función de bloque _preparecollection es la siguiente: he agregado el código de filtro de categoría en la colección, así que tengo que usar una cláusula de grupo para evitar que exista un error para la misma identificación.

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

Tenía google y obtuve respuesta y la agregué 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;
}

ingrese la descripción de la imagen aquí Pero no hay suerte, por favor ayuda a resolver esto

Zaheerabbas
fuente
¿Qué clase estás extendiendo? Mage_Adminhtml_Block_Widget_Grid?
B00MER
Sí, extiendoMage_Adminhtml_Block_Widget_Grid
Zaheerabbas
¿Qué consulta devuelve la llamada al getSelectCountSql?
Amasty

Respuestas:

17

Colecciones y carga perezosa en Magento

La razón por la que la paginación no funciona es por cómo se cuentan las colecciones y cómo funciona la carga diferida con las colecciones.

Las colecciones en Magento implementan la clase Countable. Debido a la carga lenta de colecciones en Magento, cada vez que count()se llama al método , los datos deben cargarse. Como solución alternativa a esto, las colecciones implementan un método llamado getSize(). Clonará su declaración SQL, la envolverá en a COUNT()y devolverá el resultado. Esto permitió que una colección obtuviera un recuento total sin cargar todos los datos. Esto permite agregar cosas como filtros en el último minuto.

Así es Varien_Data_Collection_Db::getSize()como se getSelectCountSql()ve su socio :

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

Básicamente, elimina límites, columnas, pedidos, etc. y deja los filtros atrás. Luego agrega un MySQL COUNT()a las columnas.

El problema

Normalmente, en una tabla, esto devolvería una fila con el recuento total. Esta es la razón por la cual se getSize()hace una fetchOne()contra la consulta. Sin embargo, al hacer cosas como uniones de tabla, bys de grupo y similares, no devolverá una fila, devolverá múltiples. Es por esto que necesita alterar el getSize()método en su colección.

La solución

Así es como debería verse su método ahora:

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

En lugar de a fetchOne(), ejecutamos una función fetchAll()envuelta en count()PHP. Ahora sus totales volverán apropiadamente.

Ryan Street
fuente
2
Así es como desearía que todas las respuestas en el SE fueran. Una solución Y algo de profundidad.
champú
4

Gran solución Quizás alguien tenga el mismo problema que nosotros, así que publicaré otra posible solución. En nuestro caso, teníamos una colección, que a veces incluía un grupo por declaración y otras no, según la cuadrícula donde se cargó la colección. Usando la solución anterior, encontramos dos problemas:

  1. Si la colección está vacía, el tamaño se valora como 1, aunque debería ser cero.
  2. En los casos en que se llamó al método getSize sin un grupo mediante una declaración en la colección, el tamaño se valora como 1, sin importar cuántos elementos haya en la colección.

Después de depurar un tiempo descubrimos que en el caso 1 la parte

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

devuelve una matriz que tiene una entrada con valor 0. Es por eso que la función de conteo devuelve 1 aunque no se encontraron entradas.

En el caso 2, la misma parte devuelve una matriz con una entrada, cuyo valor es el tamaño real de la colección. La función de conteo nuevamente devuelve 1 y no el valor.

Al buscar una alternativa, descubrimos que la colección de productos utiliza una reescritura de la función getSelectCountSql (). Adaptamos esto y lo cambiamos un poco, lo que terminó en esta solución:

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

Resuelve los dos problemas que ya mencioné y, por lo que puedo ver, también funciona para los otros casos.

norgeindian
fuente
Gracias por dar la referencia del modelo de colección de productos. Me ayudó.
Dinesh Yadav