glpzzz/form-mailer

Render a yiisoft form model to an email and send it with Symfony Mailer.

Maintainers

Package info

github.com/glpzzz/form-mailer

pkg:composer/glpzzz/form-mailer

Transparency log

Statistics

Installs: 21

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.3 2026-08-31 11:40 UTC

This package is auto-updated.

Last update: 2026-08-31 11:41:15 UTC


README

Render a yiisoft/form-model form to an email and send it with Symfony Mailer. Replaces the hand-copied MailMessage interface + EmailOutput trait + inline send wiring that TechniqueWeb sites duplicated.

Install

composer require glpzzz/form-mailer

Use

Make the form mailable — implement MailableForm, add RendersFieldTable:

use Glpzzz\FormMailer\MailableForm;
use Glpzzz\FormMailer\RendersFieldTable;
use Glpzzz\FormMailer\Captcha\CaptchaRule;
use Symfony\Component\Mime\Address;

final class ContactForm extends FormModel implements RulesProviderInterface, MailableForm
{
    use RendersFieldTable;

    public function getRules(): iterable
    {
        return [
            // ...
            'captcha' => [new Required(), new CaptchaRule('contact')],
        ];
    }

    public function mailSubject(): string   => "Quote request — {$this->name}";
    public function mailReplyTo(): ?Address  => new Address($this->email, $this->name ?? '');
    public function mailTemplate(): string   => '@form-mailer/table.html.twig';

    // exclude the captcha field from the email
    protected function mailFields(): array   => ['name', 'phone', 'email', 'comment'];
    protected function fieldType(string $n): string
        => match ($n) { 'email' => 'email', 'phone' => 'phone', default => 'text' };
}

Wire the mailer once and send after validation:

use Glpzzz\FormMailer\FormMailer;
use Glpzzz\FormMailer\MailSettings;
use Glpzzz\FormMailer\Captcha\FigletCaptcha;
use Glpzzz\FormMailer\Captcha\SessionCaptchaStore;

$captcha = new FigletCaptcha(new SessionCaptchaStore());
$mailer  = FormMailer::create(
    dsn: $config['dsn'],
    settings: MailSettings::fromArray($config), // from, to, bcc, replyTo
);

if ($form->isValid()) {
    $mailer->send($form);
}

$captcha->issue('contact');          // fresh code for the next render
echo $captcha->banner('contact');    // figlet art for the template

Templates

@form-mailer/table.html.twig is the default: sans-serif, an <h1> subject, and the field table with 0.25em cell padding and right-aligned labels. Nothing else - no wrapper, colours, or web fonts.

For a different look, ship your own template and register the directory:

FormMailer::create(dsn: ..., settings: ..., templateDirs: [__DIR__ . '/emails' => 'app']);
{# @app/contact.html.twig - mailTemplate() returns '@app/contact.html.twig' #}
{% apply inline_css %}   {# needs twig/cssinliner-extra #}
<style> /* ... */ </style>
<img src="https://example.com/logo.png" alt="" height="40">
{{ include('@form-mailer/_table.html.twig', { fields }) }}
{% endapply %}

Forms that aren't a label/value table

Skip RendersFieldTable, implement mailContext(): array by hand, and point mailTemplate() at your own template. BodyRenderer renders any Twig template; you still get FormMailer::send(), MailSettings and captcha.

What it renders

  • HTML body from your Twig template.
  • A plain-text part from the matching *.txt.twig (the bundled table.txt.twig gives a clean Label: value list); without one, a text part is derived from the HTML - crudely, or well if league/html-to-markdown is installed.