PHP项目Laravel短信模板变量替换

wen PHP项目 3

本文目录导读:

PHP项目Laravel短信模板变量替换

  1. 使用 str_replace 简单替换
  2. 使用正则表达式批量替换
  3. 使用 Laravel Str::replaceArray
  4. 创建独立的短信模板类
  5. 使用数据库存储模板
  6. 完整的 send 服务示例
  7. 控制器中使用
  8. 单元测试
  9. 推荐的实现方式

在Laravel中实现短信模板变量替换,我推荐以下几种方法:

使用 str_replace 简单替换

<?php
namespace App\Services;
class SmsService
{
    /**
     * 短信模板变量替换
     */
    public function replaceTemplateVariables(string $template, array $variables): string
    {
        foreach ($variables as $key => $value) {
            $template = str_replace('{' . $key . '}', $value, $template);
        }
        return $template;
    }
    // 使用示例
    public function sendVerificationCode(string $phone, string $code): void
    {
        $template = '您的验证码是{code},{minutes}分钟内有效。';
        $variables = [
            'code' => $code,
            'minutes' => 5
        ];
        $message = $this->replaceTemplateVariables($template, $variables);
        // 发送短信逻辑
    }
}

使用正则表达式批量替换

<?php
namespace App\Services;
class SmsTemplateService
{
    /**
     * 正则替换模板变量
     */
    public function replaceVariables(string $template, array $variables): string
    {
        return preg_replace_callback(
            '/\{(\w+)\}/',
            function($matches) use ($variables) {
                $key = $matches[1];
                return isset($variables[$key]) ? $variables[$key] : $matches[0];
            },
            $template
        );
    }
}

使用 Laravel Str::replaceArray

<?php
namespace App\Services;
use Illuminate\Support\Str;
class SmsService
{
    public function send(string $template, array $variables): string
    {
        // 方法一:使用 Str::replaceArray
        $keys = array_map(fn($key) => '{' . $key . '}', array_keys($variables));
        $message = Str::replaceArray($template, $keys, array_values($variables));
        // 方法二:使用 Str::replace
        foreach ($variables as $key => $value) {
            $template = Str::replace('{' . $key . '}', $value, $template);
        }
        return $template;
    }
}

创建独立的短信模板类

<?php
namespace App\Services\Sms;
class SmsTemplate
{
    /**
     * 模板内容
     */
    protected string $content;
    /**
     * 模板变量
     */
    protected array $variables = [];
    /**
     * 可用的短信模板
     */
    const TEMPLATE_LOGIN_CODE = '您的登录验证码是{code},请勿泄露。';
    const TEMPLATE_REGISTER = '欢迎注册,您的验证码为{code},有效期{minutes}分钟。';
    const TEMPLATE_ORDER = '您的订单{order_no}已支付成功,金额{amount}元。';
    public function __construct(string $template)
    {
        $this->content = $template;
    }
    public function with(array $variables): self
    {
        $this->variables = $variables;
        return $this;
    }
    public function render(): string
    {
        $content = $this->content;
        foreach ($this->variables as $key => $value) {
            $content = str_replace('{' . $key . '}', $value, $content);
        }
        return $content;
    }
    // 快捷方法
    public static function loginCode(string $code): string
    {
        return (new self(self::TEMPLATE_LOGIN_CODE))
            ->with(['code' => $code])
            ->render();
    }
    public static function order(string $orderNo, float $amount): string
    {
        return (new self(self::TEMPLATE_ORDER))
            ->with([
                'order_no' => $orderNo,
                'amount' => number_format($amount, 2)
            ])
            ->render();
    }
}

使用数据库存储模板

<?php
namespace App\Services;
use App\Models\SmsTemplate;
use Illuminate\Support\Facades\Cache;
class SmsTemplateService
{
    /**
     * 从数据库获取模板并渲染
     */
    public function renderByCode(string $code, array $variables): string
    {
        // 缓存模板
        $template = Cache::remember("sms_template_{$code}", 3600, function() use ($code) {
            return SmsTemplate::where('code', $code)->first();
        });
        if (!$template) {
            throw new \Exception("短信模板不存在: {$code}");
        }
        // 替换变量
        $content = $template->content;
        foreach ($variables as $key => $value) {
            $content = str_replace('{' . $key . '}', $value, $content);
        }
        return $content;
    }
}
// 数据库迁移示例
Schema::create('sms_templates', function (Blueprint $table) {
    $table->id();
    $table->string('code')->unique();
    $table->text('content');
    $table->string('description')->nullable();
    $table->timestamps();
});

完整的 send 服务示例

<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class SmsService
{
    protected $apiUrl;
    protected $appKey;
    protected $appSecret;
    public function __construct()
    {
        $this->apiUrl = config('services.sms.api_url');
        $this->appKey = config('services.sms.app_key');
        $this->appSecret = config('services.sms.app_secret');
    }
    /**
     * 发送验证码
     */
    public function sendVerificationCode(string $phone, string $code, int $minutes = 5): bool
    {
        $template = '验证码:{code},{minutes}分钟内有效,请勿泄露给他人。';
        $content = $this->renderTemplate($template, [
            'code' => $code,
            'minutes' => $minutes
        ]);
        return $this->send($phone, $content);
    }
    /**
     * 发送订单通知
     */
    public function sendOrderNotification(string $phone, array $orderData): bool
    {
        $template = '您的订单{order_no}已支付成功,支付金额¥{amount}元。';
        $content = $this->renderTemplate($template, [
            'order_no' => $orderData['order_no'],
            'amount' => $orderData['amount']
        ]);
        return $this->send($phone, $content);
    }
    /**
     * 渲染模板
     */
    protected function renderTemplate(string $template, array $variables): string
    {
        $search = array_map(fn($key) => '{' . $key . '}', array_keys($variables));
        $replace = array_values($variables);
        return str_replace($search, $replace, $template);
    }
    /**
     * 实际发送短信
     */
    protected function send(string $phone, string $content): bool
    {
        $response = Http::post($this->apiUrl, [
            'phone' => $phone,
            'content' => $content,
            'app_key' => $this->appKey,
            'timestamp' => time(),
            'sign' => $this->generateSign($phone, $content)
        ]);
        if ($response->successful()) {
            Log::info('短信发送成功', [
                'phone' => $phone,
                'content' => $content
            ]);
            return true;
        }
        Log::error('短信发送失败', [
            'phone' => $phone,
            'content' => $content,
            'response' => $response->json()
        ]);
        return false;
    }
    /**
     * 生成签名
     */
    protected function generateSign(string $phone, string $content): string
    {
        $params = [
            'phone' => $phone,
            'content' => $content,
            'timestamp' => time()
        ];
        ksort($params);
        $str = http_build_query($params) . $this->appSecret;
        return md5($str);
    }
}

控制器中使用

<?php
namespace App\Http\Controllers;
use App\Services\SmsService;
use Illuminate\Http\Request;
class SmsController extends Controller
{
    protected $smsService;
    public function __construct(SmsService $smsService)
    {
        $this->smsService = $smsService;
    }
    public function sendCode(Request $request)
    {
        $request->validate([
            'phone' => 'required|phone'
        ]);
        $code = random_int(100000, 999999);
        // 保存验证码到 session 或 cache
        cache()->put('sms_code_' . $request->phone, $code, now()->addMinutes(5));
        $result = $this->smsService->sendVerificationCode(
            $request->phone, 
            $code
        );
        return response()->json([
            'success' => $result
        ]);
    }
}

单元测试

<?php
namespace Tests\Feature;
use Tests\TestCase;
class SmsServiceTest extends TestCase
{
    public function test_template_replacement()
    {
        $service = new \App\Services\SmsService();
        $template = '您的验证码是{code},{minutes}分钟内有效。';
        $result = $this->invokePrivateMethod($service, 'renderTemplate', [
            $template,
            ['code' => '123456', 'minutes' => 5]
        ]);
        $this->assertEquals('您的验证码是123456,5分钟内有效。', $result);
    }
    protected function invokePrivateMethod($object, $methodName, $parameters = [])
    {
        $reflection = new \ReflectionClass($object);
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);
        return $method->invokeArgs($object, $parameters);
    }
}

推荐的实现方式

如果项目简单,推荐使用方式1(简单数组替换);如果项目复杂且需要管理多个模板,推荐使用方式4(独立模板类)或方式5(数据库存储模板),这样更好维护和扩展。

使用 Laravel 的 翻译函数也可以实现类似功能:

__('sms.verification_code', ['code' => $code, 'minutes' => 5]);

抱歉,评论功能暂时关闭!