File: /home/xedaptot/ai.naniguide.com/app/Model/MailListTemplate.php
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use App\Library\Contracts\TemplateSubjectInterface;
use App\Library\GeneralTagResolver;
use App\Library\MailListTemplateTagResolver;
use App\Library\Traits\HasUid;
use League\Pipeline\PipelineBuilder;
use App\Library\HtmlHandler\GenerateSpintax;
use App\Library\HtmlHandler\DecodeHtmlSpecialChars;
use App\Library\HtmlHandler\ReplaceBareLineFeed;
use App\Library\HtmlHandler\TransformTag;
use App\Library\HtmlHandler\TransformUrl;
use App\Library\HtmlHandler\MakeInlineCss;
use App\Services\TemplateService;
/**
* @property \App\Model\MailList|null $mailList
* @property \App\Model\Template|null $template
* @property string $alias
*/
class MailListTemplate extends Model implements TemplateSubjectInterface
{
use HasUid;
public static function boot()
{
parent::boot();
// When a MailListTemplate is deleted, also delete its associated Template record
static::deleting(function ($item) {
if ($item->template) {
$item->template->deleteAndCleanup();
}
});
}
/**
* Associations.
*
* @var object | collect
*/
public function mailList()
{
return $this->belongsTo(MailList::class);
}
public function template()
{
return $this->belongsTo(Template::class);
}
/**
* Render content.
*
* Tag resolution flows through TagResolverInterface: each chained resolver
* contributes a tag namespace, later resolvers override earlier ones.
* Templates use Twig syntax ({{ TAG }} / {{ list.name }} — both casings work).
*
* @var html
*/
public function renderContent($values = null, $subscriber = null, $messageId = null)
{
return (new PipelineBuilder())
->add(new TransformTag(...$this->buildResolvers($values, $subscriber, $messageId)))
->add(new TransformUrl($msgId = null, $domain = null, $trackLink = false))
->add(new DecodeHtmlSpecialChars())
->add(new GenerateSpintax())
->add(new ReplaceBareLineFeed())
->build()
->process($this->template->content);
}
/**
* Render transformed subject. Same resolver chain as renderContent — subject
* strings reference the same tag vocabulary (page_title, subscribe_button, ...).
*
* @var string
*/
public function getTransformedSubject($subscriber)
{
$subject = trans('messages.' . $this->alias);
if (empty($subject)) {
return $subject;
}
return (new PipelineBuilder())
->add(new TransformTag(...$this->buildResolvers(null, $subscriber, null)))
->build()
->process($subject);
}
/**
* Compose the resolver chain. Order = precedence (later wins via array_merge
* inside TagTranslationService): GeneralTagResolver provides ISO defaults
* (current_year, current_date, ...), MailList contributes list.* and
* Subscriber contributes subscriber.* + global flat keys (email, name, ...).
* MailListTemplateTagResolver runs last so it can override page-context tags
* like current_date with the customer-localized format and unsubscribe_url
* with the form-flow URL.
*
* @return array<\App\Library\Contracts\TagResolverInterface>
*/
private function buildResolvers($values, $subscriber, $messageId): array
{
return array_values(array_filter([
new GeneralTagResolver(),
$this->mailList,
$subscriber,
new MailListTemplateTagResolver($this, $values, $subscriber, $messageId),
]));
}
public function tags()
{
// Twig-style tag labels — match the {{ TAG }} syntax that
// {@see MailListTemplateTagResolver} + TagTranslationService resolve.
$listTags = $this->mailList->fields->map(function ($field) {
return ['name' => '{{ SUBSCRIBER_' . $field->tag . ' }}', 'required' => false];
})->toArray();
$tags = match ($this->alias) {
'sign_up_form' => [
['name' => '{{ FIELDS }}', 'required' => true],
['name' => '{{ SUBSCRIBE_BUTTON }}', 'required' => true],
],
'sign_up_confirmation_email' => [
['name' => '{{ SUBSCRIBE_CONFIRM_URL }}', 'required' => true],
],
'sign_up_welcome_email' => [
['name' => '{{ UNSUBSCRIBE_URL }}', 'required' => true],
],
'unsubscribe_form' => [
['name' => '{{ UNSUBSCRIBE_BUTTON }}', 'required' => true],
],
'profile_update_email' => [
['name' => '{{ UPDATE_PROFILE_URL }}', 'required' => true],
],
'profile_update_form' => [
['name' => '{{ FIELDS }}', 'required' => true],
['name' => '{{ UPDATE_PROFILE_BUTTON }}', 'required' => true],
['name' => '{{ UNSUBSCRIBE_URL }}', 'required' => true],
],
'sign_up_confirmation_page',
'sign_up_thankyou_page',
'unsubscribe_success_page',
'unsubscribe_goodbye_email',
'profile_update_email_sent',
'profile_update_success_page' => [],
default => [],
};
$tags[] = ['name' => '{{ LIST_NAME }}', 'required' => false];
if (!in_array($this->alias, ['sign_up_form'])) {
$tags = array_merge($listTags, $tags);
}
return $tags;
}
public function restoreSystemTemplate()
{
\DB::transaction(function () {
// Find system template by alias
$originalTemplate = SystemTemplate::findByAliasOrFail($this->alias)->template;
TemplateService::for($this)->setTemplate($originalTemplate, $this->alias);
});
}
}