0

[Series] Xây dựng RESTful API từ con số 0 với PHP Thuần & MVC - Phần 5: Gửi Email "Thật" với SMTP & PHPMailer

Chào các bạn, mình đã trở lại!

Trong môi trường phát triển, việc giả lập (Mock) email là cần thiết. Nhưng khi dự án tiến gần hơn đến giai đoạn Production, việc gửi một email xác thực hay reset mật khẩu "xịn xò" là điều bắt buộc.

Hôm nay, chúng ta sẽ "thay máu" cho lớp Mailer đơn sơ ở phần trước bằng một hệ thống gửi mail qua giao thức SMTP (Simple Mail Transfer Protocol).

1. Cài đặt "Vũ khí" PHPMailer

Thay vì tự viết lại từ đầu (đừng phát minh lại cái bánh xe!), chúng ta sẽ dùng thư viện PHPMailer. Đây là thư viện mạnh mẽ, hỗ trợ tốt SMTP, HTML mail và đính kèm tệp tin.

Mở terminal và chạy lệnh:

composer require phpmailer/phpmailer

2. Cấu hình Biến môi trường (.env)

Đừng bao giờ hardcode mật khẩu email vào code! Hãy lưu chúng vào file .env. Nếu bạn đang dùng Gmail, hãy nhớ bật App Password (Mật khẩu ứng dụng) vì Google không cho phép dùng mật khẩu chính để đăng nhập qua ứng dụng lạ nữa.

File: .env

SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your_email@gmail.com
SMTP_PASSWORD=abcd_efgh_ijkl_mnop # Mật khẩu ứng dụng (App Password)
SMTP_FROM=your_email@gmail.com
SMTP_FROM_NAME="Hệ Thống Support"

3. Tầng Core: Nâng cấp lớp Mailer.php

Chúng ta sẽ viết lại lớp Mailer để nó đọc cấu hình từ môi trường và sử dụng thư viện PHPMailer.

File: app/Core/Mailer.php

<?php
namespace App\Core;

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

class Mailer {
    public static function send($to, $subject, $body) {
        $mail = new PHPMailer(true);

        try {
            // 1. Cấu hình Server SMTP
            $mail->isSMTP();
            $mail->Host       = $_ENV['SMTP_HOST'];
            $mail->SMTPAuth   = true;
            $mail->Username   = $_ENV['SMTP_USERNAME'];
            $mail->Password   = $_ENV['SMTP_PASSWORD'];
            $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Mã hóa TLS
            $mail->Port       = $_ENV['SMTP_PORT'];

            // 2. Thiết lập Người gửi & Người nhận
            $mail->setFrom($_ENV['SMTP_FROM'], $_ENV['SMTP_FROM_NAME']);
            $mail->addAddress($to);

            // 3. Nội dung Email
            $mail->isHTML(true); // Gửi mail định dạng HTML cho đẹp
            $mail->Subject = $subject;
            $mail->Body    = $body;
            $mail->CharSet = 'UTF-8'; // Đảm bảo tiếng Việt không bị lỗi font

            $mail->send();
            return true;

        } catch (Exception $e) {
            // Ghi log lỗi nếu không gửi được
            error_log("Mailer Error: " . $mail->ErrorInfo);
            return false;
        }
    }
}

4. Tích hợp vào Logic Quên mật khẩu

Bây giờ, tại PasswordController.php, chúng ta chỉ cần gọi hàm send với nội dung HTML "xịn xò" hơn một chút.

File: app/Controllers/PasswordController.php (Cập nhật hàm forgotPassword)

// ... bên trong hàm forgotPassword() sau khi tạo $token ...

$resetLink = "http://localhost:8000/reset-password?token=$token";

$htmlContent = "
    <div style='font-family: Arial, sans-serif; line-height: 1.6;'>
        <h2 style='color: #333;'>Yêu cầu đặt lại mật khẩu</h2>
        <p>Chào bạn,</p>
        <p>Chúng tôi nhận được yêu cầu đặt lại mật khẩu cho tài khoản của bạn. Vui lòng nhấn vào nút bên dưới để tiến hành:</p>
        <p style='text-align: center;'>
            <a href='$resetLink' style='background: #007bff; color: #fff; padding: 10px 20px; text-decoration: none; border-radius: 5px;'>Đặt lại mật khẩu</a>
        </p>
        <p>Nếu bạn không yêu cầu điều này, hãy bỏ qua email này.</p>
        <hr>
        <small>Đây là email tự động, vui lòng không phản hồi.</small>
    </div>
";

Mailer::send($data['email'], 'Xác nhận đặt lại mật khẩu', $htmlContent);

Response::json(['message' => 'Link reset mật khẩu đã được gửi vào Inbox của bạn!']);

5. Kiểm thử (Test thực tế)

Hãy thử gọi API bằng curl hoặc Postman với một email thật mà bạn có thể truy cập được:

curl -X POST http://localhost:8000/api/forgot-password \
  -H "Content-Type: application/json" \
  -d '{"email":"dia_chi_email_that@gmail.com"}'

Kết quả: Kiểm tra hòm thư của bạn (đừng quên kiểm tra cả mục Spam nhé!), bạn sẽ thấy một email với giao diện chuyên nghiệp vừa cập bến!

Những lưu ý "xương máu" khi gửi Mail Gmail App Password: Đây là lỗi 90% các bạn hay gặp. Phải bật xác thực 2 lớp rồi mới tạo được Mật khẩu ứng dụng.

Hosting & Firewall: Một số nhà cung cấp Hosting (như DigitalOcean hay Vultr) thường khóa cổng 25/587 mặc định. Bạn có thể cần liên hệ họ để mở hoặc dùng các dịch vụ như SendGrid, Mailgun.

Queue (Nâng cao): Gửi email tốn thời gian (thường mất 1-3 giây). Trong các dự án lớn như tại Hasaki, người ta thường dùng Queue (như Redis + Worker) để gửi ngầm, tránh làm người dùng phải chờ đợi lâu ở màn hình.

🚀 Tạm kết cho Phần 5 Vậy là hệ thống API của chúng ta đã có thể giao tiếp với thế giới bên ngoài thông qua Email. Một bước tiến lớn về tính thực tiễn!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí