SmtpSenderStrategy::sendEnvelope()   B
last analyzed

Complexity

Conditions 9
Paths 120

Size

Total Lines 90
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 45
c 1
b 0
f 0
dl 0
loc 90
ccs 0
cts 53
cp 0
rs 7.5111
cc 9
nc 120
nop 1
crap 90

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Backbone\Abstract\AbstractStrategy;
28
use Derafu\Backbone\Attribute\Strategy;
29
use Derafu\Mail\Contract\MailPackageInterface;
30
use Derafu\Mail\Model\Envelope as MailEnvelope;
31
use Derafu\Mail\Model\Message as MailMessage;
32
use Derafu\Mail\Model\Postman as MailPostman;
33
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\EnvelopeInterface;
34
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\ExchangeBagInterface;
35
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\ExchangeResultInterface;
36
use libredte\lib\Core\Package\Billing\Component\Exchange\Contract\SenderStrategyInterface;
37
use libredte\lib\Core\Package\Billing\Component\Exchange\Exception\ExchangeException;
38
use libredte\lib\Core\Package\Billing\Component\Exchange\Support\ExchangeResult;
39
use libredte\lib\Core\Package\Billing\Component\Exchange\Support\ExchangeStatus;
40
use Symfony\Component\Mime\Address;
41
42
/**
43
 * Envío de documentos usando la estrategia SMTP de correo electrónico.
44
 */
45
#[Strategy(name: 'email.smtp', worker: 'sender', component: 'exchange', package: 'billing')]
46
class SmtpSenderStrategy extends AbstractStrategy implements SenderStrategyInterface
47
{
48
    /**
49
     * Constructor de la estrategia y sus dependencias.
50
     *
51
     * @param MailPackageInterface $mailPackage
52
     */
53
    public function __construct(
54
        private MailPackageInterface $mailPackage
55
    ) {
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61
    public function send(ExchangeBagInterface $bag): array
62
    {
63
        // Procesar cada sobre por separado.
64
        foreach ($bag->getEnvelopes() as $envelope) {
65
            $result = $this->sendEnvelope($envelope);
66
            $bag->addResult($result);
67
        }
68
69
        // Entregar los resultados de la recepción de documentos.
70
        return $bag->getResults();
71
    }
72
73
    /**
74
     * Envía los documentos de un sobre por correo.
75
     *
76
     * @param EnvelopeInterface $envelope Sobre con documentos a enviar.
77
     * @return ExchangeResultInterface Resultado del envío del sobre.
78
     */
79
    private function sendEnvelope(
80
        EnvelopeInterface $envelope
81
    ): ExchangeResultInterface {
82
        // Crear sobre con los correos del remitente y los destinatarios.
83
        $sender = $this->resolveSender($envelope);
84
        $recipients = $this->resolveRecipients($envelope);
85
        $mailEnvelope = new MailEnvelope($sender, $recipients);
86
87
        // Crear el mensaje que se enviará en el sobre del correo.
88
        $message = new MailMessage();
89
90
        // Agregar destinatarios principales del correo (TO).
91
        foreach ($recipients as $to) {
92
            $message->addTo($to);
93
        }
94
95
        // Agregar otros destinatarios si existen en la bolsa.
96
        foreach (['to', 'cc', 'bcc'] as $dest) {
97
            $emails = $envelope->getMetadata()->get($dest, []);
98
            foreach ($emails as $email) {
99
                match ($dest) {
100
                    'to' => $message->addTo($email),
101
                    'cc' => $message->addCc($email),
102
                    'bcc' => $message->addBcc($email),
103
                };
104
            }
105
        }
106
107
        // Agregar asunto al mensaje.
108
        $subject = $envelope->getMetadata()->get('subject')
109
            ?? sprintf(
110
                'Envío de documentos electrónicos de %s',
111
                $envelope->getSender()->getIdentifier()->getId()
112
            )
113
        ;
114
        $message->subject($subject);
115
116
        // Agregar mensaje renderizado como texto.
117
        $text = $envelope->getMetadata()->get('text');
118
        if ($text !== null) {
119
            $message->text($text);
120
        }
121
122
        // Agregar mensaje renderizado como HTML.
123
        $html = $envelope->getMetadata()->get('html');
124
        if ($html !== null) {
125
            $message->html($html);
126
        }
127
128
        // Procesar cada documento para incluirlo en el mensaje de correo.
129
        foreach ($envelope->getDocuments() as $document) {
130
            // Agregar el contenido del documento como TXT si se indicó.
131
            if ($document->getContent()) {
132
                $message->attach(
133
                    $document->getContent(),
134
                    $document->getID() . '.txt'
135
                );
136
            }
137
138
            // Agregar los archivos adjuntos de cada documento.
139
            foreach ($document->getAttachments() as $attachment) {
140
                $message->attach(
141
                    $attachment->getBody(),
142
                    $attachment->getFilename(),
143
                    $attachment->getContentType()
144
                );
145
            }
146
        }
147
148
        // Agregar el mensaje al sobre.
149
        $mailEnvelope->addMessage($message);
150
151
        // Crear el cartero, pasarle el sobre y enviar el correo.
152
        $postman = new MailPostman([
153
            'strategy' => 'smtp',
154
            'transport' => $this->resolveTransportOptions($envelope),
155
        ]);
156
        $postman->addEnvelope($mailEnvelope);
157
        $this->mailPackage->getExchangeComponent()->getSenderWorker()->send($postman);
158
159
        // Crear resultado del envío del sobre y agregar el error del envío por
160
        // correo si existe (si no existe se agrega `null`).
161
        $result = new ExchangeResult($envelope);
162
        $result->addStatus(new ExchangeStatus(
163
            'email.smtp',
164
            $message->getError()
165
        ));
166
167
        // Entregar resultado del envío.
168
        return $result;
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174
    public function canSend(ExchangeBagInterface|EnvelopeInterface $what): void
175
    {
176
        // Armar listado de sobres a revisar.
177
        if ($what instanceof ExchangeBagInterface) {
178
            $envelopes = $what;
179
        } else {
180
            $envelopes = [$what];
181
        }
182
183
        // Revisar cada sobre y validar que tiene los datos necesario
184
        // resolviendolos a sus valores.
185
        foreach ($envelopes as $envelope) {
186
            $this->resolveTransportOptions($envelope);
187
            $this->resolveSender($envelope);
188
            $this->resolveRecipients($envelope);
189
        }
190
    }
191
192
    /**
193
     * Resuelve y entrega los datos de transporte.
194
     *
195
     * @param EnvelopeInterface $envelope
196
     * @return array
197
     * @throws ExchangeException Si no se encuentran los datos de transporte.
198
     */
199
    private function resolveTransportOptions(EnvelopeInterface $envelope): array
200
    {
201
        $options = $envelope->getMetadata()->get('transport', []);
202
203
        if (empty($options['username']) || empty($options['password'])) {
204
            throw new ExchangeException(
205
                'Se debe especificar el usuario y contraseña de SMTP.'
206
            );
207
        }
208
209
        return $options;
210
    }
211
212
    /**
213
     * Resuelve y entrega los datos del remitente.
214
     *
215
     * @param EnvelopeInterface $envelope
216
     * @return Address
217
     * @throws ExchangeException Si no se encuentran los datos del remitente.
218
     */
219
    private function resolveSender(EnvelopeInterface $envelope): Address
220
    {
221
        $sender = $envelope->getSender()->getEmails()[0] ?? null;
222
223
        if ($sender === null) {
224
            throw new ExchangeException(
225
                'Se debe especificar el identificador con esquema EMAIL en el remitente.'
226
            );
227
        }
228
229
        return $sender;
230
    }
231
232
    /**
233
     * Resuelve y entrega los datos de los receptores.
234
     *
235
     * @param EnvelopeInterface $envelope
236
     * @return array
237
     * @throws ExchangeException Si no se encuentra al menos un receptor.
238
     */
239
    private function resolveRecipients(EnvelopeInterface $envelope): array
240
    {
241
        $recipients = $envelope->getReceiver()->getEmails();
242
243
        if (empty($recipients)) {
244
            throw new ExchangeException(
245
                'Se debe especificar al menos un identificador con esquema EMAIL en el destinatario.'
246
            );
247
        }
248
249
        return $recipients;
250
    }
251
}
252