In case you are not familiar with the Magento 2 page layout engine, here is a quick summary. This is very similar to Magento 1, but there have been a few tweaks.
A page that a user sees is built from a tree of “structural elements” (blocks and containers). To generate the HTML for a page, the structural element tree is rendered into HTML. Each structural element is asked to produce a fragment of HTML for the final page. During rendering, structural elements can ask children for their HTML to merge into the final result.
The reason for layouts is to make it easy for a module to add something to a page introduced by another module. For example, when viewing a product maybe you want to add into the side bar a link to any blog posts you have written on the product. This sort of manipulation in Magento is done at the structural element level (blocks and containers), not the HTML level. Every structural element can declare a global name (stored in the ‘name’ attribute in a layout XML file) that is used for such cross references. (To me, this ‘name’ is like an ‘id’ in HTML.)
More precisely, layout files actually contain layout instructions. Normally you declare changes you want to apply to the structure element tree that has been built up so far. For example, you can reference a container somewhere in the tree by name and add another child block to it. You can also move elements in the tree. <referenceContainer> and <referenceBlock> are the most common layout instructions which locate a container or block in the tree by ‘name’, then manipulate that element (frequently by adding or replacing children structural elements). Layout instructions are not the focus of this blog post, so I don’t mention them much further. I also don’t mention here the difference between pages, page layouts, and layout instructions.
Blocks vs Containers
Those familiar with Magento 1 will be familiar with blocks. Typically a block has a PHP class and a PHTML template file used to generate the HTML for the block. Methods of the PHP block class can be called from the PHTML template file to keep the amount of PHP in the PHTML file minimal.
There are two common patterns in Magento – a block can have a set of named children, or it can have an array of unnamed children. In Magento 2, these two patterns have been split into Blocks and Containers. (You can still find some old-style blocks with an array of children, such as Magento\Framework\View\Element\Html\Links. Ideally these would be all converted to the new approach.)
For example, the block Magento\Customer\Block\Account\Forgotpassword allows for a child structural element ‘form_additional_info’. The current terminology is to call this an ‘alias name’ – conceptually I think of it as a parameter name to reference child elements. Blocks can have both ‘arguments’ (configuration settings specified in <argument> elements in the layout file) and children structure elements (blocks/containers). The ‘as’ attribute of the child structural element holds the ‘alias name’ used by the parent block to find a child element by name. The forgot password child element allows other layout files to inject some additional content into the “I forgot my password” page.
Containers do not use the ‘as’ attribute on its children. The children are just an array to be displayed in order – there is no need to name them. Attributes ‘before’ and ‘after’ are used to help place children of containers relative to each other. Containers also do not have a PHTML file. All the cases examined so far had very simple markup, so the HTML markup to be generated by a container is currently held in attributes such as ‘htmlTag’ and ‘htmlClass’ of the <container> element.
<container name="customer.form.register.fields.before" as="form_fields_before" label="Form Fields Before" htmlTag="div" htmlClass="customer-form-before"/>
So what if you want a block to have a group of zero or more children? The answer is to put a container under a block. The container will have an ‘as’ attribute that the block refers to.
In Magento 1 there were no containers. The implementation of a block just asked for a child element by name or it asked for all children. That is, blocks did the job of a container as well. The reason for the split in Magento 2 was to help introduction of a visual design tool. (This tool is not being delivered as part of Magento 2.0 due to resource constraints.) Having blocks with named children and containers with zero or more unnamed children makes development of such a tool easier.
The following are some ideas that have floated around, but not considered important enough to make. This is where your voice can be heard – leave a comment if you think any of these changes are worth doing, or have a better recommendation.
- One could consider changing the ‘name’ attribute of blocks and containers to ‘id’ to better reflect that it is globally unique in the structural element tree. <referenceBlock> and <referenceContainer> elements could then have a ‘ref’ attribute (rather than the current ‘name’ attribute) to reference an ‘id’.
- I find myself referring to ‘as’ attributes as the child name (rather than ‘alias name’). The term ‘alias’ is not really the right concept to me. It is not an alternative way to reference the child – it is the only way a parent block references a child. The only problem with ‘child name’ is that it’s easy confused with the current ‘name’ attribute (a further reason for the previous bullet point of renaming ‘name’ to ‘id’).
- Remove the ability for blocks to have an array of children by removing the function to fetch all children. This would help ferret out the few remaining blocks with arrays of children. Each should be changed to a container.
- Or maybe you think blocks should still be able to have an array of children. In that case, the current APIs to accessing children does not allow access to children as an array without also picking up all named children. Maybe the function to return all children should only pick up those without an ‘as’ attribute set.
- Rename the <update> layout instruction in layout files (not mentioned above) to say <include> to better explain what the element does. Ok, unrelated to the rest of this blog post but it always annoyed me! 😉 Currently you say <update handle=’xxx’> which does not update the referenced handle – instead it includes the instructions in the referenced layout file into the current file.
- The XML Schema (XSD file) for layouts could be tightened up so the ‘as’ attribute is only permitted on <block> and <container> elements under <block> and <referenceBlock>. At present the ‘as’ attribute is allowed in many places where it has no meaning. This has led to some Magento layout files specifying the ‘as’ attribute in the wrong place by accident. This should be cleaned up to reduce confusion.
- Unrelated again to this blog post, the XML Schema also allows some nesting of <referenceBlock> and <referenceContainer> elements that serves no semantic purpose. For example, a <referenceBlock> inside a <referenceBlock> actually has no meaning. They should be siblings, allowing nesting to have some meaning in a future release.
- Add metadata to each block (a new function?) to return documentation about the block, the ‘alias’ names it supports for child elements, and the <argument> markup the block supports. Then develop a tool to print out this information for all available blocks for online documentation.
- If blocks did have metadata available as to the legal children alias names, this could be verified in a structure element tree to spot spelling errors in ‘as’ attribute values (in developer mode).
- For the brave, consider implementing an interactive layout file editor that can manipulate layout files and hence the structural element tree. The challenge here you need to capture the instructions to manipulate a tree, not just capture the final tree itself. But a tool that could show the final tree while editing the layout instructions (using the metadata from the previous point) would be kinda cool.
The purpose of this blog post was to clarify the reason for separating containers from blocks. While Magento 2.0 will not have a visual design editor, it is not ruled out for a future release.
I also snuck in a few personal favorite little changes that I would like to make, but do not have the clear business value to make them worth the cost of implementing. We may do some, but no guarantees. If you think they are worth introducing, or any other related changes, feel free to leave a comment.