Laravel 9中如何编写可复用的占位符文本替换通用函数/文件
邮件编辑器通用占位符动态替换方案需求与实现思路
需求背景
我正在开发一个邮件内容编辑器项目,需要将编辑器中的默认占位符替换为数据库存储的用户数据。编辑器部分已完成,但不知道如何编写可在全项目复用的通用函数/文件,实现占位符与关联模型动态值的替换。
占位符示例
原始内容:
"Hello {customer_name}, This is your content of the day {content_date}"
替换后效果:
"Hello Mano, This is your content of the day 3-Feb-2023"
注意:项目采用一对多关联模型获取内容数据,实现时需遵循该结构。
现有实现及痛点
目前已通过硬编码str_replace实现替换,以下两段代码均可正常运行,但每次新增或修改占位符都需修改业务代码,无法集中维护:
示例代码1
$contents = EmailContent::all(); foreach($contents as $inv) { $message = $inv->email_content; $message = str_replace('{customer_name}' , $inv->customer->name, $message); $message = str_replace('{content_date}', $inv->content_date, $message); }
示例代码2
$contents = EmailContent::all(); foreach($contents as $inv) { $userData = User::find($inv->user_id); $message = $inv->email_content; $message = str_replace('{user_fullname}', $userData->fullname, $emailBody); $message = str_replace('{user_company_name}' , $userData->company_name, $emailBody); }
期望实现
希望将所有占位符的替换规则集中管理在公共位置,后续新增或更新占位符时无需修改业务代码,最终需实现的替换逻辑如下:
$message = str_replace('{customer_name}' , $inv->customer->name, $message); $message = str_replace('{content_date}', $inv->content_date, $message); $message = str_replace('{user_fullname}', $userData->fullname, $emailBody); $message = str_replace('{user_company_name}' , $userData->company_name, $emailBody); // return $message; // or // echo $message;
可行实现方案
方案1:配置文件+辅助函数(推荐)
- 创建占位符配置文件
在config/placeholders.php中集中定义占位符和对应的取值逻辑:
return [ '{customer_name}' => function($data) { return $data['inv']->customer->name; }, '{content_date}' => function($data) { return $data['inv']->content_date; }, '{user_fullname}' => function($data) { return $data['user']->fullname; }, '{user_company_name}' => function($data) { return $data['user']->company_name; }, ];
- 编写通用替换辅助函数
在app/helpers.php中实现核心替换逻辑:
function replace_placeholders($content, $data) { $placeholders = config('placeholders'); foreach ($placeholders as $placeholder => $callback) { $content = str_replace($placeholder, $callback($data), $content); } return $content; }
- 业务代码中调用
$contents = EmailContent::all(); foreach($contents as $inv) { $userData = User::find($inv->user_id); $message = replace_placeholders($inv->email_content, [ 'inv' => $inv, 'user' => $userData ]); // 使用替换后的$message }
方案2:专用服务类封装
创建app/Services/PlaceholderReplacer.php类,封装所有替换逻辑:
namespace App\Services; class PlaceholderReplacer { protected $placeholders = [ '{customer_name}' => 'getCustomerName', '{content_date}' => 'getContentDate', '{user_fullname}' => 'getUserFullname', '{user_company_name}' => 'getUserCompanyName', ]; protected $data; public function __construct($data) { $this->data = $data; } public function replace($content) { foreach ($this->placeholders as $placeholder => $method) { $content = str_replace($placeholder, $this->$method(), $content); } return $content; } protected function getCustomerName() { return $this->data['inv']->customer->name; } protected function getContentDate() { return $this->data['inv']->content_date; } protected function getUserFullname() { return $this->data['user']->fullname; } protected function getUserCompanyName() { return $this->data['user']->company_name; } }
业务代码调用:
$contents = EmailContent::all(); foreach($contents as $inv) { $userData = User::find($inv->user_id); $replacer = new \App\Services\PlaceholderReplacer([ 'inv' => $inv, 'user' => $userData ]); $message = $replacer->replace($inv->email_content); // 使用替换后的$message }
方案3:模型访问器(仅适用于EmailContent场景)
如果替换逻辑仅和EmailContent模型绑定,可在模型中定义访问器:
namespace App\Models; use Illuminate\Database\Eloquent\Model; class EmailContent extends Model { public function getReplacedContentAttribute() { $content = $this->email_content; $userData = User::find($this->user_id); $replacements = [ '{customer_name}' => $this->customer->name, '{content_date}' => $this->content_date, '{user_fullname}' => $userData->fullname, '{user_company_name}' => $userData->company_name, ]; return str_replace(array_keys($replacements), array_values($replacements), $content); } }
调用时直接获取属性:
$contents = EmailContent::all(); foreach($contents as $inv) { $message = $inv->replaced_content; // 使用替换后的$message }
内容的提问来源于stack exchange,提问作者Mano Mahe
相关产品推荐
相关产品推荐

