L'impaginazione della griglia non funziona quando si utilizza la clausola group nella raccolta


9

Sto lavorando sulla griglia dei prodotti ma la sua impaginazione o il conteggio dei prodotti non funzionano (poiché visualizzano un conteggio errato). poiché la mia funzione di blocco _preparecollection è la seguente: ho aggiunto il codice filtro categoria nella raccolta, quindi devo usare la clausola di gruppo per evitare che l'errore per lo stesso ID sia già esistente.

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

Avevo google e ho ricevuto risposta e aggiungilo 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;
}

inserisci qui la descrizione dell'immagine Ma niente fortuna, aiutatemi a risolvere questo problema


Che classe stai estendendo? Mage_Adminhtml_Block_Widget_Grid?
B00MER,

Sì, ho estesoMage_Adminhtml_Block_Widget_Grid
Zaheerabbas il

Quale query restituisce la chiamata a getSelectCountSql?
Amasty,

Risposte:


17

Collezioni e Lazy Loading in Magento

Il motivo per cui l'impaginazione non funziona è dovuto al modo in cui vengono conteggiate le raccolte e al funzionamento del caricamento lento con le raccolte.

Le collezioni in Magento implementano la classe Countable. A causa del caricamento lento delle raccolte in Magento, ogni volta che count()viene chiamato il metodo , i dati devono essere caricati. Per ovviare a questo, le raccolte implementano un metodo chiamato getSize(). Clonerà la tua istruzione SQL, la avvolgerà in a COUNT()e restituirà il risultato. Ciò ha consentito a una raccolta di ottenere un conteggio totale senza caricare tutti i dati. Ciò consente di aggiungere elementi come i filtri all'ultimo minuto.

Questo è ciò Varien_Data_Collection_Db::getSize()che getSelectCountSql()assomiglia al suo partner :

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

Fondamentalmente, elimina limiti, colonne, ordini, ecc. E lascia i filtri alle spalle. Quindi aggiunge un MySQL COUNT()alle colonne.

Il problema

Normalmente, su una tabella, ciò restituisce una riga con il conteggio totale. Questo è il motivo per cui getSize()fa un fetchOne()contro l'interrogazione. Tuttavia, quando si eseguono operazioni come join di tabella, raggruppamento per byte e simili, non si restituirà una riga, ma si restituirà più. È per questo motivo che è necessario modificare il getSize()metodo nella raccolta.

La soluzione

Ecco come dovrebbe apparire il tuo metodo ora:

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

Invece di a fetchOne(), abbiamo eseguito un fetchAll()wrapping in una count()funzione PHP. Ora i tuoi totali torneranno in modo appropriato.


2
È così che desidero che tutte le risposte su SE fossero. Una soluzione E un po 'di profondità.
shampoo

4

Ottima soluzione Forse qualcuno ha lo stesso problema che abbiamo avuto, quindi posterò un'altra possibile soluzione. Nel nostro caso avevamo una raccolta, che a volte includeva un gruppo per istruzione e talvolta no, a seconda della griglia in cui era caricata la raccolta. Utilizzando la soluzione sopra, abbiamo riscontrato due problemi:

  1. Se la raccolta è vuota, la dimensione è valutata come 1, sebbene dovrebbe essere zero.
  2. Nei casi in cui il metodo getSize è stato chiamato senza un'istruzione group by sulla raccolta, la dimensione viene valutata come 1 indipendentemente dal numero di elementi presenti nella raccolta.

Dopo un po 'di debug abbiamo scoperto che nel caso 1 la parte

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

restituisce un array che ha una voce con valore 0. Ecco perché la funzione di conteggio restituisce 1 sebbene non sia stata trovata alcuna voce.

Nel caso 2 la stessa parte restituisce un array con una voce, il cui valore è la dimensione reale della raccolta. La funzione di conteggio restituisce nuovamente 1 e non il valore.

Cercando un'alternativa, abbiamo scoperto che la collezione di prodotti utilizza una riscrittura della funzione getSelectCountSql (). L'abbiamo adattato e cambiato un po ', il che si è concluso con questa soluzione:

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

Risolve i due problemi che ho già menzionato e, per quanto posso vedere, funziona anche per gli altri casi.


Grazie per aver fornito il riferimento del modello di raccolta dei prodotti. Mi ha aiutato
Dinesh Yadav,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.