Skip to content

Commit 54c47a7

Browse files
Automatically generate mailable view (#580)
1 parent 2dc9762 commit 54c47a7

16 files changed

+277
-13
lines changed

src/Generators/Statements/MailGenerator.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ class MailGenerator extends StatementGenerator
1414
public function output(Tree $tree): array
1515
{
1616
$stub = $this->filesystem->stub('mail.stub');
17+
$view_stub = $this->filesystem->stub('mail.view.stub');
1718

1819
/**
1920
* @var \Blueprint\Models\Controller $controller
2021
*/
2122
foreach ($tree->controllers() as $controller) {
22-
foreach ($controller->methods() as $method => $statements) {
23+
foreach ($controller->methods() as $statements) {
2324
foreach ($statements as $statement) {
2425
if (!$statement instanceof SendStatement) {
2526
continue;
@@ -30,19 +31,35 @@ public function output(Tree $tree): array
3031
}
3132

3233
$path = $this->getStatementPath($statement->mail());
33-
3434
if ($this->filesystem->exists($path)) {
3535
continue;
3636
}
3737

3838
$this->create($path, $this->populateStub($stub, $statement));
39+
40+
$path = $this->getViewPath($statement->view());
41+
if ($this->filesystem->exists($path)) {
42+
continue;
43+
}
44+
45+
$this->create($path, $this->populateViewStub($view_stub, $statement));
3946
}
4047
}
4148
}
4249

4350
return $this->output;
4451
}
4552

53+
private function populateViewStub(string $stub, SendStatement $statement)
54+
{
55+
return str_replace('{{ class }}', $statement->mail(), $stub);
56+
}
57+
58+
private function getViewPath($view)
59+
{
60+
return 'resources/views/' . str_replace('.', '/', $view) . '.blade.php';
61+
}
62+
4663
protected function getStatementPath(string $name)
4764
{
4865
return Blueprint::appPath() . '/Mail/' . $name . '.php';
@@ -52,6 +69,7 @@ protected function populateStub(string $stub, SendStatement $sendStatement)
5269
{
5370
$stub = str_replace('{{ namespace }}', config('blueprint.namespace') . '\\Mail', $stub);
5471
$stub = str_replace('{{ class }}', $sendStatement->mail(), $stub);
72+
$stub = str_replace('{{ view }}', $sendStatement->view(), $stub);
5573
$stub = str_replace('{{ properties }}', $this->populateConstructor('message', $sendStatement), $stub);
5674

5775
if (Blueprint::useReturnTypeHints()) {

src/Lexers/StatementLexer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,20 @@ private function analyzeRespond(string $statement)
8080
private function analyzeSend($statement)
8181
{
8282
$to = null;
83+
$view = null;
8384

8485
$found = preg_match('/\\s+to:(\\S+)/', $statement, $matches);
8586
if ($found) {
8687
$to = $matches[1];
8788
$statement = str_replace($matches[0], '', $statement);
8889
}
8990

91+
$found = preg_match('/\\s+view:(\\S+)/', $statement, $matches);
92+
if ($found) {
93+
$view = $matches[1];
94+
$statement = str_replace($matches[0], '', $statement);
95+
}
96+
9097
[$object, $with] = $this->extractTokens($statement, 2);
9198

9299
$data = [];
@@ -99,7 +106,7 @@ private function analyzeSend($statement)
99106
$type = SendStatement::TYPE_NOTIFICATION_WITH_FACADE;
100107
}
101108

102-
return new SendStatement($object, $to, $data, $type);
109+
return new SendStatement($object, $to, $data, $type, $view);
103110
}
104111

105112
private function analyzeNotify($statement)

src/Models/Statements/SendStatement.php

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

33
namespace Blueprint\Models\Statements;
44

5+
use Illuminate\Support\Str;
6+
57
class SendStatement
68
{
79
const TYPE_MAIL = 'mail';
@@ -30,12 +32,18 @@ class SendStatement
3032
*/
3133
private $type;
3234

33-
public function __construct(string $mail, string $to = null, array $data, string $type)
35+
/**
36+
* @var string
37+
*/
38+
private $view;
39+
40+
public function __construct(string $mail, ?string $to, array $data, string $type, string $view = null)
3441
{
3542
$this->mail = $mail;
3643
$this->data = $data;
3744
$this->to = $to;
3845
$this->type = $type;
46+
$this->view = $view ?? 'emails.' . Str::kebab($this->mail);
3947
}
4048

4149
public function mail()
@@ -79,6 +87,11 @@ public function isNotification()
7987
return $this->type() === SendStatement::TYPE_NOTIFICATION_WITH_FACADE || $this->type() === SendStatement::TYPE_NOTIFICATION_WITH_MODEL;
8088
}
8189

90+
public function view()
91+
{
92+
return $this->view;
93+
}
94+
8295
private function mailOutput()
8396
{
8497
$code = 'Mail::';

stubs/mail.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ class {{ class }} extends Mailable
2020
*/
2121
public function build()
2222
{
23-
return $this->view('view.name');
23+
return $this->view('{{ view }}');
2424
}
2525
}

stubs/mail.view.stub

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{-- Template for {{ class }} --}}

tests/Feature/Generators/Statements/MailGeneratorTest.php

Lines changed: 161 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public function output_writes_nothing_for_empty_tree()
3939
$this->filesystem->expects('stub')
4040
->with('mail.stub')
4141
->andReturn($this->stub('mail.stub'));
42+
$this->filesystem->expects('stub')
43+
->with('mail.view.stub')
44+
->andReturn($this->stub('mail.view.stub'));
4245

4346
$this->filesystem->shouldNotHaveReceived('put');
4447

@@ -53,6 +56,9 @@ public function output_writes_nothing_tree_without_validate_statements()
5356
$this->filesystem->expects('stub')
5457
->with('mail.stub')
5558
->andReturn($this->stub('mail.stub'));
59+
$this->filesystem->expects('stub')
60+
->with('mail.view.stub')
61+
->andReturn($this->stub('mail.view.stub'));
5662

5763
$this->filesystem->shouldNotHaveReceived('put');
5864

@@ -70,30 +76,57 @@ public function output_writes_mails()
7076
$this->filesystem->expects('stub')
7177
->with('mail.stub')
7278
->andReturn($this->stub('mail.stub'));
79+
$this->filesystem->expects('stub')
80+
->with('mail.view.stub')
81+
->andReturn($this->stub('mail.view.stub'));
7382
$this->filesystem->shouldReceive('stub')
7483
->with('constructor.stub')
7584
->andReturn($this->stub('constructor.stub'));
76-
$this->filesystem->shouldReceive('exists')
85+
$this->filesystem->expects('exists')
7786
->twice()
7887
->with('app/Mail')
7988
->andReturns(false, true);
89+
$this->filesystem->expects('exists')
90+
->twice()
91+
->with('resources/views/emails')
92+
->andReturns(false, true);
8093
$this->filesystem->expects('exists')
8194
->with('app/Mail/ReviewPost.php')
8295
->andReturnFalse();
8396
$this->filesystem->expects('makeDirectory')
8497
->with('app/Mail', 0755, true);
8598
$this->filesystem->expects('put')
8699
->with('app/Mail/ReviewPost.php', $this->fixture('mailables/review-post.php'));
100+
$this->filesystem->expects('exists')
101+
->with('resources/views/emails/review-post.blade.php')
102+
->andReturnFalse();
103+
$this->filesystem->expects('makeDirectory')
104+
->with('resources/views/emails', 0755, true);
105+
$this->filesystem->expects('put')
106+
->with('resources/views/emails/review-post.blade.php', $this->fixture('mailables/review-post-view.blade.php'));
107+
87108
$this->filesystem->expects('exists')
88109
->with('app/Mail/PublishedPost.php')
89110
->andReturnFalse();
90111
$this->filesystem->expects('put')
91112
->with('app/Mail/PublishedPost.php', $this->fixture('mailables/published-post.php'));
113+
$this->filesystem->expects('exists')
114+
->with('resources/views/emails/published-post.blade.php')
115+
->andReturnFalse();
116+
$this->filesystem->expects('put')
117+
->with('resources/views/emails/published-post.blade.php', $this->fixture('mailables/published-post-view.blade.php'));
92118

93119
$tokens = $this->blueprint->parse($this->fixture('drafts/send-statements.yaml'));
94120
$tree = $this->blueprint->analyze($tokens);
95121

96-
$this->assertEquals(['created' => ['app/Mail/ReviewPost.php', 'app/Mail/PublishedPost.php']], $this->subject->output($tree));
122+
$this->assertEquals([
123+
'created' => [
124+
'app/Mail/ReviewPost.php',
125+
'resources/views/emails/review-post.blade.php',
126+
'app/Mail/PublishedPost.php',
127+
'resources/views/emails/published-post.blade.php',
128+
],
129+
], $this->subject->output($tree));
97130
}
98131

99132
/**
@@ -104,6 +137,9 @@ public function it_only_outputs_new_mails()
104137
$this->filesystem->expects('stub')
105138
->with('mail.stub')
106139
->andReturn($this->stub('mail.stub'));
140+
$this->filesystem->expects('stub')
141+
->with('mail.view.stub')
142+
->andReturn($this->stub('mail.view.stub'));
107143
$this->filesystem->expects('exists')
108144
->with('app/Mail/ReviewPost.php')
109145
->andReturnTrue();
@@ -128,6 +164,9 @@ public function it_respects_configuration()
128164
$this->filesystem->expects('stub')
129165
->with('mail.stub')
130166
->andReturn($this->stub('mail.stub'));
167+
$this->filesystem->expects('stub')
168+
->with('mail.view.stub')
169+
->andReturn($this->stub('mail.view.stub'));
131170
$this->filesystem->expects('stub')
132171
->with('constructor.stub')
133172
->andReturn($this->stub('constructor.stub'));
@@ -141,11 +180,23 @@ public function it_respects_configuration()
141180
->with('src/path/Mail', 0755, true);
142181
$this->filesystem->expects('put')
143182
->with('src/path/Mail/ReviewPost.php', $this->fixture('mailables/mail-configured.php'));
183+
$this->filesystem->expects('exists')
184+
->with('resources/views/emails/review-post.blade.php')
185+
->andReturnFalse();
186+
$this->filesystem->expects('makeDirectory')
187+
->with('resources/views/emails', 0755, true);
188+
$this->filesystem->expects('put')
189+
->with('resources/views/emails/review-post.blade.php', $this->fixture('mailables/review-post-view.blade.php'));
144190

145191
$tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml'));
146192
$tree = $this->blueprint->analyze($tokens);
147193

148-
$this->assertEquals(['created' => ['src/path/Mail/ReviewPost.php']], $this->subject->output($tree));
194+
$this->assertEquals([
195+
'created' => [
196+
'src/path/Mail/ReviewPost.php',
197+
'resources/views/emails/review-post.blade.php',
198+
],
199+
], $this->subject->output($tree));
149200
}
150201

151202
/**
@@ -160,6 +211,9 @@ public function output_using_return_types()
160211
$this->filesystem->expects('stub')
161212
->with('mail.stub')
162213
->andReturn($this->stub('mail.stub'));
214+
$this->filesystem->expects('stub')
215+
->with('mail.view.stub')
216+
->andReturn($this->stub('mail.view.stub'));
163217
$this->filesystem->expects('stub')
164218
->with('constructor.stub')
165219
->andReturn($this->stub('constructor.stub'));
@@ -173,10 +227,113 @@ public function output_using_return_types()
173227
->with('src/path/Mail', 0755, true);
174228
$this->filesystem->expects('put')
175229
->with('src/path/Mail/ReviewPost.php', $this->fixture('mailables/return-type-declarations.php'));
230+
$this->filesystem->expects('exists')
231+
->with('resources/views/emails/review-post.blade.php')
232+
->andReturnFalse();
233+
$this->filesystem->expects('makeDirectory')
234+
->with('resources/views/emails', 0755, true);
235+
$this->filesystem->expects('put')
236+
->with('resources/views/emails/review-post.blade.php', $this->fixture('mailables/review-post-view.blade.php'));
176237

177238
$tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml'));
178239
$tree = $this->blueprint->analyze($tokens);
179240

180-
$this->assertEquals(['created' => ['src/path/Mail/ReviewPost.php']], $this->subject->output($tree));
241+
$this->assertEquals([
242+
'created' => [
243+
'src/path/Mail/ReviewPost.php',
244+
'resources/views/emails/review-post.blade.php',
245+
],
246+
], $this->subject->output($tree));
247+
}
248+
249+
/**
250+
* @test
251+
*/
252+
public function output_writes_mails_but_not_existing_templates()
253+
{
254+
$this->filesystem->expects('stub')
255+
->with('mail.stub')
256+
->andReturn($this->stub('mail.stub'));
257+
$this->filesystem->expects('stub')
258+
->with('mail.view.stub')
259+
->andReturn($this->stub('mail.view.stub'));
260+
$this->filesystem->shouldReceive('stub')
261+
->with('constructor.stub')
262+
->andReturn($this->stub('constructor.stub'));
263+
$this->filesystem->expects('exists')
264+
->twice()
265+
->with('app/Mail')
266+
->andReturns(false);
267+
$this->filesystem->expects('exists')
268+
->with('app/Mail/ReviewPost.php')
269+
->andReturnFalse();
270+
$this->filesystem->expects('put')
271+
->with('app/Mail/ReviewPost.php', $this->fixture('mailables/review-post.php'));
272+
$this->filesystem->expects('exists')
273+
->with('resources/views/emails/review-post.blade.php')
274+
->andReturnTrue();
275+
276+
$this->filesystem->expects('exists')
277+
->with('app/Mail/PublishedPost.php')
278+
->andReturnFalse();
279+
$this->filesystem->expects('put')
280+
->with('app/Mail/PublishedPost.php', $this->fixture('mailables/published-post.php'));
281+
$this->filesystem->expects('exists')
282+
->with('resources/views/emails/published-post.blade.php')
283+
->andReturnTrue();
284+
285+
$tokens = $this->blueprint->parse($this->fixture('drafts/send-statements.yaml'));
286+
$tree = $this->blueprint->analyze($tokens);
287+
288+
$this->assertEquals([
289+
'created' => [
290+
'app/Mail/ReviewPost.php',
291+
'app/Mail/PublishedPost.php',
292+
],
293+
], $this->subject->output($tree));
294+
}
295+
296+
/**
297+
* @test
298+
*/
299+
public function output_writes_mail_with_custom_template()
300+
{
301+
$this->filesystem->expects('stub')
302+
->with('mail.stub')
303+
->andReturn($this->stub('mail.stub'));
304+
$this->filesystem->expects('stub')
305+
->with('mail.view.stub')
306+
->andReturn($this->stub('mail.view.stub'));
307+
$this->filesystem->shouldReceive('stub')
308+
->with('constructor.stub')
309+
->andReturn($this->stub('constructor.stub'));
310+
$this->filesystem->expects('exists')
311+
->with('app/Mail')
312+
->andReturns(true);
313+
$this->filesystem->expects('exists')
314+
->with('resources/views/emails/admin')
315+
->andReturns(false);
316+
$this->filesystem->expects('exists')
317+
->with('app/Mail/AddedAdmin.php')
318+
->andReturnFalse();
319+
$this->filesystem->expects('put')
320+
->with('app/Mail/AddedAdmin.php', $this->fixture('mailables/added-admin.php'));
321+
$this->filesystem->expects('exists')
322+
->with('resources/views/emails/admin/added.blade.php')
323+
->andReturnFalse();
324+
$this->filesystem->expects('makeDirectory')
325+
->with('resources/views/emails/admin', 0755, true);
326+
$this->filesystem->expects('put')
327+
->with('resources/views/emails/admin/added.blade.php', $this->fixture('mailables/added-admin-view.blade.php'));
328+
329+
$tokens = $this->blueprint->parse($this->fixture('drafts/send-statement-with-view.yaml'));
330+
$tree = $this->blueprint->analyze($tokens);
331+
332+
$this->assertEquals([
333+
'created' => [
334+
'app/Mail/AddedAdmin.php',
335+
'resources/views/emails/admin/added.blade.php',
336+
],
337+
], $this->subject->output($tree));
181338
}
182339
}

0 commit comments

Comments
 (0)