Skip to content

Commit 5c53a87

Browse files
Merge pull request #57 from stackkit/feature/reply-to
Add initial draft of ReplyTo feature
2 parents 649eb30 + 61a0bad commit 5c53a87

20 files changed

+422
-3
lines changed

.gitignore

+2-1
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

+45
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ 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+
Email::compose()
123+
->replyTo(new Address('[email protected]', 'John Doe'));
124+
125+
Email::compose()
126+
->replyTo([
127+
new Address('[email protected]', 'John Doe'),
128+
new Address('[email protected]', 'Jane Doe'),
129+
]);
130+
```
131+
112132
### Using mailables
113133

114134
You may also pass a mailable to the e-mail composer.
@@ -232,3 +252,28 @@ To enable, set the following environment variable:
232252
```
233253
LARAVEL_DATABASE_EMAILS_SEND_IMMEDIATELY=true
234254
```
255+
256+
### Pruning models
257+
258+
```php
259+
use Stackkit\LaravelDatabaseEmails\Email;
260+
261+
$schedule->command('model:prune', [
262+
'--model' => [Email::class],
263+
])->everyMinute();
264+
```
265+
266+
By default, e-mails are pruned when they are older than 6 months.
267+
268+
You may change that by adding the following to the AppServiceProvider.php:
269+
270+
```php
271+
use Stackkit\LaravelDatabaseEmails\Email;
272+
273+
public function register(): void
274+
{
275+
Email::pruneWhen(function (Email $email) {
276+
return $email->where(...);
277+
});
278+
}
279+
```

composer.json

+3
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
}
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

+58
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
namespace Stackkit\LaravelDatabaseEmails;
66

7+
use Closure;
78
use Exception;
89
use Carbon\Carbon;
10+
use Illuminate\Database\Eloquent\Builder;
911
use Illuminate\Database\Eloquent\Model;
12+
use Illuminate\Database\Eloquent\Prunable;
1013

1114
/**
1215
* @property $id
@@ -15,6 +18,7 @@
1518
* @property $from
1619
* @property $cc
1720
* @property $bcc
21+
* @property $reply_to
1822
* @property $subject
1923
* @property $view
2024
* @property $variables
@@ -33,6 +37,7 @@
3337
class Email extends Model
3438
{
3539
use HasEncryptedAttributes;
40+
use Prunable;
3641

3742
/**
3843
* The table in which the e-mails are stored.
@@ -48,6 +53,8 @@ class Email extends Model
4853
*/
4954
protected $guarded = [];
5055

56+
public static ?Closure $pruneQuery = null;
57+
5158
/**
5259
* Compose a new e-mail.
5360
*
@@ -190,6 +197,26 @@ public function getBccAttribute()
190197
return $this->bcc;
191198
}
192199

200+
/**
201+
* Get the e-mail reply-to addresses.
202+
*
203+
* @return array|string
204+
*/
205+
public function getReplyTo()
206+
{
207+
return $this->reply_to;
208+
}
209+
210+
/**
211+
* Get the e-mail reply-to addresses.
212+
*
213+
* @return array|string
214+
*/
215+
public function getReplyToAttribute()
216+
{
217+
return $this->reply_to;
218+
}
219+
193220
/**
194221
* Get the e-mail subject.
195222
*
@@ -388,6 +415,16 @@ public function hasBcc(): bool
388415
return strlen($this->getRawDatabaseValue('bcc')) > 0;
389416
}
390417

418+
/**
419+
* Determine if the e-mail should sent with reply-to.
420+
*
421+
* @return bool
422+
*/
423+
public function hasReplyTo(): bool
424+
{
425+
return strlen($this->getRawDatabaseValue('reply_to') ?: '') > 0;
426+
}
427+
391428
/**
392429
* Determine if the e-mail is scheduled to be sent later.
393430
*
@@ -520,4 +557,25 @@ public function getRawDatabaseValue(string $key = null, $default = null)
520557

521558
return $this->getOriginal($key, $default);
522559
}
560+
561+
/**
562+
* @param Closure $closure
563+
* @return void
564+
*/
565+
public static function pruneWhen(Closure $closure)
566+
{
567+
static::$pruneQuery = $closure;
568+
}
569+
570+
/**
571+
* @return Builder
572+
*/
573+
public function prunable()
574+
{
575+
if (static::$pruneQuery) {
576+
return (static::$pruneQuery)($this);
577+
}
578+
579+
return $this->where('created_at', '<', now()->subMonths(6));
580+
}
523581
}

src/EmailComposer.php

+11
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

+16
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

+2
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

+17-1
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
*
@@ -137,7 +153,7 @@ private function readBody(EmailComposer $composer): void
137153

138154
$mailable = $composer->getData('mailable');
139155

140-
$composer->setData('body', view($mailable->view, $mailable->buildViewData()));
156+
$composer->setData('body', view($mailable->view, $mailable->buildViewData())->render());
141157
}
142158

143159
/**

src/Preparer.php

+30
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Stackkit\LaravelDatabaseEmails;
66

77
use Carbon\Carbon;
8+
use Illuminate\Mail\Mailables\Address;
89

910
class Preparer
1011
{
@@ -25,6 +26,8 @@ public function prepare(EmailComposer $composer): void
2526

2627
$this->prepareBcc($composer);
2728

29+
$this->prepareReplyTo($composer);
30+
2831
$this->prepareSubject($composer);
2932

3033
$this->prepareView($composer);
@@ -118,6 +121,33 @@ private function prepareBcc(EmailComposer $composer): void
118121
]);
119122
}
120123

124+
/**
125+
* Prepare the reply-to for database storage.
126+
*
127+
* @param EmailComposer $composer
128+
*/
129+
private function prepareReplyTo(EmailComposer $composer): void
130+
{
131+
$value = $composer->getData('reply_to', []);
132+
133+
if (! is_array($value)) {
134+
$value = [$value];
135+
}
136+
137+
foreach ($value as $i => $v) {
138+
if ($v instanceof Address) {
139+
$value[$i] = [
140+
'address' => $v->address,
141+
'name' => $v->name,
142+
];
143+
}
144+
}
145+
146+
$composer->getEmail()->fill([
147+
'reply_to' => json_encode($value),
148+
]);
149+
}
150+
121151
/**
122152
* Prepare the subject for database storage.
123153
*

src/Sender.php

+1
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

0 commit comments

Comments
 (0)