Magento 1.9 – Programmatically Replace Root Block from Controller

blocksmagento-1.9template

In controller, for some condition, I want to set another parent block instead of the declared one in the layout XML. The goal is to have a possibility to make checks in template file like this:

if ($this instanceof My_Module_Block_CustomBlock) { 
    // ... custom stuff in template ... 
}

I have tried append my custom block in the layout, but parent block is still which was declared in layout XML. The appended block is shown as a child1 of parent block:

public function ConversationAction(){

    // some condition:
    $converation_id = (int) $this->getRequest()->getParam('id');

    if (is_int($converation_id) && $converation_id > 0) {
        // I want to set my parent block here instead of default registered in xml layout file

        $this->loadLayout();

        $block = $this->getLayout()->createBlock('MyModule/Conversation')
            ->setData('area', 'frontend')->setTemplate('customer/Conversation.phtml');
        $this->getLayout()->getBlock('content')->append($block);

        $this->renderLayout();

    } else {
        // ... default layout and block ...

        $this->loadLayout();
        $this->renderLayout();
    }
}

Is it possible to change the root block from controller somehow ?

Best Answer

I think it is possible, but in some cases the question is wrong. You do not want to do this. If I'm looking at what you are trying to achieve, then this is not the right solution.

Templates should be really dumb and should not care about which class they are being applied to. Templates should contain all HTML you want to render and you should try to have the least amount of coded logic in there.

There are multiple ways to change the behaviour of template from a controller.

Solution 1: Passing variables

You can pass variables to a block that represent the result of your logic.

Controller method:

$converationId = (int) $this->getRequest()->getParam('id');
$this->loadLayout();
$block = $this->getLayout()->getBlock('.....');
if ($block) {
     $block->setHasConversation(  $converationId > 0  );
}

Template:

<?php if ($this->getHasConversation()): ?>
    <!-- Do conversation stuff -->
<?php else: ?>
    <!-- Do other stuff -->
<?php endif; ?>

Solution 2: Unset one of the blocks

Based on a condition you can unset the block you DON'T want to be show/rendered.

Your page template contains the shared HTML and the child blocks will

Layout XML:

<reference name="content">
    <block type="core/template" name="mymodule_page" template="'customer/page.phtml'">
        <block type="MyModule/Conversation" name="mymodule_conversation" template="'customer/conversation.phtml'"/>
        <block type="MyModule/Other" name="mymodule_other" template="'customer/other.phtml'"/>
    </block>
</reference>

Controller method:

$converationId = (int) $this->getRequest()->getParam('id');
$this->loadLayout();
$pageBlock = $this->getLayout()->getBlock('mymodule_page');
if ($pageBlock) {
     $pageBlock->unsetChild(
         ($converationId > 0 ? 'mymodule_other' : 'mymodule_page')
     );
}

Template customer/page.phtml:

/** Call getChildHtml() with no param to render all children */
$this->getChildHtml();