2

We have been using CMS blocks with the same identifiers (see image), to be able to use child themes and include the these CMS blocks to several store views.

CMS Block Identifier

After a upgrade to Magento 2.3.3 that's not working anymore.

Error

Anyone have a solution for this or can shed some light?

Thanks,

EDIT

Found This at GitHub:

https://github.com/magento/magento2/issues/4831

2
  • Please use unique identifiers and check it. It will work. Commented Nov 11, 2019 at 5:40
  • I really want continue using identifiers with the same names, as I have done in earlier versions of Magento. It's (was) a great feature when using a lot of child themes in a multistore setup.
    – Aknot
    Commented Nov 11, 2019 at 7:01

4 Answers 4

3
+50

I could replicate the same issue in my m2 local instance and identified the reason that you had this issue, also provided below a quick fix for this.

Cause of the issue

There's a difference in code between 2.3.3. and 2.2.x versions in CMS Block ResourceModel Class vendor/magento/module-cms/Model/ResourceModel/Block.php in the method getIsUniqueBlockToStores

https://github.com/magento/magento2/blob/2.3.3/app/code/Magento/Cms/Model/ResourceModel/Block.php

In this method, they check for uniqueness of the block id to selected store(s). In your case, it fails because its not unique as you have same id for both store-view and all store views.

In the recent Magento 2.3.x. version it checks the database table cms_block_store to confirm whether the static block exists, only if its a default store.

Whereas in the prior versions it does check the cms_block_store irrespective of the store scope of the block saved.

A quick fix (dont make this change, find the module given below under solution and install this in your store to fix your issue.)

removed the condition to confirm whether its a default store to add the WHERE condition to check the cms_block_store always, as it was before, in CMS Block Resource Model.

vendor/magento/module-cms/Model/ResourceModel/Block.php

/**
     * Check for unique of identifier of block to selected store(s).
     *
     * @param AbstractModel $object
     * @return bool
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getIsUniqueBlockToStores(AbstractModel $object)
    {
        $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class);
        $linkField = $entityMetadata->getLinkField();
        $stores = (array)$object->getData('store_id');
        $isDefaultStore = $this->_storeManager->isSingleStoreMode()
            || array_search(Store::DEFAULT_STORE_ID, $stores) !== false;

         // commented out the if condition below to include the default store id 0 when we check the uniqueness of the block

        //if (!$isDefaultStore) {
            //$stores[] = Store::DEFAULT_STORE_ID;
        //}

        $select = $this->getConnection()->select()
            ->from(['cb' => $this->getMainTable()])
            ->join(
                ['cbs' => $this->getTable('cms_block_store')],
                'cb.' . $linkField . ' = cbs.' . $linkField,
                []
            )
            ->where('cb.identifier = ?  ', $object->getData('identifier'));

        // commented out the if condition below to check the `cms_block_Store` tale always   
        //if (!$isDefaultStore) {
            $select->where('cbs.store_id IN (?)', $stores);
        //}


        if ($object->getId()) {
            $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId());
        }
        if ($this->getConnection()->fetchRow($select)) {
            return false;
        }
        return true;
    }

Solution (Create and Enable this Module)

Create the following directories and files as given below

File

app/code/StackExchange/Override/Model/ResourceModel/Block.php

Code

<?php

/* @author Jerome Dennis <[email protected]> */ 

namespace StackExchange\Override\Model\ResourceModel;
use Magento\Framework\Model\AbstractModel;
use Magento\Cms\Api\Data\BlockInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;

/**
 * CMS block model
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class Block extends \Magento\Cms\Model\ResourceModel\Block
{
    /**
     * Check for unique of identifier of block to selected store(s).
     *
     * @param AbstractModel $object
     * @return bool
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getIsUniqueBlockToStores(AbstractModel $object)
    {
        $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class);
        $linkField = $entityMetadata->getLinkField();

        $stores = (array)$object->getData('store_id');
        $isDefaultStore = $this->_storeManager->isSingleStoreMode()
            || array_search(Store::DEFAULT_STORE_ID, $stores) !== false;

         // commented out the if condition below to include the default store id 0 when we check the uniqueness of the block

        //if (!$isDefaultStore) {
            //$stores[] = Store::DEFAULT_STORE_ID;
        //}

        $select = $this->getConnection()->select()
            ->from(['cb' => $this->getMainTable()])
            ->join(
                ['cbs' => $this->getTable('cms_block_store')],
                'cb.' . $linkField . ' = cbs.' . $linkField,
                []
            )
            ->where('cb.identifier = ?  ', $object->getData('identifier'));

            $select->where('cbs.store_id IN (?)', $stores);

        if ($object->getId()) {
            $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId());
        }

        if ($this->getConnection()->fetchRow($select)) {
            return false;
        }

        return true;
    }
}

File

app/code/StackExchange/Override/etc/adminhtml/di.xml

Code

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Cms\Model\ResourceModel\Block" type="StackExchange\Override\Model\ResourceModel\Block" />
</config>

File

app/code/StackExchange/Override/etc/module.xml

Code

<?xml version="1.0"?>
<!--
/**
 *   StackExchange_Override
 *   Jerome Dennis <[email protected]>
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="StackExchange_Override" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Cms"/>
        </sequence>
    </module>
</config>

File

app/code/StackExchange/Override/registration.php

Code

<?php
/**
 *   StackExchange_Override
 *   Jerome Dennis <[email protected]>
 */

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'StackExchange_Override',
    __DIR__
);

I assume that you have a development / staging instance and you are on developer mode. (dont try this on production before you try this on development or staging server)

  • enable the module (php bin/magento module:enable StackExchange_Override)
  • register the module in the set up table (php bin/magento setup:upgrade)
  • remove the generated files generated\code and regenrate (php bin/magento setup:di:compile)
  • clean the cache types

This will certainly fix the issue that you face (tested and verified on my local instance). Thank You !

enter image description here enter image description here

6
  • I have tried your fix, and I can save the blocks in "All Store Views" now, that's great. But I can't save the blocks in specific "Store Views", they still show the error about the identifier properties. Am I missing something? Thanks,
    – Aknot
    Commented Nov 13, 2019 at 7:11
  • Note that this is when editing already created CMS blocks in store views, not creating new ones (haven't tested that yet).
    – Aknot
    Commented Nov 13, 2019 at 7:16
  • 1
    @Alldo you can find this modified code changes above in the file app/code/StackExchange/Override/Model/ResourceModel/Block.php which will allow you to edit and save the blocks assigned to specific store views and all store views. It also works fine for new blocks with same identifier. Basically the code above skips the validation of the uniqueness of the 'block identifier', which is justified in your case as per your business requirement and it also sounds logical to me.
    – Haijerome
    Commented Nov 13, 2019 at 23:18
  • Fantastic, works like a charm. Can't understand why they made these changes, do you @Haijerome?
    – Aknot
    Commented Nov 14, 2019 at 7:57
  • 1
    @Alldo glad it helps you. BTW it seems they have ignored to evaluate different use cases like the store owner needs to keep the same identifier for all store views and store specific for their magento e-commerce store front. The validation of default store (all store views) is totally unnecessary in my opinion. Because if the Magento core developers felt that it's necessary then they would have made the cms block identifier as a unique field in the cms block tables. I also notice a difference in opinion between various contributors of this issue.
    – Haijerome
    Commented Nov 14, 2019 at 11:25
0

The first block is set to store view "All Store Views".

Change this to a different store view and you can save the block.

3
  • But I want that block identifier name for "All Store Views", that's the point. Since upgrading Magento to version 2.3.3, that feature has stopped working.
    – Aknot
    Commented Nov 11, 2019 at 7:55
  • You can use the same block identifier name. You can't use All Store Views and another store view for a block with the same identifier name. Commented Nov 11, 2019 at 7:58
  • No not right now, but I could before, that's the problem. Look at the image in the post, its a real image taken from the actual store, not an example what I want to achieve.
    – Aknot
    Commented Nov 11, 2019 at 8:05
0

Please remove the first block from the site mentioned in your screenshot.

Then create blocks with same identifier for all available stores and save. Voila, now the site will have same identifier across all store views, hope this is enough?

Additionally, one thing to let you know is that same identifier cant be used if its set for "all store views" in a block. This is the default property of Magento and it should be. Otherwise, it wont make any sense i think. Good luck.

0

If you need to update CMS block programmatically there is more elegant solution then rewriting Magento\Cms\Model\ResourceModel\Block::getIsUniqueBlockToStores

All you need to do is to set right area before doing anything with block

public function __construct(
    ...
    \Magento\Cms\Api\BlockRepositoryInterface $blockRepository,
    \Magento\Framework\App\State $appState
    ...
)
{
    ...
    $this->blockRepository = $blockRepository;        
    $this->appState = $appState;
}

public function updateCmsBlock($block)
{
    
    $self = $this;
    try {
        $this->appState->emulateAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML, function () use ($self) {
            $additionalStores = ['2','4', '35']; //add to another stores
            $newTitle = 'A new title for block';

            $block->setData('store_id', $additionalStores);
            $block->setTitle($newTitle)
            $this->blockRepository->save($block);
        });
    } catch (\Exception $exception) {
         //do something with exception
    }
    
    
 }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.