Добрый день! У меня на сайте используются настраиваемые товары. Соответственно используется фильтр мадженты по атрибутам. Проблема заключается в следующем. Я хочу показывать на сайте товары которых нет в наличии, но при этом чтобы корректно работал фильтр по атрибутам. Поясню.
Есть товары: майка №1 (в наличии все цвета), товар майка №2 (в наличии только красный цвет) и товар майка №3 (вообще нет в наличии).
Захожу в категорию майки и вижу там три товара. У товаров майка №1 и №2 написано "В наличии", у товара майка №3 написано "Нет в наличии". Затем выбираю в фильтре "красный цвет" и в результатах должен увидеть только майку №1 и майку №2. А если выбрать зеленый цвет, то должен увидеть только майку №1. Сейчас же если в настройках склада отмечено "Display Out of Stock Products", то при использовании фильтров отображаются все товары, что не удобно (и не верно).
Подскажите, где искать проблему?
Solved! Go to Solution.
Вобщем нашел я для себя такое решение, может быть кому-нибудь пригодится.
1. Добавляем поле "in_stock" в таблицы
$installer = $this;
$installer->startSetup();
$fieldName = "in_stock";
$tableNames = [
'catalog_product_index_eav',
'catalog_product_index_eav_idx',
'catalog_product_index_eav_tmp',
'catalog_product_index_eav_decimal',
'catalog_product_index_eav_decimal_idx',
'catalog_product_index_eav_decimal_tmp',
];
foreach ($tableNames as $tableName) {
$indexName = "IDX_" . mb_strtoupper($tableName) . "_" . mb_strtoupper($fieldName);
$installer->getConnection()->addColumn($tableName, $fieldName, 'tinyint(1) not null');
$installer->getConnection()->addIndex($tableName, $indexName, [$fieldName], Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX);
}
$installer->endSetup();
2. Меняем
Mage_Catalog_Model_Resource_Product_Indexer_Eav_Abstract
protected function _prepareRelationIndex($parentIds = null)
{
...
->join(
array('i' => $idxTable),
'l.child_id = i.entity_id AND cs.store_id = i.store_id',
array('attribute_id', 'store_id', 'value', 'in_stock'))
->group(array(
'l.parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'i.in_stock'
...
}
Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source
protected function _prepareSelectIndex($entityIds = null, $attributeId = null)
{
...
$select = $adapter->select()
->from(
array('pid' => new Zend_Db_Expr(sprintf('(%s)',$subSelect->assemble()))),
array()
)
->joinLeft(
array('pis' => $this->getValueTable('catalog/product', 'int')),
'pis.entity_id = pid.entity_id AND pis.attribute_id = pid.attribute_id AND pis.store_id = pid.store_id',
array()
)
->joinLeft(
array('csi' => $this->getValueTable('cataloginventory/stock', 'item')),
'csi.product_id = pid.entity_id',
array()
)
->columns(
array(
'pid.entity_id',
'pid.attribute_id',
'pid.store_id',
'is_in_stock' => $adapter->getIfNullSql('csi.is_in_stock', 0),
'value' => $adapter->getIfNullSql('pis.value', 'pid.value')
)
)
->where('pid.attribute_id IN(?)', $attrIds);
...
}
protected function _prepareMultiselectIndex($entityIds = null, $attributeId = null)
{
...
$select = $adapter->select()
->from(
array('pvd' => $this->getValueTable('catalog/product', 'varchar')),
array('entity_id', 'attribute_id'))
->join(
array('cs' => $this->getTable('core/store')),
'',
array('store_id'))
->joinLeft(
array('csi' => $this->getValueTable('cataloginventory/stock', 'item')),
'csi.product_id = pvd.entity_id',
array('is_in_stock' => $adapter->getIfNullSql('csi.is_in_stock', 0))
)
->joinLeft(
array('pvs' => $this->getValueTable('catalog/product', 'varchar')),
'pvs.entity_id = pvd.entity_id AND pvs.attribute_id = pvd.attribute_id'
. ' AND pvs.store_id=cs.store_id',
array('value' => $productValueExpression))
->where('pvd.store_id=?',
$adapter->getIfNullSql('pvs.store_id', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID))
->where('cs.store_id!=?', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID)
->where('pvd.attribute_id IN(?)', $attrIds);
...
$data[] = array(
$row['entity_id'],
$row['attribute_id'],
$row['store_id'],
$row['is_in_stock'],
$valueId,
);
...
protected function _saveIndexData(array $data)
{
if (!$data) {
return $this;
}
$adapter = $this->_getWriteAdapter();
$adapter->insertArray($this->getIdxTable(), array('entity_id', 'attribute_id', 'store_id', 'in_stock', 'value'), $data);
return $this;
}
}
3.
Mage_Catalog_Model_Resource_Layer_Filter_Attribute
function applyFilterToCollection($filter, $values)
{
...
$connection->quoteInto("{$tableAlias}.in_stock = ?", 1
...
}
Суть в том, что при переиндексации magento формирует таблицу catalog_product_index_eav, в которую попадают значения атрибутов для товаров.
а) Если товары "не в наличии" не показываются. В таблицу catalog_product_index_eav не попадают значения атрибутов простых товаров, которые не в наличии. Таким образом при использовании фильтра (многоуровневой навигации) всё отображается корректно. В данном случае мои изменения не меняют этот кейс.
б) Если товары "не в наличии" должны отображаться. В таблицу попадают все атрибуты простых товаров, даже если они не в наличии. А проверка по наличию происходит по configurable-товару. После исправления, мы проверяем наличие также по конкретному простому товару, чей атрибут учавствует в фильтре.
ps. Надеюсь понятно объяснил.
pps. Конечно, не обязательно вносить изменения в Core. Можно/нужно написать модуль, который переопределит нужные методы.
Ну и возможно есть другое решение, буду признателен если кто поделится.
Смотреть в сторону модулей фильтрации, которые умеют фильтровать по наличию товара. Стандартный модуль фильтрует все видимые товары, а не только те, что в наличии.
Да, стандартный наличием никак не управляет. Я нашел готовый модуль и переделываю его под себя. Использую для фильтрации по наличию:
/** @var Mage_CatalogInventory_Model_Stock $model */
$model = Mage::getsingleton('cataloginventory/stock');
$model->addInStockFilterToCollection($collection);
Все работает прекрасно при фильтрации простых товаров. А с настраиваемыми такой фокус не проходит, видимо т.к. сам конфигурируемый товар в наличии, а вот простой товар с нужным атрибутом - нет. Есть возможность сделать так, чтобы фильтр работал правильно и с настраиваемыми товарами?
Вобщем нашел я для себя такое решение, может быть кому-нибудь пригодится.
1. Добавляем поле "in_stock" в таблицы
$installer = $this;
$installer->startSetup();
$fieldName = "in_stock";
$tableNames = [
'catalog_product_index_eav',
'catalog_product_index_eav_idx',
'catalog_product_index_eav_tmp',
'catalog_product_index_eav_decimal',
'catalog_product_index_eav_decimal_idx',
'catalog_product_index_eav_decimal_tmp',
];
foreach ($tableNames as $tableName) {
$indexName = "IDX_" . mb_strtoupper($tableName) . "_" . mb_strtoupper($fieldName);
$installer->getConnection()->addColumn($tableName, $fieldName, 'tinyint(1) not null');
$installer->getConnection()->addIndex($tableName, $indexName, [$fieldName], Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX);
}
$installer->endSetup();
2. Меняем
Mage_Catalog_Model_Resource_Product_Indexer_Eav_Abstract
protected function _prepareRelationIndex($parentIds = null)
{
...
->join(
array('i' => $idxTable),
'l.child_id = i.entity_id AND cs.store_id = i.store_id',
array('attribute_id', 'store_id', 'value', 'in_stock'))
->group(array(
'l.parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'i.in_stock'
...
}
Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source
protected function _prepareSelectIndex($entityIds = null, $attributeId = null)
{
...
$select = $adapter->select()
->from(
array('pid' => new Zend_Db_Expr(sprintf('(%s)',$subSelect->assemble()))),
array()
)
->joinLeft(
array('pis' => $this->getValueTable('catalog/product', 'int')),
'pis.entity_id = pid.entity_id AND pis.attribute_id = pid.attribute_id AND pis.store_id = pid.store_id',
array()
)
->joinLeft(
array('csi' => $this->getValueTable('cataloginventory/stock', 'item')),
'csi.product_id = pid.entity_id',
array()
)
->columns(
array(
'pid.entity_id',
'pid.attribute_id',
'pid.store_id',
'is_in_stock' => $adapter->getIfNullSql('csi.is_in_stock', 0),
'value' => $adapter->getIfNullSql('pis.value', 'pid.value')
)
)
->where('pid.attribute_id IN(?)', $attrIds);
...
}
protected function _prepareMultiselectIndex($entityIds = null, $attributeId = null)
{
...
$select = $adapter->select()
->from(
array('pvd' => $this->getValueTable('catalog/product', 'varchar')),
array('entity_id', 'attribute_id'))
->join(
array('cs' => $this->getTable('core/store')),
'',
array('store_id'))
->joinLeft(
array('csi' => $this->getValueTable('cataloginventory/stock', 'item')),
'csi.product_id = pvd.entity_id',
array('is_in_stock' => $adapter->getIfNullSql('csi.is_in_stock', 0))
)
->joinLeft(
array('pvs' => $this->getValueTable('catalog/product', 'varchar')),
'pvs.entity_id = pvd.entity_id AND pvs.attribute_id = pvd.attribute_id'
. ' AND pvs.store_id=cs.store_id',
array('value' => $productValueExpression))
->where('pvd.store_id=?',
$adapter->getIfNullSql('pvs.store_id', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID))
->where('cs.store_id!=?', Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID)
->where('pvd.attribute_id IN(?)', $attrIds);
...
$data[] = array(
$row['entity_id'],
$row['attribute_id'],
$row['store_id'],
$row['is_in_stock'],
$valueId,
);
...
protected function _saveIndexData(array $data)
{
if (!$data) {
return $this;
}
$adapter = $this->_getWriteAdapter();
$adapter->insertArray($this->getIdxTable(), array('entity_id', 'attribute_id', 'store_id', 'in_stock', 'value'), $data);
return $this;
}
}
3.
Mage_Catalog_Model_Resource_Layer_Filter_Attribute
function applyFilterToCollection($filter, $values)
{
...
$connection->quoteInto("{$tableAlias}.in_stock = ?", 1
...
}
Суть в том, что при переиндексации magento формирует таблицу catalog_product_index_eav, в которую попадают значения атрибутов для товаров.
а) Если товары "не в наличии" не показываются. В таблицу catalog_product_index_eav не попадают значения атрибутов простых товаров, которые не в наличии. Таким образом при использовании фильтра (многоуровневой навигации) всё отображается корректно. В данном случае мои изменения не меняют этот кейс.
б) Если товары "не в наличии" должны отображаться. В таблицу попадают все атрибуты простых товаров, даже если они не в наличии. А проверка по наличию происходит по configurable-товару. После исправления, мы проверяем наличие также по конкретному простому товару, чей атрибут учавствует в фильтре.
ps. Надеюсь понятно объяснил.
pps. Конечно, не обязательно вносить изменения в Core. Можно/нужно написать модуль, который переопределит нужные методы.
Ну и возможно есть другое решение, буду признателен если кто поделится.