LoginSignup
4
4

More than 3 years have passed since last update.

LaravelでISO-2022-JPのメールを送りたい

Posted at

Laravel でメールを送信すると、デフォルトでは文字コードが UTF-8 になります。
そこで、ISO-2022-JP で送信する方法を考えてみました。

前提

  • Laravel 6.12

やり方1

まずは、 Mailable クラスで頑張るやり方です。

コンストラクタの中に処理を追加します。また、 withSwiftMessage() の中でも setCharset()setEncoder() をコールしています。

app/Mail/CustomerRegistered.php
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Swift_DependencyContainer;
use Swift_Mime_ContentEncoder_PlainContentEncoder;
use Swift_Preferences;

class CustomerRegistered extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        /* 処理追加 ここから */
        Swift_DependencyContainer::getInstance()
            ->register('mime.qpheaderencoder')
            ->asAliasOf('mime.base64headerencoder');

        Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
        /* 処理追加 ここまで */
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('admin@example.com')
                    ->subject('件名テスト')
                    ->text('mails.customer_registered')
                    ->withSwiftMessage(function ($message) {
                       /* 処理追加 ここから */
                       $message->setCharset('iso-2022-jp');
                       $message->setEncoder(new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit'));
                       /* 処理追加 ここまで */
                    });
    }

このやり方の良いところは、メール( Mailable クラス)ごとに文字コードを設定することが明確になることだと思います。1

Swift_Preferences::getInstance()->setCharset('iso-2022-jp') をコールすると、Laravel が送信する全ての文字コードが ISO-2022-JP に設定されます。2

そのため、UTF-8 で送信したい場合は、以下のように setCharset()utf-8 を指定する必要があります。

app/Mail/CustomerChanged.php
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Swift_Preferences;

class CustomerChanged extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        /* 処理追加 ここから */
        Swift_Preferences::getInstance()->setCharset('utf-8');
        /* 処理追加 ここまで */
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('admin@example.com')
            ->subject('件名テスト')
            ->text('mails.customer_changed');
    }
}

やり方2

続いて、サービスプロバイダでがんばるやり方。

Laravel 5.4でiso-2022-jpエンコードのメールを送信する に記載されているものを、ほぼ引用させていただきました。

app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Swift;
use Swift_DependencyContainer;
use Swift_Preferences;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        /* 処理追加 ここから */
        Swift::init(function () {
            Swift_DependencyContainer::getInstance()
                ->register('mime.qpheaderencoder')
                ->asAliasOf('mime.base64headerencoder');
            Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
        });
        /* 処理追加 ここまで */
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
app/Mail/CustomerRegistered.php
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Swift_Mime_ContentEncoder_PlainContentEncoder;

class CustomerRegistered extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('admin@example.com')
                    ->subject('件名テスト')
                    ->text('mails.customer_registered')
                    ->withSwiftMessage(function ($message) {
                       /* 処理追加 ここから */
                       $message->setCharset('iso-2022-jp');
                       $message->setEncoder(new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit'));
                       /* 処理追加 ここまで */
                    });
    }

システム全体でメールを送るときは必ず ISO-2022-JP にするということであれば、このやり方が一番良いように思います。

あえて難点を挙げると、サービスプロバイダで設定するだけでなく、 Mailable クラスでも相変わらず設定しなければならないということでしょうか。たかが 2 行と思ったけど、 setEncoder() とかコールし忘れそうで...

やり方3

最後は、SwiftMailer のプラグインでがんばるやり方です。

「もう Laravel じゃないじゃん!」というツッコミもあるかと思いますが、無視します(笑)

まず、以下のようなプラグインを作ります。 beforeSendPerformed() はメール送信前に自動的にコールされるようです。

app/Common/JapaneseMailPlugin.php
namespace App\Common;

use Swift_Events_SendListener;
use Swift_Events_SendEvent;
use Swift_Mime_ContentEncoder_PlainContentEncoder;
use Swift_DependencyContainer;
use Swift_Preferences;

class JapaneseMailPlugin implements Swift_Events_SendListener
{
    public function __construct()
    {
        Swift_DependencyContainer::getInstance()
            ->register('mime.qpheaderencoder')
            ->asAliasOf('mime.base64headerencoder');

        Swift_Preferences::getInstance()->setCharset('iso-2022-jp');
    }

    /**
     * Invoked immediately before the Message is sent.
     *
     * @param Swift_Events_SendEvent $evt
     */
    public function beforeSendPerformed(Swift_Events_SendEvent $evt)
    {
        $message = $evt->getMessage();
        $message->setCharset('iso-2022-jp');
        $message->setEncoder(new Swift_Mime_ContentEncoder_PlainContentEncoder('7bit'));
    }

    /**
     * Invoked immediately after the Message is sent.
     *
     * @param Swift_Events_SendEvent $evt
     */
    public function sendPerformed(Swift_Events_SendEvent $evt)
    {
        // 特に何もしない
    }
}

一方、 Mailable クラスでは、コンストラクタで、プラグインの登録のみを行います。

namespace App\Mail;

use App\Common\JapaneseMailPlugin;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class CustomerRegistered extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        /* 処理追加 ここから */
        app()->make('swift.mailer')->registerPlugin(new JapaneseMailPlugin());
        /* 処理追加 ここまで */
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->from('admin@example.com')
                    ->subject('件名テスト')
                    ->text('mails.customer_registered');
    }
}

このやり方の良いところは、 Mailable クラスで1行だけ書けば良いということです。3 また、上記の例では withSwiftMessage() を消してしまいましたが、文字コード以外の設定を行うために withSwiftMessage() を記載しても問題ありません。

難点は、Laravel で処理が完結せず、SwiftMailer まで理解する必要があるということでしょうか。保守性を考えると、あまり良くないかもしれません。

まとめ的な感想

キレイさでいえば やり方3 でしょうけど、ちょっとトリッキーかもしれません。
そう考えると、一番理解してもらえそうなのは、 やり方2 でしょうか。

でも一番は UTF-8 にすること だと思いますけど(笑)

参考


  1. そもそも、システム内で、送信するメールごとに文字コードを分けるようなことが現実にあるのか?とは思いますが... 

  2. 一方、 withSwiftMessage() の中でコールしている setCharset() は、メール本文に文字コードを設定するためのものです。なぜ、こちらの setCharset() は各メールごとに設定する前提なのに、Swift_Preferences::setCharset() は SwiftMailer 全体に設定する前提なのか。一貫性がなくて気持ち悪い。 

  3. 逆に言えば「1行はまだ書かないといけない」ということでもあります。 SwiftMailer はシングルトン結合されているので、 registerPlugin() は1回だけ行えば十分だとは思いますが、思いの外、良い方法が思いつかなかったので、一旦、そのままにしておきます。 

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4