SmtpSenderStrategy::resolveRecipients()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 11
ccs 0
cts 7
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * LibreDTE: Biblioteca PHP (Núcleo).
7
 * Copyright (C) LibreDTE <https://www.libredte.cl>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de
20
 * GNU junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace libredte\lib\Core\Package\Billing\Component\Exchange\Worker\Sender\Strategy\Email;
26
27
use Derafu\Lib\Core\Foundation\Abstract\AbstractStrategy;
28
use Derafu\Lib\Core\Package\Prime\Component\Mail\Contract\MailComponentInterface;
29
use Derafu\Lib\Core\Package\Prime\Component\Mail\Support\Envelope as MailEnvelope;
30
use Derafu\Lib\Core\Package\Prime\Component\Mail\Support\Message as MailMessage;
31
use Derafu\Lib\Core\Package\Prime\Component\Mail\Support\Postman as MailPostman;
32
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\EnvelopeInterface;
33
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\ExchangeBagInterface;
34
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\ExchangeResultInterface;
35
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\SenderStrategyInterface;
36
use libredte\lib\Core\Package\Billing\Component\Exchange\Exception\ExchangeException;
37
use libredte\lib\Core\Package\Billing\Component\Exchange\Support\ExchangeResult;
38
use libredte\lib\Core\Package\Billing\Component\Exchange\Support\ExchangeStatus;
39
use Symfony\Component\Mime\Address;
40
41
/**
42
 * Envío de documentos usando la estrategia SMTP de correo electrónico.
43
 */
44
class SmtpSenderStrategy extends AbstractStrategy implements SenderStrategyInterface
45
{
46
    /**
47
     * Constructor de la estrategia y sus dependencias.
48
     *
49
     * @param MailComponentInterface $mailComponentInterface
50
     */
51
    public function __construct(
52
        private MailComponentInterface $mailComponentInterface
53
    ) {
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59
    public function send(ExchangeBagInterface $bag): array
60
    {
61
        // Procesar cada sobre por separado.
62
        foreach ($bag->getEnvelopes() as $envelope) {
63
            $result = $this->sendEnvelope($envelope);
64
            $bag->addResult($result);
65
        }
66
67
        // Entregar los resultados de la recepción de documentos.
68
        return $bag->getResults();
69
    }
70
71
    /**
72
     * Envía los documentos de un sobre por correo.
73
     *
74
     * @param EnvelopeInterface $envelope Sobre con documentos a enviar.
75
     * @return ExchangeResultInterface Resultado del envío del sobre.
76
     */
77
    private function sendEnvelope(
78
        EnvelopeInterface $envelope
79
    ): ExchangeResultInterface {
80
        // Crear sobre con los correos del remitente y los destinatarios.
81
        $sender = $this->resolveSender($envelope);
82
        $recipients = $this->resolveRecipients($envelope);
83
        $mailEnvelope = new MailEnvelope($sender, $recipients);
84
85
        // Crear el mensaje que se enviará en el sobre del correo.
86
        $message = new MailMessage();
87
88
        // Agregar destinatarios principales del correo (TO).
89
        foreach ($recipients as $to) {
90
            $message->addTo($to);
91
        }
92
93
        // Agregar otros destinatarios si existen en la bolsa.
94
        foreach (['to', 'cc', 'bcc'] as $dest) {
95
            $emails = $envelope->getMetadata()->get($dest, []);
96
            foreach ($emails as $email) {
97
                match ($dest) {
98
                    'to' => $message->addTo($email),
99
                    'cc' => $message->addCc($email),
100
                    'bcc' => $message->addBcc($email),
101
                };
102
            }
103
        }
104
105
        // Agregar asunto al mensaje.
106
        $subject = $envelope->getMetadata()->get('subject')
107
            ?? sprintf(
108
                'Envío de documentos electrónicos de %s',
109
                $envelope->getSender()->getIdentifier()->getId()
110
            )
111
        ;
112
        $message->subject($subject);
113
114
        // Agregar mensaje renderizado como texto.
115
        $text = $envelope->getMetadata()->get('text');
116
        if ($text !== null) {
117
            $message->text($text);
118
        }
119
120
        // Agregar mensaje renderizado como HTML.
121
        $html = $envelope->getMetadata()->get('html');
122
        if ($html !== null) {
123
            $message->html($html);
124
        }
125
126
        // Procesar cada documento para incluirlo en el mensaje de correo.
127
        foreach ($envelope->getDocuments() as $document) {
128
            // Agregar el contenido del documento como TXT si se indicó.
129
            if ($document->getContent()) {
130
                $message->attach(
131
                    $document->getContent(),
132
                    $document->getID() . '.txt'
133
                );
134
            }
135
136
            // Agregar los archivos adjuntos de cada documento.
137
            foreach ($document->getAttachments() as $attachment) {
138
                $message->attach(
139
                    $attachment->getBody(),
140
                    $attachment->getFilename(),
141
                    $attachment->getContentType()
142
                );
143
            }
144
        }
145
146
        // Agregar el mensaje al sobre.
147
        $mailEnvelope->addMessage($message);
148
149
        // Crear el cartero, pasarle el sobre y enviar el correo.
150
        $postman = new MailPostman([
151
            'strategy' => 'smtp',
152
            'transport' => $this->resolveTransportOptions($envelope),
153
        ]);
154
        $postman->addEnvelope($mailEnvelope);
155
        $this->mailComponentInterface->send($postman);
156
157
        // Crear resultado del envío del sobre y agregar el error del envío por
158
        // correo si existe (si no existe se agrega `null`).
159
        $result = new ExchangeResult($envelope);
160
        $result->addStatus(new ExchangeStatus(
161
            'email.smtp',
162
            $message->getError()
163
        ));
164
165
        // Entregar resultado del envío.
166
        return $result;
167
    }
168
169
    /**
170
     * {@inheritDoc}
171
     */
172
    public function canSend(ExchangeBagInterface|EnvelopeInterface $what): void
173
    {
174
        // Armar listado de sobres a revisar.
175
        if ($what instanceof ExchangeBagInterface) {
176
            $envelopes = $what;
177
        } else {
178
            $envelopes = [$what];
179
        }
180
181
        // Revisar cada sobre y validar que tiene los datos necesario
182
        // resolviendolos a sus valores.
183
        foreach ($envelopes as $envelope) {
184
            $this->resolveTransportOptions($envelope);
185
            $this->resolveSender($envelope);
186
            $this->resolveRecipients($envelope);
187
        }
188
    }
189
190
    /**
191
     * Resuelve y entrega los datos de transporte.
192
     *
193
     * @param EnvelopeInterface $envelope
194
     * @return array
195
     * @throws ExchangeException Si no se encuentran los datos de transporte.
196
     */
197
    private function resolveTransportOptions(EnvelopeInterface $envelope): array
198
    {
199
        $options = $envelope->getMetadata()->get('transport', []);
200
201
        if (empty($options['username']) || empty($options['password'])) {
202
            throw new ExchangeException(
203
                'Se debe especificar el usuario y contraseña de SMTP.'
204
            );
205
        }
206
207
        return $options;
208
    }
209
210
    /**
211
     * Resuelve y entrega los datos del remitente.
212
     *
213
     * @param EnvelopeInterface $envelope
214
     * @return Address
215
     * @throws ExchangeException Si no se encuentran los datos del remitente.
216
     */
217
    private function resolveSender(EnvelopeInterface $envelope): Address
218
    {
219
        $sender = $envelope->getSender()->getEmails()[0] ?? null;
220
221
        if ($sender === null) {
222
            throw new ExchangeException(
223
                'Se debe especificar el identificador con esquema EMAIL en el remitente.'
224
            );
225
        }
226
227
        return $sender;
228
    }
229
230
    /**
231
     * Resuelve y entrega los datos de los receptores.
232
     *
233
     * @param EnvelopeInterface $envelope
234
     * @return array
235
     * @throws ExchangeException Si no se encuentra al menos un receptor.
236
     */
237
    private function resolveRecipients(EnvelopeInterface $envelope): array
238
    {
239
        $recipients = $envelope->getReceiver()->getEmails();
240
241
        if (empty($recipients)) {
242
            throw new ExchangeException(
243
                'Se debe especificar al menos un identificador con esquema EMAIL en el destinatario.'
244
            );
245
        }
246
247
        return $recipients;
248
    }
249
}
250