719-286-0751 info@cadence-labs.com

Magento 2 “Use Default Value” Store View Scope Fix

This article and linked github provide a module which fixes a core bug in Magento 2.1.x and 2.2.x Open Source. This bug causes the “Use Default Value” checkboxes to become unchecked when editing a product at the store view level.

Click here to view the GitHub repository for this extension. Make sure you select the branch appropriate for your Magento version.

Overview

The Problem

Magento 2.1.x and 2.2.x Open Source has a a serious bug when editing a product at the store-view level. Merchants which multiple websites or locales often need to maintain slightly different product content at the store view level…however there is a bug, as described below:

  1. Merchant edits a product at a store view level.
  2. Merchant updates one or more store-level attributes and clicks SAVE
  3. All store level attributes in the non-opened tabs have the checkbox “Use Default Value” unchecked after refreshing the page (they are all checked on the initial page load).
  4. This happens behind the scenes, and the merchant doesn’t notice until later, and they ask themselves “Did I really come in here and uncheck all these boxes?”

The issue is caused by 2 distinct bugs in the Magento 2 source code:

  • When a product is saved, any attributes located in in a NON OPENED TAB do not properly communicate to the server that they should use the default value.
  • Magento 2 has a special module built in to auto generate the URL for a product if the merchant doesn’t enter one. This module has a side effect of incorrectly unsetting the “Use Default Value” checkbox on the URL attribute when a product is saved at the store view level.
The Solution

Our extension overrides 2 main files:


vendor/magento/module-catalog/Controller/Adminhtml/Product/Initialization/Helper.php
vendor/magento/module-catalog-url-rewrite/Observer/ProductUrlKeyAutogeneratorObserver.php

 

The first file handles the bug with attributes from non opened tabs. The magic happens below in:


# app/code/Cadence/ScopeFix/Controller/Catalog/Adminhtml/Product/Initialization/Helper.php::initializeFromData
      if (intval($currentStoreId) != 0 && $product->getId()) {

            $rootProduct = $this->productRepository->getById($product->getId());
            $resource = $this->resource;
            $connection = $resource->getConnection();
          foreach ($productData as $productKey => $productValue) {

                $attribute = $product->getResource()->getAttribute($productKey);

                if (!$attribute) {
                    // Not an EAV attribute
                    continue;
                }

                if (isset($useDefaults[$productKey])) {
                    continue;
                }

                $forceDefault = false;

                if ($attribute->getBackendType() != 'static' && $rootProduct->getData($productKey) == $productValue) {
                    $tableName = $attribute->getBackendTable();

                    $sql = "SELECT COUNT(*) FROM {$tableName} WHERE entity_id = :entity_id AND attribute_id = :attribute_id and store_id = :store_id";

                    $existingStoreLevelValue = intval($connection->fetchOne($sql, [
                        'attribute_id' => $attribute->getId(),
                        'entity_id' => $product->getId(),
                        'store_id' => $currentStoreId
                    ])) ? true : false;

                    if (!$existingStoreLevelValue) {
                        $forceDefault = true;
                    }

                }

                if ($forceDefault) {
                    if ($productKey == 'url_key') {
                        $this->registry->register('cadence_force_url_default', true, true);
                    }
                    /*
                     * Force the use of the default if:
                     * (1) Empty string and use_default wasn't sent to the server (bug since the checkboxes don't render if the tab isn't open)
                     * (2) Identical value to parent and there isn't an existing store-level override (bug since the checkboxes don't render if the tab isn't open)
                     */
                    $product->setData($productKey, null);
                    // UI component sends value even if field is disabled, so 'Use Config Settings' must be reset to false
                    if ($product->hasData('use_config_' . $productKey)) {
                        $product->setData('use_config_' . $productKey, false);
                    }
                }

            }

 

Notice that we iterate over all the product attributes which were sent to the server. We’re looking for the following behavior:

  • Magento 2 did not send a “use default value” flag for a given attribute
  • The attribute value passed for the given store view is identical to the attribute value of the default store
  • There is not an existing value at the store-view level for the given attribute (this is important – the admin user may have explicitly set the store view value to be the same as the default).

That handles all attributes except the `url_key`, the fix for that happens in:


# app/code/Cadence/ScopeFix/Plugin/CatalogUrlRewrite/Observer/ProductUrlKey/AutogeneratorObserver.php::aroundExecute
        /** @var \Magento\Catalog\Model\Product $product */
        $product = $observer->getEvent()->getProduct();

        /**
         * Check "Use Default Value" checkboxes values
         */
        $useDefaults = (array)$this->request->getPost('use_default', []);

        if ($this->registry->registry('cadence_force_url_default')
            || (isset($useDefaults['url_key']) && $useDefaults['url_key'])) {
            $product->setData('url_key', null);
            return;
        }

        $proceed($observer);

Notice that we’re intercepting the URL autogeneration to ensure it doesn’t run if the url should be looking at the default scope

Remove Existing Store-View Values

You may be finding this extension after your store already has a number of attributes improperly set to use the store view level. You can use the below SQL to totally remove all store-level values from your store. MAKE A BACKUP BEFORE RUNNING THIS SQL – IT WILL REMOVE DATA:


delete from catalog_product_entity_int where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_decimal where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_text where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_datetime where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_varchar where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_int where IFNULL(store_id, 0) <> 0;
delete from catalog_product_entity_media_gallery_value where IFNULL(store_id, 0) <> 0;

Install Our Extension

Below are instructions to manually install our scope-fix module through github. Note that if you are using Magento 2.1.x, you must select the 2.1.x branch when installing the module!!

https://github.com/cadencelabs/magento2-scope-fix

# cd to your magento root first
mkdir -p app/code/Cadence/ScopeFix && git clone https://github.com/cadencelabs/magento2-scope-fix app/code/Cadence/ScopeFix
bin/magento setup:upgrade

This extension is licensed under OSL 3.0, click here to read the license

Conclusion

After the extension is installed, the bug should be fixed moving forward (use the SQL above to fix existing data issues). Hope this helps reduce headache in your store!

Alan Barber is the Lead Web Developer at Cadence Labs and a Magento Certified developer.

Need help with Magento 2 Bugs?

We’ve had a lot of experience fixing problems with Magento 2. If you need help, head over to the Cadence Labs contact page, or email us at info@cadence-labs.com. We offer affordable rates for our Magento development services. 

Submit a Comment

Your email address will not be published. Required fields are marked *

Install our webapp on your iPhone! Tap and then Add to homescreen.
Share This