如何在Twig模板引擎中实现类似Smarty的多模板继承功能?
Nice question! I’ve worked with both Smarty and Twig, so I can walk you through how to replicate that multi-template inheritance pattern for your plugin system in Twig—since Twig handles inheritance differently out of the box, but we can mimic the behavior with a few solid approaches.
Background
First, a quick recap: Smarty lets you load a parent template + multiple child templates in one go with $smarty->display('index.tpl|child.tpl|child2.tpl'), where children inject content into the parent’s structure. Twig uses single-level {% extends %} by default, but we can achieve similar plugin-friendly results with these methods:
1. Use embed + Block Overrides (Simplest Approach)
Twig’s embed tag lets you inject one template’s content into another, which is perfect for combining multiple child templates into a parent. Here’s how to set it up:
Step 1: Define Your Parent Template (index.twig)
Create a base template with blocks that child templates (plugins) can fill or override:
<!DOCTYPE html> <html> <head> <title>{% block page_title %}Default Site Title{% endblock %}</title> </head> <body> <main>{% block main_content %}{% endblock %}</main> <aside>{% block plugin_widgets %}{% endblock %}</aside> <!-- For plugin content --> </body> </html>
Step 2: Create Child/Plugin Templates
child.twig(main content override):
{% block page_title %}Custom Page Title{% endblock %} {% block main_content %} <h1>Welcome to My Page</h1> <p>Content from the primary child template</p> {% endblock %}
child2.twig(plugin widget):
{% block plugin_widgets %} <div class="plugin-card"> <h3>My Plugin</h3> <p>This is dynamic content from a plugin template</p> </div> {% endblock %}
Step 3: Combine Them in a "Wrapper" Template
Create a single template that extends the parent and embeds all children:
{% extends 'index.twig' %} {% embed 'child.twig' %}{% endembed %} {% embed 'child2.twig' %}{% endembed %}
Then render this wrapper in PHP:
echo $twig->render('combined.twig');
2. Dynamic Template Merging via PHP
If you want to avoid creating wrapper templates (like Smarty’s pipe syntax), you can dynamically load and combine templates in PHP using Twig’s block rendering methods:
// Load all required templates $parentTemplate = $twig->load('index.twig'); $childTemplate = $twig->load('child.twig'); $child2Template = $twig->load('child2.twig'); // Render individual blocks from children $renderedData = [ 'page_title' => $childTemplate->renderBlock('page_title'), 'main_content' => $childTemplate->renderBlock('main_content'), 'plugin_widgets' => $child2Template->renderBlock('plugin_widgets'), ]; // Pass rendered blocks to the parent template echo $parentTemplate->render($renderedData);
Update your parent template to use these variables instead of blocks:
<!DOCTYPE html> <html> <head> <title>{{ page_title ?? 'Default Site Title' }}</title> </head> <body> <main>{{ main_content ?? '' }}</main> <aside>{{ plugin_widgets ?? '' }}</aside> </body> </html>
3. Build a Custom Twig Extension (Mimic Smarty’s Pipe Syntax)
For a closer match to Smarty’s syntax, create a Twig extension that parses pipe-separated template strings and combines them automatically:
Step 1: Create the Extension Class
class CombinedTemplateExtension extends \Twig\Extension\AbstractExtension { public function getFunctions(): array { return [ new \Twig\TwigFunction('render_combined', [$this, 'renderCombined'], [ 'needs_environment' => true, 'is_safe' => ['html'] ]), ]; } public function renderCombined(\Twig\Environment $env, string $templateString): string { // Split the pipe-separated template list $templates = explode('|', $templateString); $parentTemplate = array_shift($templates); // Build a temporary combined template $combinedTemplateContent = "{% extends '$parentTemplate' %}"; foreach ($templates as $child) { $combinedTemplateContent .= "{% embed '$child' %}{% endembed %}"; } // Render the temporary template $template = $env->createTemplate($combinedTemplateContent); return $template->render(); } }
Step 2: Register the Extension
$twig->addExtension(new CombinedTemplateExtension());
Step 3: Use It in PHP
Now you can call it just like Smarty’s display method:
echo $twig->getExtension(CombinedTemplateExtension::class)->renderCombined($twig, 'index.twig|child.twig|child2.twig');
Or even use it directly in a Twig template:
{{ render_combined("index.twig|child.twig|child2.twig") }}
Key Notes
- Twig’s block system supports block stacking—use
{{ parent() }}in child blocks if you want to keep the parent’s content alongside the child’s (instead of replacing it). - For plugin systems, the
embedmethod is usually the most maintainable, as it keeps template logic within Twig files instead of PHP.
内容的提问来源于stack exchange,提问作者Tommy Meier

