Laravel实现生日/纪念日短信发送及多规则配置技术问询
Got it, let's walk through how to implement birthday and anniversary SMS notifications in Laravel—including setting up multiple customizable trigger rules with advance send days and tailored content. This approach is scalable, maintainable, and follows Laravel best practices:
First, we need two key database structures: a modified users table to store date fields, and a dedicated table for SMS trigger rules.
Users Table Migration (Add Date Fields)
If your users table doesn't already have birthday/anniversary fields, run this migration:
Schema::table('users', function (Blueprint $table) { $table->date('birthday')->nullable(); $table->date('anniversary')->nullable(); // Adjust based on your use case (e.g., registration date, wedding anniversary) });
SMS Trigger Rules Table Migration
Create a table to store all your reusable trigger rules:
Schema::create('sms_trigger_rules', function (Blueprint $table) { $table->id(); $table->string('type'); // e.g., 'birthday' or 'anniversary' $table->integer('advance_days'); // Number of days to send before the event (0 = same day) $table->text('content'); // Template with variables like {{name}}, {{event_date}} $table->boolean('is_active')->default(true); $table->timestamps(); });
Define models to interact with our tables, plus helper methods to calculate trigger dates.
User Model
Add methods to compute when a user should receive SMS for each event type:
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Carbon\Carbon; class User extends Model { // Calculate the date we should send a birthday SMS (accounting for yearly recurrence) public function getBirthdayTriggerDate(int $advanceDays): ?Carbon { if (!$this->birthday) return null; $thisYearBirthday = Carbon::parse($this->birthday)->year(Carbon::now()->year); return $thisYearBirthday->subDays($advanceDays); } // Calculate trigger date for anniversaries (same logic applies) public function getAnniversaryTriggerDate(int $advanceDays): ?Carbon { if (!$this->anniversary) return null; $thisYearAnniversary = Carbon::parse($this->anniversary)->year(Carbon::now()->year); return $thisYearAnniversary->subDays($advanceDays); } }
SmsTriggerRule Model
Add a scope to easily filter active rules:
namespace App\Models; use Illuminate\Database\Eloquent\Model; class SmsTriggerRule extends Model { protected $fillable = ['type', 'advance_days', 'content', 'is_active']; // Scope to fetch only active rules public function scopeActive($query) { return $query->where('is_active', true); } }
Encapsulate your SMS provider logic in a reusable service class (works with Twilio, Aliyun SMS, or any provider):
namespace App\Services; class SmsService { public function send(string $phoneNumber, string $message): bool { // Replace this with your actual SMS provider API call // Example for Twilio: /* $twilio = new \Twilio\Rest\Client(env('TWILIO_SID'), env('TWILIO_AUTH_TOKEN')); $twilio->messages->create( $phoneNumber, [ 'from' => env('TWILIO_PHONE_NUMBER'), 'body' => $message ] ); */ // For testing, log the message instead of sending \Log::info("SMS sent to {$phoneNumber}: {$message}"); return true; } }
Don't forget to add your provider credentials to .env (e.g., TWILIO_SID, TWILIO_AUTH_TOKEN).
Create an Artisan command to check for users who need SMS each day, then schedule it to run automatically.
Generate the Command
Run:
php artisan make:command SendScheduledSms
Edit the Command Logic
namespace App\Console\Commands; use Illuminate\Console\Command; use App\Models\User; use App\Models\SmsTriggerRule; use App\Services\SmsService; use Carbon\Carbon; class SendScheduledSms extends Command { protected $signature = 'sms:send-scheduled'; protected $description = 'Send birthday/anniversary SMS based on active trigger rules'; public function handle(SmsService $smsService) { $today = Carbon::today(); $activeRules = SmsTriggerRule::active()->get(); foreach ($activeRules as $rule) { $users = $this->getEligibleUsers($rule, $today); foreach ($users as $user) { $personalizedMessage = $this->parseTemplate($rule->content, $user, $rule->type); $smsService->send($user->phone, $personalizedMessage); // Optional: Log sends to a `sms_logs` table for auditing // \App\Models\SmsLog::create(['user_id' => $user->id, 'rule_id' => $rule->id, 'message' => $personalizedMessage]); } } $this->info('Scheduled SMS sending completed successfully.'); return 0; } // Fetch users who should receive SMS for the current rule private function getEligibleUsers(SmsTriggerRule $rule, Carbon $today) { $targetDate = $today->copy()->addDays($rule->advance_days); return User::whereNotNull($rule->type) ->whereRaw( "DATE_FORMAT({$rule->type}, '%m-%d') = DATE_FORMAT(?, '%m-%d')", [$targetDate->toDateString()] ) // Optional: Add an opt-out check // ->where('receive_marketing_sms', true) ->get(); } // Replace template variables with user-specific data private function parseTemplate(string $template, User $user, string $eventType): string { $eventDate = Carbon::parse($user->{$eventType})->format('Y年m月d日'); // Adjust format to match your locale return str_replace( ['{{name}}', '{{event_date}}'], [$user->name, $eventDate], $template ); } }
Schedule the Command
Add this to app/Console/Kernel.php to run the task daily at 1 AM:
protected function schedule(Schedule $schedule) { $schedule->command('sms:send-scheduled')->dailyAt('01:00'); }
Build a simple CRUD interface to create/edit/delete trigger rules. Here's a quick controller example:
namespace App\Http\Controllers; use App\Models\SmsTriggerRule; use Illuminate\Http\Request; class SmsTriggerRuleController extends Controller { public function index() { $rules = SmsTriggerRule::all(); return view('sms-rules.index', compact('rules')); } public function create() { return view('sms-rules.create'); } public function store(Request $request) { $validated = $request->validate([ 'type' => 'required|in:birthday,anniversary', 'advance_days' => 'required|integer|min:0', 'content' => 'required|string', 'is_active' => 'boolean' ]); SmsTriggerRule::create($validated); return redirect()->route('sms-rules.index')->with('success', 'Rule created successfully!'); } // Add edit/update/destroy methods following the same pattern }
Create a Blade view for creating rules (example snippet):
<form method="POST" action="{{ route('sms-rules.store') }}"> @csrf <div class="mb-3"> <label class="form-label">Event Type</label> <select class="form-control" name="type"> <option value="birthday">Birthday</option> <option value="anniversary">Anniversary</option> </select> </div> <div class="mb-3"> <label class="form-label">Send Advance Days</label> <input type="number" class="form-control" name="advance_days" min="0" value="0"> </div> <div class="mb-3"> <label class="form-label">SMS Template</label> <textarea class="form-control" name="content">Hi {{name}}, we're excited to wish you a happy {{event_date}}! 🎉</textarea> <small class="text-muted">Available variables: {{name}}, {{event_date}}</small> </div> <div class="mb-3"> <input type="checkbox" name="is_active" checked> <label class="form-check-label">Active</label> </div> <button type="submit" class="btn btn-primary">Create Rule</button> </form>
- Leap Year Handling: For users born on Feb 29, adjust your
getEligibleUsersmethod to send on Feb 28 or March 1 in non-leap years. - Queue SMS Sends: Wrap the SMS sending logic in a Laravel Job to handle rate limits and retries automatically.
- Time Zones: Ensure your app uses the correct timezone in
config/app.phpto avoid sending SMS at odd hours. - Opt-Out: Add a
receive_marketing_smsboolean field to the users table and filter eligible users accordingly.
内容的提问来源于stack exchange,提问作者Falgun Atara

