Разбиение сетки не работает при использовании предложения group в коллекции


9

Я работаю над сеткой продуктов, но ее нумерация страниц или счетчик продуктов не работают (так как они отображают неправильный счет). так как мой блок _preparecollection работает так, как показано ниже. я добавил код фильтра категорий в коллекцию, поэтому я должен использовать предложение группы, чтобы предотвратить ошибку для того же идентификатора уже существует.

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

Я получил Google и получил ответ и добавить его в 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;
}

введите описание изображения здесь Но не повезло, пожалуйста, помогите решить эту проблему


Какой класс вы расширяете? Mage_Adminhtml_Block_Widget_Grid?
B00MER

Да, я продолжаюMage_Adminhtml_Block_Widget_Grid
Захираббас

Какой запрос возвращает вызов getSelectCountSql?
Amasty

Ответы:


17

Коллекции и Ленивая Загрузка в Magento

Причина разбиения на страницы не работает из-за того, как подсчитываются коллекции и как ленивая загрузка работает с коллекциями.

Коллекции в Magento реализуют класс Countable. Из-за отложенной загрузки коллекций в Magento при каждом count()вызове метода данные должны быть загружены. Как обходной путь, коллекции реализуют метод, который называется getSize(). Он будет клонировать ваш оператор SQL, обернуть его COUNT()и вернуть результат. Это позволило коллекции получить общий счет без загрузки всех данных. Это позволяет добавлять такие вещи, как фильтры, в последнюю минуту.

Вот так выглядит Varien_Data_Collection_Db::getSize()партнерша 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;
    }

По сути, он сбрасывает ограничения, столбцы, упорядочение и т. Д. И оставляет фильтры позади. Затем он добавляет MySQL COUNT()к столбцам.

Проблема

Обычно в одной таблице это возвращает одну строку с общим количеством. Вот почему getSize()делает fetchOne()против запроса. Однако при выполнении таких операций, как объединение таблиц, группирование и т. П., Вы не вернете одну строку, вы вернете несколько. Именно из-за этого вам нужно изменить getSize()метод в вашей коллекции.

Решение

Вот как должен выглядеть ваш метод сейчас:

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

Вместо того , чтобы fetchOne()мы провели fetchAll()завернутые в count()функции PHP. Теперь ваши итоги вернутся соответственно.


2
Вот как бы я хотел, чтобы все ответы на SE были. Решение И некоторая глубина.
шампунь

4

Отличное решение. Возможно, у кого-то такая же проблема, как у нас, поэтому я опубликую другое возможное решение. В нашем случае у нас была коллекция, которая иногда включала оператор group by, а иногда - нет, в зависимости от сетки, куда была загружена коллекция. Используя решение выше, мы обнаружили две проблемы:

  1. Если коллекция пуста, размер оценивается как 1, хотя он должен быть нулевым.
  2. В случаях, когда метод getSize вызывался без оператора group by для коллекции, размер оценивается как 1 независимо от того, сколько элементов в коллекции.

После некоторой отладки мы выяснили, что в случае 1 часть

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

возвращает массив, в котором есть одна запись со значением 0. Именно поэтому функция count возвращает 1, хотя записи не были найдены.

В случае 2 эта же часть возвращает массив с одной записью, значение которой является реальным размером коллекции. Функция count снова возвращает 1, а не значение.

В поисках альтернативы мы обнаружили, что в коллекции продуктов используется перезапись функции getSelectCountSql (). Мы адаптировали это и немного изменили, что закончилось этим решением:

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

Он решает две проблемы, которые я уже упоминал, и, насколько я вижу, он также работает и для других случаев.


Спасибо, что дали ссылку на модель коллекции продукта. Это помогло мне.
Динеш Ядав
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.