Files
2026-10-05 09:30:24 +07:00

66 lines
2.5 KiB
PHP

<?php
namespace App\Services\Email;
use App\Models\Template;
use App\Support\EmailScenarioRegistry;
use Illuminate\Http\Exceptions\HttpResponseException;
final class TemplateGuard
{
public function assertCanSave(Template $template): void
{
if ($template->exists) {
$this->assertKeepsRegisteredIdentity($template);
}
$definition = EmailScenarioRegistry::describe($template->key, $template->scenario);
if (!$definition['registered']) {
return;
}
$duplicate = Template::query()
->whereKeyNot($template->id)
->where(fn ($q) => $q->where('key', $definition['scenario'])->orWhere('scenario', $definition['scenario']))
->exists();
if ($duplicate) {
$this->reject("Scenario \"{$definition['scenario']}\" đã có template, không tạo thêm được.");
}
if (!$definition['permissions']['disable'] && (int) $template->status !== 1) {
$this->reject('Template hệ thống này không được tắt: code đang gửi email theo template này.');
}
$missing = EmailScenarioRegistry::missingVariables($definition['required_variables'], $template->subject, $template->message);
if ($missing !== []) {
$this->reject(
'Template thiếu biến bắt buộc: ' . implode(', ', array_map(fn ($key) => '{{$' . $key . '}}', $missing)),
['missing_variables' => $missing],
);
}
}
public function assertCanDelete(Template $template): void
{
$definition = EmailScenarioRegistry::describe($template->key, $template->scenario);
if (!$definition['permissions']['delete']) {
$this->reject('Template hệ thống không được xoá: code đang gửi email theo template này.');
}
}
private function assertKeepsRegisteredIdentity(Template $template): void
{
$before = EmailScenarioRegistry::describe($template->getOriginal('key'), $template->getOriginal('scenario'));
if ($before['registered'] && !$before['permissions']['change_key'] && $template->isDirty(['key', 'scenario'])) {
$this->reject("Không được đổi key/scenario của template hệ thống \"{$before['scenario']}\".");
}
}
private function reject(string $message, array $extra = []): never
{
throw new HttpResponseException(response()->json(['error' => true, 'message' => $message, ...$extra], 422));
}
}