kunstmaan/swiftmailer-bundle
Legacy Symfony bundle that integrates the Swiftmailer mailer library. Largely obsolete now that Swiftmailer is abandoned and Symfony Mailer is the standard, so it suits only older projects still relying on Swiftmailer.
Reality check first. kunstmaan/swiftmailer-bundle is a Symfony bundle — it wires a Swiftmailer-based Mailer service into the container, with optional Doctrine-backed, multilingual email templates. You cannot composer require it into a Laravel app and get anything useful: it registers a Bundle, reads a kunstmaan_swiftmailer config tree, and expects Symfony's MailerInterface. The repository is archived, has 0 stars, and the last release (v3.6.0) is a single-commit constraint widening. Treat it as something you maintain or migrate away from in a legacy Kunstmaan CMS codebase, or as a pattern reference for how to build DB-driven templated mail in Laravel.
What v3.6.0 actually changed. One thing: composer.json now allows Symfony 6 packages ("Allow symfony 6 packages"). There are no new features, no API changes, no deprecations. It does not change the transport story — Swiftmailer itself is still EOL and unmaintained; the bundle only stops blocking composer update on a Symfony 6 host. Read it as "the fork maintainer unblocked a constraint", not "the package is alive again". The archived flag and the absence of security maintenance are unchanged, and there is exactly one contributor on this release.
Minimal steps in a legacy Symfony app:
composer require kunstmaan/swiftmailer-bundle — pin the version to match your Symfony line: <3.6 for Symfony 4.x/5.x, ^3.6 only if you're on Symfony 6. Check composer.json require in the repo before installing; the tag boundaries are the whole story of this release.swiftmailer/swiftmailer and symfony/* versions together with composer why-not. If you're on Symfony 6, this bundle being installable does not mean the rest of your mail stack is healthy — check that nothing else still drags in symfony/swiftmailer-bundle.config/bundles.php under Symfony Flex, or app/AppKernel.php on the older skeleton.swiftmailer: plus the bundle key (typically kunstmaan_swiftmailer:) in config/packages/*.yaml.EmailTemplate entity into Doctrine and generate a migration — the table must exist before the mailer can render anything.Where to look first, in this order:
README.md — usually thin; skim it only for the config key names.DependencyInjection/Configuration.php — the authoritative list of config options and the template_entity class parameter.Mailer/ (the Mailer service and its interface) — this is the one class you'll actually call.Entity/EmailTemplate.php (+ repository) — schema, locale columns, subject/body fields.Resources/config/services.yml — service IDs you'll inject or decorate.Resources/views/ — the Twig layout the body is rendered into.First use case to validate the install: send a single localized welcome email where subject and body come from the database, rendered with Twig and a small array of variables. If that works end-to-end (including the correct locale fallback) — and the profiler shows the message — everything else is repetition. Do this on the exact Symfony/PHP pair you intend to ship on, because the v3.6.0 constraint bump is precisely the kind of change that shifts your minimum PHP.
Core call pattern. Inject the bundle's mailer service rather than Symfony's raw mailer — the former is what knows about template entities and Twig rendering:
use Kunstmaan\SwiftmailerBundle\Mailer\Mailer; // confirm FQCN in the repo
public function __construct(private Mailer $mailer) {}
public function notify(User $user): void
{
$template = $this->emailTemplates->findOneBySlug('welcome', $user->getLocale());
$this->mailer->send(
$template,
$user->getEmail(),
['user' => $user, 'activationUrl' => $url] // Twig context
);
}
Always pass a scalar or DTO-ish context. Passing full Doctrine entities into Twig invites lazy-loading inside the mail send path, which blows up when you move sending to a spool/queue consumer with a closed EntityManager.
Locale as a first-class concern. The whole reason to use this bundle over plain Swiftmailer is multilingual DB templates. Establish a lookup chain explicitly in your own wrapper service (requested locale → site default → hardcoded fallback), don't rely on the bundle to guess. Keep template slugs stable and treat translations as data, not code.
Spool instead of sending inline. Configure swiftmailer.spool (file or memory) and run swiftmailer:spool:send from cron/worker. This is the direct analogue of Laravel's queue driver:
| Symfony/Swiftmailer | Laravel equivalent |
|---|---|
swiftmailer.spool: { type: file } + cron spool:send |
QUEUE_CONNECTION + php artisan queue:work |
Mailer::send() inline |
Mail::to()->send() inline |
| spool + console consumer | Mail::to()->queue(new Mailable) |
EmailTemplate entity (DB, per-locale) |
email_templates table + Blade::render() inside a Mailable |
| Twig template for subject + body | Blade view + Markdown mailable |
swiftmailer:email:send dev command |
Mail::fake() + php artisan tinker |
Laravel-side pattern if you're porting. Don't emulate the bundle's DB templates with eval or string-rendered PHP — render Blade strings from the DB row inside a Mailable:
public function build(): self
{
$tpl = EmailTemplate::for('welcome', $this->locale);
return $this->subject($tpl->subject)
->view('mail.layout') // static shell, versioned in git
->with(['body' => Blade::render($tpl->body, $this->data)]);
}
Keep the layout in code and the content in the DB — that's the one design decision worth carrying over from this bundle.
Integration tipping points.
NotificationMailer) in front of the bundle mailer. Every call site then goes through one place you can later repoint at symfony/mailer or a Laravel HTTP mail service.Mail::fake() equivalents; assert on rendered subject/body, not on the transport.composer.lock for transitive symfony/* jumps you didn't intend.symfony/mailer (Nov 2021 deprecation, Nov 2022 EOL), and this bundle went archived right at that boundary. v3.6.0 loosening the Symfony constraint does not revive the transport — Swiftmailer the library is still dead code. There will be no security fixes. Any new work should target symfony/mailer (Symfony) or Laravel's built-in Mail/Mailable. Budget the migration rather than extending this.^3.6.0 exactly, run your own send/spool/profiler smoke test, and be aware that the "supports Symfony 6" line is unverified by the release itself.composer.json, and consider vendoring or forking if you must live with it — Packagist metadata for abandoned packages drifts, and tags like v3.6.0 can appear without the repo being meaningfully maintained.AppKernel::registerBundles(). A "service not found" error is almost always a registration or config-import problem, not a bug in the mailer.EmailTemplate entity won't exist in your DB until you run a diff/migration. The mailer will throw a "table not found" or return no template — and depending on the code path, may silently send nothing. Check Entity/ for the exact table name.strict_variables: true throw at send time. Validate template variables at save time in your admin UI, not at send time.memory spool only flushes at kernel terminate — a fatal error loses the mail. file spool needs write permissions on var/spool and a running spool:send job; if cron is missing, mail simply never leaves. Check the spool directory and the command's exit code before blaming the transport.kernel.debug is on), and swiftmailer:email:send for a transport smoke test. If the profiler shows the message but the inbox is empty, it's transport/DNS/spam — not the bundle.decorates: kunstmaan_swiftmailer.mailer) to add logging or a forced BCC in staging, and override Twig under templates/bundles/<BundleName>/. These three cover 90% of real customisation without forking.Mail::alwaysTo().send().How can I help you explore Laravel packages today?