Skip to content

Commit 1fae747

Browse files
authored
Merge pull request #113 from APaikens/5.2
MailService - fix email structure with multiple attachments
2 parents c46ec7d + abc6180 commit 1fae747

File tree

2 files changed

+146
-6
lines changed

2 files changed

+146
-6
lines changed

src/Service/MailService.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,19 @@ public function attachFiles(): false|Email
9696
return false;
9797
}
9898

99-
$mimeMessage = $this->message->getBody();
99+
$body = $this->message->getBody();
100+
$parts = [];
100101

101-
//generate a new Part for each attachment
102102
foreach ($this->attachments as $key => $attachment) {
103103
if (! is_file($attachment)) {
104104
continue;
105105
}
106-
$basename = is_string($key) ? $key : basename($attachment);
107-
$attachedFile = new DataPart(fopen($attachment, 'r'), $basename);
108-
$mimeMessage = new MixedPart($mimeMessage, $attachedFile);
106+
$basename = is_string($key) ? $key : basename($attachment);
107+
$parts[] = new DataPart(fopen($attachment, 'r'), $basename);
108+
}
109109

110-
$this->message->setBody($mimeMessage);
110+
if (count($parts) > 0) {
111+
$this->message->setBody(new MixedPart($body, ...$parts));
111112
}
112113

113114
return $this->message;

test/Service/MailServiceTest.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use PHPUnit\Framework\TestCase;
2020
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
2121
use Symfony\Component\Mailer\Transport\TransportInterface;
22+
use Symfony\Component\Mime\Part\DataPart;
23+
use Symfony\Component\Mime\Part\Multipart\MixedPart;
2224
use Symfony\Component\Mime\Part\TextPart;
2325

2426
class MailServiceTest extends TestCase
@@ -153,4 +155,141 @@ public function testMailResultCreatedFromException(): void
153155
$this->assertSame($customException, $mailResult->getException());
154156
$this->assertSame('Custom exception test', $mailResult->getMessage());
155157
}
158+
159+
public function testAttachFilesReturnsFalseWhenNoAttachments(): void
160+
{
161+
$this->message->html('Test body');
162+
$result = $this->mailService->attachFiles();
163+
$this->assertFalse($result);
164+
}
165+
166+
public function testAttachFilesCreatesFlatMixedPart(): void
167+
{
168+
$this->message->html('<p>Test</p>');
169+
170+
$this->mailService->addAttachment(
171+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf'
172+
);
173+
$this->mailService->addAttachment(
174+
$this->fileSystem->url() . '/data/mail/attachments/testXlsAttachment.xls'
175+
);
176+
177+
$this->mailService->attachFiles();
178+
179+
$body = $this->message->getBody();
180+
$this->assertInstanceOf(MixedPart::class, $body);
181+
182+
$parts = $body->getParts();
183+
// 1 TextPart (html) + 2 DataParts (attachments) = 3 parts at same level
184+
$this->assertCount(3, $parts);
185+
$this->assertInstanceOf(TextPart::class, $parts[0]);
186+
$this->assertInstanceOf(DataPart::class, $parts[1]);
187+
$this->assertInstanceOf(DataPart::class, $parts[2]);
188+
}
189+
190+
public function testAttachFilesSingleAttachmentIsFlat(): void
191+
{
192+
$this->message->html('<p>Single attachment</p>');
193+
194+
$this->mailService->addAttachment(
195+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf'
196+
);
197+
198+
$this->mailService->attachFiles();
199+
200+
$body = $this->message->getBody();
201+
$this->assertInstanceOf(MixedPart::class, $body);
202+
203+
$parts = $body->getParts();
204+
$this->assertCount(2, $parts);
205+
$this->assertInstanceOf(TextPart::class, $parts[0]);
206+
$this->assertInstanceOf(DataPart::class, $parts[1]);
207+
}
208+
209+
public function testAttachFilesSkipsNonExistentFiles(): void
210+
{
211+
$this->message->html('<p>Test</p>');
212+
213+
$this->mailService->addAttachment('/nonexistent/file.pdf');
214+
$this->mailService->addAttachment(
215+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf'
216+
);
217+
218+
$this->mailService->attachFiles();
219+
220+
$body = $this->message->getBody();
221+
$this->assertInstanceOf(MixedPart::class, $body);
222+
223+
$parts = $body->getParts();
224+
// only 1 valid attachment + the html body
225+
$this->assertCount(2, $parts);
226+
}
227+
228+
public function testAttachFilesPreservesCustomFilename(): void
229+
{
230+
$this->message->html('<p>Test</p>');
231+
232+
$this->mailService->addAttachment(
233+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf',
234+
'custom-ticket.pdf'
235+
);
236+
237+
$this->mailService->attachFiles();
238+
239+
$body = $this->message->getBody();
240+
$this->assertInstanceOf(MixedPart::class, $body);
241+
$parts = $body->getParts();
242+
243+
$this->assertCount(2, $parts);
244+
$attachment = $parts[1];
245+
$this->assertInstanceOf(DataPart::class, $attachment);
246+
$this->assertSame('custom-ticket.pdf', $attachment->getFilename());
247+
}
248+
249+
public function testAttachFilesWithTextPartBody(): void
250+
{
251+
$textPart = new TextPart('<h1>HTML content</h1>', 'utf-8', 'html');
252+
$this->mailService->setBody($textPart);
253+
254+
$this->mailService->addAttachment(
255+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf'
256+
);
257+
$this->mailService->addAttachment(
258+
$this->fileSystem->url() . '/data/mail/attachments/testXlsAttachment.xls'
259+
);
260+
261+
$this->mailService->attachFiles();
262+
263+
$body = $this->message->getBody();
264+
$this->assertInstanceOf(MixedPart::class, $body);
265+
266+
$parts = $body->getParts();
267+
$this->assertCount(3, $parts);
268+
$this->assertInstanceOf(TextPart::class, $parts[0]);
269+
$this->assertInstanceOf(DataPart::class, $parts[1]);
270+
$this->assertInstanceOf(DataPart::class, $parts[2]);
271+
}
272+
273+
public function testAttachFilesNoNestedMixedParts(): void
274+
{
275+
$this->message->html('<p>Test nesting</p>');
276+
277+
$this->mailService->addAttachment(
278+
$this->fileSystem->url() . '/data/mail/attachments/testPdfAttachment.pdf'
279+
);
280+
$this->mailService->addAttachment(
281+
$this->fileSystem->url() . '/data/mail/attachments/testXlsAttachment.xls'
282+
);
283+
284+
$this->mailService->attachFiles();
285+
286+
$body = $this->message->getBody();
287+
$this->assertInstanceOf(MixedPart::class, $body);
288+
$parts = $body->getParts();
289+
290+
// None of the children should be a MixedPart (no nesting)
291+
foreach ($parts as $part) {
292+
$this->assertNotInstanceOf(MixedPart::class, $part);
293+
}
294+
}
156295
}

0 commit comments

Comments
 (0)