Magento2: Saving product with non-default store scope causes untouched attributes to become store scoped if loaded using ProductRepository

Created on 16 Mar 2017  Â·  29Comments  Â·  Source: magento/magento2

This is related to #7720 and #7879 and #9486

Preconditions

  1. Magento 2.4-develop

Steps to reproduce

  1. Create a simple product that belongs to multiple websites.
  2. In a script, change the current store to non-default store
  3. Load the product using the ProductRepository class
  4. Change an attribute value for the loaded product
  5. Save the product either using the ProductRepository class or calling $product->save() (deprecated)
$this->storeManager->setCurrentStore(2);
$product = $this->productRepository->getById(1, true, 2);
$product->setShortDescription('Setting short desc');
$this->productRepository->save($product); // or $product->save();

Expected result

  1. The value for short_description should be overridden, all other attributes should remain as Use default value

Actual result

  1. All store scoped eav attributes are overridden. Values other than short_description are copied over from the default store scope values.

You can see the results of this by checking the catalog_product_entity_text table and seeing that new values for not just short_description, but description and meta_keyword as well. Checking the catalog_product_entity_{varchar,int,decimal,etc} tables will also show new entries for store scope 2 even though none of that was modified.

Checking the how the admin backend handles this, it seems the Save controller requires passing in values for whether to use default values in a param use_default. (https://github.com/magento/magento2/blob/develop/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php#L171)
This makes it tightly coupled to the admin ui, and so it is hard to replicate proper save behaviour in code.

The use case is that products are being pushed externally via the Magento api, but this makes it so that doing store scoped save operations means anytime something is changed in default scope, it needs to also update in all store scopes independently rather than inheriting from default scope like it should.

Catalog Confirmed P1 ready for dev Reported on 2.4.x Reproduced on 2.1.x Reproduced on 2.2.x Reproduced on 2.3.x Reproduced on 2.4.x S1

Most helpful comment

The product attributes are solved by using the emulation to set your script to admin store id 0

use Magento\Store\Model\App\Emulation;
use Magento\Framework\App\Area;

    public function __construct(
        FilterFactory $filter,
        Emulation $emulation
)
    {
        $this->filter = $filter;
        $this->emulation = $emulation;
    }

.....
  $this->emulation->startEnvironmentEmulation(0,Area::AREA_ADMINHTML);
                $productrep->save($product);
                $this->emulation->stopEnvironmentEmulation();
....

All 29 comments

@phirunson, thank you for your report.
We've created internal ticket(s) MAGETWO-81741 to track progress on the issue.

This is also an issue when updating a product via the REST API in 2.1.19. When updating a product under a non-default store scope, all store scoped eav attributes are overridden, not just those that are being updated.

This seems to be a major bug, and so should be addressed in 2.1.x and 2.2.x, no?

This is still an issue in CE 2.2.1. If you save a product programmaticly without touching the attributes that have a default value in that store it will save those attributes with the value from the default value and thus unsetting the 'use default value' check

We are also having this problem in 2.2.2

For the ones who have this issue. You will need to use the resourcemodel to post your own mysql queries to the database. No other solution that i have found

We are using the api to update to product data so what you are saying is that we should trash the api Magento provides and create our own calls...

I believe I have a related issue.
When creating a product with selected website id (non-single store mode) it saves attributes (at least some of them) on store view level even if it's a new product and there are no default values.

For varchar attributes you set, there will be values for selected store_id (related to a website) and admin one, for varchar attributes with default values like media_images there will only be "no_selection" on a store level.

So if you later upload an image on admin side on default scope, an image will not appear. One more problem is that you'll have to upload the image again on store view level because there is no "use default scope" checkbox in the images section.

snippet of the wall of text from our cloud support ticket:

1) Setup a clean 2.2.2 installation

2) Create a new (extra) storeview:
=> Store='Main Website Store'
=> Name='Some Random Storeview'
=> Code='random'
=> Status='Enabled'
=> Sort Order='0'

3) Make sure the shop is using flat tables
=> Stores > Configuration > Catalog > Catalog > Storefront > Use Flat Catalog Category = Yes
=> Stores > Configuration > Catalog > Catalog > Storefront > Use Flat Catalog Product = Yes

4) Create a simple product and save it
=> Fill in all required attributes with random stuff
=> Fill in all fields with storeview/website scope in all the closed tabs
=> Make sure to add 2 random images and set the 1th as base/small_image/thumbnail/swatch

5) Take note of the values in the database:

  • catalog_product_entity_int
  • catalog_product_entity_text
  • catalog_product_entity_text

Break stuff through the backend:

6) Switch the storeview to our newly created 'random' storeview
=> Don't change any data AND DON'T OPEN ANY TABS

8) Save the product on storeview scope
=> If you look at the closed tabs after the product save reloaded the page, you will see that the 'use default value' checkboxes have all been unchecked

9) Take note of the values in:

  • catalog_product_entity_int
  • catalog_product_entity_text
  • catalog_product_entity_text
    => All attributes from the closed tabs that have storeview/website scope have just had a value saved to the database

10) Switch back to the 'All store views' scope

11) Change all product attributes that have scope storeview or website with new values and save the product
=> Make sure to select the 2th image as base/small_image/thumbnail/swatch!
=> Make sure to change the meta_title, meta_keywords and meta_description

12) Take note of the values in:

  • catalog_product_entity_int
  • catalog_product_entity_text
  • catalog_product_entity_text
    => The values with store_id 0 will contain the correct value
    => values saved to the database for our new storeview during step 7 are incorrect

13) Look at the frontend where the product is using the incorrect data on our 'random' storeview
=> the image used as base/small_image/thumbail/swatch is wrong
=> the meta data in the header is wrong

The problem can be split into multiple origins:

  • Part of the problem lies with the fact that closed tabs will uncheck all 'use default value' checkboxes. This causes discrepancies between values on storeviews because (unwanted) values are being saved to the database.
    You cannot assume that a user will open all tabs when he is editting a product, just to make sure the 'use default value' checkboxes get saved properly

  • Another part of the problem is the fact that for images there is no 'use default value' option. This will cause problems on the frontend when the user assumes the value of default is being used, while actually an incorrectly saved value is being used.

Break stuff through the api:

6) Update the product's name through the api:
=> PUT '../rest/{OurRandomStoreviewCode}/V1/products/{createdProductSku}
=> request body: {"product":{"name":"Test API"}}

7) Take note of the values in:

  • catalog_product_entity_int
  • catalog_product_entity_text
  • catalog_product_entity_text
    => Besides the product's name being updated on the storeview, the database tables got filled with unwanted values for attributes that have scope storeview/website

I can't even wrap my head around this problem.
We are updating 1 attribute but the database is being filled with a lot of other (unwanted) values. If we now check the product through the adminhtml the same behaviour as the closed tabs was reproduced. All attributes in the closed tabs have had their 'use default value' checkbox unchecked...

This is still an issue in 2.2.4

The product attributes are solved by using the emulation to set your script to admin store id 0

use Magento\Store\Model\App\Emulation;
use Magento\Framework\App\Area;

    public function __construct(
        FilterFactory $filter,
        Emulation $emulation
)
    {
        $this->filter = $filter;
        $this->emulation = $emulation;
    }

.....
  $this->emulation->startEnvironmentEmulation(0,Area::AREA_ADMINHTML);
                $productrep->save($product);
                $this->emulation->stopEnvironmentEmulation();
....

Still an issue on last Magento 2 version.

Same in 2.2.5.

we are running in single store mode and creating products from the configurable products and several seemingly random attribute values are getting filled.

Hi, is there any news about this? seems quite concerning has been reproduced on 2.1, 2.2, 2.3 but still no update.

I am also having same problem. This is happening when product is updated via api on all store view in product admin grid it is showing old image though I have uploaded new image when I delete image from cache it start showing me placeholder image instead of image I have set base,small, thumbnail on all store view but it is working fine for store image.

I randomly get data from other products of other store views getting copied
into wrong products. Does anyone else see this weird behavior?

On Wed, Jan 30, 2019 at 7:40 AM irehmankhan notifications@github.com
wrote:

I am also having same problem. This is happening when product is updated
via api on all store view in product admin grid it is showing old image
though I have uploaded new image when I delete image from cache it start
showing me placeholder image instead of image I have set base,small,
thumbnail on all store view but it is working fine for store image.

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/magento/magento2/issues/8897#issuecomment-458929511,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACQbJYNsla6LxQJsMgWvm6YF2tQbOKRkks5vIZLIgaJpZM4Me0lk
.

Still happening on EE2.2.6

Still seeing this issue in 2.1.16. Any updates?

Also - there is sample code above by @versdivers using EnvironmentEmulation. Does this work and is there documentation anywhere that describes how EnvironmentEmulation works?

@abrittis I can confirm that it works since we use it for a very big synchronization module with Exact Online for our customers. But except from some forums i did not find any documentation on it no (This is the new account of @versdivers )

We're encountering the same issue here on 2.2.8 Commerce.

We have a process running that should update an attribute if some criteria are met on a store scope basis, once we save the product in this process (CRON) all attributes get overwritten with the attributes of the default scope.

The hotfix @CompactCodeEU proposed didn't work for this and the problem still persists.

Also reproduced in Magento 2.3.0 and 2.3.1. If a product is edited in the backend in a specific store view, the images are saved store specific. This store specific setting for the images cannot be removed, as there are no "Use Default Value" checkboxes at the images.

Experience in 2.3.1, load product using product repository & on save it added store_view_code as default. on export it duplicate row with just value dded store_view_code = default

Hi @engcom-Alfa. Thank you for working on this issue.
Looks like this issue is already verified and confirmed. But if you want to validate it one more time, please, go though the following instruction:

  • [ ] 1. Add/Edit Component: XXXXX label(s) to the ticket, indicating the components it may be related to.
  • [ ] 2. Verify that the issue is reproducible on 2.4-develop branch

    Details- Add the comment @magento give me 2.4-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.4-develop branch, please, add the label Reproduced on 2.4.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and _stop verification process here_!

  • [ ] 3. If the issue is not relevant or is not reproducible any more, feel free to close it.


@engcom-Alfa Thank you for verifying the issue.

Unfortunately, not enough information was provided to acknowledge ticket. Please consider adding the following:

  • [ ] Add "Component: " label(s) to this ticket based on verification result. If uncertain, you may follow the best guess

Once all required information is added, please add label "Issue: Confirmed" again.
Thanks!

:white_check_mark: Confirmed by @engcom-Alfa
Thank you for verifying the issue. Based on the provided information internal tickets MC-30077 were created

Issue Available: @engcom-Alfa, _You will be automatically unassigned. Contributors/Maintainers can claim this issue to continue. To reclaim and continue work, reassign the ticket to yourself._

Please assign that to me, I am going to fix the issue.

Other entities

I tried to reproduce the issue on other Entities to make sure if it's global issue of EAV mechanism or just related to products.

Categories

  • Admin GUI :heavy_check_mark: (Issue does not occur)
  • API Rest :x: (Problem occurs)

Hey, guys, I was having similar issue using Magento API to create products on M2 2.3.4. Calling /V1/products with website_ids: [1] element or leaving it empty and adding websites using /V1/products/{sku}/websites was resulting on having specific non default values to stores inside the associated website.

I've work around this by creating a plugin on Magento\Catalog\Model\ProductWebsiteLinkRepository::save method like below. Hope it helps someone until we have an official resolution from Magento. If you're having similar issue on another part of the code, consider using ProductWebsiteLinkRepository instead of set website_ids directly to product.

class ProductWebsiteLinkRepository
{
    /**
     * @var ProductWebsiteResourceModel
     */
    protected $productWebsiteResourceModel;

    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;

    /**
     * ProductWebsiteLinkRepository constructor.
     * @param ProductWebsiteResourceModel $productWebsiteResourceModel
     * @param ProductRepositoryInterface $productRepository
     */
    public function __construct(
        ProductWebsiteResourceModel $productWebsiteResourceModel,
        ProductRepositoryInterface $productRepository
    ) {
        $this->productWebsiteResourceModel = $productWebsiteResourceModel;
        $this->productRepository = $productRepository;
    }

    /**
     * @param MagentoProductWebsiteLinkRepository $subject
     * @param callable $proceed
     * @param ProductWebsiteLinkInterface $productWebsiteLink
     * @return bool
     * @throws CouldNotSaveException
     * @throws InputException
     */
    public function aroundSave(
        MagentoProductWebsiteLinkRepository $subject,
        callable $proceed,
        ProductWebsiteLinkInterface $productWebsiteLink
    ) {
        if (!$productWebsiteLink->getWebsiteId()) {
            throw new InputException(__('There are not websites for assign to product'));
        }

        try {
            $product = $this->productRepository->get($productWebsiteLink->getSku());
            $websitesIds = array_merge($product->getWebsiteIds(), [$productWebsiteLink->getWebsiteId()]);
            $this->productWebsiteResourceModel->addProducts($websitesIds, [$product->getId()]);
        } catch (\Exception $e) {
            throw new CouldNotSaveException(
                __(
                    'Could not assign product "%1" to websites "%2"',
                    $product->getId(),
                    $productWebsiteLink->getWebsiteId()
                ),
                $e
            );
        }
        return true;
    }
}

So in the meantime should I just send two API requests to save product data using the Rest API?
One to all and one to default? (Admin and Default values get out of sync)

Can someone please explain why are there two store views/copies of same data all over the show in the EAV/DB?
As I do not understand why there is an Admin and a Frontend store id when website has the single store/site enabled.

So saving has been bugged since at least 2016 or earlier?
How can we get an update on the status of MAGETWO-81741 fix?

This also impacts the bulk api... even when using all/ it adds store id 1 when my site is single store. How frustrating, so I can't use the bulk api.

Was this page helpful?
0 / 5 - 0 ratings