You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

1. Database Setup

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();
});
2. Model Definitions

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);
    }
}
3. SMS Service Integration

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).

4. Daily Scheduled Task

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');
}
5. Manage SMS Rules via Admin Interface

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>
6. Edge Cases & Pro Tips
  • Leap Year Handling: For users born on Feb 29, adjust your getEligibleUsers method 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.php to avoid sending SMS at odd hours.
  • Opt-Out: Add a receive_marketing_sms boolean field to the users table and filter eligible users accordingly.

内容的提问来源于stack exchange,提问作者Falgun Atara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:21:17