See below example
$product = $this->productRepository->get('test-sku');
// $product->getCustomAttribute('custom_attr_code')->getValue() == 0
$product->setCustomAttribute('custom_attr_code', 1); // changing value from 0 to 1
$this->productRepository->save($product);
// $product->get('custom_attr_code') == 0
// and $product->getCustomAttribute('custom_attr_code')->getValue() == 0
ANY values in $_data[self::CUSTOM_ATTRIBUTES] is lost, and not saved properly.
This is because the product repository issues a product model load which loads all attributes, custom or not, into the _data array. Anytime the app calls ProductInterface::getCustomAttributes (which it does try to do when saving, to grab any values there) any custom attribute 'key' in $_data['custom_attributes']['key'] is overwritten because that key exists in $_data['key']. This overwriting happens here.
Note: This is a larger issue for those leveraging the webapi data object input converter. That converter will throw exceptions when any input keys reference attribute codes not present in the ProductInterface schema, hence they must be set in a nested $inputData['custom_attributes'] node. But, once nested there, that data will never be saved.
I'm not sure of the issue, but I think they _do_ work, but they're a bit confusing. Maybe you can find something useful in this discussion: http://magento.stackexchange.com/questions/87452/how-do-the-extension-attributes-work-in-magento-2
Are you sure the products attribute set contains the custom attribute, and that the attributes property is_user_defined is 1?
I found once I get those right it works, at least for customers, and I think for products, too, iirc...
Note: this is about custom attributes, not extension attributes, right?
Note: this is about custom attributes, not extension attributes, right?
Correct, but there's some mentioning about custom attributes in that SO link, too.
Yes @Vinai the attributes are user defined and set up properly. See the code I linked above, it is very clear that anything set via setCustomAttribute($key, $value) is overwritten. The only way to get data to save on products is to have the data in the $_data property directly via setData($key, $value), but at that point you are not coding against the interface.
Bump. I don't think core team realizes this is a rather large oversight which makes the entire webapi for EAV entities close to useless. Should be a high priority.
@SamTay, thank you for reporting. Please, provide the used version. If the problem is actual for a specific tag, please, specify it and be sure that the latest update was used.
I found it on EE 2.0.7 but it still exists on the CE develop branch.. just look at the code link I posted in my initial comment, it should be obvious.
@SamTay
Thank you.
If you think that this is a bug, please provide description according to the template
Clone Magento2 from any version since general availability.
Set up access to product repository API such as through test framework/module
[Insert original issue comment]
The only way to have these work as intended is to use a builder such as \Magento\Catalog\Api\Data\ProductInterfaceFactory and set sku along with any data changes. While that might seem like an easy work around, the product repository is central to the app and it seems like a bad idea to leave this fragility in place.
Any time a product model load is issued (such as during ProductRepository::get), all of the custom attributes are loaded directly into the _data array. Thus any further usage of custom attributes interface methods are useless because once a custom attributes key exists in the _data property, it will always be preferred over the _data['custom_attributes'] array.
I've stumbled upon this issue as I was getting the same error that custom attributes on a customer entity cannot be saved and get ignored.
Are you sure the products attribute set contains the custom attribute, and that the attributes > property
is_user_definedis 1?
After digging around for hours in the source code ... I realized that is_user_defined is not needed but more important when you create your new attribute on the entity in the UPgradeData script:
// Hardcoded attribute group id ==> 1
$customerSetup->addAttributeToSet(
Customer::ENTITY, CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER, 1,
GeneralTerms::ATTRIBUTE_NAME, 1200
);
Don't forget it to add it to an attribute set ;-) After that custom_attributes are not an issue anymore, at least on the customer entity 8-)
@samtay, thank you for your report.
We've created internal ticket(s) MAGETWO-84530 to track progress on the issue.
Has anybody managed to make a patch for this in the meantime, or got a solid workaround? I'm thinking of using an observer on catalog_product_save_before and using setData() as described here: https://magento.stackexchange.com/a/229280/191
I'm not sure if is the same error, but looks like a lot to me.
When I update a custom attribute of a product in multi stores, the thumbnail of this product is unset.
I don't lose the image, just the set up thumbnail.
Loading a product by Magento\Catalog\Model\ProductRepository.
I update same custom attribute and then save this using ProductRepository.
$product->setData('material', implode(',', $material));
$this->_productRepository->save($product);
To fix it, I use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory to save.
I removed the label "Issue: Confirmed"
it requires to be re-triaged/re-checked is a problem still actual for 2.4-develop or was already fixed
Most helpful comment
Step 1.
Clone Magento2 from any version since general availability.
Step 2.
Set up access to product repository API such as through test framework/module
Step 3.
[Insert original issue comment]
The only way to have these work as intended is to use a builder such as
\Magento\Catalog\Api\Data\ProductInterfaceFactoryand setskualong with any data changes. While that might seem like an easy work around, the product repository is central to the app and it seems like a bad idea to leave this fragility in place.Recap
Any time a product model
loadis issued (such as duringProductRepository::get), all of the custom attributes are loaded directly into the_dataarray. Thus any further usage of custom attributes interface methods are useless because once a custom attributes key exists in the_dataproperty, it will always be preferred over the_data['custom_attributes']array.