Skip to content

Commit febc195

Browse files
committed
added support for file attachments
1 parent 2577b1b commit febc195

14 files changed

+395
-146
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"orchestra/testbench": "^3.5",
3333
"orchestra/database": "^3.5",
3434
"phpunit/phpunit": "^6.0",
35-
"mockery/mockery": "^1.0"
35+
"mockery/mockery": "^1.0",
36+
"dompdf/dompdf": "^0.8.2"
3637
}
3738
}

database/migrations/emails.stub

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Create{{tableClassName}}Table extends Migration
2323
$table->string('view', 255);
2424
$table->binary('variables')->nullable();
2525
$table->binary('body');
26+
$table->binary('attachments');
2627
$table->integer('attempts')->default(0);
2728
$table->boolean('sending')->default(0);
2829
$table->boolean('failed')->default(0);

src/Email.php

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* @property $view
2020
* @property $variables
2121
* @property $body
22+
* @property $attachments
2223
* @property $attempts
2324
* @property $sending
2425
* @property $failed
@@ -162,6 +163,16 @@ public function getBody()
162163
return $this->body;
163164
}
164165

166+
/**
167+
* Get the e-mail attachments.
168+
*
169+
* @return array
170+
*/
171+
public function getAttachments()
172+
{
173+
return $this->attachments;
174+
}
175+
165176
/**
166177
* Get the number of times this e-mail was attempted to send.
167178
*
@@ -340,26 +351,7 @@ public function markAsFailed(Exception $exception)
340351
*/
341352
public function send()
342353
{
343-
if ($this->isSent()) {
344-
return;
345-
}
346-
347-
$this->markAsSending();
348-
349-
if (app()->runningUnitTests()) {
350-
Event::dispatch('before.send');
351-
}
352-
353-
Mail::send([], [], function (Message $message) {
354-
$message->to($this->getRecipient())
355-
->cc($this->hasCc() ? $this->getCc() : [])
356-
->bcc($this->hasBcc() ? $this->getBcc() : [])
357-
->subject($this->getSubject())
358-
->from(config('mail.from.address'), config('mail.from.name'))
359-
->setBody($this->getBody(), 'text/html');
360-
});
361-
362-
$this->markAsSent();
354+
(new Sender)->send($this);
363355
}
364356

365357
/**

src/EmailComposer.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,15 @@ public function setData($key, $value)
5858
* Get a data value.
5959
*
6060
* @param string $key
61+
* @param mixed $default
6162
* @return mixed
6263
*/
63-
public function getData($key)
64+
public function getData($key, $default = null)
6465
{
66+
if (!is_null($default) && !$this->hasData($key)) {
67+
return $default;
68+
}
69+
6570
return $this->data[$key];
6671
}
6772

@@ -146,7 +151,7 @@ public function view($view)
146151
* Set the e-mail variables.
147152
*
148153
* @param array $variables
149-
* @return static
154+
* @return EmailComposer
150155
*/
151156
public function variables($variables)
152157
{
@@ -192,6 +197,39 @@ public function mailable(Mailable $mailable)
192197
return $this;
193198
}
194199

200+
/**
201+
* Attach a file to the e-mail.
202+
*
203+
* @param string $file
204+
* @param array $options
205+
* @return static
206+
*/
207+
public function attach($file, $options = [])
208+
{
209+
$attachments = $this->hasData('attachments') ? $this->getData('attachments') : [];
210+
211+
$attachments[] = compact('file', 'options');
212+
213+
return $this->setData('attachments', $attachments);
214+
}
215+
216+
/**
217+
* Attach in-memory data as an attachment.
218+
*
219+
* @param string $data
220+
* @param string $name
221+
* @param array $options
222+
* @return $this
223+
*/
224+
public function attachData($data, $name, array $options = [])
225+
{
226+
$attachments = $this->hasData('rawAttachments') ? $this->getData('rawAttachments') : [];
227+
228+
$attachments[] = compact('data', 'name', 'options');
229+
230+
return $this->setData('rawAttachments', $attachments);
231+
}
232+
195233
/**
196234
* Send the e-mail.
197235
*

src/HasEncryptedAttributes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ trait HasEncryptedAttributes
3030
'cc',
3131
'bcc',
3232
'variables',
33+
'attachments',
3334
];
3435

3536
/**

src/MailableReader.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Buildcode\LaravelDatabaseEmails;
44

5+
use function call_user_func_array;
56
use Exception;
67
use Illuminate\Mail\Mailable;
78

@@ -23,6 +24,8 @@ public function read(EmailComposer $composer)
2324
$this->readSubject($composer);
2425

2526
$this->readBody($composer);
27+
28+
$this->readAttachments($composer);
2629
}
2730

2831
/**
@@ -106,4 +109,22 @@ private function readBody(EmailComposer $composer)
106109

107110
$composer->setData('body', $composer->getData('mailable')->render());
108111
}
112+
113+
/**
114+
* Read the mailable attachments to the email composer.
115+
*
116+
* @param EmailComposer $composer
117+
*/
118+
private function readAttachments(EmailComposer $composer)
119+
{
120+
$mailable = $composer->getData('mailable');
121+
122+
foreach ((array)$mailable->attachments as $attachment) {
123+
call_user_func_array([$composer, 'attach'], $attachment);
124+
}
125+
126+
foreach ((array)$mailable->rawAttachments as $rawAttachment) {
127+
call_user_func_array([$composer, 'attachData'], $rawAttachment);
128+
}
129+
}
109130
}

src/Preparer.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public function prepare(EmailComposer $composer)
2929

3030
$this->prepareBody($composer);
3131

32+
$this->prepareAttachments($composer);
33+
3234
$this->prepareScheduled($composer);
3335
}
3436

@@ -164,6 +166,34 @@ private function prepareBody(EmailComposer $composer)
164166
$composer->getEmail()->fill(compact('body'));
165167
}
166168

169+
/**
170+
* Prepare the e-mail attachments.
171+
*
172+
* @param EmailComposer $composer
173+
*/
174+
private function prepareAttachments(EmailComposer $composer)
175+
{
176+
$attachments = [];
177+
178+
foreach ((array)$composer->getData('attachments', []) as $attachment) {
179+
$attachments[] = [
180+
'type' => 'attachment',
181+
'attachment' => $attachment,
182+
];
183+
}
184+
185+
foreach ((array)$composer->getData('rawAttachments', []) as $rawAttachment) {
186+
$attachments[] = [
187+
'type' => 'rawAttachment',
188+
'attachment' => $rawAttachment,
189+
];
190+
}
191+
192+
$composer->getEmail()->fill([
193+
'attachments' => json_encode($attachments),
194+
]);
195+
}
196+
167197
/**
168198
* Prepare the scheduled date for database storage.
169199
*

src/Sender.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Buildcode\LaravelDatabaseEmails;
4+
5+
use Illuminate\Mail\Message;
6+
use Illuminate\Support\Facades\Event;
7+
use Illuminate\Support\Facades\Mail;
8+
9+
class Sender
10+
{
11+
/**
12+
* Send the given e-mail.
13+
*
14+
* @param Email $email
15+
*/
16+
public function send(Email $email)
17+
{
18+
if ($email->isSent()) {
19+
return;
20+
}
21+
22+
$email->markAsSending();
23+
24+
if (app()->runningUnitTests()) {
25+
Event::dispatch('before.send');
26+
}
27+
28+
Mail::send([], [], function (Message $message) use ($email) {
29+
$message->to($email->getRecipient())
30+
->cc($email->hasCc() ? $email->getCc() : [])
31+
->bcc($email->hasBcc() ? $email->getBcc() : [])
32+
->subject($email->getSubject())
33+
->from(config('mail.from.address'), config('mail.from.name'))
34+
->setBody($email->getBody(), 'text/html');
35+
36+
foreach ((array)$email->getAttachments() as $attachment) {
37+
if ($attachment['type'] == 'attachment') {
38+
$message->attach($attachment['attachment']['file'], $attachment['attachment']['options']);
39+
} else if($attachment['type'] == 'rawAttachment') {
40+
$message->attachData($attachment['attachment']['data'], $attachment['attachment']['name'], $attachment['attachment']['options']);
41+
}
42+
}
43+
});
44+
45+
$email->markAsSent();
46+
}
47+
}

tests/DatabaseInteractionTest.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace Tests;
44

5-
use Buildcode\LaravelDatabaseEmails\Email;
65
use Illuminate\Support\Facades\DB;
6+
use Dompdf\Dompdf;
77

88
class DatabaseInteractionTest extends TestCase
99
{
@@ -160,4 +160,48 @@ function recipient_should_be_swapped_for_test_address_when_in_testing_mode()
160160

161161
$this->assertEquals('test@address.com', $email->getRecipient());
162162
}
163+
164+
/** @test */
165+
function attachments_should_be_saved_correctly()
166+
{
167+
$email = $this->composeEmail()
168+
->attach(__DIR__ . '/files/pdf-sample.pdf')
169+
->send();
170+
171+
$this->assertCount(1, $email->getAttachments());
172+
173+
$attachment = $email->getAttachments()[0];
174+
175+
$this->assertEquals('attachment', $attachment['type']);
176+
$this->assertEquals(__DIR__ . '/files/pdf-sample.pdf', $attachment['attachment']['file']);
177+
178+
$email = $this->composeEmail()
179+
->attach(__DIR__ . '/files/pdf-sample.pdf')
180+
->attach(__DIR__ . '/files/pdf-sample-2.pdf')
181+
->send();
182+
183+
$this->assertCount(2, $email->getAttachments());
184+
185+
$this->assertEquals(__DIR__ . '/files/pdf-sample.pdf', $email->getAttachments()[0]['attachment']['file']);
186+
$this->assertEquals(__DIR__ . '/files/pdf-sample-2.pdf', $email->getAttachments()[1]['attachment']['file']);
187+
}
188+
189+
/** @test */
190+
function in_memory_attachments_should_be_saved_correctly()
191+
{
192+
$pdf = new Dompdf;
193+
$pdf->loadHtml('Hello CI!');
194+
$pdf->setPaper('A4', 'landscape');
195+
196+
$email = $this->composeEmail()
197+
->attachData($pdf->outputHtml(), 'generated.pdf', [
198+
'mime' => 'application/pdf'
199+
])
200+
->send();
201+
202+
$this->assertCount(1, $email->getAttachments());
203+
204+
$this->assertEquals('rawAttachment', $email->getAttachments()[0]['type']);
205+
$this->assertEquals($pdf->outputHtml(), $email->getAttachments()[0]['attachment']['data']);
206+
}
163207
}

tests/MailableReaderTest.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ function it_extracts_the_body()
5454

5555
$this->assertEquals("Name: John Doe\n", $composer->getData('body'));
5656
}
57+
58+
/** @test */
59+
function it_extracts_attachments()
60+
{
61+
$email = Email::compose()->mailable(new TestMailable())->send();
62+
63+
$attachments = $email->getAttachments();
64+
65+
$this->assertCount(2, $attachments);
66+
67+
$this->assertEquals('attachment', $attachments[0]['type']);
68+
$this->assertEquals(__DIR__ . '/files/pdf-sample.pdf', $attachments[0]['attachment']['file']);
69+
70+
$this->assertEquals('rawAttachment', $attachments[1]['type']);
71+
$this->assertEquals('order.html', $attachments[1]['attachment']['name']);
72+
$this->assertEquals('<p>Thanks for your oder</p>', $attachments[1]['attachment']['data']);
73+
}
5774
}
5875

5976
class TestMailable extends Mailable
@@ -68,7 +85,11 @@ public function __construct()
6885
$this->to('john@doe.com')
6986
->cc(['john+cc@doe.com', 'john+cc2@doe.com'])
7087
->bcc(['john+bcc@doe.com', 'john+bcc2@doe.com'])
71-
->subject('Your order has shipped!');
88+
->subject('Your order has shipped!')
89+
->attach(__DIR__ . '/files/pdf-sample.pdf', [
90+
'mime' => 'application/pdf',
91+
])
92+
->attachData('<p>Thanks for your oder</p>', 'order.html');
7293
}
7394

7495
/**

0 commit comments

Comments
 (0)