Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getChildHtml, $useCache and child containers #4919

Closed
wants to merge 7 commits into from
Closed

getChildHtml, $useCache and child containers #4919

wants to merge 7 commits into from

Conversation

bentideswell
Copy link
Contributor

When calling the following method, the $useCache parameter is not applied to child containers, meaning that any blocks that are part of a child container are always taken from the cache, regardless of the initial value of $useCache.

\Magento\Framework\View\Element\AbstractBlock::getChildHtml($alias = '', $useCache = true)

This occurs because the \Magento\Framework\View\Layout::renderNonCachedElement method does not take $useCache as a parameter. This method then calls _renderContainer, which also doesn't take $useCache as a parameter. When _renderContainer then calls renderElement, although renderElement can accept the $useCache parameter, the value has been lost and the $useCache value is not passed to this method. As $useCache has a default value of true, blocks that are child to the container (that is child to the original block) always come from the cache, even if the developer specifically stated not to use the cache in the first call to getChildHtml.

To reproduce this, setup a layout that has the following:

<referenceContainer name="content">
    <block class="Magento\Framework\View\Element\Template" name="my.block" template="Vendor_Module::block1.phtml">
        <container name="my.container">
            <block class="Magento\Framework\View\Element\Template" name="my.block2" template="Vendor_Module::block2.phtml" />
        </container>
    </block>
</referenceContainer>

You will need to change Vendor_Module for the correct path to the template files in the above XML.

In block1.phtml, add the following code:

<?php

    echo '$useCache is not set so the value should be the same each time.<br/>';

    for ($i = 0; $i < 5; $i++) {
        echo $this->getChildHtml('my.container');
    }

    echo '$useCache is now set to false so the value should be different each time as the cache is disabled.<br/>';

    for ($i = 0; $i < 5; $i++) {
        echo $this->getChildHtml('my.container', false);
    }

?>

In block2.phtml, add the following code:

<?php echo 'Random ' . rand(1, 9999) . '<br/>' ?>

For the first round of calling getChildHtml, the second parameter is skipped so the default value of true is used. The expected output is that the random number will be the same each time as after the first call, the result will be loaded from the cache.

On the second round of calling getChildHtml, the second parameter is set as false so the expected result would be that the output would be different each time. This is not what happens though and the same value is returned each time.

If you try this again with my changes, when $useCache is set to false, the output is different each time, meaning the cache is skipped for all child blocks.

This is my first pull request but hopefully it is a useful one. It's a sticking point for a project I am working on so I am happy get this merged as quick as possible!

@bentideswell bentideswell changed the title Fixed $useCache for containers getChildHtml, $useCache and child containers Jun 7, 2016
@bentideswell
Copy link
Contributor Author

I have now signed the CLA.

@bentideswell
Copy link
Contributor Author

For anyone else who is currently suffering with this issue, I have the following temporary solution so that you can keep developing. In the block that you're calling getChildHtml from, add the following code:

/**
 * Hack required to get containers to clear the cache
 *
 * @param string $alias = ''
 * @param bool $useCache = true
 * @return string
**/
public function getChildHtml($alias = '', $useCache = true)
{
    if (!$useCache && $alias !== '') {  
        $childName = $this->getLayout()->getChildName($this->getNameInLayout(), $alias);

        if (!$childName) {
            $childName = $alias;
        }

        $this->_clearCacheOnContainers($childName);
    }

    return parent::getChildHtml($alias, $useCache);
}

/**
 * This method clears the block cache on child containers.
 * It does this by going through each block and regenerating the HTML
 * When the container is loaded, it always use the cache
 * As the cache has been regenerated with the correct content, this is okay
 *
 * @param string $containerName
 * @return $this
**/
protected function _clearCacheOnContainers($blockName)
{
    $layout = $this->getLayout();

    if ($childNames = $layout->getChildNames($blockName)) {
        foreach($childNames as $childName) {
            if ($layout->isBlock($childName)) {
                $layout->renderElement($childName, false);
            }
            else {
                $this->_clearCacheOnContainers($childName);
                $layout->renderElement($childName, false);
            }
        }
    }

    return $this;
}

@vkublytskyi
Copy link

Closed as PR does not have changed files

@vkublytskyi vkublytskyi closed this Aug 1, 2016
@tdgroot
Copy link
Member

tdgroot commented Mar 9, 2018

@bentideswell currently struggling with this bug as well. I'll try to free up some time to fix this issue.

@bentideswell
Copy link
Contributor Author

@tdgroot the fix posted above should resolve the issue. I've restructured my code not to need it but before I did this, the above worked well.

@tdgroot
Copy link
Member

tdgroot commented Mar 9, 2018

@bentideswell I've created a pull request: #14029

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants