本文目录导读:

- 使用
str_replace简单替换 - 使用正则表达式批量替换
- 使用 Laravel Str::replaceArray
- 创建独立的短信模板类
- 使用数据库存储模板
- 完整的 send 服务示例
- 控制器中使用
- 单元测试
- 推荐的实现方式
在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]);