如何扩展Timber实现以句柄引用模板的Include功能?
Great question! I’ve worked on similar Timber/Twig setups before, so here’s how you can replicate Fractal’s convenient @handle include shortcut to clean up your template code:
Core Idea
We’ll create a custom Twig extension that intercepts {% include %} calls, detects when you’re using an @-prefixed handle, and maps it to the full path of your component template. This works seamlessly with Timber since it’s built on top of Twig.
Step 1: Build the Twig Extension
Add this code to your WordPress theme’s functions.php file or a custom plugin—it overrides Twig’s default include parser to handle @handles:
class FractalStyleHandleExtension extends \Twig\Extension\AbstractExtension { private $timber; public function __construct(\Timber\Timber $timber) { $this->timber = $timber; } public function getTokenParsers() { return [ new class extends \Twig\TokenParser\IncludeTokenParser { public function parse(\Twig\Token $token) { // Parse the original include expression $expr = $this->parser->getExpressionParser()->parseExpression(); // Check if we're dealing with an @handle if ($expr instanceof \Twig\Node\Expression\ConstantExpression && str_starts_with($expr->getAttribute('value'), '@')) { $handle = ltrim($expr->getAttribute('value'), '@'); // Map the handle to your component's template path // Adjust this pattern to match your project's structure $templatePath = "templates/components/{$handle}/{$handle}.twig"; // Replace the @handle with the full path $expr = new \Twig\Node\Expression\ConstantExpression($templatePath, $token->getLine()); } // Handle remaining include logic (variables, only, ignore missing) $variables = null; $only = false; $ignoreMissing = false; if ($this->parser->getStream()->nextIf(\Twig\Token::NAME_TYPE, 'with')) { $variables = $this->parser->getExpressionParser()->parseExpression(); } if ($this->parser->getStream()->nextIf(\Twig\Token::NAME_TYPE, 'only')) { $only = true; } if ($this->parser->getStream()->nextIf(\Twig\Token::NAME_TYPE, 'ignore missing')) { $ignoreMissing = true; } $this->parser->getStream()->expect(\Twig\Token::BLOCK_END_TYPE); return new \Twig\Node\IncludeNode($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); } } ]; } public function getName() { return 'fractal_style_handle_extension'; } } // Register the extension with Timber add_filter('timber/twig', function($twig) { $twig->addExtension(new FractalStyleHandleExtension(new \Timber\Timber())); return $twig; });
Step 2: Customize Path Mappings (Optional)
If your component paths don’t follow a strict handle/handle.twig pattern (like your example where @basicteaser maps to templates/components/teaser/basicTeaser.twig), add a lookup array to handle exceptions:
Inside the parse() method, replace the $templatePath line with this:
// Define custom handle-to-path mappings $handleMappings = [ 'basicteaser' => 'templates/components/teaser/basicTeaser.twig', 'featured-card' => 'templates/components/cards/featured.twig', // Add more mappings as needed ]; // Use custom mapping if available, else fall back to default pattern $templatePath = isset($handleMappings[$handle]) ? $handleMappings[$handle] : "templates/components/{$handle}/{$handle}.twig";
Step 3: Use the @handle Shortcut
Now you can clean up your includes just like in Fractal:
{# Before: long path #} {% include 'templates/components/teaser/basicTeaser.twig' %} {# After: clean @handle #} {% include '@basicteaser' %} {# Works with variables too! #} {% include '@featured-card' with {title: 'My Featured Card', image: post.thumbnail} only %}
Key Notes
- Handle Uniqueness: Just like Fractal, this relies on your component handles being unique—avoid duplicate @handles to prevent path conflicts.
- Template Locations: Make sure your component paths align with where Timber searches for templates. If you’ve customized template directories via the
timber/twig/template_dirsfilter, adjust the$templatePathvalues accordingly. - Error Handling: For production, you might want to add checks for missing templates (e.g., throw a warning if the mapped path doesn’t exist) to catch typos early.
内容的提问来源于stack exchange,提问作者Sascha

