A layout for a piece of a page. The skeleton owns the markup, the caller owns the content - and you can use it three times on one page.
You will learn:
- what
{embed}does that{include}and{layout}cannot - how a component declares its holes and its parameters
- why blocks inside an embed cannot collide with blocks outside it
php 11-embed/example.php{layout} gives a whole page a skeleton. {include} drops a fragment in. Neither lets you say: here is a box with a fixed structure, and here is what goes inside it, right now, at this call site.
That is {embed} - include and layout in one tag.
{embed '@panel.latte', modifier: 'wide'}
{block title}This month's picks{/block}
{block body}
<ul>...</ul>
{/block}
{/embed}The component is an ordinary template with blocks and parameters:
<section class="panel panel--{$modifier}">
<h2 class="panel__title">{block title}{/block}</h2>
<div class="panel__body">
{block body}Nothing here yet.{/block}
</div>
</section>The example uses it three times on one page: a wide panel with a book list, a narrow one with opening hours, and a third that overrides only the title and inherits the default body. The class names, the heading level and the nesting live in exactly one file - change panel__body to something else and every panel on the site follows.
You could pass the body in as a string parameter, and people do, and it is miserable: you end up building HTML in PHP, or escaping things by hand, or inventing a mini-language of flags (showFooter, titleTag, bodyClass) so the partial can decide what to render. {embed} lets the caller write plain markup in the place where it belongs.
The difference in one line: {include} passes values into a template, {embed} passes markup.
Blocks inside an embed live in their own layer. Two panels on the same page can both define title without colliding, and neither collides with a title block belonging to the page's layout. This is the property that makes components composable at all - otherwise the third panel you add to a page would silently overwrite the first.
The isolation cuts both ways: inside an embed you can include blocks defined in the embed, blocks from the embedded template, and definitions from an {import} - but not the outer page's ordinary blocks. Nor does the embedded template see the caller's variables; it gets what you pass in the tag, which is the same discipline definitions have.
You can also embed a block instead of a file - {embed myComponent, ...} - handy when the skeleton is small enough to live next to its usage.
<section class="panel panel--wide">
<h2 class="panel__title">This month's picks</h2>
<div class="panel__body">
<ul>
<li>It Works on My Machine by Marta Novak</li>
<li>Escaping & Other Life Skills by Petr Svoboda</li>
</ul>
...
<section class="panel panel--narrow">
<h2 class="panel__title">Opening hours</h2>
<div class="panel__body">
<p>Weekdays 9-18, Saturday 9-12.</p>
...
<section class="panel panel--narrow">
<h2 class="panel__title">Newsletter</h2>
<div class="panel__body">
Nothing here yet.
</div>
</section>Three panels, one skeleton, and the third one shows the default body because it never overrode that block.
- Add a
{block footer}to@panel.lattewith a default, then override it in one panel only. - Give two panels a
{block title}containing the same text and confirm they do not interfere - that is the isolation doing its job. - Try
{include title}from outside an embed. It is not visible there, and the error says so.