Laravel 5.6:达到指定时间时给用户发送邮件的最佳实现方案
Hey there! Let's walk through the best way to implement this scheduled email feature in Laravel 5.6—since you've already dug into the docs, I'll focus on the practical, step-by-step implementation that ties task scheduling, queues, and emails together smoothly.
The core idea is straightforward:
- Set up a scheduled task that runs regularly to check for records where the
endtimestamp has arrived. - For each matching record, send an email to the associated user (using a queue to avoid blocking the scheduler).
- Mark records as processed to prevent duplicate emails.
1. Add a "processed" flag to your table
First, we need a way to track which records have already had their email sent—otherwise, the scheduler will keep sending the same email every minute. Create a migration to add a boolean field:
php artisan make:migration add_email_sent_to_your_table --table=your_table_name
Open the generated migration file and add the field:
public function up() { Schema::table('your_table_name', function (Blueprint $table) { $table->boolean('email_sent')->default(false); }); } public function down() { Schema::table('your_table_name', function (Blueprint $table) { $table->dropColumn('email_sent'); }); }
Run the migration:
php artisan migrate
2. Create a custom Artisan Command
We'll use a dedicated Artisan command to handle checking records and queuing emails. Generate the command:
php artisan make:command SendExpirationEmails
Open app/Console/Commands/SendExpirationEmails.php and update it with this logic:
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use App\YourModel; // Replace with your actual model name use App\Mail\ExpirationNotification; use Illuminate\Support\Facades\Mail; class SendExpirationEmails extends Command { protected $signature = 'emails:send-expirations'; protected $description = 'Send expiration emails to users when their end timestamp arrives'; public function __construct() { parent::__construct(); } public function handle() { // Fetch records where end time is now or past, and email hasn't been sent yet $expiredRecords = YourModel::where('end', '<=', now()) ->where('email_sent', false) ->get(); foreach ($expiredRecords as $record) { $user = $record->user; // Assuming you have a user() relationship defined in your model if ($user) { // Queue the email (instead of sending synchronously) Mail::to($user)->queue(new ExpirationNotification()); // Mark the record as processed $record->update(['email_sent' => true]); } } $this->info("Processed {$expiredRecords->count()} expiration records."); } }
3. Create the Email Notification Class
Generate a Mailable class for your fixed-content email:
php artisan make:mail ExpirationNotification
Open app/Mail/ExpirationNotification.php and customize the email content:
<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; use App\User; class ExpirationNotification extends Mailable { use Queueable, SerializesModels; public $user; public function __construct(User $user) { $this->user = $user; } public function build() { return $this->subject('Your Expiration Notice') ->view('emails.expiration'); // Create this Blade view for your email content } }
Create the Blade view at resources/views/emails/expiration.blade.php with your fixed email content:
<!DOCTYPE html> <html> <head> <title>Expiration Notice</title> </head> <body> <p>Hi {{ $user->name }},</p> <p>This is your fixed-content expiration email.</p> </body> </html>
4. Configure Queue (Optional but Recommended)
Using a queue ensures that sending emails doesn't slow down your scheduled task. For Laravel 5.6, let's use the database queue driver:
- First, create the queue tables:
php artisan queue:table php artisan migrate
- Update
.envto use the database queue:
QUEUE_DRIVER=database
- Start a queue worker to process the queued emails:
php artisan queue:work --daemon
Pro tip: Use a process manager like Supervisor to keep the queue worker running in production.
5. Schedule the Command
Register your command in app/Console/Kernel.php so it runs regularly:
protected function schedule(Schedule $schedule) { // Run the command every minute to check for expired records $schedule->command('emails:send-expirations')->everyMinute(); }
Finally, set up a Cron job on your server to run Laravel's scheduler every minute. Add this line to your Crontab (run crontab -e to edit):
* * * * * php /path/to/your/project/artisan schedule:run >> /dev/null 2>&1
- Timezone Consistency: Make sure Laravel's
APP_TIMEZONEin.envmatches the timezone used to store theendtimestamp in your database—otherwise, your expiration checks will be off. - Relationships: Ensure your model has a
user()relationship defined so you can easily fetch the associated user from the record. - Testing: You can test the command manually by running
php artisan emails:send-expirationsto make sure it works before relying on the scheduler.
内容的提问来源于stack exchange,提问作者pmiranda

