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)); } }