Skip to content

Navigation Menu

Sign in
Appearance settings

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 81abb4e

Browse filesBrowse files
committed
feature #35050 [Mailer] added tag/metadata support (kbond)
This PR was merged into the 5.1-dev branch. Discussion ---------- [Mailer] added tag/metadata support | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #35047 | License | MIT | Doc PR | todo This is an alternative to #34766 for adding tag and metadata support in a more generalized way. Most transports allow for open/click tracking headers - maybe this should be handled in a similar way? I added implementations for the Postmark (SMTP and API) and Mailgun (SMTP and API) transports. I can add others and tests/docs if this is acceptable. ### Example: ```php use Symfony\Component\Mailer\Header\MetadataHeader; use Symfony\Component\Mailer\Header\TagHeader; $email->getHeaders()->add(new TagHeader('password-reset')); $email->getHeaders()->add(new MetadataHeader('Color', 'blue')); $email->getHeaders()->add(new MetadataHeader('Client-ID', '12345')); ``` The Postmark/Mailgun providers will parse these into their own headers/payload. For transports that don't support tags/metadata, these are just added as custom headers: ``` X-Tag: password-reset X-Metadata-Color: blue X-Metadata-Client-ID: 12345 ``` Commits ------- f2cdafc [Mailer] added tag/metadata support
2 parents a2b6085 + f2cdafc commit 81abb4e
Copy full SHA for 81abb4e

23 files changed

+516
-4
lines changed

‎src/Symfony/Component/Mailer/Bridge/Mailchimp/Tests/Transport/MandrillApiTransportTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/Tests/Transport/MandrillApiTransportTest.php
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillApiTransport;
1616
use Symfony\Component\Mailer\Envelope;
17+
use Symfony\Component\Mailer\Header\MetadataHeader;
18+
use Symfony\Component\Mailer\Header\TagHeader;
1719
use Symfony\Component\Mime\Address;
1820
use Symfony\Component\Mime\Email;
1921

@@ -61,4 +63,42 @@ public function testCustomHeader()
6163
$this->assertCount(1, $payload['message']['headers']);
6264
$this->assertEquals('foo: bar', $payload['message']['headers'][0]);
6365
}
66+
67+
public function testTagAndMetadataHeaders()
68+
{
69+
$email = new Email();
70+
$email->getHeaders()->add(new TagHeader('password-reset'));
71+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
72+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
73+
$envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]);
74+
75+
$transport = new MandrillApiTransport('ACCESS_KEY');
76+
$method = new \ReflectionMethod(MandrillApiTransport::class, 'getPayload');
77+
$method->setAccessible(true);
78+
$payload = $method->invoke($transport, $email, $envelope);
79+
80+
$this->assertArrayHasKey('message', $payload);
81+
$this->assertArrayNotHasKey('headers', $payload['message']);
82+
$this->assertArrayHasKey('tags', $payload['message']);
83+
$this->assertSame(['password-reset'], $payload['message']['tags']);
84+
$this->assertArrayHasKey('metadata', $payload['message']);
85+
$this->assertSame(['Color' => 'blue', 'Client-ID' => '12345'], $payload['message']['metadata']);
86+
}
87+
88+
public function testCanHaveMultipleTags()
89+
{
90+
$email = new Email();
91+
$email->getHeaders()->add(new TagHeader('password-reset,user'));
92+
$envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]);
93+
94+
$transport = new MandrillApiTransport('ACCESS_KEY');
95+
$method = new \ReflectionMethod(MandrillApiTransport::class, 'getPayload');
96+
$method->setAccessible(true);
97+
$payload = $method->invoke($transport, $email, $envelope);
98+
99+
$this->assertArrayHasKey('message', $payload);
100+
$this->assertArrayNotHasKey('headers', $payload['message']);
101+
$this->assertArrayHasKey('tags', $payload['message']);
102+
$this->assertSame(['password-reset', 'user'], $payload['message']['tags']);
103+
}
64104
}

‎src/Symfony/Component/Mailer/Bridge/Mailchimp/Tests/Transport/MandrillHttpTransportTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/Tests/Transport/MandrillHttpTransportTest.php
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillHttpTransport;
16+
use Symfony\Component\Mailer\Header\MetadataHeader;
17+
use Symfony\Component\Mailer\Header\TagHeader;
18+
use Symfony\Component\Mime\Email;
1619

1720
class MandrillHttpTransportTest extends TestCase
1821
{
@@ -41,4 +44,23 @@ public function getTransportData()
4144
],
4245
];
4346
}
47+
48+
public function testTagAndMetadataHeaders()
49+
{
50+
$email = new Email();
51+
$email->getHeaders()->addTextHeader('foo', 'bar');
52+
$email->getHeaders()->add(new TagHeader('password-reset,user'));
53+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
54+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
55+
56+
$transport = new MandrillHttpTransport('key');
57+
$method = new \ReflectionMethod(MandrillHttpTransport::class, 'addMandrillHeaders');
58+
$method->setAccessible(true);
59+
$method->invoke($transport, $email);
60+
61+
$this->assertCount(3, $email->getHeaders()->toArray());
62+
$this->assertSame('foo: bar', $email->getHeaders()->get('FOO')->toString());
63+
$this->assertSame('X-MC-Tags: password-reset,user', $email->getHeaders()->get('X-MC-Tags')->toString());
64+
$this->assertSame('X-MC-Metadata: '.json_encode(['Color' => 'blue', 'Client-ID' => '12345']), $email->getHeaders()->get('X-MC-Metadata')->toString());
65+
}
4466
}
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Mailchimp\Tests\Transport;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillSmtpTransport;
16+
use Symfony\Component\Mailer\Header\MetadataHeader;
17+
use Symfony\Component\Mailer\Header\TagHeader;
18+
use Symfony\Component\Mime\Email;
19+
20+
class MandrillSmtpTransportTest extends TestCase
21+
{
22+
public function testTagAndMetadataHeaders()
23+
{
24+
$email = new Email();
25+
$email->getHeaders()->addTextHeader('foo', 'bar');
26+
$email->getHeaders()->add(new TagHeader('password-reset,user'));
27+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
28+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
29+
30+
$transport = new MandrillSmtpTransport('user', 'password');
31+
$method = new \ReflectionMethod(MandrillSmtpTransport::class, 'addMandrillHeaders');
32+
$method->setAccessible(true);
33+
$method->invoke($transport, $email);
34+
35+
$this->assertCount(3, $email->getHeaders()->toArray());
36+
$this->assertSame('foo: bar', $email->getHeaders()->get('FOO')->toString());
37+
$this->assertSame('X-MC-Tags: password-reset,user', $email->getHeaders()->get('X-MC-Tags')->toString());
38+
$this->assertSame('X-MC-Metadata: '.json_encode(['Color' => 'blue', 'Client-ID' => '12345']), $email->getHeaders()->get('X-MC-Metadata')->toString());
39+
}
40+
}

‎src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\Mailer\Envelope;
1616
use Symfony\Component\Mailer\Exception\HttpTransportException;
17+
use Symfony\Component\Mailer\Header\MetadataHeader;
18+
use Symfony\Component\Mailer\Header\TagHeader;
1719
use Symfony\Component\Mailer\SentMessage;
1820
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
1921
use Symfony\Component\Mime\Email;
@@ -111,6 +113,18 @@ private function getPayload(Email $email, Envelope $envelope): array
111113
continue;
112114
}
113115

116+
if ($header instanceof TagHeader) {
117+
$payload['message']['tags'] = explode(',', $header->getValue());
118+
119+
continue;
120+
}
121+
122+
if ($header instanceof MetadataHeader) {
123+
$payload['message']['metadata'][$header->getKey()] = $header->getValue();
124+
125+
continue;
126+
}
127+
114128
$payload['message']['headers'][] = $name.': '.$header->getBodyAsString();
115129
}
116130

+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Mailchimp\Transport;
13+
14+
use Symfony\Component\Mailer\Envelope;
15+
use Symfony\Component\Mailer\Header\MetadataHeader;
16+
use Symfony\Component\Mailer\Header\TagHeader;
17+
use Symfony\Component\Mailer\SentMessage;
18+
use Symfony\Component\Mime\Message;
19+
use Symfony\Component\Mime\RawMessage;
20+
21+
/**
22+
* @author Kevin Bond <kevinbond@gmail.com>
23+
*/
24+
trait MandrillHeadersTrait
25+
{
26+
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
27+
{
28+
if ($message instanceof Message) {
29+
$this->addMandrillHeaders($message);
30+
}
31+
32+
return parent::send($message, $envelope);
33+
}
34+
35+
private function addMandrillHeaders(Message $message): void
36+
{
37+
$headers = $message->getHeaders();
38+
$metadata = [];
39+
40+
foreach ($headers->all() as $name => $header) {
41+
if ($header instanceof TagHeader) {
42+
$headers->addTextHeader('X-MC-Tags', $header->getValue());
43+
$headers->remove($name);
44+
} elseif ($header instanceof MetadataHeader) {
45+
$metadata[$header->getKey()] = $header->getValue();
46+
$headers->remove($name);
47+
}
48+
}
49+
50+
if ($metadata) {
51+
$headers->addTextHeader('X-MC-Metadata', json_encode($metadata));
52+
}
53+
}
54+
}

‎src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
class MandrillHttpTransport extends AbstractHttpTransport
2626
{
27+
use MandrillHeadersTrait;
28+
2729
private const HOST = 'mandrillapp.com';
2830
private $key;
2931

‎src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillSmtpTransport.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillSmtpTransport.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
class MandrillSmtpTransport extends EsmtpTransport
2222
{
23+
use MandrillHeadersTrait;
24+
2325
public function __construct(string $username, string $password, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
2426
{
2527
parent::__construct('smtp.mandrillapp.com', 587, true, $dispatcher, $logger);

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailchimp/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": "^7.2.5",
20-
"symfony/mailer": "^4.4|^5.0"
20+
"symfony/mailer": "^5.1"
2121
},
2222
"require-dev": {
2323
"symfony/http-client": "^4.4|^5.0"

‎src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunApiTransportTest.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunApiTransport;
1616
use Symfony\Component\Mailer\Envelope;
17+
use Symfony\Component\Mailer\Header\MetadataHeader;
18+
use Symfony\Component\Mailer\Header\TagHeader;
1719
use Symfony\Component\Mime\Address;
1820
use Symfony\Component\Mime\Email;
1921

@@ -64,4 +66,29 @@ public function testCustomHeader()
6466
$this->assertArrayHasKey('h:x-mailgun-variables', $payload);
6567
$this->assertEquals($json, $payload['h:x-mailgun-variables']);
6668
}
69+
70+
public function testTagAndMetadataHeaders()
71+
{
72+
$json = json_encode(['foo' => 'bar']);
73+
$email = new Email();
74+
$email->getHeaders()->addTextHeader('X-Mailgun-Variables', $json);
75+
$email->getHeaders()->add(new TagHeader('password-reset'));
76+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
77+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
78+
$envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]);
79+
80+
$transport = new MailgunApiTransport('ACCESS_KEY', 'DOMAIN');
81+
$method = new \ReflectionMethod(MailgunApiTransport::class, 'getPayload');
82+
$method->setAccessible(true);
83+
$payload = $method->invoke($transport, $email, $envelope);
84+
85+
$this->assertArrayHasKey('h:x-mailgun-variables', $payload);
86+
$this->assertEquals($json, $payload['h:x-mailgun-variables']);
87+
$this->assertArrayHasKey('o:tag', $payload);
88+
$this->assertSame('password-reset', $payload['o:tag']);
89+
$this->assertArrayHasKey('v:Color', $payload);
90+
$this->assertSame('blue', $payload['v:Color']);
91+
$this->assertArrayHasKey('v:Client-ID', $payload);
92+
$this->assertSame('12345', $payload['v:Client-ID']);
93+
}
6794
}

‎src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunHttpTransportTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailgun/Tests/Transport/MailgunHttpTransportTest.php
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunHttpTransport;
16+
use Symfony\Component\Mailer\Header\MetadataHeader;
17+
use Symfony\Component\Mailer\Header\TagHeader;
18+
use Symfony\Component\Mime\Email;
1619

1720
class MailgunHttpTransportTest extends TestCase
1821
{
@@ -45,4 +48,23 @@ public function getTransportData()
4548
],
4649
];
4750
}
51+
52+
public function testTagAndMetadataHeaders()
53+
{
54+
$email = new Email();
55+
$email->getHeaders()->addTextHeader('foo', 'bar');
56+
$email->getHeaders()->add(new TagHeader('password-reset'));
57+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
58+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
59+
60+
$transport = new MailgunHttpTransport('key', 'domain');
61+
$method = new \ReflectionMethod(MailgunHttpTransport::class, 'addMailgunHeaders');
62+
$method->setAccessible(true);
63+
$method->invoke($transport, $email);
64+
65+
$this->assertCount(3, $email->getHeaders()->toArray());
66+
$this->assertSame('foo: bar', $email->getHeaders()->get('foo')->toString());
67+
$this->assertSame('X-Mailgun-Tag: password-reset', $email->getHeaders()->get('X-Mailgun-Tag')->toString());
68+
$this->assertSame('X-Mailgun-Variables: '.json_encode(['Color' => 'blue', 'Client-ID' => '12345']), $email->getHeaders()->get('X-Mailgun-Variables')->toString());
69+
}
4870
}
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Mailgun\Tests\Transport;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunSmtpTransport;
16+
use Symfony\Component\Mailer\Header\MetadataHeader;
17+
use Symfony\Component\Mailer\Header\TagHeader;
18+
use Symfony\Component\Mime\Email;
19+
20+
/**
21+
* @author Kevin Bond <kevinbond@gmail.com>
22+
*/
23+
class MailgunSmtpTransportTest extends TestCase
24+
{
25+
public function testTagAndMetadataHeaders()
26+
{
27+
$email = new Email();
28+
$email->getHeaders()->addTextHeader('foo', 'bar');
29+
$email->getHeaders()->add(new TagHeader('password-reset'));
30+
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
31+
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
32+
33+
$transport = new MailgunSmtpTransport('user', 'password');
34+
$method = new \ReflectionMethod(MailgunSmtpTransport::class, 'addMailgunHeaders');
35+
$method->setAccessible(true);
36+
$method->invoke($transport, $email);
37+
38+
$this->assertCount(3, $email->getHeaders()->toArray());
39+
$this->assertSame('foo: bar', $email->getHeaders()->get('foo')->toString());
40+
$this->assertSame('X-Mailgun-Tag: password-reset', $email->getHeaders()->get('X-Mailgun-Tag')->toString());
41+
$this->assertSame('X-Mailgun-Variables: '.json_encode(['Color' => 'blue', 'Client-ID' => '12345']), $email->getHeaders()->get('X-Mailgun-Variables')->toString());
42+
}
43+
}

‎src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\Mailer\Envelope;
1616
use Symfony\Component\Mailer\Exception\HttpTransportException;
17+
use Symfony\Component\Mailer\Header\MetadataHeader;
18+
use Symfony\Component\Mailer\Header\TagHeader;
1719
use Symfony\Component\Mailer\SentMessage;
1820
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
1921
use Symfony\Component\Mime\Email;
@@ -114,6 +116,18 @@ private function getPayload(Email $email, Envelope $envelope): array
114116
continue;
115117
}
116118

119+
if ($header instanceof TagHeader) {
120+
$payload['o:tag'] = $header->getValue();
121+
122+
continue;
123+
}
124+
125+
if ($header instanceof MetadataHeader) {
126+
$payload['v:'.$header->getKey()] = $header->getValue();
127+
128+
continue;
129+
}
130+
117131
$payload['h:'.$name] = $header->getBodyAsString();
118132
}
119133

0 commit comments

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