In a custom module UpgradeSchema, I've this constructor :
function __construct(
AttributeManagementInterfaceFactory $attributeManagementFactory,
) {
$this->attributeManagementFactory = $attributeManagementFactory;
}
and this upgrade method :
public function upgrade( ModuleDataSetupInterface $setup, ModuleContextInterface $context ) {
$attribute_id = 135;
foreach([4, 9, 10] as $set_id) {
// Get the attributes list for current attribute set
$attributeManagement = $this->attributeManagementFactory->create();
$attributes = $attributeManagement->getAttributes(\Magento\Catalog\Model\Product::ENTITY, $set_id);
if($attributes[$attribute_id]) {
var_dump($attributes[$attribute_id]->getAttributeSetId());
}
}
}
The product attribute with the id 135 is assign in three Attribute Sets (ids : 4, 9 and 10 in the foreach).
When I run the setup:upgrade
, the var_dump outputs :
string(1) "4"
string(1) "4"
string(1) "4"
Three times the first Attribute Set ids, instead of 4, 9 and 10.
When I look into the AttributeManagement::getAttributes(), the load is called three times here :
$attributeCollection = $this->attributeCollection
->setAttributeSetFilter($attributeSet->getAttributeSetId())
->load();
return $attributeCollection->getItems();
But into the load method called (Magento\Framework\Data\Collection\AbstractDb::load()), Magento check if the collection is loaded (and returns the collection) or not (and so goes on) :
public function load($printQuery = false, $logQuery = false)
{
if ($this->isLoaded()) {
return $this;
}
return $this->loadWithFilter($printQuery, $logQuery);
}
I can't understand why, but it's a fact that the collection is not flagged as loaded the first time (hopefully) and flagged to true the two others times Magento check. And that despite I create new object in the foreach of my Upgrade script :
$attributeManagement = $this->attributeManagementFactory->create();
Someone to explains this phenomenon ? Or/And to explains how to fix it ?
Best Answer
In the constructor of the class
AttributeManagement
, the collection is injected like that :In other words, we inject a
\Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection
singleton. And when a singleton is injected, it will be this same object (not a new one) wherever you will inject it again : in the same class, in an other class, etc... And I had a lack of knowledge about that.So when, in my example, I recreate a new object
AttributeManagement
which injectCollection
singleton in its own constructor, in fact it get the already createdCollection
singleton, with already items loaded, already filters added and everything...Conclusion : Collection should be injected thanks to the Factory pattern :
And that change just a little where it's called
And that works. But, that would be rewrite the core and it's forbidden. So I will not use the current
getAttributes()
method but I will copy and paste my solution directly in myupgrade
method.