- Create AnsibleDeployStage using framework's Process module for secure command execution - Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments - Add force_deploy flag support in Ansible playbook to override stale locks - Use PHP deployment module as orchestrator (php console.php deploy:production) - Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal Architecture: - BuildStage → AnsibleDeployStage → HealthCheckStage for production - Process module provides timeout, error handling, and output capture - Ansible playbook supports rollback via rollback-git-based.yml - Zero-downtime deployments with health checks
143 lines
4.8 KiB
PHP
143 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Notification\Templates;
|
|
|
|
use App\Framework\Notification\ValueObjects\NotificationChannel;
|
|
use App\Framework\Notification\ValueObjects\NotificationPriority;
|
|
|
|
/**
|
|
* Notification Template
|
|
*
|
|
* Reusable template with placeholders for dynamic content
|
|
* Supports per-channel customization and variable substitution
|
|
*/
|
|
final readonly class NotificationTemplate
|
|
{
|
|
/**
|
|
* @param TemplateId $id Unique template identifier
|
|
* @param string $name Template name (e.g., 'order.shipped', 'user.welcome')
|
|
* @param string $titleTemplate Title with placeholders (e.g., 'Order {{order_id}} shipped')
|
|
* @param string $bodyTemplate Body with placeholders
|
|
* @param array<NotificationChannel, ChannelTemplate> $channelTemplates Per-channel customization
|
|
* @param NotificationPriority $defaultPriority Default priority for notifications using this template
|
|
* @param array<string> $requiredVariables Variables that must be provided when rendering
|
|
* @param array<string, mixed> $defaultVariables Default values for optional variables
|
|
*/
|
|
public function __construct(
|
|
public TemplateId $id,
|
|
public string $name,
|
|
public string $titleTemplate,
|
|
public string $bodyTemplate,
|
|
public array $channelTemplates = [],
|
|
public NotificationPriority $defaultPriority = NotificationPriority::NORMAL,
|
|
public array $requiredVariables = [],
|
|
public array $defaultVariables = []
|
|
) {
|
|
if (empty($name)) {
|
|
throw new \InvalidArgumentException('Template name cannot be empty');
|
|
}
|
|
|
|
if (empty($titleTemplate)) {
|
|
throw new \InvalidArgumentException('Title template cannot be empty');
|
|
}
|
|
|
|
if (empty($bodyTemplate)) {
|
|
throw new \InvalidArgumentException('Body template cannot be empty');
|
|
}
|
|
}
|
|
|
|
public static function create(
|
|
string $name,
|
|
string $titleTemplate,
|
|
string $bodyTemplate
|
|
): self {
|
|
return new self(
|
|
id: TemplateId::generate(),
|
|
name: $name,
|
|
titleTemplate: $titleTemplate,
|
|
bodyTemplate: $bodyTemplate
|
|
);
|
|
}
|
|
|
|
public function withChannelTemplate(
|
|
NotificationChannel $channel,
|
|
ChannelTemplate $template
|
|
): self {
|
|
return new self(
|
|
id: $this->id,
|
|
name: $this->name,
|
|
titleTemplate: $this->titleTemplate,
|
|
bodyTemplate: $this->bodyTemplate,
|
|
channelTemplates: [...$this->channelTemplates, $channel => $template],
|
|
defaultPriority: $this->defaultPriority,
|
|
requiredVariables: $this->requiredVariables,
|
|
defaultVariables: $this->defaultVariables
|
|
);
|
|
}
|
|
|
|
public function withPriority(NotificationPriority $priority): self
|
|
{
|
|
return new self(
|
|
id: $this->id,
|
|
name: $this->name,
|
|
titleTemplate: $this->titleTemplate,
|
|
bodyTemplate: $this->bodyTemplate,
|
|
channelTemplates: $this->channelTemplates,
|
|
defaultPriority: $priority,
|
|
requiredVariables: $this->requiredVariables,
|
|
defaultVariables: $this->defaultVariables
|
|
);
|
|
}
|
|
|
|
public function withRequiredVariables(string ...$variables): self
|
|
{
|
|
return new self(
|
|
id: $this->id,
|
|
name: $this->name,
|
|
titleTemplate: $this->titleTemplate,
|
|
bodyTemplate: $this->bodyTemplate,
|
|
channelTemplates: $this->channelTemplates,
|
|
defaultPriority: $this->defaultPriority,
|
|
requiredVariables: $variables,
|
|
defaultVariables: $this->defaultVariables
|
|
);
|
|
}
|
|
|
|
public function withDefaultVariables(array $defaults): self
|
|
{
|
|
return new self(
|
|
id: $this->id,
|
|
name: $this->name,
|
|
titleTemplate: $this->titleTemplate,
|
|
bodyTemplate: $this->bodyTemplate,
|
|
channelTemplates: $this->channelTemplates,
|
|
defaultPriority: $this->defaultPriority,
|
|
requiredVariables: $this->requiredVariables,
|
|
defaultVariables: [...$this->defaultVariables, ...$defaults]
|
|
);
|
|
}
|
|
|
|
public function hasChannelTemplate(NotificationChannel $channel): bool
|
|
{
|
|
return isset($this->channelTemplates[$channel]);
|
|
}
|
|
|
|
public function getChannelTemplate(NotificationChannel $channel): ?ChannelTemplate
|
|
{
|
|
return $this->channelTemplates[$channel] ?? null;
|
|
}
|
|
|
|
public function validateVariables(array $variables): void
|
|
{
|
|
foreach ($this->requiredVariables as $required) {
|
|
if (!array_key_exists($required, $variables)) {
|
|
throw new \InvalidArgumentException(
|
|
"Required variable '{$required}' is missing"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|