まとめサイト。
一切laravelの公式サイトを見ていないという・・・。
laravelでメールを送信する方法
1.ファザード
2.メイラブル
3.ノーティファイ
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のルート一覧
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');
リンクを作成する
@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
<!-- ドロップダウンメニューに追記 -->
<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 に追記 ステップフォームのスタイルを作成。
<!-- 追記 -->
<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>
メイルファザードとメイラブル送信
入力フォームを作成する。
@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
確認フォーム
@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)
@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
<?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
<?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
<?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に作成する。
お問い合わせ内容を受け付けました。<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
お問い合わせ内容を受け付けました。
HTMLは書かない。<br>とか
■メールアドレス
{{ $email }}
■タイトル
{{ $title }}
■お問い合わせ内容
{{ nl2br($body) }}
ジョブファイルを作成する
メイラブル送信するファイルと
一時画像を削除するファイルを作成する
app\Jobs\SendHelloEmail.php
app\Jobs\DeleteFile.php
php artisan make:job SendHelloEmail
php artisan make:job DeleteFile
<?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));
}
}
<?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
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モデルの編集
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
@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">×</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
友達登録画面の作成
@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
//友達一覧 + ここから送信も
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
<?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 [];
}
}
友達へのメールのテンプレート
@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
<?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
<?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送信+多重送信
//メールファザードで送信
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');
}
テンプレートファイル
お問い合わせ内容を受け付けました。<br>
<br>
■メールアドレス<br>
{{ $email }}<br>
<br>
■タイトル<br>
{{ $title }}<br>
<br>
■お問い合わせ内容<br>
{{ nl2br($body) }}<br>
テキストメールテンプレートファイル
お問い合わせ内容を受け付けました。
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
<?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
<?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
<?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に作成する
@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ファイル
<?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()
{
}
}