La pagination de la grille ne fonctionne pas lors de l'utilisation de la clause de groupe dans la collection

9

Je travaille sur la grille des produits mais sa pagination ou le nombre de produits ne fonctionne pas (car ils affichent un nombre incorrect). comme ma fonction de _preparecollection de bloc est comme ci-dessous. j'ai ajouté le code de filtre de catégorie dans la collection ainsi je dois employer la clause de groupe pour empêcher l'erreur pour le même id existent déjà.

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

J'avais google et obtenu une réponse et l'ajouter à 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;
}

entrez la description de l'image ici Mais pas de chance, aidez à résoudre ce problème

Zaheerabbas
la source
Quelle classe étendez-vous? Mage_Adminhtml_Block_Widget_Grid?
B00MER
Oui, je prolongeMage_Adminhtml_Block_Widget_Grid
Zaheerabbas
Quelle requête renvoie un appel à getSelectCountSql?
Amasty

Réponses:

17

Collections et chargement paresseux dans Magento

La pagination ne fonctionne pas en raison de la façon dont les collections sont comptées et du fonctionnement du chargement paresseux avec les collections.

Les collections dans Magento implémentent la classe Countable. En raison du chargement paresseux des collections dans Magento, chaque fois que la méthode count()est appelée, les données doivent être chargées. Pour contourner ce problème, les collections implémentent une méthode appelée getSize(). Il clonera votre instruction SQL, l'enveloppera dans un COUNT()et retournera le résultat. Cela a permis à une collection d'obtenir un nombre total sans charger toutes les données. Cela permet d'ajouter des éléments tels que des filtres à la dernière minute.

Voici à quoi ressemble son Varien_Data_Collection_Db::getSize()partenaire 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;
    }

Fondamentalement, il supprime les limites, les colonnes, les commandes, etc. et laisse les filtres derrière. Ensuite, il ajoute un MySQL COUNT()aux colonnes.

Le problème

Normalement, sur une table, cela retournerait une ligne avec le nombre total. C'est pourquoi getSize()fait un fetchOne()contre la requête. Cependant, lorsque vous effectuez des opérations telles que des jointures de table, des bys de groupe, etc., vous ne retournerez pas une ligne, vous en retournerez plusieurs. C'est à cause de cela que vous devez modifier la getSize()méthode dans votre collection.

La solution

Voici à quoi devrait ressembler votre méthode maintenant:

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

Au lieu d'un fetchOne(), nous avons exécuté un fetchAll()enveloppé dans une count()fonction PHP. Maintenant, vos totaux reviendront de manière appropriée.

Ryan Street
la source
2
C'est ainsi que je souhaite que toutes les réponses sur le SE soient. Une solution ET une certaine profondeur.
shampooing du
4

Excellente solution. Peut-être que quelqu'un a le même problème que nous, alors je publierai une autre solution possible. Dans notre cas, nous avions une collection, qui comprenait parfois une instruction group by et parfois non, selon la grille où la collection était chargée. En utilisant la solution ci-dessus, nous avons trouvé deux problèmes:

  1. Si la collection est vide, la taille est évaluée à 1, bien qu'elle devrait être nulle.
  2. Dans les cas où la méthode getSize a été appelée sans instruction group by sur la collection, la taille est évaluée à 1 quel que soit le nombre d'éléments dans la collection.

Après avoir débogué un moment, nous avons découvert que, dans le cas 1, la partie

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

renvoie un tableau qui a une entrée avec la valeur 0. C'est pourquoi la fonction count renvoie 1 bien qu'aucune entrée n'ait été trouvée.

Dans le cas 2, la même partie renvoie un tableau avec une entrée, dont la valeur est la taille réelle de la collection. La fonction de comptage renvoie à nouveau 1 et non la valeur.

En recherchant une alternative, nous avons découvert que la collection de produits utilise une réécriture de la fonction getSelectCountSql (). Nous l'avons adapté et changé un peu, ce qui a abouti à cette solution:

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

Cela résout les deux problèmes que j'ai déjà mentionnés et, pour autant que je sache, cela fonctionne également pour les autres cas.

norvégien
la source
Merci de donner la référence du modèle de collection de produits. Ça m'a aidé.
Dinesh Yadav