9

I want to change to override a block's template with my custom template. But it does not have a "name", it only has a "as". The one I want to override is:

<block class="Magento\Sales\Block\Adminhtml\Order\View\Items\Renderer\DefaultRenderer"
       as="default"
       template="order/view/items/renderer/default.phtml"/>
7

How to override a template having layout ALIAS.

This answer is a possible example, you can follow this to override ALIAS template.

I have created two example modules, Vendor_Module has layout with alias template, We are overriding this alias by Vendortwo_Moduletwo module.

Assume you know the steps to create module, i am not posting entire module creation.

Module 1

\app\code\Vendor\Module\etc\frontend\routes.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
    <router id="standard">
        <route id="module" frontName="module">
            <module name="Vendor_Module" />
        </route>
    </router>
</config>

\app\code\Vendor\Module\view\frontend\layout\module_test_test.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
     <body>     
        <referenceContainer name="content">         
            <block class="Vendor\Module\Block\Test\Test" name="test_test" template="test/test.phtml">
                <block class="Vendor\Module\Block\Test\Test" as="testali" template="test/testali.phtml"/>
            </block>
        </referenceContainer>      
    </body>
</page>

Module 2

\app\code\Vendortwo\Moduletwo\etc\frontend\routes.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
    <router id="standard">
        <route id="moduletwo" frontName="moduletwo">
            <module name="Vendortwo_Moduletwo" />
        </route>
    </router>
</config>

\app\code\Vendortwo\Moduletwo\view\frontend\layout\default.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <referenceBlock name="test_test">
            <block class="Vendortwo\Moduletwo\Block\Two\Two" as="testali" template="two/twoalias.phtml"/>
        </referenceBlock>
</page>

After removing cache, i run http://localhost/magento210/module/test/test

The alias template is override by Vendortwo_Moduletwo two/twoalias.phtml

enter image description here

  • So, is this overriding a block by its alias? What if I don't want to override it but add another block after it? – Jānis Elmeris Nov 7 '17 at 14:26
3

This is how to do it properly, and without hacks.

I did not look up OP's use case, but I needed to be able to modify the renderers within the cart. The problem is that, like in OP's case, the Magento_Checkout module does not provide names to the renderers, which means they cannot be referenced and their templates changed using traditional or documented methods. However, after some sleuthing, I discovered how to do it using the tools Magento2 provides us directly in the layout XML.

Note that there are other places where this same approach works, such as in the Magento\Sales\Block\Items\AbstractItems block. The Magento_Checkout and Magento_Sales modules are the two that make most use of item renderers, so this covers many of the queries that would lead someone to changing a block's template without a name. The reason this was posted is because of the inevitability of others looking for how to modify renderer templates in the checkout or sales modules.

I'm going to provide the solution first, and then explain it in detail for anyone that wants to know why it works.

Solution

Add the following to the checkout_cart_index.xml layout file:

<referenceBlock name="checkout.cart.form">
    <arguments>
        <argument name="overridden_templates" xsi:type="array">
            <item name="default" xsi:type="string">LinusShops_Moneymaker::Magento_Checkout/cart/item/default.phtml</item>
            <item name="simple" xsi:type="string">LinusShops_Moneymaker::Magento_Checkout/cart/item/simple.phtml</item>
            <item name="configurable" xsi:type="string">LinusShops_Moneymaker::Magento_Checkout/cart/item/configurable.phtml</item>
        </argument>
    </arguments>
</referenceBlock>

Note that the module name and path need to be modified to reflect your codebase.

Explanation

This works by leveraging the overridden_templates block data, which is not defined by default.

In Magento_Checkout, the checkout_cart_index.xml layout file defines the following block:

<block class="Magento\Checkout\Block\Cart\Grid" name="checkout.cart.form" as="cart-items" template="cart/form.phtml" after="cart.summary">
    <block class="Magento\Framework\View\Element\RendererList" name="checkout.cart.item.renderers" as="renderer.list"/>
    <block class="Magento\Framework\View\Element\Text\ListText" name="checkout.cart.order.actions"/>
</block>

It then defines a couple of those renderers in the checkout_cart_item_renderers.xml layout file:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="checkout_item_price_renderers"/>
    <body>
        <referenceBlock name="checkout.cart.item.renderers">
            <block class="Magento\Checkout\Block\Cart\Item\Renderer" as="default" template="cart/item/default.phtml">
                <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions" name="checkout.cart.item.renderers.default.actions" as="actions">
                    <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit" name="checkout.cart.item.renderers.default.actions.edit" template="Magento_Checkout::cart/item/renderer/actions/edit.phtml"/>
                    <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove" name="checkout.cart.item.renderers.default.actions.remove" template="Magento_Checkout::cart/item/renderer/actions/remove.phtml"/>
                </block>
            </block>
            <block class="Magento\Checkout\Block\Cart\Item\Renderer" as="simple" template="cart/item/default.phtml">
                <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions" name="checkout.cart.item.renderers.simple.actions" as="actions">
                    <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit" name="checkout.cart.item.renderers.simple.actions.edit" template="Magento_Checkout::cart/item/renderer/actions/edit.phtml"/>
                    <block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove" name="checkout.cart.item.renderers.simple.actions.remove" template="Magento_Checkout::cart/item/renderer/actions/remove.phtml"/>
                </block>
            </block>
        </referenceBlock>
    </body>
</page>

Unfortunately, they cannot be referenced by their aliases, default and simple, respectively.

However, looking into the Magento\Checkout\Block\Cart\Grid Block, which is named checkout.cart.form, and is the parent of the renderers, it can be noted that there's a call to the getItemHtml method in the associated template, cart/form.phtml. That method then calls getItemRenderer. Both of these methods are defined in Grid's parent class, AbstractBlock. This is where the overridden_templates data is used:

/**
 * Retrieve item renderer block
 *
 * @param string|null $type
 * @return \Magento\Framework\View\Element\Template
 * @throws \RuntimeException
 */
public function getItemRenderer($type = null)
{
    if ($type === null) {
        $type = self::DEFAULT_TYPE;
    }
    $rendererList = $this->_getRendererList();
    if (!$rendererList) {
        throw new \RuntimeException('Renderer list for block "' . $this->getNameInLayout() . '" is not defined');
    }
    $overriddenTemplates = $this->getOverriddenTemplates() ?: [];
    $template = isset($overriddenTemplates[$type]) ? $overriddenTemplates[$type] : $this->getRendererTemplate();
    return $rendererList->getRenderer($type, self::DEFAULT_TYPE, $template);
}

With this knowledge, populating the block with data from layout XML is straightforward using Magento2's arguments syntax.

  • 1
    This should be accepted as the true solution. Simple and effective. Proper Magento2 way. Great explanation. Thank you! – iva Apr 5 '18 at 9:22
2

My solution is not universal, it is a "dirty hack" but it can be useful in certain cases. My sample is for frontend renderer, not for adminhtml (I suppose it should be the same).

Setup break point in \Magento\Framework\Data\Structure::getChildId with condition "$parentId == 'checkout.cart.item.renderers'" (this is a name for parent block as you can see in checkout_cart_item_renderers.xml layout). All child blocks have own (computed) names:

enter image description here

Use this names in your module's layout update:

    <referenceBlock name="checkout.cart.item.renderers_schedule_block4">
        <action method="setTemplate">
            <argument name="template" xsi:type="string">Vendor_Module::cart/item/default.phtml</argument>
        </action>
    </referenceBlock>
  • 2
    To anyone looking at this, be aware that this will fall over if you even just give it an aloof stare. Don't build your house with cards. Those numbers are not guaranteed. – danemacmillan Feb 27 '18 at 20:48
0

Please see my answer here: https://magento.stackexchange.com/a/239387/14403

I believe that is the solution that will work best for you. The solution includes overriding any block/template that does not have a name only alias.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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