string, 'text' => string, 'url' => string|null, 'payload' => string|null] */ public array $buttons = []; /** * Variables/parameters configuration */ public array $variables = []; /** * Approval status */ public string $approvalStatus = self::STATUS_UNKNOWN; /** * Rejection reason (if rejected) */ public ?string $rejectionReason = null; /** * Template category */ public ?string $category = null; /** * Date created */ public ?string $dateCreated = null; /** * Date updated */ public ?string $dateUpdated = null; /** * Raw provider data */ public array $rawData = []; /** * Create a new NormalizedTemplate * * @param string $id * @param string $name * @param string $provider */ public function __construct(string $id, string $name, string $provider) { $this->id = $id; $this->name = $name; $this->provider = $provider; } /** * Create from array * * @param array $data * @return static */ public static function fromArray(array $data): static { $template = new static( $data['id'] ?? '', $data['name'] ?? '', $data['provider'] ?? 'unknown' ); foreach ($data as $key => $value) { $camelKey = lcfirst(str_replace('_', '', ucwords($key, '_'))); if (property_exists($template, $camelKey)) { $template->$camelKey = $value; } elseif (property_exists($template, $key)) { $template->$key = $value; } } return $template; } /** * Convert to array * * @return array */ public function toArray(): array { // Get original types array for Twilio compatibility // Twilio returns types as ['twilio/text', 'twilio/quick-reply', etc.] $types = $this->getOriginalTypes(); return [ 'id' => $this->id, 'name' => $this->name, 'provider' => $this->provider, 'language' => $this->language, 'type' => $this->type, 'body' => $this->body, 'header' => $this->header, 'header_type' => $this->headerType, 'footer' => $this->footer, 'media_url' => $this->mediaUrl, 'buttons' => $this->buttons, 'variables' => $this->variables, 'approval_status' => $this->approvalStatus, 'rejection_reason' => $this->rejectionReason, 'category' => $this->category, 'date_created' => $this->dateCreated, 'date_updated' => $this->dateUpdated, // Compatibility fields (for existing Vue components) 'sid' => $this->id, // Alias for Twilio compatibility 'types' => $types, // Original types array for Vue compatibility ]; } /** * Get original types array for backwards compatibility * * @return array */ protected function getOriginalTypes(): array { // If we have raw data with types (Twilio format), use those if (!empty($this->rawData['types']) && is_array($this->rawData['types'])) { return array_keys($this->rawData['types']); } // Otherwise, construct from normalized type with provider prefix $prefix = $this->provider === 'twilio' ? 'twilio/' : ''; return [$prefix . $this->type]; } /** * Check if template is approved * * @return bool */ public function isApproved(): bool { return $this->approvalStatus === self::STATUS_APPROVED; } /** * Check if template has buttons * * @return bool */ public function hasButtons(): bool { return !empty($this->buttons); } /** * Check if template has media * * @return bool */ public function hasMedia(): bool { return !empty($this->mediaUrl) || $this->type === self::TYPE_MEDIA; } /** * Get required variable count * * @return int */ public function getVariableCount(): int { if (!empty($this->variables)) { return count($this->variables); } // Count placeholders in body {{1}}, {{2}}, etc. if ($this->body) { preg_match_all('/\{\{(\d+)\}\}/', $this->body, $matches); return count(array_unique($matches[1] ?? [])); } return 0; } }