Skip to content

Commit 45836fa

Browse files
committed
Add initial draft of ReplyTo feature
1 parent f056113 commit 45836fa

19 files changed

+236
-2
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
vendor/
33
.DS_Store
44
composer.lock
5-
.phpunit.result.cache
5+
.phpunit.result.cache
6+
.phpunit.cache

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ Email::compose()
109109
110110
```
111111

112+
### Reply-To
113+
114+
```php
115+
<?php
116+
117+
use Stackkit\LaravelDatabaseEmails\Email;
118+
119+
Email::compose()
120+
121+
```
122+
112123
### Using mailables
113124

114125
You may also pass a mailable to the e-mail composer.

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
"l6": [
5555
"composer require laravel/framework:8.* orchestra/testbench:6.* --no-interaction --no-update",
5656
"composer update --prefer-stable --prefer-dist --no-interaction"
57+
],
58+
"test": [
59+
"CI_DB_DRIVER=sqlite CI_DB_DATABASE=:memory: phpunit"
5760
]
5861
}
5962
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Schema;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Database\Migrations\Migration;
6+
7+
class AddReplyToToEmailsTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::table('emails', function (Blueprint $table) {
17+
$table->binary('reply_to')->nullable()->after('bcc');
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*
24+
* @return void
25+
*/
26+
public function down()
27+
{
28+
//
29+
}
30+
}

src/Email.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* @property $from
1616
* @property $cc
1717
* @property $bcc
18+
* @property $reply_to
1819
* @property $subject
1920
* @property $view
2021
* @property $variables
@@ -190,6 +191,26 @@ public function getBccAttribute()
190191
return $this->bcc;
191192
}
192193

194+
/**
195+
* Get the e-mail reply-to addresses.
196+
*
197+
* @return array|string
198+
*/
199+
public function getReplyTo()
200+
{
201+
return $this->reply_to;
202+
}
203+
204+
/**
205+
* Get the e-mail reply-to addresses.
206+
*
207+
* @return array|string
208+
*/
209+
public function getReplyToAttribute()
210+
{
211+
return $this->reply_to;
212+
}
213+
193214
/**
194215
* Get the e-mail subject.
195216
*
@@ -388,6 +409,16 @@ public function hasBcc(): bool
388409
return strlen($this->getRawDatabaseValue('bcc')) > 0;
389410
}
390411

412+
/**
413+
* Determine if the e-mail should sent with reply-to.
414+
*
415+
* @return bool
416+
*/
417+
public function hasReplyTo(): bool
418+
{
419+
return strlen($this->getRawDatabaseValue('reply_to') ?: '') > 0;
420+
}
421+
391422
/**
392423
* Determine if the e-mail is scheduled to be sent later.
393424
*

src/EmailComposer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ public function bcc($bcc): self
139139
return $this->setData('bcc', $bcc);
140140
}
141141

142+
/**
143+
* Define the reply-to address(es).
144+
*
145+
* @param string|array $replyTo
146+
* @return self
147+
*/
148+
public function replyTo($replyTo): self
149+
{
150+
return $this->setData('reply_to', $replyTo);
151+
}
152+
142153
/**
143154
* Set the e-mail subject.
144155
*

src/Encrypter.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public function encrypt(EmailComposer $composer): void
1717

1818
$this->encryptRecipients($composer);
1919

20+
$this->encryptReplyTo($composer);
21+
2022
$this->encryptFrom($composer);
2123

2224
$this->encryptSubject($composer);
@@ -36,6 +38,20 @@ private function setEncrypted(EmailComposer $composer): void
3638
$composer->getEmail()->setAttribute('encrypted', 1);
3739
}
3840

41+
/**
42+
* Encrypt the e-mail reply-to.
43+
*
44+
* @param EmailComposer $composer
45+
*/
46+
private function encryptReplyTo(EmailComposer $composer): void
47+
{
48+
$email = $composer->getEmail();
49+
50+
$email->fill([
51+
'reply_to' => $composer->hasData('reply_to') ? encrypt($email->reply_to) : '',
52+
]);
53+
}
54+
3955
/**
4056
* Encrypt the e-mail addresses of the recipients.
4157
*

src/HasEncryptedAttributes.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ trait HasEncryptedAttributes
1818
'from',
1919
'cc',
2020
'bcc',
21+
'reply_to',
2122
'subject',
2223
'variables',
2324
'body',
@@ -33,6 +34,7 @@ trait HasEncryptedAttributes
3334
'from',
3435
'cc',
3536
'bcc',
37+
'reply_to',
3638
'variables',
3739
];
3840

src/MailableReader.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public function read(EmailComposer $composer): void
3434

3535
$this->readBcc($composer);
3636

37+
$this->readReplyTo($composer);
38+
3739
$this->readSubject($composer);
3840

3941
$this->readBody($composer);
@@ -115,6 +117,20 @@ private function readBcc(EmailComposer $composer): void
115117
$composer->bcc($bcc);
116118
}
117119

120+
/**
121+
* Read the mailable reply-to to the email composer.
122+
*
123+
* @param EmailComposer $composer
124+
*/
125+
private function readReplyTo(EmailComposer $composer): void
126+
{
127+
$replyTo = $this->convertMailableAddresses(
128+
$composer->getData('mailable')->replyTo
129+
);
130+
131+
$composer->replyTo($replyTo);
132+
}
133+
118134
/**
119135
* Read the mailable subject to the email composer.
120136
*

src/Preparer.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public function prepare(EmailComposer $composer): void
2525

2626
$this->prepareBcc($composer);
2727

28+
$this->prepareReplyTo($composer);
29+
2830
$this->prepareSubject($composer);
2931

3032
$this->prepareView($composer);
@@ -118,6 +120,18 @@ private function prepareBcc(EmailComposer $composer): void
118120
]);
119121
}
120122

123+
/**
124+
* Prepare the reply-to for database storage.
125+
*
126+
* @param EmailComposer $composer
127+
*/
128+
private function prepareReplyTo(EmailComposer $composer): void
129+
{
130+
$composer->getEmail()->fill([
131+
'reply_to' => json_encode($composer->getData('reply_to', [])),
132+
]);
133+
}
134+
121135
/**
122136
* Prepare the subject for database storage.
123137
*

src/Sender.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ private function buildMessage(Message $message, Email $email): void
4646
$message->to($email->getRecipient())
4747
->cc($email->hasCc() ? $email->getCc() : [])
4848
->bcc($email->hasBcc() ? $email->getBcc() : [])
49+
->replyTo($email->hasReplyTo() ? $email->getReplyTo() : [])
4950
->subject($email->getSubject())
5051
->from($email->getFromAddress(), $email->getFromName());
5152

src/SentMessage.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class SentMessage
1313
public $to = [];
1414
public $cc = [];
1515
public $bcc = [];
16+
public $replyTo = [];
1617
public $subject = '';
1718
public $body = '';
1819
public $attachments = [];
@@ -38,6 +39,10 @@ public static function createFromSymfonyMailer(\Symfony\Component\Mime\Email $em
3839
$sentMessage->bcc[$address->getAddress()] = $address->getName();
3940
}
4041

42+
foreach ($email->getReplyTo() as $address) {
43+
$sentMessage->replyTo[$address->getAddress()] = $address->getName();
44+
}
45+
4146
$sentMessage->subject = $email->getSubject();
4247
$sentMessage->body = $email->getHtmlBody();
4348
$sentMessage->attachments = array_map(function (DataPart $dataPart) {
@@ -58,6 +63,7 @@ public static function createFromSwiftMailer(\Swift_Mime_SimpleMessage $message)
5863
$sentMessage->to = $message->getTo();
5964
$sentMessage->cc = $message->getCc();
6065
$sentMessage->bcc = $message->getBcc();
66+
$sentMessage->replyTo = $message->getReplyTo();
6167
$sentMessage->subject = $message->getSubject();
6268
$sentMessage->body = $message->getBody();
6369
$sentMessage->attachments = array_map(function(Swift_Mime_SimpleMimeEntity $entity) {

src/Validator.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Exception;
88
use Carbon\Carbon;
99
use InvalidArgumentException;
10+
use const FILTER_VALIDATE_EMAIL;
1011

1112
class Validator
1213
{
@@ -33,6 +34,8 @@ public function validate(EmailComposer $composer): void
3334

3435
$this->validateBcc($composer);
3536

37+
$this->validateReplyTo($composer);
38+
3639
$this->validateSubject($composer);
3740

3841
$this->validateView($composer);
@@ -118,6 +121,25 @@ private function validateBcc(EmailComposer $composer): void
118121
}
119122
}
120123

124+
/**
125+
* Validate the reply-to addresses.
126+
*
127+
* @param EmailComposer $composer
128+
* @throws InvalidArgumentException
129+
*/
130+
private function validateReplyTo(EmailComposer $composer): void
131+
{
132+
if (! $composer->hasData('reply_to')) {
133+
return;
134+
}
135+
136+
foreach ((array) $composer->getData('reply_to') as $replyTo) {
137+
if (! filter_var($replyTo, FILTER_VALIDATE_EMAIL)) {
138+
throw new InvalidargumentException('E-mail address [' . $replyTo . '] is invalid');
139+
}
140+
}
141+
}
142+
121143
/**
122144
* Validate the e-mail subject.
123145
*

tests/DatabaseInteractionTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ public function cc_and_bcc_should_be_saved_correctly()
4444
$this->assertEquals(['[email protected]'], $email->getBcc());
4545
}
4646

47+
/** @test */
48+
public function reply_to_should_be_saved_correctly()
49+
{
50+
$email = $this->sendEmail([
51+
'reply_to' => $replyTo = [
52+
53+
],
54+
]);
55+
56+
$this->assertEquals(json_encode($replyTo), DB::table('emails')->find(1)->reply_to);
57+
$this->assertTrue($email->hasReplyTo());
58+
$this->assertEquals(['[email protected]'], $email->getReplyTo());
59+
}
60+
4761
/** @test */
4862
public function subject_should_be_saved_correclty()
4963
{

tests/EncryptionTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ public function cc_and_bb_should_be_encrypted_and_decrypted()
4646
$this->assertEquals($bcc, $email->getBcc());
4747
}
4848

49+
/** @test */
50+
public function reply_to_should_be_encrypted_and_decrypted()
51+
{
52+
$email = $this->sendEmail([
53+
'reply_to' => $replyTo = ['[email protected]', '[email protected]'],
54+
]);
55+
56+
$this->assertEquals($replyTo, decrypt($email->getRawDatabaseValue('reply_to')));
57+
58+
$this->assertEquals($replyTo, $email->getReplyTo());
59+
}
60+
4961
/** @test */
5062
public function the_subject_should_be_encrypted_and_decrypted()
5163
{

tests/MailableReaderTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ public function it_extracts_bcc_addresses()
5454
$this->assertEquals(['[email protected]', '[email protected]'], $composer->getData('bcc'));
5555
}
5656

57+
/** @test */
58+
public function it_extracts_reply_to_addresses()
59+
{
60+
$composer = Email::compose()->mailable($this->mailable());
61+
62+
$this->assertEquals(['[email protected]', '[email protected]'], $composer->getData('reply_to'));
63+
}
64+
5765
/** @test */
5866
public function it_extracts_the_subject()
5967
{
@@ -137,6 +145,7 @@ public function build()
137145
return $this->to('[email protected]')
138146
139147
148+
140149
->subject('Your order has shipped!')
141150
->attach(__DIR__ . '/files/pdf-sample.pdf', [
142151
'mime' => 'application/pdf',
@@ -168,7 +177,7 @@ public function envelope(): Envelope
168177
],
169178
170179
171-
[],
180+
172181
'Your order has shipped!'
173182
);
174183
}

0 commit comments

Comments
 (0)