Laravel11でメール送信処理を書く

2024.08.13 09:00
2024.08.13 09:22
Laravel11でメール送信処理を書く

Laravel11とMailableでフォーム送信処理を書いてみました。
今回はお問い合わせフォームを作る想定でやってみます。
ファイル構成はこんな感じ。
今回はメール送信処理なので、入力画面と完了画面は省略します。

|-/
  |-app/
    |-Http/
      |-Controllers/
        |-ContactController.php
      |-Requests/
        |-Contact/
          |-SendRequest.php
      |-UseCases/
        |-Contact/
          |-SendUseCase.php
    |-Models/
      |-Contact/
        |-MailForAdmin.php
        |-MailForCustomer.php
  |-resources/
    |-views/
      |-contact/
        |-index.blade.php
        |-mail_admin.blade.php
        |-mail_customer.blade.php
        |-comp.blade.php
  |-routes
    |-web.php

処理によってリクエストやユースケース、ドメインにそれぞれ分けています。
最初にroutes.php。

<?php

use Illuminate\Support\Facades\Route;

$namespace = '\App\Http\Controllers';

Route::get('/contact', $namespace . '\ContactController@index').name('contact.index');
Route::post('/contact', $namespace . '\ContactController@send').name('contact.send');
Route::get('/contact_comp', $namespace . '\ContactController@comp').name('contact.comp');

次にContactController.php。

<?php declare(strict_types=1);

namespace app\Http\Controllers;

use Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use app\Http\Requests\Contact\SendRequest;
use app\Http\UseCases\Contact\SendUseCase;

class ContactController extends Controller
{
    public function index(): View
    {
        // ビューに変数セットして表示
        return view('general::contact.index.contents');
    }

    /**
     * @param SendRequest $request
     * @param SendUseCase $useCase
     * @return RedirectResponse
     */
    public function send(SendRequest $request, SendUseCase $useCase): RedirectResponse
    {        
        // 二重送信防止
        $request->session()->regenerateToken();

        // 登録・メール送信処理
        $useCase($request);

        // 完了画面へ移動
        return redirect(route('contact.comp'));
    }

    /**
     * @return View
     */
    public function comp(): View
    {
        // ビューに変数セットして表示
        return view('general::contact.comp.contents');
    }
}

コントローラーは処理の割り振りに特化しています。
次にリクエスト。

<?php declare(strict_types=1);

namespace app\Http\Requests\Contact;

use Illuminate\Foundation\Http\FormRequest;

class SendRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|string',
            'msg' => 'required|string',
            'g-recaptcha-response' => 'required|recaptchav3:contact,0.5'
        ];
    }

    /**
     * Set custom messages for validator errors.
     *
     * @return array
     */
    public function messages()
    {
        return [
            'name.required' => ':attributeは必須です',
            'email.required' => ':attributeは必須です',
            'email.email' => ':attributeの型が正しくありません',
            'msg.required' => ':attributeは必須です',
            'g-recaptcha-response.required' => '処理ができませんでした',
            'g-recaptcha-response.recaptcha' => '処理ができませんでした.',
        ];
    }

    /**
     * Set custom attributes for validator errors.
     *
     * @return array
     */
    public function attributes()
    {
        return [
            'name' => 'お名前',
            'email' => 'メールアドレス',
            'msg' => 'お問い合わせ内容',
            'validation.recaptchav3' => '投稿スコア',
        ];
    }
}

POSTされたデータをチェックします。
データにはGoogleReCAPTCHAのチェックも入れている他、
コントローラーで二重投稿防止の役目も果たしています。
次にユースケース。

<?php declare(strict_types=1);

namespace app\Http\UseCases\Contact;

use app\Models\Contact\Contact;
use app\Models\Contact\MailForAdmin;
use app\Models\Contact\MailForCustomer;
use app\Http\Requests\Contact\SendRequest;
use Illuminate\Support\Facades\Mail;

class SendUseCase
{
    /**
     * @var SendRequest
     */
    private SendRequest $request;

    /**
     * @param SendRequest $request
     * @return bool
     */
    public function __invoke(SendRequest $request): bool
    {
        // データをドメインオブジェクトにして返す
        $contact = new Contact($request->all());

        // メールを送信する
        try {
            // 管理者に送る
            Mail::send(new MailForAdmin($contact));
            // ユーザーにレシートメールを送る
            Mail::send(new MailForCustomer($contact));
            // 処理完了でtrueを返す
            return true;
        } catch (\Exception $e) {
            // エラーがあればログを書き込む
            Logger($e->getMessage());
            // エラー処理をここに書きます。
        }
    }
}

ユースケースは実際のお問い合わせ処理の流れを書いています。
コントローラーに実際の処理を書かせないためにユースケースに切り出しています。
次にモデルです。送信するメールを作成するモデルを2つ作成します。
まずは管理者用。

<?php

namespace app\Models\Contact;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class MailForAdmin extends Mailable
{
    use Queueable, SerializesModels;

    // 引数で受け取ったデータ用の変数
    protected Array $data;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(array $data)
    {
        $this->data = $contact;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        $sendToAddress = '送信先の管理者用アドレスを入れます';
        $fromName = $this->data['name'];
        $fromAddress = '送信元のアドレスを入れます';

        return new Envelope(
            // 送信元のメールアドレスを指定
            from: new Address($fromAddress, $fromName),
            // 送信先のメールアドレスを指定
            to: $sendToAddress,
            // 返信時のメールアドレスを指定
            replyTo: [
                new Address($fromAddress, $fromName),
            ],
            // 件名を指定
            subject: 'お問い合わせを受け付けました',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments(): array
    {
        return [];
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            // メールに使うviewを指定
            text: 'contact.send.mail_admin',
            // viewに入れるデータを指定
            with: [
                'data' => [
                    'name' => $this->data['name'],
                    'email' => $this->data['name'],
                    'msg' => $this->data['name'],
                ]
            ]
        );
    }
}

次にユーザー用。

<?php

namespace app\Models\Contact;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class MailForCustomer extends Mailable
{
    use Queueable, SerializesModels;

    // 引数で受け取ったデータ用の変数
    protected Array $data;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(Contact $contact)
    {
        $this->data = $contact;
    }

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        $sendToAddress = $this->data['email'];
        $fromName = $this->data['name'];
        $fromAddress = '送信元のアドレスを入れます';

        return new Envelope(
            // 送信元のメールアドレスを指定
            from: new Address($fromAddress, $fromName),
            // 送信先のメールアドレスを指定
            to: $sendToAddress,
            // 返信時のメールアドレスを指定
            replyTo: [
                new Address($fromAddress, $fromName),
            ],
            // 件名を指定
            subject: 'お問い合わせを受け付けました',
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array
     */
    public function attachments(): array
    {
        return [];
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            // メールに使うviewを指定
            text: 'contact.send.mail_customer',
            // viewに入れるデータを指定
            with: [
                'data' => [
                    'name' => $this->data['name'],
                    'email' => $this->data['name'],
                    'msg' => $this->data['name'],
                ]
            ]
        );
    }
}

それぞれ送り先や送信元、件名、送るメールの文面などをここで作っています。
次にメールの文面のviewです。
まずは管理者用。

━━━━━━━━━━━━━━━━━
お問い合わせを受信
━━━━━━━━━━━━━━━━━

受付日時 <?php echo date("Y年m月d日 H時i分") . "\n"?>

お名前
{{$data['name']}} 様

メールアドレス
{{$data['email']}}

お問い合わせ内容
{{$data['msg']}}

--------------------------
署名
--------------------------

次にユーザー用。

━━━━━━━━━━━━━━━━━
お問い合わせを受付しました
━━━━━━━━━━━━━━━━━

受付日時 <?php echo date("Y年m月d日 H時i分") . "\n"?>

お名前
{{$data['name']}} 様

メールアドレス
{{$data['email']}}

お問い合わせ内容
{{$data['msg']}}

--------------------------
署名
--------------------------

これで送信処理が完成しました。
ブロックごとに切り分けているので、改修もしやすいですね。

今回は以上です!