Magento – Change block type programmatically from controller

blockscontrollersmagento-1.9

How i can change block type programmatically from controller.

Best Answer

Here i change login form url without change .phtml and layout xml file.

Original login handler is

<customer_account_login translate="label">
    <label>Customer Account Login Form</label>
    <!-- Mage_Customer -->
    <remove name="right"/>
    <remove name="left"/>

    <reference name="root">
        <action method="setTemplate"><template>page/1column.phtml</template></action>
    </reference>
    <reference name="content">
        <block type="customer/form_login" name="customer_form_login" template="customer/form/login.phtml" />
    </reference>
</customer_account_login>

I going to change "customer_form_login" block block type

Create Controller which extend Mage_Customer_AccountController which contain loginAction()

class Namespace_ModuleName_AccountController extends Mage_Customer_AccountController{
public function loginAction()   {
  if ($this->_getSession()->isLoggedIn()) {
        $this->_redirect('*/*/');
        return;
    }
    $this->getResponse()->setHeader('Login-Required', 'true');
    $this->loadLayout();

    $update = $this->getLayout()->getUpdate();
    $update->addHandle('customer_account_login');
    $this->loadLayoutUpdates();
    $this->generateLayoutXml();
    $this->generateLayoutBlocks();
    $this->_isLayoutLoaded = true;

    $blockList = $this->getLayout()->getBlock('customer_form_login');
    $template = $blockList->getTemplate();
    $blockChildBlock = $blockList->getChild();

    $this->getLayout()->unsetBlock('customer_form_login');
    $parentBlock = $this->getLayout()->createBlock('blockname/customer_form_login','customer_form_login')->setTemplate($template);

    if($blockChildBlock){
        foreach($blockChildBlock as $childBlock){
            $this->getChildBlockLogin($parentBlock,$childBlock);
        }
    }
    $this->_initLayoutMessages('customer/session');
    $this->_initLayoutMessages('catalog/session');
    $this->renderLayout();   
 }
}

public function getChildBlockLogin($parentBlock,$childBlock){

    /*echo "ChildB: ".$childBlock->getNameInLayout()."<br/>";
    echo "Alias: ".$childBlock->getBlockAlias()."<br/>";
    echo "Child: ".$childBlock->countChildren()."<br/>";
    echo "Type: ".$childBlock->getType()."<br/>";
    echo "Template: ".$childBlock->getTemplate()."<br/>";*/
    $childName = $childBlock->getNameInLayout();

    if($childBlock->getBlockAlias()!=''){
        $childName = $childBlock->getBlockAlias();
    }
    if($childBlock->getTemplate()!=''){
        //$child = $this->getLayout()->createBlock($childBlock->getType(),$childName)->setTemplate($childBlock->getTemplate());
        $parentBlock->setChild($childName,$childBlock);
    }
    else{
        //$child = $this->getLayout()->createBlock($childBlock->getType(),$childName);
        $parentBlock->setChild($childName,$childBlock);
    }
    $parentBlockData = $this->getLayout()->getBlock($childBlock->getNameInLayout());
    $blockChildBlockData = $parentBlockData->getChild();
    if($blockChildBlockData){
        foreach($blockChildBlockData as $childBlockData){
            $this->getChildBlockLogin($parentBlockData,$childBlockData);
        }
    }       
 }
}

Create block "\app\code\local\Namespace\ModuleName\Block\Customer\Form\Login.php"

class ModuleName_ModuleName_Block_Customer_Form_Login extends Mage_Customer_Block_Form_Login{
  public function getPostActionUrl(){
     return Mage::getUrl('modulefrontname/account/loginPost');
 }
}

From this code you can also copy original block child block in new created block. Try below code to test child blocks

<customer_account_login translate="label">
    <label>Customer Account Login Form</label>
    <!-- Mage_Customer -->
    <remove name="right"/>
    <remove name="left"/>

    <reference name="root">
        <action method="setTemplate"><template>page/1column.phtml</template></action>
    </reference>
    <reference name="content">
        <block type="customer/form_login" name="customer_form_login" template="customer/form/login.phtml">
            <block type="core/template" name="test.block" template="customer/form/test.phtml" >
                <block type="core/template" name="test.block1" as="testB1" template="customer/form/test1.phtml" />
            </block>
        </block>
    </reference>
</customer_account_login>

Create file in "\app\design\frontend\base\default\template\customer\form\test.phtml"

<?php echo $this->getChildHtml('testB1')?>

Create file in "\app\design\frontend\base\default\template\customer\form\test1.phtml"

Child Block<br/>

This code return error while enable cache from admin. To fixed that issue we have to comment below code

/*$update = $this->getLayout()->getUpdate();
$update->addHandle('customer_account_login');
$this->loadLayoutUpdates();
$this->generateLayoutXml();
$this->generateLayoutBlocks();
$this->_isLayoutLoaded = true;*/

and created layout file in "\app\design\frontend\base\default\layout\layoutname.xml "

<?xml version="1.0"?>
<layout version="0.1.0">
    <layout_handler translate="label">
        <update handle="customer_account_login"/>
    </handler>
</layout>

This is not best solution for me because i did not want to use .xml file. Please suggest me if any one get good solution for this issue.