-
반응형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'Laravel' 카테고리의 다른 글
Test(테스트, TDD) (0) 2021.02.05 인스타그램 API 연동 (2) 2021.02.05 whereExists (0) 2020.08.18 Object array 유효성 검사하는법 (0) 2020.07.19 Job(Queue) (0) 2020.06.14