Die Rasterpaginierung funktioniert nicht, wenn die Gruppenklausel in der Sammlung verwendet wird

9

Ich arbeite am Produktraster, aber die Paginierung oder Produktanzahl funktioniert nicht (da die falsche Anzahl angezeigt wird). Da meine block _preparecollection-Funktion wie folgt ist. Ich habe Kategoriefiltercode in der Sammlung hinzugefügt, daher muss ich die Gruppenklausel verwenden, um zu verhindern, dass bereits ein Fehler für dieselbe ID vorhanden ist.

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

Ich hatte Google und bekam eine Antwort und fügte sie hinzu 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;
}

Geben Sie hier die Bildbeschreibung ein Aber kein Glück, bitte helfen Sie, dies zu lösen

Zaheerabbas
quelle
Welche Klasse erweitern Sie? Mage_Adminhtml_Block_Widget_Grid?
B00MER
Ja, ich verlängereMage_Adminhtml_Block_Widget_Grid
Zaheerabbas
Welche Abfrage gibt den Aufruf von getSelectCountSql zurück?
Amasty

Antworten:

17

Sammlungen und Lazy Loading in Magento

Der Grund, warum die Paginierung nicht funktioniert, liegt darin, wie Sammlungen gezählt werden und wie das verzögerte Laden mit Sammlungen funktioniert.

Sammlungen in Magento implementieren die Klasse Countable. Aufgrund des verzögerten Ladens von Sammlungen in Magento count()müssen die Daten bei jedem Aufruf der Methode geladen werden. Um dies zu umgehen, implementieren Sammlungen eine Methode namens getSize(). Es wird Ihre SQL-Anweisung klonen, in a einschließen COUNT()und das Ergebnis zurückgeben. Dadurch konnte eine Sammlung eine Gesamtanzahl erhalten, ohne alle Daten zu laden. Auf diese Weise können beispielsweise Filter in letzter Minute hinzugefügt werden.

So sieht Varien_Data_Collection_Db::getSize()der Partner getSelectCountSql()aus:

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

Grundsätzlich werden Grenzwerte, Spalten, Reihenfolge usw. gelöscht und die Filter bleiben zurück. Anschließend wird COUNT()den Spalten ein MySQL hinzugefügt.

Das Problem

Normalerweise würde dies für eine Tabelle eine Zeile mit der Gesamtzahl zurückgeben. Aus diesem Grund getSize()wird eine fetchOne()Abfrage durchgeführt. Wenn Sie jedoch Dinge wie Tabellenverknüpfungen, Gruppenbys und dergleichen ausführen, geben Sie nicht eine Zeile zurück, sondern mehrere. Aus diesem Grund müssen Sie die getSize()Methode in Ihrer Sammlung ändern .

Die Lösung

So sollte Ihre Methode jetzt aussehen:

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

Anstelle von a fetchOne()haben wir eine fetchAll()in eine count()PHP verpackte Funktion ausgeführt. Jetzt werden Ihre Summen entsprechend zurückgegeben.

Ryan Street
quelle
2
So wünschte ich mir alle Antworten auf die SE. Eine Lösung UND etwas Tiefe.
Shampoo
4

Tolle Lösung. Vielleicht hat jemand das gleiche Problem wie wir, also werde ich eine andere mögliche Lösung veröffentlichen. In unserem Fall hatten wir eine Sammlung, die manchmal eine Gruppe nach Anweisung enthielt und manchmal nicht, abhängig vom Raster, in das die Sammlung geladen wurde. Mit der obigen Lösung haben wir zwei Probleme gefunden:

  1. Wenn die Sammlung leer ist, wird die Größe mit 1 bewertet, obwohl sie Null sein sollte.
  2. In den Fällen, in denen die getSize-Methode ohne group by-Anweisung in der Sammlung aufgerufen wurde, wird die Größe mit 1 bewertet, unabhängig davon, wie viele Elemente sich in der Sammlung befinden.

Nach einer Weile des Debuggens stellten wir fest, dass in Fall 1 das Teil

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

Gibt ein Array mit einem Eintrag mit dem Wert 0 zurück. Aus diesem Grund gibt die Zählfunktion 1 zurück, obwohl keine Einträge gefunden wurden.

In Fall 2 gibt derselbe Teil ein Array mit einem Eintrag zurück, dessen Wert der tatsächlichen Größe der Sammlung entspricht. Die Zählfunktion gibt wieder 1 und nicht den Wert zurück.

Bei der Suche nach einer Alternative haben wir festgestellt, dass die Produktsammlung ein Umschreiben der Funktion getSelectCountSql () verwendet. Wir haben dies angepasst und ein wenig geändert, was zu dieser Lösung führte:

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

Es löst die beiden Probleme, die ich bereits erwähnt habe, und soweit ich sehen kann, funktioniert es auch für die anderen Fälle.

norgeindian
quelle
Vielen Dank, dass Sie die Referenz des Produktsammlungsmodells angegeben haben. Es hat mir geholfen.
Dinesh Yadav