Force products collection to use EAV instead of flat table
-
03-10-2020 - |
質問
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?
解決
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>
他のヒント
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.