Laravel

Mail(메일)

짱구를왜말려? 2020. 11. 1. 19:12
반응형
SMALL

# .env 설정

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

 

# 메일 템플릿 생성

php artisan make:mail PasswordResetCreated --markdown=emails.passwordResets.created

@ PasswordReset.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PasswordReset extends Model
{
    use HasFactory;

    protected $fillable = ["email", "token"];

    protected $primaryKey = "email";

    public $timestamps = false;

    public function resetUrl()
    {
        return config("app.url")."/passwordResets/$this->token/edit";
    }
}

@ PasswordResetCreated.php

<?php

namespace App\Mail;

use App\Models\PasswordReset;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class PasswordResetCreated extends Mailable
{
    use Queueable, SerializesModels;

    protected $receiver;

    protected $passwordReset;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(User $receiver, PasswordReset $passwordReset)
    {
        $this->receiver = $receiver;

        $this->passwordReset = $passwordReset;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('emails.passwordResets.created')
                ->subject("[".config("app.name")."] ".__("socialLogin.passwordReset")["send"])
                ->with(["receiver" => $this->receiver, "passwordReset" => $this->passwordReset]);
    }
}

 

# 메일 기본 템플릿 커스텀하기

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

* html은 resources/views/vendor/mail/html의 layout, footer, header, message 등을 고치면 됨.

(내가 markdown으로 새로 만든 메일 view가 message.blade.php의 $slot 내용으로 들어가는거임)

 

* css는 resources/views/vendor/mail/html/themes/default.css를 수정

 

@ default.css

@charset "utf-8";
@import url(https://fonts.googleapis.com/earlyaccess/notosanskr.css);

html {margin:0;padding:0;}
body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,address,big,cite,code,del,dfn,em,font,img,ins,q,s,samp,small,strike,sub,sup,tt,var,b,u,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,input,textarea,select,button,
table,th,td,ul, li {margin:0;padding:0;border:0;font-size:16px; font-family: 'Noto Sans KR', sans-serif;color:#333;font-weight:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0; box-sizing: border-box;}
header, footer, section, article, aside, nav, details, menu, figure, figcaption {display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}

a:link , a:visited {text-decoration:none;}
a:hover,a:focus {text-decoration:none;}
*{-webkit-text-size-adjust:none;}

input,textarea,select,button{font-size:16px;font-family:'Noto Sans KR', sans-serif;}
img,fieldset {border:0;}
form,fieldset,input {margin:0;padding:0;}
em,address,optgroup {font-style:normal;font-weight:normal;}
button {border:none 0;margin:0;padding:0;overflow:visible;cursor:pointer;background:none;}


ol,ul,li {list-style:none;}
table {border-spacing:0;border-collapse:collapse;}
hr {display:none;}
legend {position:absolute;top:-1000px;left:-1000px;visibility:hidden;}
caption {height:0;line-height:0;font-size:0px;visibility:hidden;}

.skipNavi {position:absolute;left:0;top:0;z-index:999;width:100%;text-align:center;}
.skipNavi a {position:absolute;top:-999px;left:-999px;}
.skipNavi a:focus, .skipNavi a:active, .skipNavi a:hover {display:block;top:0;left:0;padding:7px 10px 5px;background:#000;color:#fff;font-weight:bold;font-size:16px;text-decoration:none;}

input[placeholder] { color:#999;}
::-webkit-input-placeholder { /* WebKit browsers */ color:#999;}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ color:#999;}
::-moz-placeholder {/* Mozilla Firefox 19+*/ color:#999;}
:-ms-input-placeholder {/* ie10+*/ color:#999;}

input {color:#333 !important;}

select {
    cursor:pointer;
}


p,a,span, select, input {word-break:break-all;}
summary {display:none;}
@media screen and (max-width:500px){
    a, p, span, button {font-size:14px;}
}
.wrap-mobile {width:800px; margin:100px auto; padding:40px; max-width:100%; background-color:#fafafa;}
.wrap-mobile .container {background-color:#fff;}
.header {padding:20px; background-color:#1C3351; text-align: center;}
.header .logo {font-size:24px; color:#fff; text-align: center; font-weight:600;}
.content {padding:40px;}
.content .title.type01 {margin-bottom:20px; font-size:18px; font-weight:500; text-align: center;}
.content .btn.type01 {display:inline-block; max-width:200px; margin:0 auto; margin-top:20px; padding:10px 20px; color:#fff; background-color:#1C3351; text-align: center;}
.footer {padding:40px; text-align: center;}
.footer p {font-size:14px; color:#999;}
.body.type01 {margin-bottom:10px;}

@ layout.blade.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div class="wrap-mobile">
    <div class="container">
        {{ $header ?? '' }}

        <div class="content">
            {{ $slot }}
        </div>

        {{ $footer ?? '' }}
    </div>
</div>
</body>
</html>

@ message.blade.php(여기의 $slot부분이 우리가 만든 메일 view 내용이 들어갈 부분)

@component('mail::layout')
    {{-- Header --}}

    @slot('header')
        @component('mail::header', ['url' => config('app.url')])
            {{ config('app.name') }}
        @endcomponent
    @endslot

   {{-- Body --}}
    {{ $slot }}

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

@ header.blade.php

<header class="header">
    <a href="{{ config("app.url") }}">
        <!-- <img src="/img/logo.png" alt=""> -->
        {{config("app.name")}}
    </a>
</header>

@ footer.blade.php

<footer class="footer">
    {{ Illuminate\Mail\Markdown::parse($slot) }}
</footer>

 

@ views/passwordResets/created.blade.php(이 부분이 message.blade.php의 slot로 들어가는거임)

@component('mail::message')
    <div id="resetPassword">
        <h3 class="title type01">비밀번호 초기화</h3>

        <p class="body type01">안녕하세요, {{$receiver ? $receiver->email : ""}}님,</p>

        <p class="body type01">
            {{config("app.name")}} 서비스 계정의 비밀번호를 초기화하시려면 비밀번호 초기화 버튼을 눌러주시기 바랍니다.
        </p>

        <div class="btns" style="text-align:center;">
            <a href="{{$passwordReset ? $passwordReset->resetUrl() : ''}}" class="btn type01 width-100 bg-primary">비밀번호 초기화</a>
        </div>
    </div>
@endcomponent

 

# 메일 보내기

@ PasswordResetController.php

<?php


namespace ShinHyungJune\SocialLogin\Http;

use App\Mail\PasswordResetCreated;
use App\Models\PasswordReset;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
use Laravel\Socialite\Facades\Socialite;

class PasswordResetController extends Controller
{
    public function create()
    {
        return Inertia::render("PasswordResets/Create");
    }

    public function store(Request $request)
    {
        $request->validate([
            "email" => "required|email|string|max:500"
        ]);

        $message = "";

        if(!User::where("email", $request->email)->exists()) {
            $message = __("socialLogin.passwordReset.send_fail");

            return Inertia::render("PasswordResets/Create", ["message" => $message]);
        }

        $token = random_int(100000000,999999999);

        $passwordReset = PasswordReset::where("email", $request->email)->first();

        $passwordReset ? $passwordReset->update([
            "email" => $request->email,
            "token" => $token
        ]) : $passwordReset = PasswordReset::create([
            "email" => $request->email,
            "token" => $token
        ]);

        Mail::to($request->email)->send(new PasswordResetCreated(User::where("email", $request->email)->first(), $passwordReset));

        $message = __("socialLogin.passwordReset.send_success");

        return Inertia::render("PasswordResets/Create", ["message" => $message]);
    }

    public function edit(Request $request)
    {
        return Inertia::render("PasswordResets/Edit", [
            "email" => $request->email,
            "token" => $request->token
        ]);
    }
    
    public function update(Request $request)
    {
        $request->validate([
            "email" => "required|email|max:500",
            "token" => "required|string|max:5000",
            "password" => "required|string|min:8|max:500|confirmed"
        ]);


        $passwordReset = PasswordReset::where("email", $request->email)
            ->where("token", $request->token)
            ->first();

        $user = User::where("email", $request->email)->first();

        $message = __("socialLogin.passwordReset.reset_fail");

        if($user && $passwordReset){
            $user->update(["password" => Hash::make($request->password)]);

            $message = __("socialLogin.passwordReset.reset_success");
        }

        return Inertia::render("PasswordResets/Edit", ["message" => $message]);
    }
}

 

# 메일 미리 보기

@ web.php

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/


Route::get("/mailable", function(){
   return (new \App\Mail\PasswordResetCreated(new \App\User(), new \App\PasswordReset()));
});

 

LIST