Podział na strony nie działa, gdy w kolekcji jest używana klauzula grupy


9

Pracuję na siatce produktów, ale podział na strony lub liczba produktów nie działa (ponieważ wyświetla niepoprawną liczbę). ponieważ moja funkcja blokowania _preparecollection jest jak poniżej. dodałem kod filtru kategorii w kolekcji, więc muszę użyć klauzuli grupy, aby zapobiec wystąpieniu błędu dla tego samego identyfikatora.

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

Miałem google, otrzymałem odpowiedź i dodałem ją do 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;
}

wprowadź opis zdjęcia tutaj Ale nie powodzenia, pomóżcie rozwiązać ten problem


Jaką klasę poszerzasz? Mage_Adminhtml_Block_Widget_Grid?
B00MER

Tak, przedłużamMage_Adminhtml_Block_Widget_Grid
Zaheerabbas,

Jakie zapytanie zwraca wywołanie do getSelectCountSql?
Amasty,

Odpowiedzi:


17

Kolekcje i Leniwe Ładowanie w Magento

Nie działa podział na strony, ponieważ liczone są kolekcje i jak działa leniwe ładowanie kolekcji.

Kolekcje w Magento implementują klasę Countable. Ze względu na leniwe ładowanie kolekcji w Magento, przy każdym count()wywołaniu metody dane muszą zostać załadowane. Aby obejść ten problem, kolekcje implementują metodę o nazwie getSize(). Sklonuje twoją instrukcję SQL, owinie ją COUNT()i zwróci wynik. To pozwoliło kolekcji uzyskać całkowitą liczbę bez ładowania wszystkich danych. Pozwala to na dodanie takich elementów jak filtry w ostatniej chwili.

Oto jak wygląda Varien_Data_Collection_Db::getSize()jego partner 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;
    }

Zasadniczo porzuca limity, kolumny, kolejność itp. I pozostawia filtry. Następnie dodaje MySQL COUNT()do kolumn.

Problem

Zwykle w przypadku jednej tabeli zwracany byłby jeden wiersz z całkowitą liczbą. Dlatego getSize()robi to fetchOne()przeciwko zapytaniu. Jednak podczas wykonywania takich czynności, jak łączenia tabel, grupowania, itp., Nie zwrócisz jednego wiersza, zwrócisz wiele. Z tego powodu musisz zmienić getSize()metodę w swojej kolekcji.

Rozwiązanie

Tak powinna teraz wyglądać Twoja metoda:

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

Zamiast a fetchOne()uruchomiliśmy fetchAll()zawiniętą funkcję count()PHP. Teraz sumy zostaną odpowiednio zwrócone.


2
Tak chciałbym, żeby były wszystkie odpowiedzi na SE. Rozwiązanie ORAZ trochę głębi.
szampon

4

Świetne rozwiązanie. Może ktoś ma taki sam problem jak my, więc opublikuję inne możliwe rozwiązanie. W naszym przypadku mieliśmy kolekcję, która czasami zawierała grupę według instrukcji, a czasem nie, w zależności od siatki, w której kolekcja została załadowana. Korzystając z powyższego rozwiązania, znaleźliśmy dwa problemy:

  1. Jeśli kolekcja jest pusta, rozmiar ma wartość 1, chociaż powinna wynosić zero.
  2. W przypadkach, w których metoda getSize została wywołana bez grupy przez instrukcję w kolekcji, rozmiar jest wyceniany na 1 bez względu na liczbę elementów w kolekcji.

Po debugowaniu chwilę okazało się, że w przypadku 1 część

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

zwraca tablicę, która ma jeden wpis o wartości 0. Dlatego funkcja count zwraca 1, chociaż nie znaleziono żadnych wpisów.

W przypadku 2 ta sama część zwraca tablicę z jednym wpisem, którego wartością jest rzeczywisty rozmiar kolekcji. Funkcja liczenia ponownie zwraca 1, a nie wartość.

Szukając alternatywy, dowiedzieliśmy się, że kolekcja produktów korzysta z przepisania funkcji getSelectCountSql (). Dostosowaliśmy to i nieco zmieniliśmy, co zakończyło się tym rozwiązaniem:

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

Rozwiązuje dwa problemy, o których już wspomniałem i, o ile widzę, działa również w innych przypadkach.


Dziękujemy za podanie modelu kolekcji produktów. Pomogło mi to.
Dinesh Yadav
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.