Files
portal-api/app/Services/Email/EmailTemplateRenderer.php
T
2026-10-05 09:30:24 +07:00

67 lines
2.5 KiB
PHP

<?php
namespace App\Services\Email;
use App\Exceptions\InvalidTemplateVariableException;
use App\Models\Template;
use App\Support\MailPlaceholders;
/**
* Render Template subject/message: chỉ thay thế placeholder dạng {{key}} đã khai báo
* trong metadata.variables. Không eval(), không compile Blade/PHP.
*/
class EmailTemplateRenderer
{
private const PLACEHOLDER_PATTERN = MailPlaceholders::PATTERN;
/**
* @return array{subject: string, html: string}
*/
public function render(Template $template, array $data): array
{
$variables = $template->variables();
$this->assertKnownVariables($template->scenario ?? $template->name ?? 'unknown', (string) $template->subject, $variables);
$this->assertKnownVariables($template->scenario ?? $template->name ?? 'unknown', (string) $template->message, $variables);
return [
'subject' => $this->replace((string) $template->subject, $variables, $data),
'html' => $this->replace((string) $template->message, $variables, $data),
];
}
private function assertKnownVariables(string $scenario, string $content, array $variables): void
{
$used = MailPlaceholders::extract($content);
$invalid = array_values(array_diff($used, array_keys($variables)));
if ($invalid !== []) {
throw new InvalidTemplateVariableException($scenario, $invalid);
}
}
private function replace(string $content, array $variables, array $data): string
{
return preg_replace_callback(self::PLACEHOLDER_PATTERN, function (array $match) use ($variables, $data) {
$key = $match[1];
if (!isset($variables[$key]) && !array_key_exists($key, $data)) {
return $match[0];
}
$definition = $variables[$key] ?? [];
$value = $data[$key] ?? $definition['example'] ?? '';
return $this->formatValue((string) $value, $definition['type'] ?? 'string');
}, $content) ?? $content;
}
private function formatValue(string $value, string $type): string
{
return match ($type) {
// html: nội dung tin cậy do Email Builder sinh ra sẵn (đã được builder escape khi build).
'html' => $value,
'url' => filter_var($value, FILTER_VALIDATE_URL) ? htmlspecialchars($value, ENT_QUOTES, 'UTF-8') : '',
default => htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),
};
}
}