Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
MailQueueItem
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
6 / 6
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromRow
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
1
 extractNullableString
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 extractNullableInt
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 extractNullableDateTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 extractAttachments
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Mail\Domain\Model;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use DateTimeImmutable;
12
13/**
14 * Outgoing Email Queue Record Aggregate Model.
15 *
16 * @package App\Modules\Mail\Domain\Model
17 */
18final readonly class MailQueueItem
19{
20    /**
21     * MailQueueItem constructor.
22     *
23     * @param int                    $id              Queue item ID.
24     * @param int|null               $templateId      Originating template ID.
25     * @param int                    $smtpId          SMTP server ID to use.
26     * @param string                 $recipientEmail  Recipient email address.
27     * @param string|null            $recipientName   Optional recipient name.
28     * @param string|null            $ccEmails        Optional CC addresses (comma-separated).
29     * @param string|null            $bccEmails       Optional BCC addresses (comma-separated).
30     * @param string                 $subject         Email subject.
31     * @param string                 $bodyHtml        HTML body.
32     * @param string|null            $bodyText        Plain text body.
33     * @param array<int, string>     $attachments     File attachment paths.
34     * @param string                 $status          Queue status (pending, processing, sent, failed, cancelled).
35     * @param string                 $priority        Priority (urgent, high, normal).
36     * @param string                 $dispatchMode    Dispatch mode (automatic, manual).
37     * @param int                    $attemptsCount   Current execution attempts count.
38     * @param int                    $maxAttempts     Maximum delivery attempts.
39     * @param string|null            $lastError       Last delivery error message.
40     * @param DateTimeImmutable|null $scheduledAt     Scheduled send time.
41     * @param DateTimeImmutable|null $sentAt          Actual sent timestamp.
42     */
43    public function __construct(
44        public int $id,
45        public ?int $templateId,
46        public int $smtpId,
47        public string $recipientEmail,
48        public ?string $recipientName = null,
49        public ?string $ccEmails = null,
50        public ?string $bccEmails = null,
51        public string $subject = '',
52        public string $bodyHtml = '',
53        public ?string $bodyText = null,
54        public array $attachments = [],
55        public string $status = 'pending',
56        public string $priority = 'normal',
57        public string $dispatchMode = 'automatic',
58        public int $attemptsCount = 0,
59        public int $maxAttempts = 3,
60        public ?string $lastError = null,
61        public ?DateTimeImmutable $scheduledAt = null,
62        public ?DateTimeImmutable $sentAt = null
63    ) {
64    }
65
66    /**
67     * Instantiates MailQueueItem from database record.
68     *
69     * @param array<string, mixed> $row Database associative row.
70     * @return self Populated MailQueueItem instance.
71     */
72    public static function fromRow(array $row): self
73    {
74        return new self(
75            id: (int)($row['id'] ?? 0),
76            templateId: self::extractNullableInt($row, 'template_id'),
77            smtpId: (int)($row['smtp_id'] ?? 0),
78            recipientEmail: (string)($row['recipient_email'] ?? ''),
79            recipientName: self::extractNullableString($row, 'recipient_name'),
80            ccEmails: self::extractNullableString($row, 'cc_emails'),
81            bccEmails: self::extractNullableString($row, 'bcc_emails'),
82            subject: (string)($row['subject'] ?? ''),
83            bodyHtml: (string)($row['body_html'] ?? ''),
84            bodyText: self::extractNullableString($row, 'body_text'),
85            attachments: self::extractAttachments($row),
86            status: (string)($row['status'] ?? 'pending'),
87            priority: (string)($row['priority'] ?? 'normal'),
88            dispatchMode: (string)($row['dispatch_mode'] ?? 'automatic'),
89            attemptsCount: (int)($row['attempts_count'] ?? 0),
90            maxAttempts: (int)($row['max_attempts'] ?? 3),
91            lastError: self::extractNullableString($row, 'last_error'),
92            scheduledAt: self::extractNullableDateTime($row, 'scheduled_at'),
93            sentAt: self::extractNullableDateTime($row, 'sent_at')
94        );
95    }
96
97    /**
98     * Extracts nullable string from associative row.
99     *
100     * @param array<string, mixed> $row Database row.
101     * @param string               $key Target key name.
102     * @return string|null Extracted non-empty string or null.
103     */
104    private static function extractNullableString(array $row, string $key): ?string
105    {
106        $val = $row[$key] ?? null;
107        return (is_string($val) && $val !== '') ? $val : null;
108    }
109
110    /**
111     * Extracts positive nullable integer from row.
112     *
113     * @param array<string, mixed> $row Database row.
114     * @param string               $key Target key name.
115     * @return int|null Extracted integer ID or null.
116     */
117    private static function extractNullableInt(array $row, string $key): ?int
118    {
119        $val = (int)($row[$key] ?? 0);
120        return $val > 0 ? $val : null;
121    }
122
123    /**
124     * Extracts nullable DateTimeImmutable from row.
125     *
126     * @param array<string, mixed> $row Database row.
127     * @param string               $key Target key name.
128     * @return DateTimeImmutable|null Parsed datetime or null.
129     */
130    private static function extractNullableDateTime(array $row, string $key): ?DateTimeImmutable
131    {
132        $val = $row[$key] ?? null;
133        return (is_string($val) && $val !== '') ? new DateTimeImmutable($val) : null;
134    }
135
136    /**
137     * Extracts attachments list from row JSON string.
138     *
139     * @param array<string, mixed> $row Database row.
140     * @return array<int, string> Array of valid string paths.
141     */
142    private static function extractAttachments(array $row): array
143    {
144        $raw = $row['attachments_json'] ?? '[]';
145        $decoded = is_string($raw) ? (array)json_decode($raw, true) : [];
146        return array_values(array_filter(array_map('strval', $decoded)));
147    }
148}