Symfony Advent Calendar 2019 15日目の記事です。
はじめに
メールを業務で利用する場合は、定型文を用意して、例えば、お客様の名前とか日付とかを、送信する相手毎に置換をしてから送信をすることが多いです。
メールの本文を、str_replace
で置換をしてもよいのですが、テンプレートエンジンのtwigを使ってみたくなります。
Twig: HTML & CSSを試す
Twigを使ってメールを送る方法について見ていきます。
Mailerドキュメントに、Twig: HTML & CSSの項目がありますので、そのまま実行してみます。
class MailerController extends AbstractController
{
/**
* @Route("/mailer", name="mailer")
*/
public function index(MailerInterface $mailer)
{
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
// path of the Twig template to render
->htmlTemplate('emails/signup.html.twig')
// pass variables (name => value) to the template
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
])
;
$mailer->send($email);
return $this->render('mailer/index.html.twig', [
'controller_name' => 'MailerController',
]);
}
}
このソースは、https://github.com/idani/symfony_mailer_tutorial3 で保管しています。
今回のコミットは以下で見れます。
https://github.com/idani/symfony_mailer_tutorial3/commit/39d7caa797f0d1e663ba517553d8b23b8b319410
http://localhost:8000/mailer を開くと、以下の画面が表示されました。メール送信ができたようです。一番下のデバッグ用のツールバーのメールアイコンにも、メールが1通送信されたことが表示されていますね。
http://127.0.0.1:8025/ で動作しているMailDevを確認すると、1通メールが届いていました。
デフォルトで表示されるのは、TwigでレンダリングされたHTMLメールになります。
MailDevで設定を切り替えて、テキストメールにしてみます。
その場合は、HTMLからテキストを抽出したテキストメールが表示されます。
誤解されないように説明すると、MailDevはメールソースに記載されたテキストメールの部分を表示しています。テキストのメールは、symfony/mailerによって、配信前にHtmlからテキストを抽出して、メールソースに組み込まれています。
メールソースは以下です。
Content-Type
がtext/plain; charset=utf-8
と指定されたテキストと、text/html; charset=utf-8
と指定されたHTMLがquoted-printable
でエンコードされて送られています。
From: fabien@example.com
To: ryan@example.com
Subject: Thanks for signing up!
Message-ID: <a7169f3be53c5470e3765e9fc649f5b1@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 15:53:55 +0900
Content-Type: multipart/alternative;
boundary="_=_symfony_1575528835_8df53d757797744c9475726f052de156_=_"
--_=_symfony_1575528835_8df53d757797744c9475726f052de156_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Welcome !
You signed up as foo the following email:
ryan@e=
xample.com
Click here to activate your account
(this link=
is valid until December 12th)
--_=_symfony_1575528835_8df53d757797744c9475726f052de156_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<h1>Welcome !</h1>
<p>
You signed up as foo the following email:=
</p>
<p><code>ryan@example.com</code></p>
<p>
<a href=3D"#"=
>Click here to activate your account</a>
(this link is valid until De=
cember 12th)
</p>
--_=_symfony_1575528835_8df53d757797744c9475726f052de156_=_--
Text Contentを試す。
Mailerドキュメントを読み進めると、Text Contentの部分で、メールに入っているテキスト部分をカスタマイズできるようです。
さっそく、サンプルソースに->textTemplate('emails/signup.txt.twig')
を追加します。
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
// path of the Twig template to render
->htmlTemplate('emails/signup.html.twig')
// pass variables (name => value) to the template
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
])
->textTemplate('emails/signup.txt.twig')
;
$mailer->send($email);
Welcome !
You signed up as foo the following email:
{{ email.to[0].address }}
Click here to activate your account
http://www.example.com/xxxxxxxxxxx
(this link is valid until {{ expiration_date|date('F jS') }})
実際に送信をされたメールを見ると、修正通りのテキストメールが送られていました。
From: fabien@example.com
To: ryan@example.com
Subject: Thanks for signing up!
Message-ID: <f6cc0e2166ff05ab5816d2bb26255080@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 16:27:04 +0900
Content-Type: multipart/alternative;
boundary="_=_symfony_1575530824_57df7e1ab6fbb47d7ffce4184ba1587c_=_"
--_=_symfony_1575530824_57df7e1ab6fbb47d7ffce4184ba1587c_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Welcome !
You signed up as foo the following email:
ryan@=
example.com
Click here to activate your account
http:/=
/www.example.com/xxxxxxxxxxx
(this link is valid until December 12th)
--_=_symfony_1575530824_57df7e1ab6fbb47d7ffce4184ba1587c_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<h1>Welcome !</h1>
<p>
You signed up as foo the following email:=
</p>
<p><code>ryan@example.com</code></p>
<p>
<a href=3D"#"=
>Click here to activate your account</a>
(this link is valid until De=
cember 12th)
</p>
--_=_symfony_1575530824_57df7e1ab6fbb47d7ffce4184ba1587c_=_--
テキストメールだけ送る
これまではHTMLメールが主役で、テキストメールがおまけ的な感じでした。
Twigを使って、テキストメールだけ送るにはどうしたらよいでしょうか?
さきほどのサンプルコードから、htmlTemplate
を外してみましょう。
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
// path of the Twig template to render
//->htmlTemplate('emails/signup.html.twig') <=コメントアウト
// pass variables (name => value) to the template
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
])
->textTemplate('emails/signup.txt.twig')
;
$mailer->send($email);
MailDevでは、意図した通り、テキストメールが見れました。
画像では変化がないので、メールソースを見ていただくと、テキストメールになっていることがわかります。
From: fabien@example.com
To: ryan@example.com
Subject: Thanks for signing up!
Message-ID: <3d16b774412841ddbdef19077a4e8fc2@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 16:33:07 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Welcome !
You signed up as foo the following email:
ryan@=
example.com
Click here to activate your account
http:/=
/www.example.com/xxxxxxxxxxx
(this link is valid until December 12th)
日本語テンプレートで送信をしてみる
問題ないと思いますが、テンプレートを日本語にしてみます。
Welcome !
ようこそ !
You signed up as {{ username }} the following email:
次のメールアドレスで{{ username }}としてサインアップしました。
{{ email.to[0].address }}
Click here to activate your account
アカウントを有効にするにはここをクリックしてください
http://www.example.com/xxxxxxxxxxx
(this link is valid until {{ expiration_date|date('F jS') }})
(このリンクは、{{ expiration_date|date('F jS') }}まで有効です。
送信すると、以下のように問題なく日本語も表示されました。
From: fabien@example.com
To: ryan@example.com
Subject: Thanks for signing up!
Message-ID: <0072478296801e97a3472e1ce06a9f38@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 16:41:55 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Welcome !
=E3=82=88=E3=81=86=E3=81=93=E3=81=9D =EF=BC=81
You sig=
ned up as foo the following email:
=E6=AC=A1=E3=81=AE=E3=83=A1=
=E3=83=BC=E3=83=AB=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=81=A7foo=E3=81=
=A8=E3=81=97=E3=81=A6=E3=82=B5=E3=82=A4=E3=83=B3=E3=82=A2=E3=83=83=E3=83=
=97=E3=81=97=E3=81=BE=E3=81=97=E3=81=9F=E3=80=82
ryan@example.com=
Click here to activate your account
=E3=82=A2=E3=82=
=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E6=9C=89=E5=8A=B9=E3=81=AB=E3=81=
=99=E3=82=8B=E3=81=AB=E3=81=AF=E3=81=93=E3=81=93=E3=82=92=E3=82=AF=E3=83=
=AA=E3=83=83=E3=82=AF=E3=81=97=E3=81=A6=E3=81=8F=E3=81=A0=E3=81=95=E3=81=
=84
http://www.example.com/xxxxxxxxxxx
(this link is valid u=
ntil December 12th)
=EF=BC=88=E3=81=93=E3=81=AE=E3=83=AA=E3=83=B3=
=E3=82=AF=E3=81=AF=E3=80=81December 12th=E3=81=BE=E3=81=A7=E6=9C=89=
=E5=8A=B9=E3=81=A7=E3=81=99=E3=80=82
Base64で送信する
ここまでできても合格なのですが、可能であれば、quoted-printable
ではなく、base64
で送信をしたいです。
どうしたらよいのでしょうか?
TemplatedEmail.phpを見てみると、Email.phpの派生クラスであることがわかります。
Email.phpの中身は、symfony/mailerでbase64やiso-2022-jpを試してみるで見ていきましたが、quoted-printable
固定となってしまいます。
そうすると、Twigでテキストをレンダリングして、それをメール本文として指定するのが良さそうです。
Creating and Using TemplatesのRendering a Template in Servicesに、サービス内でのレンダリング方法が記載されています。
こちらを参考にして、サンプルコードを変更します。
Base64のメールの作成方法は、symfony/mailerでbase64やiso-2022-jpを試してみるから流用しますので、ガラッと変わります。
mb_language("uni");
mb_internal_encoding("UTF-8");
$subject = mb_encode_mimeheader('Thanks for signing up! 登録してくれてありがとうございます!!');
$subject = str_replace("\r\n", '', $subject);
$headers = (new Headers())
->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
->addTextHeader('Subject', $subject)
;
$body = $twig->render('emails/signup.txt.twig', [
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
'email' => 'you@example.com',
]);
$textContent = new TextPart($body, 'utf-8', 'plain', 'base64');
$email = new Message($headers, $textContent);
$mailer->send($email);
テンプレートの方も、メールアドレスなどの情報も、自動で設定されていたようです。
このためメールアドレスも個別に指定するように変更します。
Welcome !
ようこそ !
You signed up as {{ username }} the following email:
次のメールアドレスで{{ username }}としてサインアップしました。
{{ email }}
Click here to activate your account
アカウントを有効にするにはここをクリックしてください
http://www.example.com/xxxxxxxxxxx
(this link is valid until {{ expiration_date|date('F jS') }})
(このリンクは、{{ expiration_date|date('F jS') }}まで有効です。
想定通り、Twigを使ってテキストメールをBase64で送信することができました。
From: =?UTF-8?B?6YCB5L+h6ICF5ZCN?= <hello@example.com>
To: =?UTF-8?B?5Y+X5L+h6ICF5ZCN?= <you@example.com>
Subject: Thanks for signing up!
=?UTF-8?B?55m76Yyy44GX44Gm44GP44KM44Gm44GC44KK44GM?=
=?UTF-8?B?44Go44GG44GU44GW44GE44G+44GZ77yB77yB?=
Message-ID: <a98efb8654df5bc230c364e0d1e476f4@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 17:15:07 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: base64
V2VsY29tZSAhCuOCiOOBhuOBk+OBnSDvvIEKCiAgICBZb3Ugc2lnbmVkIHVwIGFzIGZvbyB0aGUg
Zm9sbG93aW5nIGVtYWlsOgogICAg5qyh44Gu44Oh44O844Or44Ki44OJ44Os44K544GnZm9v44Go
44GX44Gm44K144Kk44Oz44Ki44OD44OX44GX44G+44GX44Gf44CCCgogICAgeW91QGV4YW1wbGUu
Y29tCgoKICAgIENsaWNrIGhlcmUgdG8gYWN0aXZhdGUgeW91ciBhY2NvdW50CiAgICDjgqLjgqvj
gqbjg7Pjg4jjgpLmnInlirnjgavjgZnjgovjgavjga/jgZPjgZPjgpLjgq/jg6rjg4Pjgq/jgZfj
gabjgY/jgaDjgZXjgYQKCiAgICBodHRwOi8vd3d3LmV4YW1wbGUuY29tL3h4eHh4eHh4eHh4CiAg
ICAodGhpcyBsaW5rIGlzIHZhbGlkIHVudGlsIERlY2VtYmVyIDEydGgpCiAgICDvvIjjgZPjga7j
g6rjg7Pjgq/jga/jgIFEZWNlbWJlciAxMnRo44G+44Gn5pyJ5Yq544Gn44GZ44CCCg==
TwigのテンプレートをDBに保存する
メールの本文をテンプレートという形で保存をして、レンダリングすることもできました。
ところが、メールは本文以外にサブジェクトというデータも必要です。
サブジェクトはDBに保存し、メールの本文のテンプレートはファイルシステムというのは微妙ですよね。
TwigのテンプレートもDBに保存できるとよいですね。
調べてみると、Twigのドキュメントで、Using a Database to store Templatesがありました。
ただ、こちらは純粋にTwigのテンプレートの読み出し先をDBにするということみたいです。
サイトとしてHTMLを表示するためでしたらよいのですが、メールとしては使いづらいですね。
それよりは、その次に記載のTwigのテンプレートを変数として受け取って処理をするための、Loading a Template from a Stringが良さそうです。
サンプルでテンプレートを読み込む部分を書き換えます。
public function index(MailerInterface $mailer, Environment $twig)
{
mb_language("uni");
mb_internal_encoding("UTF-8");
$subject = mb_encode_mimeheader('Thanks for signing up! 登録してくれてありがとうございます!!');
$subject = str_replace("\r\n", '', $subject);
$headers = (new Headers())
->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
->addTextHeader('Subject', $subject)
;
$twigEmailTemplate =<<<EOL
Welcome !
ようこそ !
これはTwigテンプレートを変数から読み込んでレンダリングしたメールです。
You signed up as {{ username }} the following email:
~~~中略~~~
(このリンクは、{{ expiration_date|date('F jS') }}まで有効です。
EOL;
$template = $twig->createTemplate($twigEmailTemplate);
$body = $template->render([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
'email' => 'you@example.com',
]);
$textContent = new TextPart($body, 'utf-8', 'plain', 'base64');
$email = new Message($headers, $textContent);
$mailer->send($email);
return $this->render('mailer/index.html.twig', [
'controller_name' => 'MailerController',
]);
}
まとめ
Twigを使って簡単にメールが送れることがわかりました。
Base64にするには、面倒なので、良さそうな修正案を考える必要てプルリクした方がよいですね。