sfk/email-template-bundle
A Symfony bundle for managing and rendering email templates, giving you a central place to store, edit, and reuse HTML email layouts with Twig-style placeholders so transactional messages stay consistent across your application.
This is a Symfony2 bundle, not a Laravel package — despite being listed in a Laravel context. There is no service provider, no facade, and no Composer auto-discovery for Laravel. Treat it as reference material or as something you'd port, not something you'd composer require into a Laravel app.
Minimal steps:
Resources/config/services.xml first — it tells you the real public surface: the templating service, the mailer/renderer, and the Twig loader. The README is thin; the service definitions and DependencyInjection/Configuration.php are the actual docs.EmailTemplate with subject/body/html fields).If you're staying in Laravel, the fastest path is to read the source (~a few hundred lines) and reimplement the two ideas you actually need: a template resolver (DB → file fallback) and a renderer that merges a layout with the resolved body.
Twig-first rendering (file templates). The bundle registers a Twig loader so templates can be referenced by name, rendered with context, and handed to Swift Mailer. In Laravel terms this maps cleanly to Blade + Mailables: keep the layout in resources/views/emails/layout.blade.php, render the body, and inject it. The bundle's pattern of "render subject and body separately, then compose" is worth copying — it lets the subject be templated independently.
Database-backed templates. The entity typically stores a key/name, locale, subject, and body. The service resolves by key (+ locale), falls back to a default, and renders. This is the pattern to port to Laravel:
email_templates table: key, locale, subject, body, is_active.Blade::render() or a Mailable that pulls the body), not Twig.Layering: layout + body. Don't store full HTML in the DB. Store the body fragment and render it inside a versioned layout. This is the single most important pattern in the bundle and the one that keeps DB templates maintainable.
Preview before send. The bundle's separation of render and send makes preview trivial. Mirror that: a render() method returning [subject, html, text] that you can hit from a route or artisan command, and a send() that consumes it. Never couple rendering to the mail transport.
Localization. The bundle's locale-aware lookup maps to Laravel's app()->setLocale() around render time, with a fallback chain (fr_CA → fr → en). Implement the fallback explicitly; don't rely on the DB query to do it.
Integration tips for a Laravel port:
EmailTemplateRepository + EmailTemplateRenderer pair; keep Mailables thin and delegate to the renderer.Cache::remember keyed by key.locale.updated_at) — DB hits per email add up in queues.Illuminate\Support\ServiceProvider, no config publish, no facade. Composer will happily install it and nothing will wire up. Budget time for a port, not an integration.{!! !!} on stored bodies is an RCE-adjacent footgun. Sanitize on save and render with a restricted context.locale → default) is the kind of thing that silently sends English to French users when a row is missing. Log fallbacks in non-production.render-only path and a test that renders every active template on deploy (a simple artisan command iterating keys). This catches broken templates before users do.saved/deleted model events and key by updated_at. Editing a template in admin and not seeing the change is the #1 support ticket for this class of package.How can I help you explore Laravel packages today?