Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Commit 1c279ec

Browse filesBrowse the repository at this point in the historyBrowse files
committed
[Mailer] Add RemoteTemplateEmail to send emails rendered from provider-hosted templates
Add a first-class abstraction for emails rendered by the mail provider from a template hosted on its side: * `RemoteTemplateEmail` carries a template reference and its variables, and allows sending without a local text/HTML body; * transports opt in by implementing `RemoteTemplateTransportInterface` and mapping the template to their native API payload; other transports fail with an explicit exception instead of silently dropping the template; * implement the interface in the Brevo, Resend and Sendgrid bridges; * deprecate the "templateid" and "params" magic headers of the Brevo bridge in favor of the new abstraction.
1 parent ff431bb commit 1c279ec
Copy full SHA for 1c279ec

24 files changed

+548-21Lines changed: 548 additions & 21 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎UPGRADE-8.2.md‎

Copy file name to clipboardExpand all lines: UPGRADE-8.2.md
+5Lines changed: 5 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ HttpFoundation
1818

1919
* Add argument `$version` to `UriSigner::sign()`, `UriSigner::check()`, `UriSigner::checkRequest()`, and `UriSigner::verify()`
2020

21+
Mailer
22+
------
23+
24+
* Deprecate the "templateid" and "params" email headers in the Brevo bridge, use a `RemoteTemplateEmail` instead
25+
2126
Serializer
2227
----------
2328

Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Brevo/CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
8.2
5+
---
6+
7+
* Add support for sending emails rendered from a remote template via `RemoteTemplateEmail`
8+
* Deprecate the "templateid" and "params" email headers, use a `RemoteTemplateEmail` instead
9+
410
6.4
511
---
612

Collapse file

‎src/Symfony/Component/Mailer/Bridge/Brevo/Tests/Transport/BrevoApiTransportTest.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Brevo/Tests/Transport/BrevoApiTransportTest.php
+44-8Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
namespace Symfony\Component\Mailer\Bridge\Brevo\Tests\Transport;
1313

1414
use PHPUnit\Framework\Attributes\DataProvider;
15+
use PHPUnit\Framework\Attributes\Group;
16+
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
1517
use PHPUnit\Framework\TestCase;
18+
use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait;
1619
use Symfony\Component\HttpClient\MockHttpClient;
1720
use Symfony\Component\HttpClient\Response\JsonMockResponse;
1821
use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoApiTransport;
@@ -21,13 +24,16 @@
2124
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
2225
use Symfony\Component\Mailer\Header\MetadataHeader;
2326
use Symfony\Component\Mailer\Header\TagHeader;
27+
use Symfony\Component\Mailer\RemoteTemplateEmail;
2428
use Symfony\Component\Mime\Address;
2529
use Symfony\Component\Mime\Email;
2630
use Symfony\Component\Mime\Part\DataPart;
2731
use Symfony\Contracts\HttpClient\ResponseInterface;
2832

2933
class BrevoApiTransportTest extends TestCase
3034
{
35+
use ExpectUserDeprecationMessageTrait;
36+
3137
#[DataProvider('getTransportData')]
3238
public function testToString(BrevoApiTransport $transport, string $expected)
3339
{
@@ -54,15 +60,12 @@ public static function getTransportData(): \Generator
5460

5561
public function testCustomHeader()
5662
{
57-
$params = ['param1' => 'foo', 'param2' => 'bar'];
5863
$json = json_encode(['"custom_header_1' => 'custom_value_1']);
5964

6065
$email = new Email();
6166
$email->getHeaders()
6267
->add(new MetadataHeader('custom', $json))
6368
->add(new TagHeader('TagInHeaders'))
64-
->addTextHeader('templateId', 1)
65-
->addParameterizedHeader('params', 'params', $params)
6669
->addTextHeader('foo', 'bar');
6770
$envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]);
6871

@@ -75,15 +78,48 @@ public function testCustomHeader()
7578

7679
$this->assertArrayHasKey('tags', $payload);
7780
$this->assertEquals('TagInHeaders', current($payload['tags']));
78-
$this->assertArrayHasKey('templateId', $payload);
79-
$this->assertEquals(1, $payload['templateId']);
80-
$this->assertArrayHasKey('params', $payload);
81-
$this->assertEquals('foo', $payload['params']['param1']);
82-
$this->assertEquals('bar', $payload['params']['param2']);
8381
$this->assertArrayHasKey('foo', $payload['headers']);
8482
$this->assertEquals('bar', $payload['headers']['foo']);
8583
}
8684

85+
public function testRemoteTemplate()
86+
{
87+
$email = (new RemoteTemplateEmail())
88+
->template('42', ['param1' => 'foo', 'param2' => 'bar']);
89+
$envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]);
90+
91+
$transport = new BrevoApiTransport('ACCESS_KEY');
92+
$method = new \ReflectionMethod(BrevoApiTransport::class, 'getPayload');
93+
$payload = $method->invoke($transport, $email, $envelope);
94+
95+
$this->assertSame(42, $payload['templateId']);
96+
$this->assertSame(['param1' => 'foo', 'param2' => 'bar'], $payload['params']);
97+
$this->assertArrayNotHasKey('subject', $payload);
98+
$this->assertArrayNotHasKey('textContent', $payload);
99+
$this->assertArrayNotHasKey('htmlContent', $payload);
100+
}
101+
102+
#[IgnoreDeprecations]
103+
#[Group('legacy')]
104+
public function testDeprecatedTemplateHeaders()
105+
{
106+
$this->expectUserDeprecationMessage(\sprintf('Since symfony/brevo-mailer 8.2: Using the "templateid" email header to select a Brevo template is deprecated, use a "%s" instead.', RemoteTemplateEmail::class));
107+
$this->expectUserDeprecationMessage(\sprintf('Since symfony/brevo-mailer 8.2: Using the "params" email header to define the variables of a Brevo template is deprecated, use a "%s" instead.', RemoteTemplateEmail::class));
108+
109+
$email = new Email();
110+
$email->getHeaders()
111+
->addTextHeader('templateId', 1)
112+
->addParameterizedHeader('params', 'params', ['param1' => 'foo', 'param2' => 'bar']);
113+
$envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]);
114+
115+
$transport = new BrevoApiTransport('ACCESS_KEY');
116+
$method = new \ReflectionMethod(BrevoApiTransport::class, 'getPayload');
117+
$payload = $method->invoke($transport, $email, $envelope);
118+
119+
$this->assertSame(1, $payload['templateId']);
120+
$this->assertSame(['param1' => 'foo', 'param2' => 'bar'], $payload['params']);
121+
}
122+
87123
public function testSendThrowsForErrorResponse()
88124
{
89125
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
Collapse file

‎src/Symfony/Component/Mailer/Bridge/Brevo/Transport/BrevoApiTransport.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Brevo/Transport/BrevoApiTransport.php
+16-2Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
use Symfony\Component\Mailer\Exception\HttpTransportException;
1818
use Symfony\Component\Mailer\Header\MetadataHeader;
1919
use Symfony\Component\Mailer\Header\TagHeader;
20+
use Symfony\Component\Mailer\RemoteTemplateEmail;
2021
use Symfony\Component\Mailer\SentMessage;
2122
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
23+
use Symfony\Component\Mailer\Transport\RemoteTemplateTransportInterface;
2224
use Symfony\Component\Mime\Address;
2325
use Symfony\Component\Mime\Email;
2426
use Symfony\Component\Mime\Header\Headers;
@@ -30,7 +32,7 @@
3032
/**
3133
* @author Pierre TANGUY
3234
*/
33-
final class BrevoApiTransport extends AbstractApiTransport
35+
final class BrevoApiTransport extends AbstractApiTransport implements RemoteTemplateTransportInterface
3436
{
3537
public function __construct(
3638
#[\SensitiveParameter] private string $key,
@@ -88,11 +90,21 @@ private function formatAddresses(array $addresses): array
8890

8991
private function getPayload(Email $email, Envelope $envelope): array
9092
{
93+
$template = $email instanceof RemoteTemplateEmail ? $email->getRemoteTemplate() : null;
94+
9195
$payload = [
9296
'sender' => $this->formatAddress($envelope->getSender()),
9397
'to' => $this->formatAddresses($this->getRecipients($email, $envelope)),
94-
'subject' => $email->getSubject(),
9598
];
99+
if (null === $template || null !== $email->getSubject()) {
100+
$payload['subject'] = $email->getSubject();
101+
}
102+
if (null !== $template) {
103+
$payload['templateId'] = (int) $template->getReference();
104+
if ($template->getVariables()) {
105+
$payload['params'] = $template->getVariables();
106+
}
107+
}
96108
if ($attachments = $this->prepareAttachments($email)) {
97109
$payload['attachment'] = $attachments;
98110
}
@@ -154,11 +166,13 @@ private function prepareHeadersAndTags(Headers $headers): array
154166
continue;
155167
}
156168
if ('templateid' === $name) {
169+
trigger_deprecation('symfony/brevo-mailer', '8.2', 'Using the "templateid" email header to select a Brevo template is deprecated, use a "%s" instead.', RemoteTemplateEmail::class);
157170
$headersAndTags[$header->getName()] = (int) $header->getValue();
158171

159172
continue;
160173
}
161174
if ('params' === $name) {
175+
trigger_deprecation('symfony/brevo-mailer', '8.2', 'Using the "params" email header to define the variables of a Brevo template is deprecated, use a "%s" instead.', RemoteTemplateEmail::class);
162176
$headersAndTags[$header->getName()] = $header->getParameters();
163177

164178
continue;
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Brevo/composer.json
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
],
1818
"require": {
1919
"php": ">=8.4.1",
20-
"symfony/mailer": "^7.4|^8.0"
20+
"symfony/deprecation-contracts": "^2.5|^3",
21+
"symfony/mailer": "^8.2"
2122
},
2223
"require-dev": {
2324
"symfony/http-client": "^7.4|^8.0",
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Resend/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
8.2
5+
---
6+
7+
* Add support for sending emails rendered from a remote template via `RemoteTemplateEmail`
8+
49
7.1
510
---
611

Collapse file

‎src/Symfony/Component/Mailer/Bridge/Resend/Tests/Transport/ResendApiTransportTest.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Resend/Tests/Transport/ResendApiTransportTest.php
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Mailer\Exception\HttpTransportException;
2121
use Symfony\Component\Mailer\Header\MetadataHeader;
2222
use Symfony\Component\Mailer\Header\TagHeader;
23+
use Symfony\Component\Mailer\RemoteTemplateEmail;
2324
use Symfony\Component\Mime\Address;
2425
use Symfony\Component\Mime\Email;
2526
use Symfony\Component\Mime\Part\DataPart;
@@ -81,6 +82,37 @@ public function testCustomHeader()
8182
$this->assertEquals('bar', $payload['headers']['foo']);
8283
}
8384

85+
public function testRemoteTemplate()
86+
{
87+
$email = (new RemoteTemplateEmail())
88+
->template('tpl_123', ['firstName' => 'Fabien']);
89+
$envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]);
90+
91+
$transport = new ResendApiTransport('ACCESS_KEY');
92+
$method = new \ReflectionMethod(ResendApiTransport::class, 'getPayload');
93+
$payload = $method->invoke($transport, $email, $envelope);
94+
95+
$this->assertSame(['id' => 'tpl_123', 'variables' => ['firstName' => 'Fabien']], $payload['template']);
96+
$this->assertArrayNotHasKey('subject', $payload);
97+
$this->assertArrayNotHasKey('text', $payload);
98+
$this->assertArrayNotHasKey('html', $payload);
99+
}
100+
101+
public function testRemoteTemplateWithSubject()
102+
{
103+
$email = (new RemoteTemplateEmail())
104+
->subject('Hello!')
105+
->template('tpl_123');
106+
$envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]);
107+
108+
$transport = new ResendApiTransport('ACCESS_KEY');
109+
$method = new \ReflectionMethod(ResendApiTransport::class, 'getPayload');
110+
$payload = $method->invoke($transport, $email, $envelope);
111+
112+
$this->assertSame('Hello!', $payload['subject']);
113+
$this->assertSame(['id' => 'tpl_123'], $payload['template']);
114+
}
115+
84116
public function testSendThrowsForErrorResponse()
85117
{
86118
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
Collapse file

‎src/Symfony/Component/Mailer/Bridge/Resend/Transport/ResendApiTransport.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Resend/Transport/ResendApiTransport.php
+14-2Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
use Symfony\Component\Mailer\Exception\HttpTransportException;
1818
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
1919
use Symfony\Component\Mailer\Header\TagHeader;
20+
use Symfony\Component\Mailer\RemoteTemplateEmail;
2021
use Symfony\Component\Mailer\SentMessage;
2122
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
23+
use Symfony\Component\Mailer\Transport\RemoteTemplateTransportInterface;
2224
use Symfony\Component\Mime\Address;
2325
use Symfony\Component\Mime\Email;
2426
use Symfony\Component\Mime\Header\Headers;
@@ -30,7 +32,7 @@
3032
/**
3133
* @author Mathieu Santostefano <msantostefano@proton.me>
3234
*/
33-
final class ResendApiTransport extends AbstractApiTransport
35+
final class ResendApiTransport extends AbstractApiTransport implements RemoteTemplateTransportInterface
3436
{
3537
public function __construct(
3638
#[\SensitiveParameter] private readonly string $apiKey,
@@ -94,11 +96,21 @@ private function formatAddresses(array $addresses): array
9496

9597
private function getPayload(Email $email, Envelope $envelope): array
9698
{
99+
$template = $email instanceof RemoteTemplateEmail ? $email->getRemoteTemplate() : null;
100+
97101
$payload = [
98102
'from' => $this->formatAddress($envelope->getSender()),
99103
'to' => $this->formatAddresses($this->getRecipients($email, $envelope)),
100-
'subject' => $email->getSubject(),
101104
];
105+
if (null === $template || null !== $email->getSubject()) {
106+
$payload['subject'] = $email->getSubject();
107+
}
108+
if (null !== $template) {
109+
$payload['template'] = ['id' => $template->getReference()];
110+
if ($template->getVariables()) {
111+
$payload['template']['variables'] = $template->getVariables();
112+
}
113+
}
102114
if ($attachments = $this->prepareAttachments($email)) {
103115
$payload['attachments'] = $attachments;
104116
}
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Resend/composer.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=8.4.1",
20-
"symfony/mailer": "^7.4|^8.0"
20+
"symfony/mailer": "^8.2"
2121
},
2222
"require-dev": {
2323
"symfony/http-client": "^7.4|^8.0",
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Sendgrid/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
8.2
5+
---
6+
7+
* Add support for sending emails rendered from a remote template via `RemoteTemplateEmail`
8+
49
8.1
510
---
611

0 commit comments

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