Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 97eb49b

Browse filesBrowse files
committed
[Mailer][TwigBridge] Add support for translatable subject
1 parent 86c8a8a commit 97eb49b
Copy full SHA for 97eb49b

File tree

9 files changed

+87
-6
lines changed
Filter options

9 files changed

+87
-6
lines changed

‎src/Symfony/Bridge/Twig/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/CHANGELOG.md
+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add `is_granted_for_user()` Twig function
88
* Add `field_id()` Twig form helper function
9+
* Add `TemplatedEmail::getTranslatableSubject()` method
910

1011
7.2
1112
---

‎src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php
+20-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,29 @@
1818
*/
1919
class TemplatedEmail extends Email
2020
{
21+
private string|\Stringable|null $subject = null;
2122
private ?string $htmlTemplate = null;
2223
private ?string $textTemplate = null;
2324
private ?string $locale = null;
2425
private array $context = [];
2526

27+
/**
28+
* @return $this
29+
*/
30+
public function subject(string|\Stringable $subject): static
31+
{
32+
parent::subject($subject);
33+
34+
$this->subject = $subject;
35+
36+
return $this;
37+
}
38+
39+
public function getTranslatableSubject(): string|\Stringable|null
40+
{
41+
return $this->subject;
42+
}
43+
2644
/**
2745
* @return $this
2846
*/
@@ -100,7 +118,7 @@ public function markAsRendered(): void
100118
*/
101119
public function __serialize(): array
102120
{
103-
return [$this->htmlTemplate, $this->textTemplate, $this->context, parent::__serialize(), $this->locale];
121+
return [$this->htmlTemplate, $this->textTemplate, $this->context, parent::__serialize(), $this->locale, $this->subject];
104122
}
105123

106124
/**
@@ -110,6 +128,7 @@ public function __unserialize(array $data): void
110128
{
111129
[$this->htmlTemplate, $this->textTemplate, $this->context, $parentData] = $data;
112130
$this->locale = $data[4] ?? null;
131+
$this->subject = $data[5] ?? null;
113132

114133
parent::__unserialize($parentData);
115134
}

‎src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php
+17
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer;
2121
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2222
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
23+
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
2324
use Symfony\Component\Serializer\Serializer;
25+
use Symfony\Component\Translation\IdentityTranslator;
26+
use Symfony\Component\Translation\TranslatableMessage;
2427

2528
class TemplatedEmailTest extends TestCase
2629
{
@@ -44,19 +47,22 @@ public function testSerialize()
4447
->htmlTemplate('text.html.twig')
4548
->context($context = ['a' => 'b'])
4649
->locale($locale = 'fr_FR')
50+
->subject($subject = new TranslatableMessage('hello {{ name }}', ['name' => 'John'], 'greetings'))
4751
;
4852

4953
$email = unserialize(serialize($email));
5054
$this->assertEquals('text.txt.twig', $email->getTextTemplate());
5155
$this->assertEquals('text.html.twig', $email->getHtmlTemplate());
5256
$this->assertEquals($context, $email->getContext());
5357
$this->assertEquals($locale, $email->getLocale());
58+
$this->assertEquals($subject, $email->getTranslatableSubject());
5459
}
5560

5661
public function testSymfonySerialize()
5762
{
5863
// we don't add from/sender to check that validation is not triggered to serialize an email
5964
$e = new TemplatedEmail();
65+
$e->subject(new TranslatableMessage('hello.world'));
6066
$e->to('you@example.com');
6167
$e->textTemplate('email.txt.twig');
6268
$e->htmlTemplate('email.html.twig');
@@ -67,6 +73,7 @@ public function testSymfonySerialize()
6773

6874
$expectedJson = <<<EOF
6975
{
76+
"subject": "hello.world",
7077
"htmlTemplate": "email.html.twig",
7178
"textTemplate": "email.txt.twig",
7279
"locale": "en",
@@ -84,6 +91,15 @@ public function testSymfonySerialize()
8491
}
8592
],
8693
"headers": {
94+
"subject": [
95+
{
96+
"value": "hello.world",
97+
"name": "Subject",
98+
"lineLength": 76,
99+
"lang": null,
100+
"charset": "utf-8"
101+
}
102+
],
87103
"to": [
88104
{
89105
"addresses": [
@@ -108,6 +124,7 @@ public function testSymfonySerialize()
108124
$serializer = new Serializer([
109125
new ArrayDenormalizer(),
110126
new MimeMessageNormalizer($propertyNormalizer),
127+
new TranslatableNormalizer(new IdentityTranslator()),
111128
new ObjectNormalizer(null, null, null, $extractor),
112129
$propertyNormalizer,
113130
], [new JsonEncoder()]);

‎src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php
+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
return static function (ContainerConfigurator $container) {
1919
$container->services()
2020
->set('twig.mailer.message_listener', MessageListener::class)
21-
->args([null, service('twig.mime_body_renderer')])
21+
->args([
22+
null,
23+
service('twig.mime_body_renderer'),
24+
'$translator' => service('translator')->ignoreOnInvalid(),
25+
])
2226
->tag('kernel.event_subscriber')
2327

2428
->set('twig.mime_body_renderer', BodyRenderer::class)

‎src/Symfony/Component/Mailer/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/CHANGELOG.md
+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Add DSN param `source_ip` to allow binding to a (specific) IPv4 or IPv6 address.
1010
* Add DSN param `require_tls` to enforce use of TLS/STARTTLS
1111
* Add `DkimSignedMessageListener`, `SmimeEncryptedMessageListener`, and `SmimeSignedMessageListener`
12+
* Add support for translatable subject in `TemplatedEmail`
1213

1314
7.2
1415
---

‎src/Symfony/Component/Mailer/EventListener/MessageListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/EventListener/MessageListener.php
+15-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Mailer\EventListener;
1313

14+
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1415
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1516
use Symfony\Component\Mailer\Event\MessageEvent;
1617
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
@@ -19,9 +20,11 @@
1920
use Symfony\Component\Mime\Header\Headers;
2021
use Symfony\Component\Mime\Header\MailboxListHeader;
2122
use Symfony\Component\Mime\Message;
23+
use Symfony\Component\Translation\TranslatableMessage;
24+
use Symfony\Contracts\Translation\TranslatorInterface;
2225

2326
/**
24-
* Manipulates the headers and the body of a Message.
27+
* Manipulates the headers, subject, and the body of a Message.
2528
*
2629
* @author Fabien Potencier <fabien@symfony.com>
2730
*/
@@ -45,6 +48,7 @@ public function __construct(
4548
private ?Headers $headers = null,
4649
private ?BodyRendererInterface $renderer = null,
4750
array $headerRules = self::DEFAULT_RULES,
51+
private ?TranslatorInterface $translator = null,
4852
) {
4953
foreach ($headerRules as $headerName => $rule) {
5054
$this->addHeaderRule($headerName, $rule);
@@ -68,6 +72,7 @@ public function onMessage(MessageEvent $event): void
6872
}
6973

7074
$this->setHeaders($message);
75+
$this->translateSubject($message);
7176
$this->renderMessage($message);
7277
}
7378

@@ -115,6 +120,15 @@ private function setHeaders(Message $message): void
115120
}
116121
}
117122

123+
private function translateSubject(Message $message): void
124+
{
125+
if (!$message instanceof TemplatedEmail || !$this->translator || !$message->getTranslatableSubject() instanceof TranslatableMessage) {
126+
return;
127+
}
128+
129+
$message->subject($message->getTranslatableSubject()->trans($this->translator, $message->getLocale()));
130+
}
131+
118132
private function renderMessage(Message $message): void
119133
{
120134
if (!$this->renderer) {

‎src/Symfony/Component/Mailer/Tests/EventListener/MessageListenerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Tests/EventListener/MessageListenerTest.php
+24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Mailer\Tests\EventListener;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1516
use Symfony\Component\Mailer\Envelope;
1617
use Symfony\Component\Mailer\Event\MessageEvent;
1718
use Symfony\Component\Mailer\EventListener\MessageListener;
@@ -20,6 +21,8 @@
2021
use Symfony\Component\Mime\Header\MailboxListHeader;
2122
use Symfony\Component\Mime\Header\UnstructuredHeader;
2223
use Symfony\Component\Mime\Message;
24+
use Symfony\Component\Translation\TranslatableMessage;
25+
use Symfony\Contracts\Translation\TranslatorInterface;
2326

2427
class MessageListenerTest extends TestCase
2528
{
@@ -114,4 +117,25 @@ public static function provideHeaders(): iterable
114117
];
115118
yield 'Capitalized header rule (case-insensitive), replace if set' => [$initialHeaders, $defaultHeaders, $defaultHeaders, $rules];
116119
}
120+
121+
public function testTranslatableSubject()
122+
{
123+
$message = new TemplatedEmail();
124+
$message->subject(new TranslatableMessage('hello.world'));
125+
$listener = new MessageListener(translator: new class implements TranslatorInterface {
126+
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
127+
{
128+
return 'Hello World';
129+
}
130+
131+
public function getLocale(): string
132+
{
133+
return 'en';
134+
}
135+
});
136+
$event = new MessageEvent($message, new Envelope(new Address('sender@example.com'), [new Address('recipient@example.com')]), 'smtp');
137+
$listener->onMessage($event);
138+
139+
$this->assertSame('Hello World', $message->getSubject());
140+
}
117141
}

‎src/Symfony/Component/Mailer/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/composer.json
+2-1
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
"psr/event-dispatcher": "^1",
2222
"psr/log": "^1|^2|^3",
2323
"symfony/event-dispatcher": "^6.4|^7.0",
24-
"symfony/mime": "^7.2",
24+
"symfony/mime": "^7.3",
2525
"symfony/service-contracts": "^2.5|^3"
2626
},
2727
"require-dev": {
2828
"symfony/console": "^6.4|^7.0",
2929
"symfony/http-client": "^6.4|^7.0",
3030
"symfony/messenger": "^6.4|^7.0",
31+
"symfony/translation": "^6.4|^7.0",
3132
"symfony/twig-bridge": "^6.4|^7.0"
3233
},
3334
"conflict": {

‎src/Symfony/Component/Mime/Email.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mime/Email.php
+2-2
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ class Email extends Message
5858
/**
5959
* @return $this
6060
*/
61-
public function subject(string $subject): static
61+
public function subject(string|\Stringable $subject): static
6262
{
63-
return $this->setHeaderBody('Text', 'Subject', $subject);
63+
return $this->setHeaderBody('Text', 'Subject', (string) $subject);
6464
}
6565

6666
public function getSubject(): ?string

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.