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

まとめサイト。

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

https://www.yoheim.net/blog.php?q=20181207

https://reffect.co.jp/laravel/laravel-send-email

https://www.ritolab.com/entry/210


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

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

<details><summary>0.プロジェクトを作ってまとめてみる</summary><div>
0.プロジェクトを作ってまとめてみる
----

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

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のルート一覧

```php5: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');
```
リンクを作成する

```html: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
```

```html: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 に追記　ステップフォームのスタイルを作成。

```php5: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>
```
メイルファザードとメイラブル送信
----
入力フォームを作成する。

```php5: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
```
確認フォーム

```php5: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)

```html: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
```

```php5: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
```

```php5: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
```

```php5: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に作成する。

```php5: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
```

```php5: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
```

```php5: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));
    }
}
```

```php5: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の設定をする

```env
#変更
QUEUE_CONNECTION=database 
```

```
php artisan queue:table
```

```
php artisan migrate
```
実行するとqueue送信が可能になる。

```
php artisan queue:work
```

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

```
php artisan make:model Friend -a
```

```php5: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モデルの編集

```php5:
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

```php5: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
```
友達登録画面の作成

```php5: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

```php5: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

```php5: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 [];
    }
}
```
友達へのメールのテンプレート

```php5: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

```php5:
@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

```php5:
    //メール本文入力ホーム
    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
```

```php: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
```

```php5: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()
    {
    }
}
```

</div></details>


1.Mailファサードを使ったメール送信
---
html送信+text送信+多重送信

```php5: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');
    }
```
テンプレートファイル

```php5: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クラスの作成

```php5:コントローラー
    //メーラブルで送信
    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
```

```php5: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

```php5:
class MailableSend extends Mailable
 //普通の時dispatchのときはコメントアウト
 implements ShouldQueue
{
```
jobファイルで送信するとき

```
app\Jobs\SendHelloEmail.php
```

```php5: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で送信
---

```php5:コントローラー

    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
```

```php5: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に作成する

```php5: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で送信
---

```php5:コントローラー
    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ファイル

```php5: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クラスを作成するだけ

```php5:
<?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()
    {

    }
}
```