1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

laravel メールをまとめてみる(*´∀`*)

Last updated at Posted at 2021-08-28

まとめサイト。

一切laravelの公式サイトを見ていないという・・・。

laravelでメールを送信する方法
1.ファザード
2.メイラブル
3.ノーティファイ

0.プロジェクトを作ってまとめてみる
0.プロジェクトを作ってまとめてみる ----

まだ最終確認していません。

https://into-the-program.com/laravel-create-contact-form/

上のサイトを参考にしててまとめてみる。
動作の流れは、お問い合わせフォーム->確認フォーム->送信 となっている。

laravel new mailTutorial

テンプレートととしていれておくと便利。

composer require laravel/ui
php artisan ui bootstrap
php artisan ui bootstrap --auth
npm install && npm run dev

作成したテーブル一覧

  • お馴染み
  • users
  • queue送信のための
  • jobs
  • usersテーブルの友達にメールを送るためのタグ付のためのテーブル
  • friends

web.phpのルート一覧

routes\web.php
Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

//コンタクトページのためのルート
Route::get('contact',[App\Http\Controllers\ContactController::class, 'index'])->name('contact.index');
Route::post('contact/confirm',[App\Http\Controllers\ContactController::class, 'confirm'])->name('contact.confirm');
//コンタクトのインデックスページにリターンするメソッド
Route::post('contact/index_return',[App\Http\Controllers\ContactController::class, 'index_return'])->name('contact.index_return');



//メール送信メソッドへのルート
// メールfacades送信
Route::post('mail_facades_send',[App\Http\Controllers\MailController::class, 'mail_facades_send'])->name('mail_facades_send');
// メイラブル送信
Route::post('mailable_send',[App\Http\Controllers\MailController::class, 'mailable_send'])->name('mailable_send');

// notification
Route::middleware(['auth'])->group(function () {
//friends一覧
Route::get('/users/friends',[App\Http\Controllers\UserController::class,'friends_index'])->name('friends.index');
//friends登録form
Route::get('/users/friends/register',[App\Http\Controllers\UserController::class,'friends_register_form'])->name('friends.register_form');
//friends登録メソッド
Route::post('/users/friends/register',[App\Http\Controllers\UserController::class,'register_friends'])->name('register_friends');
//friendsにnotify送信メソッド
Route::post('/friends_mail_send',[App\Http\Controllers\UserController::class,'friends_mail_send'])->name('friends_mail_send');
});

// notification+mailable
//入力ホーム
Route::get('/users/customer',[App\Http\Controllers\UserController::class,'index'])->name('users.index');
//確認ホーム
Route::post('/users/confirm',[App\Http\Controllers\UserController::class,'confirm'])->name('users.confirm');
//送信メソッド
Route::post('/users/notify_send',[App\Http\Controllers\UserController::class,'mail_send'])->name('users.mail_send');

リンクを作成する

resources\views\welcome.blade.php
@if (Route::has('login'))
    <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
        @auth
            <a href="{{ url('/home') }}" class="text-sm text-gray-700 underline">Home</a>
            <!-- 追記 -->
            <a href="{{ route('friends.index') }}" class="ml-4 text-sm text-gray-700 underline">friend Page</a>

        @else
            <a href="{{ route('login') }}" class="text-sm text-gray-700 underline">Log in</a>
            @if (Route::has('register'))
                <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">Register</a>
            @endif
        @endauth
        <!-- 追記 -->
        <a href="{{ route('contact.index') }}" class="ml-4 text-sm text-gray-700 underline">Contact Page</a>
        <!-- 追記 -->
        <a href="{{ route('users.index') }}" class="ml-4 text-sm text-gray-700 underline">Customer_sendPage</a>
    </div>
@endif
resources\views\layouts\app.blade.php
<!-- ドロップダウンメニューに追記 -->
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
    <!-- 追記 -->
    <a class="dropdown-item" href="{{ route('friends.index') }}">
        frinds
    </a>
    <!-- /追記 -->
    <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault();
            document.getElementById('logout-form').submit();">
        {{ __('Logout') }}
    </a>

    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
        @csrf
    </form>
</div>

layouts.app に追記 ステップフォームのスタイルを作成。

resources\views\layouts\app.blade.php
    <!-- 追記 -->
    <style>
        footer {
            background-color: #555;
            color: white;
            padding: 15px;
        }
        .step_box {
            background-color: grey;
            border-radius: 25%;
            padding-inline: 25px;
            display: inline-block;
            height: 100px;
            line-height: 100px;
            color: white;
            box-sizing: border-box;
        }
        .triangle {
            font-size: 50px;
        }
        .active {
            background-color: tomato;
        }
    </style>
    <!-- 追記 -->
    @stack('css')
-------

    <!-- 追記 -->
    @stack('js')
</bodY>

メイルファザードとメイラブル送信

入力フォームを作成する。

resources\views\contact\index.blade.php
@extends('layouts.app')
@push('css')
    <style>
        .custom-file {
            max-width: 20rem;
            overflow: hidden;
        }
        .custom-file-label {
            white-space: nowrap;
        }
    </style>
@endpush
@section('content')
    <div class="d-flex justify-content-center align-items-center mt-5">
        <div class="step_box active">お問い合わせ</div>
        <div class="triangle"></div>
        <div class="step_box">確認フォーム</div>
        <div class="triangle"></div>
        <div class="step_box">送信完了</div>
    </div>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-8 justify-">
                <div class="card">
                    <div class="card-header">お問い合わせ</div>

                    <div class="card-body">
                        @if ($errors->any())
                            <div class="alert alert-danger">
                                <ul>
                                    @foreach ($errors->all() as $error)
                                        <li>{{ $error }}</li>
                                    @endforeach
                                </ul>
                            </div>
                        @endif

                        <form id="mail_form" method="POST" enctype="multipart/form-data">
                            @csrf

                            <label id="email">メールアドレス</label>
                            <input class="form-control @error('email') is-invalid @enderror" name="email"
                                value="{{ old('email') }}" type="text">
                            @if ($errors->has('email'))
                                <span class="invalid-feedback" role="alert">
                                    <p class="error-message">{{ $errors->first('email') }}</p>
                                </span>
                            @endif

                            <label id="title">タイトル</label>
                            <input class="form-control @error('email') is-invalid @enderror" name="title"
                                value="{{ old('title') }}" type="text">
                            @if ($errors->has('title'))
                                <span class="invalid-feedback" role="alert">
                                    <p class="error-message">{{ $errors->first('title') }}</p>
                                </span>
                            @endif

                            <label id="body">お問い合わせ内容</label>
                            <textarea class="form-control @error('email') is-invalid @enderror" name="body">{{ old('body') }}
                                                </textarea>
                            <!-- errorのほうが便利 -->
                            @error('body')
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $message }}</strong>
                                </span>
                            @enderror

                            <div class="form-group mt-3">
                                <label for="file">ファイル複数選択可</label>
                                <div id="file" class="input-group">
                                    <div class="custom-file">
                                        <input type="file" id="customfile"
                                            class="custom-file-input @error('file') is-invalid @enderror"
                                            name="customfile[]" multiple accept=".png,.jpg,.jpeg,.pdf,.doc,.xlsx"
                                            value="{{ old('customfile') }}" />
                                        <label class="custom-file-label" for="customfile"
                                            data-browse="参照">画像ファイルの添付</label>
                                    </div>
                                    <div class="input-group-append">
                                        <button type="button" class="btn btn-outline-secondary reset">取消</button>
                                    </div>

                                    @if (old('tmp_img_files'))
                                    <div id="preview">
                                        @foreach (explode(',', old('tmp_img_files')) as $tmp_img_file)
                                        <div class="d-inline-block mr-1 mt-1"><img class="img-thumbnail"  style="height:100px;" src="{{ asset('storage/' . $tmp_img_file) }}"
                                        alt=""><div class="small text-muted text-center">{{ $tmp_img_file }}</div></div>


                                        @endforeach
                                    </div>
                                @endif
                                </div>
                            </div>
                            @if ($errors->has('customfile'))
                                <span class="invalid-feedback" role="alert">
                                    <p class="error-message">{{ $errors->first('file') }}</p>
                                </span>
                            @endif

                            <button class="btn btn-primary btn-sm mt-3" type="submit">
                                確認フォームへ
                            </button>
                            @if (old('tmp_files'))
                            @endif

                        </form>

                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

@push('js')
    <script>
        //これは遊び
        $('#mail_form').one('submit', function(event) {
            event.preventDefault();
            $('#mail_form').attr('action', "{{ route('contact.confirm') }}").submit();
        });
        //https://v4.bootstrap-guide.com/javascript/forms/file-browser
        $('.custom-file-input').on('change', handleFileSelect);
        function handleFileSelect(evt) {
            $('#preview').remove();
            // 繰り返し実行時の処理
            $(this).parents('.input-group').after('<div id="preview"></div>');
            var files = evt.target.files;
            for (var i = 0, f; f = files[i]; i++) {
                var reader = new FileReader();
                reader.onload = (function(theFile) {
                    return function(e) {
                        if (theFile.type.match('image.*')) {
                            var $html = [
                                '<div class="d-inline-block mr-1 mt-1"><img class="img-thumbnail" src="', e
                                .target.result, '" title="', escape(theFile.name),
                                '" style="height:100px;" /><div class="small text-muted text-center">',
                                escape(theFile.name), '</div></div>'
                            ].join(''); // 画像では画像のプレビューとファイル名の表示
                        } else {
                            var $html = ['<div class="d-inline-block mr-1"><span class="small">', escape(theFile
                                .name), '</span></div>'].join(''); //画像以外はファイル名のみの表示
                        }
                        $('#preview').append($html);
                    };
                })(f);
                reader.readAsDataURL(f);
            }
            $(this).next('.custom-file-label').html(+files.length + '個のファイルを選択しました');
        }
        //ファイルの取消
        $('.reset').click(function() {
            $(this).parent().prev().children('.custom-file-label').html('ファイル選択...');
            $('#preview').remove();
            $('.custom-file-input').val('');
        })
    </script>
@endpush

確認フォーム

resources\views\contact\confirm.blade.php
@extends('layouts.app')
@section('content')
    <div class="d-flex justify-content-center align-items-center mt-5">
        <div class="step_box">お問い合わせ</div>
        <div class="triangle"></div>
        <div class="step_box active">確認フォーム</div>
        <div class="triangle"></div>
        <div class="step_box">送信完了</div>
    </div>

    <div class="container">
        <div class="row justify-content-center mt-5">
            <div class="col-8">
                <div class="card">
                    <div class="card-header">確認フォーム</div>

                    <div class="card-body">
                        <dl class="">
                            <dt>email</dt>
                            <dd>{{ $request->email }}</dd>
                            <dt>タイトル</dt>
                            <dd>{{ $request->title }}</dd>
                            <dt>お問い合わせ内容</dt>
                            <dd>{{ nl2br($request->body) }}</dd>
                            <dt>画像ファイル</dt>
                            @isset($tmp_files[$img_pth])
                            <dd class="d-flex flex-wrap align-content-around">
                                @forelse ($tmp_files[$img_pth] as $tmp_file)
                                <img class="img-fluid m-1" width="100px" src="{{ asset(Storage::url($tmp_file))}}"
                                alt="">
                                @empty
                                @endforelse
                            </dd>
                            @endisset
                        </dl>

                        <form id="mail_send" method="POST" enctype="multipart/form-data">
                            @csrf
                            <input name="email" value="{{ $request->email }}" type="hidden">
                            <input name="title" value="{{ $request->title }}" type="hidden">
                            <input name="body" value="{{ $request->body }}" type="hidden">
                            @isset($tmp_files[$img_pth])
                            <input name="tmp_img_files" value="{{$img_pth}}" type="hidden">
                            @endisset

                            <input name="csrf_name" value="confirm" type="hidden">

                            <div class="d-flex justify-content-end flex-wrap">
                                <button class="btn btn-danger m-1" type="submit" name="index_return" value="back">
                                    入力内容修正
                                </button>
                                <button class="btn btn-primary m-1" type="submit" name="mail_facades_send" value="submit">
                                    ファザード送信する
                                </button>
                                <button class="btn btn-primary m-1" type="submit" name="mailable_send" value="submit">
                                    メイラブル送信する
                                </button>
                                <button class="btn btn-primary m-1" type="submit" name="notify_send" value="submit">
                                    notify送信する
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

@push('js')
    <script>
        //1回だけイベントを実行する。永久ループになるため、
        $('#mail_send').one('submit', function(e) {
            e.preventDefault();

            //クリックした要素を取得できる。
            click_name = $(document.activeElement).attr('name');

            //attrではんくpropを使用する。
            $('button[type=submit]').prop('disabled', true);;

            if (click_name == 'index_return') {
                //atr複数の場合オブジェクトにする
                $('#mail_send').attr({
                    'action': "{{ route('contact.index_return') }}"
                });
            }

            if (click_name == 'mail_facades_send') {
                $('#mail_send').attr('action', "{{ route('mail_facades_send') }}");
            }

            if (click_name == 'mailable_send') {
                $('#mail_send').attr('action', "{{ route('mailable_send') }}");
            }

            $('#mail_send').submit();
        });
    </script>
@endpush

送信完了ページ(contact\thanks.blade.php)

resources\views\contact\thanks.blade.php
@extends('layouts.app')

@section('content')
<div class="d-flex justify-content-center align-items-center mt-5">
    <div class="step_box">お問い合わせ</div><div class="triangle"></div><div class="step_box">確認フォーム</div><div class="triangle"></div><div class="step_box active">送信完了</div>
    </div>
    <div class="flex-grow-1 d-flex justify-content-center align-items-center">
</div>
<div class="d-flex flex-column justify-content-center align-items-center mt-5">

<h1>{{ __('送信完了') }}</h1>
<a class="btn btn-sm btn-primary" href="{{ route('contact.index') }}">問い合わせる。</a>
</div>
@endsection

コンタクトコントローラーの作成

php artisan make:controller ContactController
app\Http\Controllers\ContactController.php
 <?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;

class ContactController extends Controller
{
    public function index()
    {
        return view('contact.index');
    }

    public function index_return()
    {
        // oldで値を取得することができる。
        return redirect()->route('contact.index')->withInput();    
    }

    public function confirm(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'title' => 'required|',
            'body' => 'required|',
            'cutomfile.*' => [
                'file',
                'image',
                'mimes:jpeg,png,jpg,pdf,doc,xlsx'
            ],
        ], [
            'required' => ':attributeは必須やねん',
            'email' => ':attributeはメールやねん',
        ], [
            'email' => 'メール',
            'title' => 'タイトル',
            'body' => '内容',
            'cutomfile'=>'添付ファイル'
        ]);

        $tmp_files = [];
        $img_pth='';
        if ($request->has('customfile')) {
            $img_pth = uniqid("tmp_img_") ."/";
            $notImg_pth = uniqid("tmp_notImg_") ."/";
            foreach ($request->file('customfile') as $key => $tmp_file) {
                if ($tmp_file->isValid()) {
                    //画像 か それ以外
                    if (Str::is('image*', $request->customfile[0]->getMimeType())) {
                        //一時保存pathを作成(一時path + ファイル名)
                        $tmp_files[$img_pth][$key] = $img_pth. Str::of($tmp_file->getClientOriginalName())->replace(' ', '_');
                        // storage\appからの相対パス 画像を保存している。//取得はsrc="{{ asset('storage/'.$tmp_file)
                        $tmp_file->storeAs("public", $tmp_files[$img_pth][$key]);
                    }else{
                        $tmp_files[$notImg_pth][$key] = $notImg_pth.Str::of($tmp_file->getClientOriginalName())->replace(' ', '_');
                        $tmp_file->storeAs("public", $tmp_files[$notImg_pth][$key]);
                    }
                }
            }
        }
        return view('contact.confirm', compact('request', 'tmp_files','img_pth'));
    }
}

メール送信のためのコントローラー
分けたほうがすっきりすると思う
app\Http\Controllers\MailController.php

php artisan make:controller MailController
app\Http\Controllers\MailController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests\MaileRequest;
use Illuminate\Support\Facades\Mail;
use App\Mail\MailableSend;
use Illuminate\Support\Facades\Storage;
use App\Jobs\SendHelloEmail;
use App\Jobs\DeleteFile;


class MailController extends Controller
{
    //メールファザードで送信
    public function mail_facades_send(MaileRequest $request)
    {
        $tmp_files = Storage::files('public/'.$request->tmp_img_files);

        //メールをキューを使って送信できない。
        Mail::send(
            // /*html送信*/ 'mail_templates.html_mail'/* 第1引数 viewsPath */,
            /*text送信*/ ['text' => 'mail_templates.text_mail'/*viewsPath*/],

            $request->all()/**第2引数 配列[]*/,

            function ($message/*中身不明dd($message)*/) use ($request, $tmp_files) {
                // 第3引数にはコールバック関数を指定し、
                // その中で、送信先やタイトルの指定を行う.
                $message
                    ->to($request->email)
                    ->bcc('admin@sample.com')
                    //envより優先される。なければenvが渡る。
                    ->from('laravel@sample.co.jp', 'Laravel事務局')
                    //storage_pathはstorageディレクトリの直下link等は不要
                    ->subject("お問い合わせありがとうございます。");

                //複数ファイルを添付する。
                foreach ($tmp_files as $key => $tmp_file) {
                    $message->attach(public_path(Storage::url($tmp_file)));
                }
            }
        );

        // // *** メールファザードで複数送信したい場合。*** // //
            // $emails = ['tester@blahdomain.com', 'anotheremail@blahdomian.com'];
            // Mail::send('mail_templates.html_mail', $request->all(), function ($message) use ($request, $emails)
            // {
            //     $message->from('no-reply@yourdomain.com', 'Joe Smoe');
            //    // $message->to( $request->input('emails') );
            //     $message->to( $emails);
            //     //Add a subject
            //     $message->subject("New Email From Your site");
            // });

        // storage\appからの相対パス
        // Storage::delete('public/'.$request->tmp_img_files);

        Storage::deleteDirectory('public/'.$request->tmp_img_files);

        //2重送信を防止できる。リダイレクト先はExceptionsでhandleする。
        $request->session()->regenerateToken();
        return view('contact.thanks');
    }

    //メーラブルで送信
    public function mailable_send(MaileRequest $request)
    {
        ////複数人に送信する場合配列で渡す
            $to = [$request->email, 'ABC@gmail.com'];

        //// メールにエイリアスをつけたい場合
            // $to = [
            //     ['email' => 'AAA@text.com','name' => 'Test', ],
            //     ['email' => 'BBB@text.com','name' => 'Test2',],
            // ];

            //でもOK usersテーブルのemail、nameの列が使用される。
            // $users = User::all(); $to = $users

        //ファイルを配列で取得
        $tmp_files = Storage::files('public/'.$request->tmp_img_files);

        //queue送信やjobではrequestオブジェクトでは駄目
        $inputs = $request->all();

        // //普通に送信
        // Mail::to($to)->send(new MailableSend($inputs/*$requestでもOK*/,$tmp_files));

        //queue送信 第2引数では$requestオブジェクトをそのまま取得できない
        // Mail::to($to)->queue(new MailableSend($inputs/*$requestはダメ絶対駄目*/,$tmp_files));

        //dispatchでjobファイルを発火 第2引数では$requestオブジェクトをそのまま取得できない
        SendHelloEmail::dispatch($inputs/*$requestはダメ絶対駄目*/,$tmp_files,$to);

        //  画像ファイルを削除
        // Storage::delete('public/'.$request->tmp_img_files);
         //ディレクトリを削除
        // Storage::deleteDirectory('public/'.$request->tmp_img_files);

        //queue送信するときは送る前に削除するからjobで実行しないとエラーになる
        DeleteFile::dispatch($request->tmp_img_files);

        //2重送信を防止できる。リダイレクト先はExceptionsでhandleする。
        $request->session()->regenerateToken();
        return view('contact.thanks');
    }
}

mailableクラスを作成する
app\Mail\MailableSend.php

php artisan make:mail MailableSend
app\Mail\MailableSend.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;

class MailableSend extends Mailable
//メイラブルでqueue送信するときはコメントアウトを解除(dispatchはコメントアウト)
// implements ShouldQueue
{
    use Queueable, SerializesModels;

    protected $request;
    protected $tmp_files

    //引数で受け取った変数を利用する publicの場合はそのまま利用できる。
    public function __construct($request, $tmp_files)
    {
        //protectedの場合$thisで受け取る
        $this->request = $request;
        $this->tmp_files = $tmp_files;
    }

    public function build()
    {
        // リクエストの変数展開
        extract($this->request);
        $tmp_files = $this->tmp_files;
        //            //htmlメール $this->view
        $mail = $this->view('mail_templates.html_mail', compact('email', 'title', 'body','tmp_files'))
            // //テキストメールもいける。
            // $this->text('mail_templates.text_mail',compact('email','title','body'))

            // //to はコントローラーで指定する。
            // // ✗->to($request->email)
            ->bcc('admin@sample.com')
            // //envより優先される。なければenvが渡る。
            ->from('laravel@sample.co.jp', 'Laravel事務局')
            ->subject("お問い合わせありがとうございます。");

            //添付送信
        foreach ($this->tmp_files as  $tmp_file) {
            $mail->attach(public_path(Storage::url($tmp_file)));
        }
        return $mail;
    }
}

メイルのテンプレートを作成する
htmlとtxtでviewsに作成する。

resources\views\mail_templates\html_mail.blade.php
お問い合わせ内容を受け付けました<br>
<br>
メールアドレス<br>
{!! $email !!}<br>
<br>
タイトル<br>
{!! $title !!}<br>
<br>
お問い合わせ内容<br>
{!! nl2br($body) !!}<br>

添付画像<br>

@foreach ($tmp_files as $tmp_file)
    <img src="{{ asset(Storage::url($tmp_file)) }}" width="200px">
@endforeach
resources\views\mail_templates\text_mail.blade.php
お問い合わせ内容を受け付けました
HTMLは書かない<br>とか

メールアドレス
{{ $email }}
タイトル
{{ $title }}
お問い合わせ内容
{{ nl2br($body) }}

ジョブファイルを作成する
メイラブル送信するファイルと
一時画像を削除するファイルを作成する
app\Jobs\SendHelloEmail.php
app\Jobs\DeleteFile.php

php artisan make:job SendHelloEmail
php artisan make:job DeleteFile
app\Jobs\SendHelloEmail.php
<?php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use App\Mail\MailableSend;

class SendHelloEmail
implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;


    //ジョブがタイムアウトになるまでの秒数 defautは60秒
    public $timeout = 120;

    protected $request;
    protected $tmp_files;
    protected $to;

    public function __construct($request, $tmp_files,$to)
    {
        $this->request = $request;
        $this->tmp_files = $tmp_files;
        $this->to = $to;
    }

    public function handle()
    {
        //複数人に送信する場合配列で渡す
        $to = $this->to;
        Mail::to($to)->send(new MailableSend($this->request, $this->tmp_files));
    }
}
app\Jobs\DeleteFile.php
<?php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;

class DeleteFile implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $tmp_img_files;

    public function __construct($tmp_img_files)
    {
        $this->tmp_img_files = $tmp_img_files;
    }

    public function handle()
    {
        Storage::deleteDirectory('public/'.$this->tmp_img_files);
    }
}

JobとQueueの設定をする

# 変更
QUEUE_CONNECTION=database 
php artisan queue:table
php artisan migrate

実行するとqueue送信が可能になる。

php artisan queue:work

notificationで送信

フレンドに送信させるため、フレンドモデルを作成する。モデルを作成するのはリレーションする必要があるため。1対多のテーブルを作成する。

php artisan make:model Friend -a
database\migrations\xxxx_create_friends_table.php
    Schema::create('friends', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id') ->constrained() ->onUpdate('cascade') ->onDelete('cascade');
        $table->foreignId('friend_id') ->constrained('users','id') ->onUpdate('cascade') ->onDelete('cascade');
        $table->timestamps();
        $table->unique(["user_id","friend_id"]);
    });

Userモデルの編集

public function friends(){
    //公式 return $this->hasMany(Comment::class, 'foreign_key', 'local_key');
    return $this->hasMany(Friend::class,'user_id','id');
}

Friendモデルの編集

app\Models\Friend.php
protected $fillable = [
    'user_id',
    'friend_id',
];

public function user()
{
    // 公式 return $this->belongsTo(User::class, 'foreign_key', 'owner_key');
    return $this->belongsTo(User::class,'friend_id','id');

    //公式 return $this->hasOne(Phone::class, 'foreign_key', 'local_key');
    //return $this->hasOne(User::class, 'id', 'friend_id');
}

友達一覧画面+送信画面を作成
resources\views\Users\friends\index.blade.php

resources\views\Users\friends\index.blade.php
@extends('layouts.app')

@section('content')
    <div class="container flex-grow-1 d-flex flex-column">
        @if (session()->has('success'))
        <div class="alert alert-success alert-dismissible fade show" role="alert">
            <strong>{{ session('success') }}</strong>
            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>

        @endif
        <div class="row  flex-grow-1 justify-content-center align-items-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">友達一覧</div>
                    <div class="card-body">
                        <p>{{ Auth::user()->name }}</p>
                        <input type="checkbox" name="" id="mail_checbox"><label for="mail_checbox">一斉送信</label>
                        <form method="POST" action="{{ route('friends_mail_send') }}">
                            @csrf
                            <table>
                                <thead>
                                    <th>送信</th>
                                    <th>名前</th>
                                </thead>
                                <tbody>

                                    @foreach ($friends as $friend)
                                        <tr>
                                            <td><input type="checkbox" name="friend_ids[][friend_id]"
                                                    value="{{ $friend->friend_id }}" id="{{ $friend->friend_id }}"></td>
                                            <td><label for="{{ $friend->friend_id }}">{{ $friend->user->name }}</label></td>
                                        </tr>
                                    @endforeach
                                </tbody>
                            </table>
                            <div class="text-right mb-3">
                            <a class="btn btn-sm btn-secondary" href="{{ route('friends.register_form') }}">友達を登録する</a>
                        </div>
                            <div class="border p-3">
                                <div class="text-center"><span class="text-secondary">通知内容</span></div>
                                <label>タイトル</label>
                                <input class="form-control @error('email') is-invalid @enderror" name="title" value="{{ old('title') }}" type="text">
                                @if ($errors->has('title'))
                                <span class="invalid-feedback" role="alert">
                                    <p class="error-message">{{ $errors->first('title') }}</p>
                                </span>
                                @endif
                                <label>内容</label>
                                <textarea rows="5" class="form-control @error('email') is-invalid @enderror" name="body">{{ old('body') }}
                            </textarea>
                                @if ($errors->has('body'))
                                <span class="invalid-feedback" role="alert">
                                    <p class="error-message">{{ $errors->first('body') }}</p>
                                </span>
                                @endif
                                <button
                                class="btn btn-secondary btn-sm mt-3"
                                type="submit"
                                >
                                友達にメール送信
                            </button>
                        </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

@push('js')
    <script>
        $('#mail_checbox').on('change',function(){
            if($('#mail_checbox').prop('checked')){
                $("input[name^='friend_ids']").prop('checked',true)
            }else{
                $("input[name^='friend_ids']").prop('checked',false)
            }
        });
    </script>
@endpush

友達登録画面の作成

resources\views\Users\friends\register_friends.blade.php
@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header d-flex">
                        <span>お問い合わせ</span>
<a class="btn btn-sm btn-secondary w-25 ml-auto" href="{{ route('friends.index') }}">ホーム</a>
                    </div>
                    <div class="card-body">
                        <p>{{ Auth::user()->name }}</p>
                        <input type="checkbox" name="" id="register_all"><label for="register_all">登録</label>
                        <input type="checkbox" name="" id="delete_all"><label for="delete_all">登録解除</label>
                        <form id="friends_form" action="{{ route('register_friends') }}" method="POST">
                            @csrf
                        <table>
                            <thead>
                                <th>登録</th>
                                <th>解除</th>
                                <th>名前</th>
                            </thead>
                                <tbody>
                                    @foreach ($users as $user)
                                        <tr>
                                            @if($friends->contains('friend_id',$user->id))
                                                <td></td>
                                                <td><input type="checkbox" name="friend_delete_ids[][friend_id]" value="{{ $user->id }}" id="{{$user->id}}"></td>
                                            @else
                                            <td><input type="checkbox" name="friend_register_ids[][friend_id]" value="{{ $user->id }}" id="{{$user->id}}"></td>
                                            <td></td>
                                            @endif
                                            <td><label for="{{ $user->id }}">{{ $user->name }}</label></td>
                                        </tr>
                                    @endforeach
                                    <th colspan="2">
                                        <input id="friends_btn" class="btn btn-primary" type="submit" value="友達登録">
                                    </th>
                        </tbody>
                        </table>
                    </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

@push('js')
<script>
$('#friends_btn').on('click',function(event){
    event.preventDefault();
    $('#friends_form').submit();
});
$('#register_all').on('change',function(){
    if($('#register_all').prop('checked')){

        $("input[name^='friend_register_ids']").prop('checked',true);
    }else{
        $("input[name^='friend_register_ids']").prop('checked',false);
    }
});
$('#delete_all').on('change',function(){
    if($('#delete_all').prop('checked')){

        $("input[name^='friend_delete_ids']").prop('checked',true);
    }else{
        $("input[name^='friend_delete_ids']").prop('checked',false);
    }
});
</script>
@endpush

UserControllerを作成する

php artisan make:controller UserController

app\Http\Controllers\UserController.php

app\Http\Controllers\UserController.php

    //友達一覧 + ここから送信も
    public function friends_index(){
        $user = User::find(Auth::id());
        //friendsをgetして、
        $friends=$user->friends()->get();
        //定番の N対1 対策
        $friends->load('user');
        return view('Users.friends.index',compact('user','friends'));
    }


    // 友達登録//ファンクション名はpathに合わせてみた。
    public function friends_register_form(){
        $user = User::findOrFail(Auth::id());

        $users = User::where('id', '!=', $user->id)->get();
        $friends = $user->friends()->get();
        return view('users.friends.register_friends', compact('users', 'friends'));

    }

    //友達登録メソッド
    public function register_friends(Request $request)
    {
        DB::beginTransaction();
        try {
            $user = User::find(Auth::id());

            if ($request->has('friend_register_ids')) {
                $user->friends()->createMany(
                    $request->friend_register_ids
                );
            }

            if ($request->has('friend_delete_ids')) {
                $user->friends()->whereIn('friend_id', $request->friend_delete_ids)->delete();
            }

            DB::commit();
            return view('friends.index');
        } catch (\Exception $e) {
            DB::rollback();
            return redirect()->route('friends.register_form')->withInput();
        }
    }
 
    //友達へ送信メソッド
    public function friends_mail_send(Request $request){
        $friends = User::whereIn('id',$request->friend_ids)->get();
        $user = User::find(Auth::id());
        Notification::send($friends, new friends_mail_send($user,$request->except('friend_ids'),$friends));
        return redirect()->route('friends.index')->with('success','送信しました');
    }

notificationファイルの作成

php artisan make:notification friends_mail_send

app\Notifications\friends_mail_send.php

app\Notifications\friends_mail_send.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class friends_mail_send extends Notification
//queue送信するときはコメントアウトを解除したらいいだけ。
//implements ShouldQueue
{
    use Queueable;
    protected $user;
    protected $request;
    protected $friends;

    public function __construct($user, $request, $friends)
    {
        $this->user = $user;
        $this->request = $request;
        $this->friends = $friends;
    }

    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->markdown('mail_templates.friends_mail_send', [
                'user'  => $this->user,
                'title' => $this->request['title'],
                'body' => $this->request['body'],
                'for_friend' => $notifiable,
                'friends' => $this->friends,
                // 'url' => $this->url,
                // 'token' => $this->token,
            ])
            // ->error()//actionのボタンがdanger
            ->success()//actionのボタンがsuccess
            ->line('アクションの前のライン1.')
            ->line('アクションの前のライン2.')
            ->line('アクションの前のライン3.')
            ->action('Notification Action', Url('/'))
            ->line('アクションの後のライン4')
            ->line('アクションの後のライン5.')
            ->subject("{$this->user->name}からあなた宛に通知があります");
    }

    public function toArray($notifiable)
    {
        return [];
    }
}

友達へのメールのテンプレート

resources\views\mail_templates\friends_mail_send.blade.php
@component('mail::layout')
{{-- Header logoにリンクがはれるコンポーネント --}}
@slot('header')
{{-- @component('mail::header', ['url' => $url])
{{ config('app.name') }}
@endcomponent --}}
@endslot

{{-- Body --}}
{{$user->name}}さんから連絡です
- {{ ($for_friend['name'])  }}さんへ

@component('mail::panel')
## その他<br>
@foreach ($friends as $friend)
@if ($friend['id'] !== $for_friend['id'])
{{$friend['name'] }}<br>
@endif
@endforeach
さん達へ
@endcomponent

タイトル
----
{{ $title }}

# 内容
{{ Illuminate\Mail\Markdown::parse($body) }}
{{ $body }}

{{-- Intro Lines actionより上のline --}}
@foreach ($introLines as $line)
{{ $line }}

@endforeach

{{-- Outro Lines actionより下のline --}}
@foreach ($outroLines 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

@component('mail::table')
| Laravel       | Table         | Example  |
| ------------- |:-------------:| --------:|
| Col 2 is      | Centered      | $10      |
| Col 3 is      | Right-Aligned | $20      |
@endcomponent

@component('mail::button', ['url' => 'https://qiita.com/','color'=>'success'])
google
@endcomponent

{{-- Subcopy actionButtonが効かなかった時の予備--}}
@isset($actionText)
@slot('subcopy')
@lang(
    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
    'into your web browser:',
    [
        'actionText' => $actionText,
    ]
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
@endslot
@endisset

{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot

@endcomponent

notification+mailable

今週のお勧めメニューをcustomerに送信する

入力ホーム
resources\views\Users\friends\index.blade.php

@extends('layouts.app')
@push('css')
    <style>
        .custom-file {
            max-width: 20rem;
            overflow: hidden;
        }

        .custom-file-label {
            white-space: nowrap;
        }

        .imagePreview {
            width: 100%;
            height: 200px;
            display: block;
            border: solid 1px;
        }

    </style>
@endpush
@section('content')

    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-8 justify-">
                <div class="card">
                    <div class="card-header">入力ホーム</div>

                    <div class="card-body">
                        @if ($errors->any())
                            <div class="alert alert-danger">
                                <ul>
                                    @foreach ($errors->all() as $error)
                                        <li>{{ $error }}</li>
                                    @endforeach
                                </ul>
                            </div>
                        @endif

                        <form id="mail_send" method="POST" enctype="multipart/form-data"
                            action="{{ route('users.confirm') }}">
                            @csrf

                            <p>information</p>
                            <div class="card-body border">
                                <label id="infomation">今週のお知らせ</label>
                                <input class="form-control @error('infomation') is-invalid @enderror" name="infomation"
                                    value="{{ old('infomation') }}" type="text">
                                @if ($errors->has('infomation'))
                                    <span class="invalid-feedback" role="alert">
                                        <p class="error-message">{{ $errors->first('infomation') }}</p>
                                    </span>
                                @endif

                                <label id="infomation_url">ページのURL</label>
                                <input class="form-control @error('infomation_url') is-invalid @enderror"
                                    name="infomation_url" value="{{ old('infomation_url') }}" type="text">
                                @if ($errors->has('infomation_url'))
                                    <span class="invalid-feedback" role="alert">
                                        <p class="error-message">{{ $errors->first('infomation_url') }}</p>
                                    </span>
                                @endif
                            </div>

                            <p>メイン</p>
                            <div class="card-body border">
                                <div class="form-group mt-3">
                                    <label for="file">mainImage</label>

                                    <div class="form-row align-items-center no-gutters p-1 m-0 justify-content-center">
                                        <div id="file" class="input-group main col-md-7">
                                            <div class="custom-file">
                                                <input type="file" id="mainImg-file"
                                                    class="mainImg-file-input @error('file') is-invalid @enderror"
                                                    name="img-files[]" accept=".png,.jpg,.jpeg" />
                                                <label class="custom-file-label main-lavel" for="mainImg-file"
                                                    data-browse="参照">mainImage</label>
                                            </div>

                                            <div class="input-group-append">
                                                <button type="button"
                                                    class="btn btn-outline-secondary main_reset">取消</button>
                                            </div>
                                        </div>
                                        <div class="imagePreview col-md-5 p-0">
                                            <img class="img-fluid imagePreview-main" src="" alt="メイン画像">
                                        </div>
                                    </div>

                                    <label id="mainMessage">mainMessage</label>
                                    <textarea class="form-control @error('email') is-invalid @enderror" name="mainMessage"
                                        value="{{ old('mainMessage') }}" type="text" rows="5">
                                                                                    </textarea>
                                    @if ($errors->has('mainMessage'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('mainMessage') }}</p>
                                        </span>
                                    @endif
                                </div>
                            </div>

                            <div class="card-body border mt-2">
                                <div class="form-group mt-3  no-gutters ">
                                    <label for="file">first_menus_img</label>
                                    <div class="form-row align-items-center p-1  justify-content-center .menus">
                                        <div id="file" class="input-group main col-md-7">
                                            <div class="custom-file">
                                                <input type="file" id="firstImg-file"
                                                    class="menus_img firstImg-file-input @error('file') is-invalid @enderror"
                                                    name="img-files[]" accept=".png,.jpg,.jpeg" />
                                                <label class="custom-file-label" for="firstImg-file"
                                                    data-browse="参照">first_manue_img</label>
                                            </div>

                                            <div class="input-group-append">
                                                <button type="button" class="btn btn-outline-secondary reset">取消</button>
                                            </div>
                                        </div>
                                        <div class="imagePreview col-md-5 p-0">
                                            <img class="img-fluid imagePreview-menus" src="" alt="ファースト画像">
                                        </div>
                                    </div>
                                    <label id="first_menus_infomation">first_menus_infomation</label>
                                    <input class="form-control @error('first_menus_infomation') is-invalid @enderror"
                                        name="first_menus_infomation" type="text">
                                    @if ($errors->has('first_menus_infomation'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('first_menus_infomation') }}</p>
                                        </span>
                                    @endif

                                    <label id="first_menus_price">first_menus_price</label>
                                    <input class="form-control @error('first_menus_price') is-invalid @enderror"
                                        name="first_menus_price" type="number">
                                    @if ($errors->has('first_menus_price'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('first_menus_price') }}</p>
                                        </span>
                                    @endif
                                </div>
                            </div>

                            <div class="card-body border mt-2">
                                <div class="form-group mt-3">
                                    <label for="file">second_menus_img</label>
                                    <div class="form-row align-items-center no-gutters p-1  justify-content-center .menus">
                                        <div id="file" class="input-group main col-md-7">
                                            <div class="custom-file">
                                                <input type="file" id="secondImg-file"
                                                    class="menus_img secondImg-file-input @error('file') is-invalid @enderror"
                                                    name="img-files[]" accept=".png,.jpg,.jpeg" />
                                                <label class="custom-file-label" for="secondImg-file"
                                                    data-browse="参照">second_manue_img</label>
                                            </div>

                                            <div class="input-group-append">
                                                <button type="button" class="btn btn-outline-secondary reset">取消</button>
                                            </div>
                                        </div>
                                        <div class="imagePreview col-md-5 p-0">
                                            <img class="img-fluid imagePreview-menus" src="" alt="セカンド画像">
                                        </div>
                                    </div>
                                    <label id="second_menus_infomation">second_menus_infomation</label>
                                    <input class="form-control @error('second_menus_infomation') is-invalid @enderror"
                                        name="second_menus_infomation" type="text">
                                    @if ($errors->has('second_menus_infomation'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('second_menus_infomation') }}</p>
                                        </span>
                                    @endif

                                    <label id="second_menus_price">second_menus_price</label>
                                    <input class="form-control @error('second_menus_price') is-invalid @enderror"
                                        name="second_menus_price" value="{{ old('second_menus_price') }}" type="number">
                                    @if ($errors->has('second_menus_price'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('second_menus_price') }}</p>
                                        </span>
                                    @endif
                                </div>
                            </div>

                            <div class="card-body border mt-2">
                                <div class="form-group mt-3">
                                    <label for="file">third_menus_img</label>
                                    <div class="form-row align-items-center no-gutters p-1  justify-content-center .menus">
                                        <div id="file" class="input-group main col-md-7">
                                            <div class="custom-file">
                                                <input type="file" id="thirdImg-file"
                                                    class="menus_img thirdImg-file-input @error('file') is-invalid @enderror"
                                                    name="img-files[]" accept=".png,.jpg,.jpeg" />
                                                <label class="custom-file-label" for="thirdImg-file"
                                                    data-browse="参照">third_manue_img</label>
                                            </div>

                                            <div class="input-group-append">
                                                <button type="button" class="btn btn-outline-secondary reset">取消</button>
                                            </div>
                                        </div>
                                        <div class="imagePreview col-md-5 p-0">
                                            <img class="img-fluid imagePreview-menus" src="" alt="サード画像">
                                        </div>
                                    </div>
                                    <label id="third_menus_infomation">third_menus_infomation</label>
                                    <input class="form-control @error('email') is-invalid @enderror"
                                        name="third_menus_infomation" type="text">
                                    @if ($errors->has('third_menus_infomation'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('third_menus_infomation') }}</p>
                                        </span>
                                    @endif

                                    <label id="third_menus_price">third_menus_price</label>
                                    <input class="form-control @error('third_menus_price') is-invalid @enderror"
                                        name="third_menus_price" type="number">
                                    @if ($errors->has('third_menus_price'))
                                        <span class="invalid-feedback" role="alert">
                                            <p class="error-message">{{ $errors->first('third_menus_price') }}</p>
                                        </span>
                                    @endif
                                </div>
                            </div>

                            <button class="btn btn-primary btn-sm mt-3" type="submit" id="confirm">
                                確認フォームへ
                            </button>

                            <button class="btn btn-primary btn-sm mt-3" type="submit" id='mail_send'>
                                メール送信
                            </button>

                        </form>

                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

@push('js')

    <script>
        //1回だけイベントを実行する。永久ループになるため、
        $('#mail_send').one('submit', function(e) {
            e.preventDefault();

            //クリックした要素を取得できる。
            click_name = $(document.activeElement).attr('id');

            //attrではんくpropを使用する。
            $('button[type=submit]').prop('disabled', true);;

            if (click_name == 'mail_send') {
                //atr複数の場合オブジェクトにする
                $('#mail_send').attr({
                    'action': "{{ route('users.mail_send') }}"
                });
            }

            if (click_name == 'confirm') {
                $('#mail_send').attr('action', "{{ route('users.confirm') }}");
            }
            $('#mail_send').submit();
        });
        // .custom-file-input
        $('.mainImg-file-input').on('change', handleFileSelect);

        function handleFileSelect(evt) {
            $('.imagePreview-main').attr({
                src: '',
                alt: ''
            })
            var files = evt.target.files;
            var reader = new FileReader();
            reader.onload = (function(theFile) {
                return function(e) {
                    if (theFile.type.match('image.*')) {
                        $('.imagePreview-main').attr({
                            src: e.target.result,
                            alt: theFile.name
                        });
                        $.cookie('main', e.target.result);
                    } else {
                        alert('画像ではありません');
                    }
                };
            })(files[0]);
            reader.readAsDataURL(files[0]);
            $(this).next('.custom-file-label').html(files[0].name);
        }

        $(document).on('change', '.menus_img', function() {
            var input = $(this);

            var files = !!this.files ? this.files : [];
            input.next('.custom-file-label').html(files[0].name);

            if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support
            if (/^image/.test(files[0].type)) { // only image file

                var reader = new FileReader(); // instance of the FileReader
                reader.readAsDataURL(files[0]); // read the local file

                reader.onloadend = function() {
                    input.parent().parent().next('.imagePreview').find('.imagePreview-menus').attr('src', this
                        .result);
                }
            }
        });

        //メインファイルの取消
        $('.main_reset').click(function() {
            $('.imagePreview-main').attr({
                src: '',
                alt: 'メイン画像'
            });
            $('.main-lavel').html('');
        })

        //ファイルの取消
        $('.reset').click(function() {
            $(this).parent().prev().children('.custom-file-label').html('ファイル選択...');
            $(this).parent().parent().next('.imagePreview').find('.imagePreview-menus').attr('src', '');
            $('.custom-file-input').val('');
        })
    </script>
@endpush

UserControllerの編集
app\Http\Controllers\UserController.php

    //メール本文入力ホーム
    public function index()
    {
        return view('Users.customer.index');
    }

    //確認画面
    public function confirm(Request $request)
    {
        $tmp_folder="img/".uniqid("tmp_")."/";
        $tmp_files =[];
        foreach ($request->file('img-files') as $key => $tmp_file) {
            if ($tmp_file->isValid()) {
                //画像の$pathを作成 'storage'なら保存/削除 'public'なら取得となるよう作成する。
                $tmp_files[$key] = $tmp_folder.Str::of($tmp_file->getClientOriginalName())->replace(' ', '_');
                // storage\appからの相対パス 画像を保存している。
                $tmp_file->storeAs("public", $tmp_files[$key]);
                // src="{{ asset('storage/'.$tmp_file)
            }
        }
        return view('mail_templates.html_customer_mail', compact('request','tmp_files'));
    }

    //notify送信メソッド
    public function mail_send(Request $request)
    {
        $users = User::all();
        $tmp_folder="img/".uniqid("tmp_")."/";
        $tmp_files =[];
        //画像を保存
        foreach ($request->file('img-files') as $key => $tmp_file) {
            if ($tmp_file->isValid()) {
                //画像の$pathを作成 'storage'なら保存/削除 'public'なら取得となるよう作成する。
                $tmp_files[$key] = $tmp_folder.Str::of($tmp_file->getClientOriginalName())->replace(' ', '_');
                // storage\appからの相対パス 画像を保存している。
                $tmp_file->storeAs("public", $tmp_files[$key]);
                // src="{{ asset('storage/'.$tmp_file)
            }
        }
        $inputs = $request->all();
        // notification+mailable
        Notification::send($users, new User_mail_send/*noficationクラス*/($inputs,$tmp_files));
        return redirect()->route('users.index');
    }

notificationファイルの作成
app\Notifications\User_mail_send.php

php artisan make:notification User_mail_send
app\Notifications\User_mail_send.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use App\Mail\User_mailable_send;
use Illuminate\Support\Facades\Request;


class User_mail_send extends Notification
//queue送信するときはコメントアウトを解除したらいいだけ。
// implements ShouldQueue
{
    use Queueable;

    protected $request;
    protected $tmp_files;

    public function __construct($request, $tmp_files)
    {
        $this->request = $request;
        $this->tmp_files = $tmp_files;
    }


    public function via($notifiable)
    {
        return ['mail'];
    }


    public function toMail($notifiable)
    {
        $request = $this->request;
        $tmp_files = $this->tmp_files;

        //mailableで送信
        return (new \App\Mail\User_mailable_send()/**空のmailableクラスを作成するだけ*/)
            ->view('mail_templates.html_customer_mail', compact('request', 'tmp_files'))
            ->from(config('mail.from.address'))
            //->to()はここでも指定する必要がある必須。
            ->to($notifiable->email)/** Notification::send($users<-コレが$notifiable, new User_mail_send($inputs,$tmp_files)); */
            ->subject('今週のおすすめメニュー');
    }


    public function toArray($notifiable)
    {
        return [
        ];
    }
}

空のmailableクラスを作成する

php artisan make:mail User_mailable_send
app\Mail\User_mailable_send.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class User_mailable_send extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
    }

    public function build()
    {
    }
}

1.Mailファサードを使ったメール送信

html送信+text送信+多重送信

app\Http\Controllers\MailController.php
    //メールファザードで送信
    public function mail_facades_send(MaileRequest $request)
    {
        $tmp_files = Storage::files('public/'.$request->tmp_img_files);

        //メールファザードではをキューを使って送信できない。
        Mail::send(
            /*html送信*/ 'mail_templates.html_mail',/* 第1引数 viewsPath */
            // /*text送信*/ ['text' => 'mail_templates.text_mail'/*viewsPath*/],

            $request->all()/**第2引数 配列[]*/,

            function ($message/*中身不明dd($message)*/) use ($request, $tmp_files) {
                // 第3引数にはコールバック関数を指定し、
                // その中で、送信先やタイトルの指定を行う.
                $message
                    ->to($request->email)
                    ->bcc('admin@sample.com')
                    //envより優先される。なければenvが渡る。
                    ->from('laravel@sample.co.jp', mb_encode_mimeheader('laravel事務局'))
                    ->subject("お問い合わせありがとうございます。");

                //複数ファイルを添付する。
                foreach ($tmp_files as $key => $tmp_file) {
                    $message->attach(public_path(Storage::url($tmp_file)));
                }
            }
        );

        // // *** メールファザードで複数送信したい場合。*** // //
            // $emails = ['tester@blahdomain.com', 'anotheremail@blahdomian.com'];
            // Mail::send('mail_templates.html_mail', $request->all(), function ($message) use ($request, $emails)
            // {
            //     $message->from('no-reply@yourdomain.com', 'Joe Smoe');
            //    // $message->to( $request->input('emails') );
            //     $message->to( $emails);
            //     //Add a subject
            //     $message->subject("New Email From Your site");
            // });

        //ファイルの削除
        // Storage::delete('public/'.$request->tmp_img_files);
        Storage::deleteDirectory('public/'.$request->tmp_img_files);

        //2重送信を防止できる。リダイレクト先はExceptionsでhandleする。
        $request->session()->regenerateToken();
        return view('contact.thanks');
    }

テンプレートファイル

resources\views\mailTemplates\mail.blade.php
お問い合わせ内容を受け付けました<br>
<br>
メールアドレス<br>
{{ $email }}<br>
<br>
タイトル<br>
{{ $title }}<br>
<br>
お問い合わせ内容<br>
{{ nl2br($body) }}<br>

テキストメールテンプレートファイル

resources\views\mailTemplates\mail_text.blade.php
お問い合わせ内容を受け付けました。
HTMLは書かない。<br>とか

■メールアドレス
{{ $email }}
■タイトル
{{ $title }}
■お問い合わせ内容
{{ nl2br($body) }}

2.Maileableで送信

1.Mailableクラスの作成

コントローラー
    //メーラブルで送信
    public function mailable_send(MaileRequest $request)
    {

        ////複数人に送信する場合配列で渡す
            $to = [$request->email, 'ABC@gmail.com'];

        //// メールにエイリアスをつけたい場合
            // $to = [
            //     ['email' => 'AAA@text.com','name' => 'Test', ],
            //     ['email' => 'BBB@text.com','name' => 'Test2',],
            // ];

            // $users = User::all(); $to = $users //でもOK usersテーブルのemail、nameの列が使用される。

        //ファイルを配列で取得
        $tmp_files = Storage::files('public/'.$request->tmp_img_files);

        $inputs = $request->all();

        // //普通に送信
        Mail::to($to)->send(new MailableSend($inputs/*$requestでもOK*/,$tmp_files));

        //queue送信 引数では$requestオブジェクトをそのまま取得できない
        // Mail::to($to)->queue(new MailableSend($inputs/*$requestはダメ絶対駄目*/,$tmp_files));

        //dispatchでjobファイルを発火 引数では$requestオブジェクトをそのまま取得できない
        // SendHelloEmail::dispatch($inputs/*$requestはダメ絶対駄目*/,$tmp_files,$to);

        //  画像を削除
         //ファイルを削除
        // Storage::delete('public/'.$request->tmp_img_files);
         //ディレクトリを削除
        // Storage::deleteDirectory('public/'.$request->tmp_img_files);

        //queue送信するときは送る前に削除するからjobで実行しないとエラーになる
        DeleteFile::dispatch($request->tmp_img_files);

        //2重送信を防止できる。リダイレクト先はExceptionsでhandleする。
        $request->session()->regenerateToken();
        return view('contact.thanks');
    }

メイラブルクラスを作成する。

php artisan make:mail SendTestMail
app\Mail\MailableSend.php
<?php
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;

class MailableSend extends Mailable
 //普通の時dispatchのときはコメントアウト
// implements ShouldQueue
{
    use Queueable, SerializesModels;

    protected $request;
    protected $tmp_files;

    //引数で受け取った変数を利用する publicの場合はそのまま利用できる。
    public function __construct($request, $tmp_files)
    {
        //protectedの場合$thisで受け取る
        $this->request = $request;
        $this->tmp_files = $tmp_files;
    }

    public function build()
    {
        // リクエストの変数展開
        extract($this->request);
        $tmp_files = $this->tmp_files;
        //            //htmlメール $this->view
        $mail = $this->view('mail_templates.html_mail', compact('email', 'title', 'body','tmp_files'))
            // //テキストメールもいける。
            // $this->text('mail_templates.text_mail',compact('email','title','body'))

            // //to はコントローラーで指定する。
            // // ✗->to($request->email)
            ->bcc('admin@sample.com')
            // //envより優先される。なければenvが渡る。
            ->from('laravel@sample.co.jp', 'Laravel事務局')
            ->subject("お問い合わせありがとうございます。");

            //添付送信
        foreach ($this->tmp_files as  $tmp_file) {
            $mail->attach(public_path(Storage::url($tmp_file)));
        }
        return $mail;
    }
}

queue送信するときは implements ShouldQueue を追記してやるだけでmailableでの設定はOK

class MailableSend extends Mailable
 //普通の時dispatchのときはコメントアウト
 implements ShouldQueue
{

jobファイルで送信するとき

app\Jobs\SendHelloEmail.php
app\Jobs\SendHelloEmail.php
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use App\Mail\MailableSend;

class SendHelloEmail
implements ShouldQueue //queue送信するときは追記
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    //ジョブがタイムアウトになるまでの秒数 defautは60秒
    public $timeout = 120;

    protected $request;
    protected $tmp_files;
    protected $to;

    public function __construct($request, $tmp_files,$to)
    {
        $this->request = $request;
        $this->tmp_files = $tmp_files;
        $this->to = $to;
    }

    public function handle()
    {
        //複数人に送信する場合配列で渡す
        $to = $this->to;
        Mail::to($to)->send(new MailableSend($this->request, $this->tmp_files));
    }
}

3.notifyで送信

コントローラー

    public function friends_mail_send(Request $request){
        $friends = User::whereIn('id',$request->friend_ids)->get();
        $user = User::find(Auth::id());
        
        // $friend = User::find($request->friend_id);
        // $friend->notify(new friends_mail_send());

        //複数人送信する時
        Notification::send($friends, new friends_mail_send($user,$request->except('friend_ids'),$friends));
        return redirect()->route('friends.index')->with('success','送信しました');
    }

notificationを作成する(マークダウンを作成して送信)

php artisan make:notification friends_mail_send
app\Notifications\friends_mail_send.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class friends_mail_send extends Notification
//queue送信するときはコメントアウトを解除したらいいだけ。
implements ShouldQueue
{
    use Queueable;
    protected $user;
    protected $request;
    protected $friends;

    public function __construct($user, $request, $friends)
    {
        $this->user = $user;
        $this->request = $request;
        $this->friends = $friends;
    }

    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->markdown('mail_templates.friends_mail_send', [
                'user'  => $this->user,
                'title' => $this->request['title'],
                'body' => $this->request['body'],
                'for_friend' => $notifiable,
                'friends' => $this->friends,
                // 'url' => $this->url,
                // 'token' => $this->token,
            ])
            // ->error()//actionのボタンがdanger
            ->success()//actionのボタンがsuccess
            ->line('アクションの前のライン1.')
            ->line('アクションの前のライン2.')
            ->line('アクションの前のライン3.')
            ->action('Notification Action', Url('/'))
            ->line('アクションの後のライン4')
            ->line('アクションの後のライン5.')
            ->subject("{$this->user->name}からあなた宛に通知があります");
    }

    public function toArray($notifiable)
    {
        return [];
    }
}

マークダウンファイルを作成
viewsに作成する

resources\views\mail_templates\friends_mail_send.blade.php
@component('mail::layout')
{{-- logoにリンクがはれるコンポーネント --}}
@slot('header')
{{-- @component('mail::header', ['url' => $url])
{{ config('app.name') }}
@endcomponent --}}
@endslot

{{-- Body --}}
{{$user->name}}さんから連絡です
- {{ ($for_friend['name'])  }}さんへ

@component('mail::panel')
## その他<br>
@foreach ($friends as $friend)
@if ($friend['id'] !== $for_friend['id'])
{{$friend['name'] }}
@endif
@endforeach
さん達へ
@endcomponent

タイトル
----
{{ $title }}

# 内容
{{ Illuminate\Mail\Markdown::parse($body) }}
{{ $body }}

{{-- Intro Lines actionより上のline --}}
@foreach ($introLines as $line)
{{ $line }}

@endforeach

{{-- Outro Lines actionより下のline --}}
@foreach ($outroLines 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

@component('mail::table')
| Laravel       | Table         | Example  |
| ------------- |:-------------:| --------:|
| Col 2 is      | Centered      | $10      |
| Col 3 is      | Right-Aligned | $20      |
@endcomponent

@component('mail::button', ['url' => 'https://qiita.com/','color'=>'success'])
google
@endcomponent

{{-- Subcopy actionButtonが効かなかった時の予備--}}
@isset($actionText)
@slot('subcopy')
@lang(
    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
    'into your web browser:',
    [
        'actionText' => $actionText,
    ]
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
@endslot
@endisset

{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot

@endcomponent

既存のマークダウンを編集するときは

php artisan vendor:publish --tag=laravel-mail

notificationをmailableで送信

コントローラー
    public function mail_send(Request $request)
    {
        $inputs = $request->all();
        // notification+mailable
        Notification::send($users, new User_mail_send/*noficationクラス*/($inputs,$tmp_files));
        return redirect()->route('users.index');
    }

nofiticationsファイル

app\Notifications\User_mail_send.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;


class User_mail_send extends Notification
//queue送信するときはコメントアウトを解除したらいいだけ。
// implements ShouldQueue
{
    use Queueable;

    protected $request;
    protected $tmp_files;

    public function __construct($request, $tmp_files)
    {
        $this->request = $request;
        $this->tmp_files = $tmp_files;
    }


    public function via($notifiable)
    {
        return ['mail'];
    }


    public function toMail($notifiable)
    {
        $request = $this->request;
        $tmp_files = $this->tmp_files;

        //mailableで送信
        return (new \App\Mail\User_mailable_send()/**空のmailableクラスを作成するだけ*/)
            ->view('mail_templates.html_customer_mail', compact('request', 'tmp_files'))
            ->from(config('mail.from.address'))
            //->to()はここでも指定する必要がある必須。
            ->to($notifiable->email)/** Notification::send($users<-コレが$notifiable, new User_mail_send($inputs,$tmp_files)); */
            ->subject('今週のおすすめメニュー');
    }


    public function toArray($notifiable)
    {
        return [
        ];
    }
}

空のmailableクラスを作成するだけ

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class User_mailable_send extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
    }

    public function build()
    {

    }
}
1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?