ถ้าใครที่ใช้ laravel ตั้งแต่เวอร์ชั่น 5.0 เป็นต้นมาคงจะเจอเคสนี้กันเป็นประจำ โดยปกติแล้วเวลาเราใช้คำสั่ง
php artisan make:auth
เราจะได้ระบบพื้นฐานในการยืนยันตัวผู้ใช้เช่น ระบบ login, logout, register, forgotpassword, resetpassword
แต่ถ้าใครที่ลองเล่นจริงๆจะเจอปัญหาว่า เมื่อเราเข้าหน้าลืมรหัสผ่าน laravel จะให้กรอก email และกดยืนยันเพื่อรับ email ในการรีเซ็ตรหัสผ่านหน้าตาแบบนี้
พอกด send password reset link เราจะได้ email ที่มีลิงค์มาให้ประมานนี้
http://test.test/password/reset/b4eca5d17c644b6b46292bb8497093bd433a2e663191051334f7bf748aaf5ec0
หลังจากเรากดลิงค์นี้ใน email เราจะเจอกับหน้านี้
ซึ่งมันเจอแค่นี้จริงๆ แล้วช่องที่ให้กรอก email ทำไมมันถึงไม่มี email ของ user มันหายไปไหน ? Laravel ไม่ได้ทำมาให้นั่นเองเพราะเค้าคิดว่าแต่ละ project อาจจะนำไปใช้ไม่เหมือนกันเลยไม่ได้ใส่มาให้เรามาดูวิธีทำกันดีกว่า
ในส่วนของ doc ที่เขียนเกี่ยวกับเรื่องนี้มีอยู่นิดเดียวเล่นทำเอางงใครอยากอ่านตามลิงก์นี้เลย อยู่ล่างสุดของหน้าเลย
เอาละปัญหามันคือตอนที่ส่ง email ไปหา user เนี่ย laravel ไม่ได้แนบ email ไปกับ URL ทีนี้วิธีแก้ปัญหาก็เอา email แนบไปกับ URL แล้วตอนกด URL กลับมาก็สั่งให้มันไปอยู่ใน input ด้วยสิ ไม่เห็นจะยาก !! หายไป 30 นาที…..
ขั้นแรกให้เรา override method sendPasswordResetNotification ที่อยู่ใน model user ก่อน
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
ไอ้เจ้า method sendPasswordResetNotification เนี่ยมันไปเรียกใช้ notification mail ของ laravel อยู่เราต้องสร้าง noti เพื่อส่งเมลใหม่ที่เรา custom เองโดยการใช้คำสั่ง
php artisan make:notification ResetPasswordNotification
เราจะได้ไฟล์ที่ App\Notifications\ResetPasswordNotification.php
จากนั้นเพิ่ม code ตามนี้ครับ
public $token;
public function __construct($token) {
$this->token = $token;
}
** สร้างตัวแปล $token ขึ้นมาก่อนหลังจากนั้นใน __construct รับ $token ส่งให้ $this->token อีกที
หลังจากนั้นเลื่อนลงไปหา method toMail แล้วเปลี่ยน code ตามด้านล่างนี้ครับ
public function toMail($notifiable) {
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url') . route('password.reset', [$this->token, 'e' => encrypt($notifiable->email)], false)))
->line('If you did not request a password reset, no further action is required.');
}
ในส่วนของ action คือการสร้างปุ่มในเมลหน้าตาเมลที่ส่งไปจะเป็นแบบนี้
สังเกตุว่าตรง ->action เราเพิ่ม param ไปอีกตัวคือ
'e' => encrypt($notifiable->email)
** $notifiable คือตัวแปรที่ laravel โยน model ทั้งก้อนเข้ามาให้ถ้ายังจำกันได้เราสร้าง notification ตัวนี้ไว้ใน user model มันเลยโยน user คนนั้นกลับมาเราสามารถเรียก username, name, email หรืออื่นๆได้หมดเลยในที่นี้ผมเรียกแค่ email
จะสังเกตุได้ว่าผมเอาฟังก์ชั่น encrypt แปลงค่าของ email เพราะว่าเวลาอยู่ใน url มันจะไม่สวยครับมันจะกลายเป็นแบบนี้
http://test.test/password/reset/b4eca5d17c644b6b46292bb8497093bd433a2e663191051334f7bf748aaf5ec0?e=test%40test.com
คงไม่สวยแน่ๆแต่ถ้าแปลงแล้วจะกลายเป็นแบบนี้
http://test.test/password/reset/41d42e322245efaee37da9c2cfe452bd9242a5f34742bbebbd74657ac0afeb4b?e=eyJpdiI6Ik9VNnZIaVwvbEVoMlwvOVMwdDFoZmFiZz09IiwidmFsdWUiOiJNMHArV2pcL2tqOXRPMElHNllpbFJrN20rWFp1dXo0dEVTVXhXMG9PTVRmVT0iLCJtYWMiOiI2NTZiMGEyOWNjYjYxYWE3NGU0ZWFmMzkyYTE0MzJiYjY4MTk2OTc5MjMyZTJlMDdiZmE1NjYxNjRjNDE0ZjQ1In0
หลังจากนั้นให้ไปที่ App\Http\Controllers\Auth\ResetPasswordController.php
แล้ว override method showResetForm ของ ralavel อีกครั้งด้วย code นี้
public function showResetForm(Request $request, $token = null) {
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => decrypt($request->query('e'))]
);
}
** ทำการแปลงค่าที่ได้จาก query string ด้วย decrypt ก็จะได้ email กลับมาแล้วส่งกลับไปหน้าฟอร์มที่เหลือเราไม่ต้องแก้อะไรครับ
พอเรากดลิงก์จากเมลกลับมาก็จะพบว่ามี email อยู่ในช่อง email ของ form เรียบร้อยแล้ว เราอาจจะไปเพิ่ม readonly ใน input เพื่อไม่ให้ user แก้ไขได้เท่านี้ก็เรียบร้อย
จริงๆหลายคนอาจจะไม่สนใจตรงส่วนนี้เพราะถ้า user กด URL มาก็ให้เค้ากรอกเมลอีกรอบสิจะเป็นอะไรไปแต่แน่นอนว่า UX ของโปรแกรมเราอาจจะไม่ดีนักเพราะ user ต้องมากรอกเองอีกรอบทำให้ user สบายๆเข้าไว้เค้าจะได้จ่ายตังแบบสบายๆให้เราด้วย ฮ่าๆๆ วันหน้าจะมี pain อะไรอย่าลืมติดตามกันครับ
สำหรับใครที่อยากส่งเมลออกจากระบบ Laravel แนะนำให้ใช้ SMTP Email Service จะส่งถึงมากกว่าใช้ SMTP ของ Share host มาดูวิธีการสมัคร Sendinblue SMTP กันครับ