Laravel项目中,如何基于MySQL促销表Datetime通过Firebase发推送通知
Hey Leo, great question! Let's walk through exactly how to build this promotion push notification system using Laravel, Firebase Cloud Messaging (FCM), and Cron jobs—here's a step-by-step guide tailored to your needs:
First, we'll integrate FCM into your Laravel project using the official Kreait Firebase package (it's well-maintained and plays nicely with Laravel):
- Install the package via Composer:
composer require kreait/laravel-firebase - Publish the Firebase config file to customize settings:
php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" - Download your Firebase service account JSON file from the Firebase Console, then update
config/firebase.phpto point to this file (set thecredentialspath to your JSON file's location).
Assuming your promotions table already has a start_time field, we'll add a flag to track if we've sent the notification (to avoid duplicates):
- Add an
is_notifiedboolean column to yourpromotionstable (use a migration):
Then update the migration file:php artisan make:migration add_is_notified_to_promotions_tablepublic function up() { Schema::table('promotions', function (Blueprint $table) { $table->boolean('is_notified')->default(false); }); } - Run the migration:
php artisan migrate - Update your
Promotionmodel to cast the date and boolean fields:namespace App\Models; use Illuminate\Database\Eloquent\Model; class Promotion extends Model { protected $fillable = ['name', 'start_time', 'is_notified']; protected $casts = [ 'start_time' => 'datetime', 'is_notified' => 'boolean', ]; }
Also, ensure your users table has a device_token field to store the FCM tokens from Android/iOS users.
We'll build a Laravel console command that checks for promotions starting now and sends push notifications:
- Generate the command:
php artisan make:command SendPromotionNotifications - Open the generated file at
app/Console/Commands/SendPromotionNotifications.phpand update it with this logic:namespace App\Console\Commands; use App\Models\Promotion; use Illuminate\Console\Command; use Kreait\Firebase\Messaging\CloudMessage; use Kreait\Firebase\Messaging\Notification; class SendPromotionNotifications extends Command { protected $signature = 'promotions:send-notifications'; protected $description = 'Send push notifications for promotions that are starting now'; public function handle() { // Grab promotions that started in the last minute (to account for Cron timing) and haven't been notified $pendingPromotions = Promotion::where('start_time', '<=', now()) ->where('start_time', '>', now()->subMinute()) ->where('is_notified', false) ->get(); if ($pendingPromotions->isEmpty()) { $this->info('No pending promotion notifications to send.'); return; } $messaging = app('firebase.messaging'); foreach ($pendingPromotions as $promotion) { // Get all valid user device tokens (adjust this query to match your user model logic) $deviceTokens = \App\Models\User::whereNotNull('device_token') ->pluck('device_token') ->toArray(); if (empty($deviceTokens)) { $this->warn("No device tokens found for promotion: {$promotion->name}"); continue; } // Build the notification content $notification = Notification::create( 'New Promotion Live!', "Don't miss out on our latest promotion: {$promotion->name}" ); // Create the FCM message with optional data for your app $message = CloudMessage::new() ->withNotification($notification) ->withData([ 'promotion_id' => $promotion->id, 'promotion_name' => $promotion->name, // Add any other data your mobile apps need to handle the notification ]); try { // Send the notification to all users in one batch $messaging->sendMulticast($message, $deviceTokens); // Mark the promotion as notified to prevent duplicate sends $promotion->update(['is_notified' => true]); $this->info("Successfully sent notifications for promotion: {$promotion->name}"); } catch (\Exception $e) { $this->error("Failed to send notifications for {$promotion->name}: {$e->getMessage()}"); } } } }
To run our command automatically, we'll set up Laravel's scheduler and server Cron:
- First, add this line to your server's Crontab (run
crontab -eto edit it):
Replace* * * * * cd /path-to-your-laravel-project && php artisan schedule:run >> /dev/null 2>&1/path-to-your-laravel-projectwith the actual path to your project. - Then, register the command in
app/Console/Kernel.phpso the scheduler knows about it:protected function schedule(Schedule $schedule) { // Run the command every minute to check for starting promotions $schedule->command('promotions:send-notifications')->everyMinute(); }
We use everyMinute() because Cron can't run more frequently than once per minute—this ensures we catch promotions as they start without delay.
- For Android/iOS apps, make sure they're configured with FCM: Android uses the
google-services.jsonfile, iOS uses theGoogleService-Info.plistfile from Firebase Console. - When users open your app, fetch their FCM token and send it to your Laravel backend to store in the
users.device_tokenfield. - For iOS, don't forget to enable push notifications in your Apple Developer account and upload your APNs certificate to Firebase Console.
- Prevent Duplicates: The
is_notifiedflag is non-negotiable—it ensures we don't resend notifications for the same promotion in future Cron runs. - Error Handling: The try/catch block lets the process continue even if some tokens fail (e.g., expired tokens), and logs errors for debugging.
- Time Zone Consistency: Ensure your Laravel app's time zone (set in
config/app.php) matches the time zone used in your MySQLstart_timevalues. - Batch Sending: Using
sendMulticastis far more efficient than sending individual messages, especially with large user bases.
内容的提问来源于stack exchange,提问作者Leo Lee

