カテゴリー
Laravel

Mailableのプレビュー時、Markdownのテンプレートで@component()なHTMLがエスケープされてしまうンゴ

Mailableを実装したものは、簡単にプレビューできるようになっとる。例えば、こんな感じにルーティングする。

Route::get('mailable', function () {
    return (new ClassImplementsMailable)
        ->subject('foobar')
        ->markdown('emails.foobar', []);
});

そうすると、メールの中身がどんなふうに組み立てられるかを確認することができる。

今回は、Auth関連のユーザー登録時にメールアドレス検証のために送信されるメールの内容を調整したかった。いろいろいじりたかったので、Mailableを実装したクラスに置き換える。

vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php
を継承したクラスのtoMail()をこんな感じにする。


    /**
     * Build the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable);
        }

        return (new ClassImplementsMailable)
           ->markdown(); // ここでいろいろ

returnが\Illuminate\Notifications\Messages\MailMessageとなっているのでtoMail()の呼び出し元を見てみる。

    /**
     * Send the given notification.
     *
     * @param  mixed  $notifiable
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return void
     */
    public function send($notifiable, Notification $notification)
    {
        $message = $notification->toMail($notifiable);

        if (! $notifiable->routeNotificationFor('mail', $notification) &&
            ! $message instanceof Mailable) {
            return;
        }

        if ($message instanceof Mailable) {
            return $message->send($this->mailer);
        }

        $this->mailer->send(
            $this->buildView($message),
            array_merge($message->data(), $this->additionalMessageData($notification)),
            $this->messageBuilder($notifiable, $notification, $message)
        );

インスタンスがMailableだった場合の処理も用意されてるので、問題無い。きっと。

ここで、件のメール内容のプレビューの話に戻る。

というわけでまずは、元々使われていたMarkdownのテンプレートをベースに調整しようとしたのだが…。

出たわね。

テンプレートはこう。

@component('mail::message')
    {{-- Greeting --}}
    @if (! empty($greeting))
        # {{ $greeting }}
    @else
        @if ($level === 'error')
            # @lang('Whoops!')
        @else
            # @lang('Hello!')
        @endif
    @endif

    {{-- Intro Lines --}}
    @foreach ($introLines as $line)
        {{ $line }}

    @endforeach

    {{-- Action Button --}}
    @isset($actionText)
        <?php
        switch ($level) {
            case 'success':
            case 'error':
                $color = $level;
                break;
            default:
                $color = 'primary';
        }
        ?>
        @component('mail::button', ['url' => $actionUrl, 'color' => $color])
            {{ $actionText }}
        @endcomponent
    @endisset

    {{-- Outro Lines --}}
    @foreach ($outroLines as $line)
        {{ $line }}

    @endforeach

    {{-- Salutation --}}
    @if (! empty($salutation))
        {{ $salutation }}
    @else
        @lang('Regards'),<br>{{ config('app.name') }}
    @endif

    {{-- Subcopy --}}
    @isset($actionText)
        @component('mail::subcopy')
            @lang(
                "If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
                'into your web browser: [:actionURL](:actionURL)',
                [
                    'actionText' => $actionText,
                    'actionURL' => $actionUrl,
                ]
            )
        @endcomponent
    @endisset
@endcomponent

どうやら、@componentの中身がエスケープされて表示されてしまっている模様。

調べたところ、Laravelの特定のバージョンで起こるとかなんとか。みなさん、いろいろ試行錯誤していたようだが、結果、解決策は…。

@component('mail::message')
{{-- Greeting --}}
@if (! empty($greeting))
# {{ $greeting }}
@else
@if ($level === 'error')
# @lang('Whoops!')
@else
# @lang('Hello!')
@endif
@endif

{{-- Intro Lines --}}
@foreach ($introLines as $line)
{{ $line }}

@endforeach

{{-- Action Button --}}
@isset($actionText)
<?php
switch ($level) {
case 'success':
case 'error':
$color = $level;
break;
default:
$color = 'primary';
}
?>
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
{{ $actionText }}
@endcomponent
@endisset

{{-- Outro Lines --}}
@foreach ($outroLines as $line)
{{ $line }}

@endforeach

{{-- Salutation --}}
@if (! empty($salutation))
{{ $salutation }}
@else
@lang('Regards'),<br>{{ config('app.name') }}
@endif

{{-- Subcopy --}}
@isset($actionText)
@component('mail::subcopy')
@lang(
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
'into your web browser: [:actionURL](:actionURL)',
[
'actionText' => $actionText,
'actionURL' => $actionUrl,
]
)
@endcomponent
@endisset
@endcomponent

インデントを消す。

ええやん。

おそらく最新のLaravelやとおこらないと思うんやけども、いつかのために。ちなみにバージョンはv5.7.28やった。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です