Series Laravel Email & Mailtrap #2: Truyền dữ liệu động và Đính kèm hóa đơn
1. Truyền dữ liệu vào Mailable qua Constructor
Để truyền dữ liệu từ bên ngoài vào trong Email, chúng ta sẽ sử dụng hàm khởi tạo __construct() của class WelcomeEmail.
Một tính năng cực kỳ thông minh của Laravel là: Bất kỳ thuộc tính nào được khai báo là public trong class Mailable sẽ tự động có mặt bên trong file Blade View. Bạn không cần phải dùng hàm with() rườm rà như khi return view ở Controller.
Hãy mở lại file app/Mail/WelcomeEmail.php và độ lại nó:
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailables\Attachment; // Thêm thư viện đính kèm
class WelcomeEmail extends Mailable
{
use Queueable, SerializesModels;
// Khai báo biến public. Blade sẽ tự động hiểu biến $customerData
public array $customerData;
/**
* Nhận dữ liệu từ Controller truyền vào
*/
public function __construct(array $data)
{
$this->customerData = $data;
}
public function envelope(): Envelope
{
return new Envelope(
// Tiêu đề email cũng có thể gắn dữ liệu động!
subject: 'Vé điện tử của bạn, ' . $this->customerData['name'] . '!',
);
}
public function content(): Content
{
return new Content(
view: 'emails.welcome',
);
}
}
2. Cập nhật Blade View để hứng dữ liệu
Bây giờ, biến $customerData đã sẵn sàng trong view. Hãy mở file resources/views/emails/welcome.blade.php và sử dụng cú pháp {{ }} quen thuộc của Blade để in dữ liệu ra:
<!DOCTYPE html>
<html>
<head>
<title>Vé điện tử AFC Metro</title>
<style>
.highlight { color: #d32f2f; font-weight: bold; }
.box { border: 1px solid #ccc; padding: 15px; margin-top: 20px; }
</style>
</head>
<body>
<h1>Xin chào {{ $customerData['name'] }}!</h1>
<p>Cảm ơn bạn đã sử dụng dịch vụ của tuyến Metro. Dưới đây là thông tin chuyến đi của bạn:</p>
<div class="box">
<ul>
<li><strong>Ga khởi hành:</strong> {{ $customerData['station'] }}</li>
<li><strong>Số tiền:</strong> <span class="highlight">{{ number_format($customerData['price']) }} VNĐ</span></li>
<li><strong>Mã vé:</strong> {{ $customerData['ticket_code'] }}</li>
</ul>
</div>
<p>Vui lòng xem file hướng dẫn đính kèm bên dưới để biết cách quẹt vé tại cổng (Turnstile).</p>
</body>
</html>
3. Đính kèm File (Attachments)
Quay lại file WelcomeEmail.php, chúng ta sẽ khai thác hàm attachments() mà Laravel đã tạo sẵn ở dưới cùng của class.
Giả sử bạn có một file PDF tên là huong-dan-quet-the.pdf đang nằm trong thư mục storage/app/public/.
/**
* Lấy danh sách các file đính kèm cho email.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
// Đính kèm file từ storage và đổi tên file khi khách hàng tải về
Attachment::fromStorageDisk('public', 'huong-dan-quet-the.pdf')
->as('Huong_Dan_Su_Dung_Metro.pdf')
->withMime('application/pdf'),
];
}
Lưu ý: Bạn nhớ ném một file PDF bất kỳ vào thư mục storage/app/public/ để test nhé.
4. Bắn thử Email động từ Route/Controller
Mọi mảnh ghép đã hoàn thiện. Giờ ta chỉ việc truyền một mảng dữ liệu vào khi khởi tạo new WelcomeEmail(...).
Mở routes/web.php và sửa lại logic test:
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
use Illuminate\Support\Str;
Route::get('/test-email', function () {
// 1. Giả lập dữ liệu truy vấn được từ Database
$data = [
'name' => 'Hiếu Nguyễn',
'station' => 'Ga Bến Thành',
'price' => 15000,
'ticket_code' => strtoupper(Str::random(8)) // Random mã 8 ký tự
];
// 2. Truyền dữ liệu vào Mailable và bắn đi
Mail::to('khachhang_test@gmail.com')->send(new WelcomeEmail($data));
return 'Email động kèm file đã được gửi thành công vào Mailtrap!';
});
Tổng kết Bài 2
Bây giờ, khi bạn F5 lại đường dẫn /test-email và mở Dashboard của Mailtrap, bạn sẽ thấy một sự lột xác hoàn toàn. Email không còn khô khan nữa mà đã đích danh gọi tên khách hàng, thông số tiền bạc được format chuẩn xác, tiêu đề linh hoạt, và đặc biệt là có một kẹp ghim (Attachment) chứa file PDF sẵn sàng để tải xuống.
Nhờ việc đóng gói gọn gàng mọi logic xử lý data, template và file đính kèm vào chung một class Mailable, các Controller/Service của bạn sẽ giữ được trạng thái Clean Code tối đa. Khi gọi gửi mail, nó chỉ chiếm đúng 1 dòng code duy nhất!
All rights reserved