antalaron/doctrine-twig-bundle
Install the Bundle
composer require antalaron/doctrine-twig-bundle
Enable the Bundle
Add to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
Antalaron\DoctrineTwigBundle\AntalaronDoctrineTwigBundle::class => ['all' => true],
Configure the Bundle
Add to config/packages/antalaron_doctrine_twig.yaml (Symfony 4+):
antalaron_doctrine_twig:
entity: App\Entity\TemplateEntity # Your Doctrine entity storing templates
template_field: content # Field containing Twig template content
id_field: id # Field used as template ID
Create a Template Entity
Define a Doctrine entity (e.g., TemplateEntity) with fields like:
// src/Entity/TemplateEntity.php
#[ORM\Entity]
class TemplateEntity {
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column(type: 'text')]
private string $content; // Twig template content
// Getters/setters...
}
First Use Case: Render a Template
use Antalaron\DoctrineTwigBundle\Loader\DoctrineLoader;
// In a controller/service:
$template = $entityManager->getRepository(TemplateEntity::class)
->findOneBy(['id' => 1]);
$twig = $this->container->get('twig');
$rendered = $twig->render('doctrine://1', ['name' => 'John']);
// Fetch and render a template dynamically
$templateId = $request->get('template_id');
$rendered = $twig->render("doctrine://{$templateId}", $context);
// src/Twig/AppExtension.php
class AppExtension extends \Twig\Extension\AbstractExtension {
public function getGlobals() {
return [
'dynamic_templates' => $this->getTemplatesFromDB(),
];
}
}
Register the extension in config/packages/twig.yaml:
twig:
extensions:
- App\Twig\AppExtension
Leverage Twig’s Cache:
The bundle integrates with Twig’s cache system. Ensure your TemplateEntity is cached appropriately:
# config/packages/framework.yaml
framework:
twig:
cache: '%kernel.cache_dir%/twig'
Manual Cache Invalidation: Clear Twig cache when templates are updated:
$this->container->get('twig')->getEnvironment()->clearCacheFile(
$this->container->getParameter('kernel.cache_dir').'/twig/doctrine_*.php'
);
// In a form type or service
$template->setContent($sanitizedTwigContent);
# config/packages/easy_admin.yaml
easy_admin:
entities:
TemplateEntity:
class: App\Entity\TemplateEntity
list: [id, name]
form: [content]
Template Parsing Errors:
Twig_Loader_Filesystem to test parsing):
$loader = new \Twig\Loader\Filesystem([], '/tmp');
$twig = new \Twig\Environment($loader);
try {
$twig->createTemplate($templateContent);
} catch (\Twig_Error_Syntax $e) {
throw new \RuntimeException("Invalid Twig syntax: {$e->getMessage()}");
}
Circular Dependencies:
{% include 'doctrine://2' %}) may cause infinite loops.{% include 'doctrine://' ~ templateId %}
Performance Overhead:
Symfony\Component\Cache):
$cache = $this->container->get('cache.app');
$cached = $cache->get("template_{$templateId}", function() use ($templateId) {
return $this->getTemplateFromDB($templateId);
});
Symfony 4+ Kernel Awareness:
AppKernel (Symfony 2/3). For Symfony 4+, ensure the bundle is registered in config/bundles.php.autoconfigure: true in config/packages/framework.yaml if needed.Enable Twig Debugging:
# config/packages/twig.yaml
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
Log Template Renders:
$twig->addExtension(new \Twig\Extension\DebugExtension());
SQL Queries:
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
profiling: true
Custom Loader:
Extend the DoctrineLoader to add logic (e.g., ACL checks):
class CustomDoctrineLoader extends DoctrineLoader {
public function getSourceContext($name) {
$template = parent::getSourceContext($name);
if (!$this->isAllowed($template->getId())) {
throw new \Twig_Error_Loader("Access denied");
}
return $template;
}
}
Register it in services.yaml:
services:
Antalaron\DoctrineTwigBundle\Loader\DoctrineLoader:
class: App\Twig\CustomDoctrineLoader
Event Listeners: Trigger actions when templates are rendered (e.g., analytics):
// src/EventListener/TemplateRenderListener.php
class TemplateRenderListener implements \Twig\EventListenerInterface {
public function onLoad(\Twig\Event\LoadEvent $event) {
if (strpos($event->getTemplate()->getSourceContext()->getName(), 'doctrine://') === 0) {
// Log or track template usage
}
}
}
Register the listener in services.yaml:
services:
App\EventListener\TemplateRenderListener:
tags: ['twig.event_listener']
Template Versioning:
Add a version field to TemplateEntity and implement a custom loader to handle updates:
class VersionedDoctrineLoader extends DoctrineLoader {
public function getSourceContext($name) {
$template = parent::getSourceContext($name);
if ($this->hasNewerVersion($template->getId())) {
return $this->getLatestVersion($template->getId());
}
return $template;
}
}
How can I help you explore Laravel packages today?