Question

In Magento 2, how can I temporarily disable the flat catalog? I have a product collection associated with a frontend store and want it to be loaded via EAV tables.

I looked how the collections determine if flat tables should be used, but did not find a way to inject the setting anywhere.

In Magento 1 I would have changed the loaded configuration value for "flat catalog enabled":

Mage::app()->getStore($storeId)->setConfig('catalog/frontend/flat_catalog_product', 0);

Do I still need to resort to global state like this? If so, how? Or is there a more elegant way?

Was it helpful?

Solution

The object which is responsible to determine if flat index is available (class Magento\Catalog\Model\Indexer\Product\Flat\State) is an immutable shared instance. But it is possible to use our own instance, using virtual types.

This is my di.xml:

  <virtualType name="disabledFlatStateProductCollectionFactory" type="Magento\Catalog\Model\ResourceModel\Product\CollectionFactory">
    <arguments>
      <argument name="instanceName" xsi:type="string">disabledFlatStateProductCollection</argument>
    </arguments>
  </virtualType>
  <virtualType name="disabledFlatStateProductCollection" type="Magento\Catalog\Model\ResourceModel\Product\Collection">
    <arguments>
      <argument name="catalogProductFlatState" xsi:type="object">disabledFlatState</argument>
    </arguments>
  </virtualType>
  <virtualType name="disabledFlatState" type="Magento\Catalog\Model\Indexer\Product\Flat\State">
    <arguments>
      <argument name="isAvailable" xsi:type="boolean">false</argument>
    </arguments>
  </virtualType>

Now, I have a virtual product collection factory type, where my own "State" instance with $isAvailable = false is used eventually:

disabledFlatStateProductCollectionFactory
 |
 + disabledFlatStateProductCollection
    |
    + disabledFlatState

And for the classes where I need a collection factory with disabled flat index, I specify the virtual type disabledFlatStateProductCollectionFactory for the corresponding constructor parameter:

<arguments>
  <argument name="collectionFactory" xsi:type="object">disabledFlatStateProductCollectionFactory</argument>
</arguments>

OTHER TIPS

When a product collection is loaded, the fact that it uses EAV or flat tables is determined by this result \Magento\Catalog\Model\ResourceModel\Product\Collection::isEnabledFlat().
You can write an around or after plugin, that returns false if you are in the context of a certain store view.

Or even better, the values for flat flag are stored (cached) in the member _flatEnabled from the same class.

public function isEnabledFlat()
{
    if (!isset($this->_flatEnabled[$this->getStoreId()])) {
        $this->_flatEnabled[$this->getStoreId()] = $this->getFlatState()->isAvailable();
    }
    return $this->_flatEnabled[$this->getStoreId()];
}

You can write the same around or after plugin for the method \Magento\Catalog\Model\Indexer\Product\Flat\State::isAvailable().
This way your plugin is executed only once. It could be helpful if you have heavy logic behind it or if it is used in other places.

This looks more elegant that changing a config value on the fly.

The more elegant way would be to use the same code as the code that enables the flat mode when saving the configuration. It can be found under Magento/Catalog/Model/Indexer/Product/Flat/System/Config/Mode :

public function processValue()
{
    if ((bool)$this->getValue() != (bool)$this->getOldValue()) {
        if ((bool)$this->getValue()) {
            $this->indexerState->loadByIndexer(\Magento\Catalog\Model\Indexer\Product\Flat\Processor::INDEXER_ID);
            $this->indexerState->setStatus(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID);
            $this->indexerState->save();
        } else {
            $this->_productFlatIndexerProcessor->getIndexer()->setScheduled(false);
        }
    }
}

So I'm pretty sure you could do something like that:

$this->_productFlatIndexerProcessor->getIndexer()->setScheduled(false);

Where $this->_productFlatIndexerProcessor is an instance of \Magento\Catalog\Model\Indexer\Product\Flat\Processor.

Possible alternative

However, that method does not save the config so when the system checks if flat is enabled via the config it still returns true.

A possible alternative (to be tested) would be to use a plugin on the isFlatEnabled method from Magento\Catalog\Model\Indexer\Product\Flat\State (the method is actually defined in the Magento\Catalog\Model\Indexer\AbstractFlatState class).

Based on what you want to achieve you could setup an after plugin to force that method to return false under certain conditions.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top