8

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?

7

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>
  • Substitution is the most powerful thing that DI implementation of Magento gives you! Good solution, personal upvote from me! – Ivan Chepurnyi Oct 4 '16 at 14:35
  • This doesn't work for me. :( If I have my custom class: public function __construct( \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory ) { $this->_collectionFactory = $collectionFactory; } With the di.xml as you explained, I don't see that Magento is using virtual type instead of regular product collection. – mstojanov Jul 18 '17 at 7:36
6
+100

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.

  • I found a different solution without any custom code, using virtual types. But since yours comes closest and gave an important hint, have a <del>kitkat</del> <ins>bounty</ins> – Fabian Schmengler Oct 4 '16 at 14:02
  • How to write an after plugin for \Magento\Catalog\Model\Indexer\Product\Flat\State::isAvailable()? – Liam Mitchell Jan 25 at 1:11
1

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.

  • I'm pretty sure, calling setScheduled(false) on the indexer does not work because it's only disabling scheduled indexing and won't have impact on the collections. But regardless, it also saves the mode, which is definitely not what I want. – Fabian Schmengler Sep 27 '16 at 14:10
  • @fschmengler totally right, specially because the code that checks if the flat table is enabled uses the configuration directly. The alternative solution will overcome that though ;) – Raphael at Digital Pianism Sep 27 '16 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.