Files
portal-api/app/Models/Template.php
T
2026-10-05 09:30:24 +07:00

126 lines
3.9 KiB
PHP

<?php
namespace App\Models;
use App\Support\EmailTemplateShell;
use App\Support\MailGlobals;
use App\Support\MailPlaceholders;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Template extends Model
{
use HasFactory;
public const CACHE_PREFIX = 'email_template:';
protected $table = 'template';
protected $fillable = [
'type_id',
'scenario',
'key',
'format',
'invoice_template_id',
'name',
'subject',
'message',
'metadata',
'builder_data',
'status',
];
protected $casts = [
'status' => 'integer',
'metadata' => 'array',
'builder_data' => 'array',
];
protected static function booted(): void
{
static::saved(fn (self $template) => $template->forgetCache());
static::deleted(fn (self $template) => $template->forgetCache());
}
public function forgetCache(): void
{
if ($this->scenario) {
Cache::forget(self::CACHE_PREFIX . $this->scenario);
}
if ($this->key) {
Cache::forget(self::CACHE_PREFIX . $this->key);
}
}
/** Danh sách variable hợp lệ khai báo trong metadata.variables, key theo variable key. */
public function variables(): array
{
return collect($this->metadata['variables'] ?? [])->keyBy('key')->all();
}
/**
* @param array<int, array{key: string, label?: string|null, example?: string|null}> $overrides
*/
public function syncVariables(array $overrides = []): void
{
$declared = $this->variables();
$used = MailPlaceholders::extract($this->subject, $this->message);
$overrides = collect($overrides)->keyBy('key');
$variables = collect(array_unique([...array_keys($declared), ...$used]))
->reject(fn (string $key) => ($declared[$key]['custom'] ?? false) && !in_array($key, $used, true))
->map(function (string $key) use ($declared, $overrides) {
$variable = $declared[$key] ?? ['key' => $key, 'label' => MailGlobals::LABELS[$key] ?? $key, 'type' => 'string', 'custom' => true];
$override = $overrides->get($key, []);
if (filled($override['label'] ?? null)) {
$variable['label'] = trim($override['label']);
}
if (array_key_exists('example', $override)) {
$variable['example'] = (string) $override['example'];
}
return $variable;
})
->values()
->all();
$this->metadata = [...($this->metadata ?? []), 'variables' => $variables];
}
/** Mail sender config khai báo trong metadata.mail (from_email, from_name, reply_to, reply_to_name, cc, bcc). */
public function mailConfig(): array
{
return $this->metadata['mail'] ?? [];
}
public function invoiceTemplate()
{
return $this->belongsTo(self::class, 'invoice_template_id');
}
/**
* Wrap $contentHtml with the admin-editable "layout" template (scenario=layout),
* replacing its {{$content}} placeholder.
*
* Không còn fallback về blade: mọi email đều render từ tbl.template. Thiếu layout thì
* dùng khung header/footer chuẩn trong code thay vì gửi ra nội dung trần.
*/
public static function renderWithLayout(?string $subject, string $contentHtml): string
{
$layout = self::where('scenario', 'layout')->where('status', 1)->first();
$body = $layout && $layout->message
? str_replace('{{$content}}', $contentHtml, $layout->message)
: EmailTemplateShell::wrap(EmailTemplateShell::body($contentHtml));
return EmailTemplateShell::document($subject, $body);
}
public function type()
{
return $this->belongsTo(\App\Models\Type::class);
}
}