diff --git a/src/Generators/ControllerGenerator.php b/src/Generators/ControllerGenerator.php index 30c301a6..2d276a18 100644 --- a/src/Generators/ControllerGenerator.php +++ b/src/Generators/ControllerGenerator.php @@ -102,10 +102,10 @@ private function buildMethods(Controller $controller) foreach ($statements as $statement) { if ($statement instanceof SendStatement) { $body .= self::INDENT . $statement->output() . PHP_EOL; - if ($statement->type() === 'notification') { + if ($statement->type() === SendStatement::TYPE_NOTIFICATION_WITH_FACADE) { $this->addImport($controller, 'Illuminate\\Support\\Facades\\Notification'); $this->addImport($controller, config('blueprint.namespace') . '\\Notification\\' . $statement->mail()); - } else { + } elseif ($statement->type() === SendStatement::TYPE_MAIL) { $this->addImport($controller, 'Illuminate\\Support\\Facades\\Mail'); $this->addImport($controller, config('blueprint.namespace') . '\\Mail\\' . $statement->mail()); } diff --git a/src/Generators/Statements/MailGenerator.php b/src/Generators/Statements/MailGenerator.php index fb766d06..4541ef5c 100644 --- a/src/Generators/Statements/MailGenerator.php +++ b/src/Generators/Statements/MailGenerator.php @@ -32,7 +32,7 @@ public function output(array $tree): array continue; } - if ($statement->type() !== 'mail') { + if ($statement->type() !== SendStatement::TYPE_MAIL) { continue; } diff --git a/src/Generators/Statements/NotificationGenerator.php b/src/Generators/Statements/NotificationGenerator.php index 67c0816d..5b971270 100644 --- a/src/Generators/Statements/NotificationGenerator.php +++ b/src/Generators/Statements/NotificationGenerator.php @@ -33,7 +33,9 @@ public function output(array $tree): array continue; } - if ($statement->type() !== 'notification') { + if ($statement->type() !== SendStatement::TYPE_NOTIFICATION_WITH_FACADE + && $statement->type() !== SendStatement::TYPE_NOTIFICATION_WITH_MODEL + ) { continue; } diff --git a/src/Generators/TestGenerator.php b/src/Generators/TestGenerator.php index c4e15da7..80e6617d 100644 --- a/src/Generators/TestGenerator.php +++ b/src/Generators/TestGenerator.php @@ -112,23 +112,25 @@ protected function buildTestCases(Controller $controller) foreach ($statements as $statement) { if ($statement instanceof SendStatement) { - if ($statement->type() === 'notification') { + if ($statement->type() === SendStatement::TYPE_NOTIFICATION_WITH_FACADE + || $statement->type() === SendStatement::TYPE_NOTIFICATION_WITH_MODEL + ) { $this->addImport($controller, 'Illuminate\\Support\\Facades\\Notification'); $this->addImport($controller, config('blueprint.namespace') . '\\Notification\\' . $statement->mail()); $setup['mock'][] = 'Notification::fake();'; - $assertion = sprintf('Notification::assertSent(%s::class', $statement->mail()); + $assertion = sprintf( + 'Notification::assertSentTo($%s, %s::class', + str_replace('.', '->', $statement->to()), + $statement->mail() + ); - if ($statement->data() || $statement->to()) { + if ($statement->data()) { $conditions = []; $variables = []; $assertion .= ', function ($notification)'; - if ($statement->to()) { - $conditions[] = '$notification->hasTo($' . str_replace('.', '->', $statement->to()) . ')'; - } - foreach ($statement->data() as $data) { if (Str::studly(Str::singular($data)) === $context) { $variables[] .= '$' . $data; diff --git a/src/Lexers/StatementLexer.php b/src/Lexers/StatementLexer.php index c29d0994..417192b1 100644 --- a/src/Lexers/StatementLexer.php +++ b/src/Lexers/StatementLexer.php @@ -40,6 +40,9 @@ public function analyze(array $tokens): array case 'send': $statements[] = $this->analyzeSend($statement); break; + case 'notify': + $statements[] = $this->analyzeNotify($statement); + break; case 'validate': $statements[] = $this->analyzeValidate($statement); break; @@ -120,14 +123,26 @@ private function analyzeSend($statement) $data = preg_split('/,([ \t]+)?/', substr($with, 5)); } - $type = 'mail'; + $type = SendStatement::TYPE_MAIL; if (Str::endsWith($object, 'Notification')) { - $type = 'notification'; + $type = SendStatement::TYPE_NOTIFICATION_WITH_FACADE; } return new SendStatement($object, $to, $data, $type); } + private function analyzeNotify($statement) + { + [$model, $notification, $with] = $this->extractTokens($statement, 3); + + $data = []; + if (!empty($with)) { + $data = preg_split('/,([ \t]+)?/', substr($with, 5)); + } + + return new SendStatement($notification, $model, $data, SendStatement::TYPE_NOTIFICATION_WITH_MODEL); + } + private function analyzeValidate($statement) { return new ValidateStatement(preg_split('/,([ \t]+)?/', $statement)); diff --git a/src/Models/Statements/SendStatement.php b/src/Models/Statements/SendStatement.php index faaa12c8..32a52da0 100644 --- a/src/Models/Statements/SendStatement.php +++ b/src/Models/Statements/SendStatement.php @@ -5,6 +5,12 @@ class SendStatement { + const TYPE_MAIL = 'mail'; + + const TYPE_NOTIFICATION_WITH_FACADE = 'notification_with_facade'; + + const TYPE_NOTIFICATION_WITH_MODEL = 'notification_with_model'; + /** * @var string */ @@ -58,7 +64,15 @@ public function data(): array public function output() { - return $this->type() === 'mail' ? $this->mailOutput() : $this->notificationOutput(); + if ($this->type() === self::TYPE_NOTIFICATION_WITH_FACADE) { + return $this->notificationFacadeOutput(); + } + + if ($this->type() === self::TYPE_NOTIFICATION_WITH_MODEL) { + return $this->notificationModelOutput(); + } + + return $this->mailOutput(); } private function mailOutput() @@ -80,7 +94,7 @@ private function mailOutput() return $code; } - private function notificationOutput() + private function notificationFacadeOutput() { $code = 'Notification::'; @@ -97,6 +111,24 @@ private function notificationOutput() return $code; } + private function notificationModelOutput() + { + $code = ''; + + if ($this->to()) { + $code .= sprintf('$%s->', str_replace('.', '->', $this->to())); + $code .= 'notify(new ' . $this->mail() . '('; + } + + if ($this->data()) { + $code .= $this->buildParameters($this->data()); + } + + $code .= '));'; + + return $code; + } + private function buildParameters(array $data) { $parameters = array_map(function ($parameter) { diff --git a/tests/Feature/Generator/ControllerGeneratorTest.php b/tests/Feature/Generator/ControllerGeneratorTest.php index 2afc92e6..aac2c025 100644 --- a/tests/Feature/Generator/ControllerGeneratorTest.php +++ b/tests/Feature/Generator/ControllerGeneratorTest.php @@ -167,6 +167,8 @@ public function controllerTreeDataProvider() { return [ ['drafts/readme-example.yaml', 'app/Http/Controllers/PostController.php', 'controllers/readme-example.php'], + ['drafts/readme-example-notification-facade.yaml', 'app/Http/Controllers/PostController.php', 'controllers/readme-example-notification-facade.php'], + ['drafts/readme-example-notification-model.yaml', 'app/Http/Controllers/PostController.php', 'controllers/readme-example-notification-model.php'], ['drafts/crazy-eloquent.yaml', 'app/Http/Controllers/PostController.php', 'controllers/crazy-eloquent.php'], ['drafts/nested-components.yaml', 'app/Http/Controllers/Admin/UserController.php', 'controllers/nested-components.php'], ['drafts/respond-statements.yaml', 'app/Http/Controllers/Api/PostController.php', 'controllers/respond-statements.php'], diff --git a/tests/Feature/Generator/Statements/NotificationGeneratorTest.php b/tests/Feature/Generator/Statements/NotificationGeneratorTest.php index edf5a1a4..bd52edb2 100644 --- a/tests/Feature/Generator/Statements/NotificationGeneratorTest.php +++ b/tests/Feature/Generator/Statements/NotificationGeneratorTest.php @@ -64,18 +64,19 @@ public function output_writes_nothing_tree_without_validate_statements() /** * @test + * @dataProvider notificationDraftProvider */ - public function output_writes_notifications() + public function output_writes_notifications($draft) { $this->files->expects('stub') ->with('notification.stub') ->andReturn(file_get_contents('stubs/notification.stub')); - $this->files->expects('stub') - ->with('partials/constructor.stub') - ->andReturn(file_get_contents('stubs/partials/constructor.stub')); - - // var_dump($this->fixture('notifications/review-post.php'));die(); + if ($draft === 'drafts/send-statements-notification-facade.yaml') { + $this->files->expects('stub') + ->with('partials/constructor.stub') + ->andReturn(file_get_contents('stubs/partials/constructor.stub')); + } $this->files->shouldReceive('exists') ->twice() @@ -95,9 +96,7 @@ public function output_writes_notifications() $this->files->expects('put') ->with('app/Notification/PublishedPostNotification.php', $this->fixture('notifications/published-post.php')); - - - $tokens = $this->blueprint->parse($this->fixture('drafts/send-statements-notification.yaml')); + $tokens = $this->blueprint->parse($this->fixture($draft)); $tree = $this->blueprint->analyze($tokens); $this->assertEquals(['created' => ['app/Notification/ReviewPostNotification.php', 'app/Notification/PublishedPostNotification.php']], $this->subject->output($tree)); @@ -119,7 +118,7 @@ public function it_only_outputs_new_notifications() ->with('app/Notification/PublishedPostNotification.php') ->andReturnTrue(); - $tokens = $this->blueprint->parse($this->fixture('drafts/send-statements-notification.yaml')); + $tokens = $this->blueprint->parse($this->fixture('drafts/send-statements-notification-facade.yaml')); $tree = $this->blueprint->analyze($tokens); $this->assertEquals([], $this->subject->output($tree)); @@ -148,9 +147,17 @@ public function it_respects_configuration() $this->files->expects('put') ->with('src/path/Notification/ReviewNotification.php', $this->fixture('notifications/notification-configured.php')); - $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example-notification.yaml')); + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example-notification-facade.yaml')); $tree = $this->blueprint->analyze($tokens); $this->assertEquals(['created' => ['src/path/Notification/ReviewNotification.php']], $this->subject->output($tree)); } + + public function notificationDraftProvider() + { + return [ + ['drafts/send-statements-notification-facade.yaml'], + ['drafts/send-statements-notification-model.yaml'] + ]; + } } diff --git a/tests/Feature/Generator/TestGeneratorTest.php b/tests/Feature/Generator/TestGeneratorTest.php index c75dc969..fe9cda1d 100644 --- a/tests/Feature/Generator/TestGeneratorTest.php +++ b/tests/Feature/Generator/TestGeneratorTest.php @@ -143,7 +143,8 @@ public function controllerTreeDataProvider() { return [ ['drafts/readme-example.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/readme-example.php'], - ['drafts/readme-example-notification.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/readme-example-notification.php'], + ['drafts/readme-example-notification-facade.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/readme-example-notification.php'], + ['drafts/readme-example-notification-model.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/readme-example-notification.php'], ['drafts/respond-statements.yaml', 'tests/Feature/Http/Controllers/Api/PostControllerTest.php', 'tests/respond-statements.php'], ['drafts/full-crud-example.yaml', 'tests/Feature/Http/Controllers/PostControllerTest.php', 'tests/full-crud-example.php'], ]; diff --git a/tests/Feature/Lexers/StatementLexerTest.php b/tests/Feature/Lexers/StatementLexerTest.php index 299440da..9a30770b 100644 --- a/tests/Feature/Lexers/StatementLexerTest.php +++ b/tests/Feature/Lexers/StatementLexerTest.php @@ -166,6 +166,7 @@ public function it_returns_a_send_statement() $this->assertEquals('ReviewMail', $actual[0]->mail()); $this->assertNull($actual[0]->to()); $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_MAIL, $actual[0]->type()); } /** @@ -185,6 +186,7 @@ public function it_returns_a_send_statement_to_only() $this->assertEquals('ReviewMail', $actual[0]->mail()); $this->assertEquals('post.author', $actual[0]->to()); $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_MAIL, $actual[0]->type()); } /** @@ -204,6 +206,7 @@ public function it_returns_a_send_statement_with_only() $this->assertEquals('ReviewMail', $actual[0]->mail()); $this->assertNull($actual[0]->to()); $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_MAIL, $actual[0]->type()); } /** @@ -223,6 +226,167 @@ public function it_returns_a_send_statement_to_and_with() $this->assertEquals('ReviewMail', $actual[0]->mail()); $this->assertEquals('post.author', $actual[0]->to()); $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_MAIL, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_type_notification_facade() + { + $tokens = [ + 'send' => 'ReviewNotification' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertNull($actual[0]->to()); + $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_FACADE, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_to_only_type_notification_facade() + { + $tokens = [ + 'send' => 'ReviewNotification to:post.author' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('post.author', $actual[0]->to()); + $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_FACADE, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_with_only_type_notification_facade() + { + $tokens = [ + 'send' => 'ReviewNotification with:foo, bar, baz' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertNull($actual[0]->to()); + $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_FACADE, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_to_and_with_type_notification_facade() + { + $tokens = [ + 'send' => 'ReviewNotification to:post.author with:foo, bar, baz' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('post.author', $actual[0]->to()); + $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_FACADE, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_with_type_notification_model() + { + $tokens = [ + 'notify' => 'user ReviewNotification' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('user', $actual[0]->to()); + $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_MODEL, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_to_only_type_notification_model() + { + $tokens = [ + 'notify' => 'post.author ReviewNotification' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('post.author', $actual[0]->to()); + $this->assertSame([], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_MODEL, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_with_only_type_notification_model() + { + $tokens = [ + 'notify' => 'user ReviewNotification with:foo, bar, baz' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('user', $actual[0]->to()); + $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_MODEL, $actual[0]->type()); + } + + /** + * @test + */ + public function it_returns_a_send_statement_to_and_with_type_notification_model() + { + $tokens = [ + 'notify' => 'post.author ReviewNotification with:foo, bar, baz' + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(1, $actual); + $this->assertInstanceOf(SendStatement::class, $actual[0]); + + $this->assertEquals('ReviewNotification', $actual[0]->mail()); + $this->assertEquals('post.author', $actual[0]->to()); + $this->assertEquals(['foo', 'bar', 'baz'], $actual[0]->data()); + $this->assertEquals(SendStatement::TYPE_NOTIFICATION_WITH_MODEL, $actual[0]->type()); } /** diff --git a/tests/fixtures/controllers/readme-example-notification-facade.php b/tests/fixtures/controllers/readme-example-notification-facade.php new file mode 100644 index 00000000..0ee99ad4 --- /dev/null +++ b/tests/fixtures/controllers/readme-example-notification-facade.php @@ -0,0 +1,44 @@ +all()); + + Notification::send($post->author, new ReviewNotification($post)); + + SyncMedia::dispatch($post); + + event(new NewPost($post)); + + $request->session()->flash('post.title', $post->title); + + return redirect()->route('post.index'); + } +} diff --git a/tests/fixtures/controllers/readme-example-notification-model.php b/tests/fixtures/controllers/readme-example-notification-model.php new file mode 100644 index 00000000..29e1f817 --- /dev/null +++ b/tests/fixtures/controllers/readme-example-notification-model.php @@ -0,0 +1,42 @@ +all()); + + $post->author->notify(new ReviewNotification($post)); + + SyncMedia::dispatch($post); + + event(new NewPost($post)); + + $request->session()->flash('post.title', $post->title); + + return redirect()->route('post.index'); + } +} diff --git a/tests/fixtures/drafts/readme-example-notification.yaml b/tests/fixtures/drafts/readme-example-notification-facade.yaml similarity index 100% rename from tests/fixtures/drafts/readme-example-notification.yaml rename to tests/fixtures/drafts/readme-example-notification-facade.yaml diff --git a/tests/fixtures/drafts/readme-example-notification-model.yaml b/tests/fixtures/drafts/readme-example-notification-model.yaml new file mode 100644 index 00000000..aa35097f --- /dev/null +++ b/tests/fixtures/drafts/readme-example-notification-model.yaml @@ -0,0 +1,20 @@ +models: + Post: + title: string:400 + content: longtext + published_at: nullable timestamp + +controllers: + Post: + index: + query: all:posts + render: post.index with:posts + + store: + validate: title, content + save: post + notify: post.author ReviewNotification with:post + dispatch: SyncMedia with:post + fire: NewPost with:post + flash: post.title + redirect: post.index diff --git a/tests/fixtures/drafts/send-statements-notification.yaml b/tests/fixtures/drafts/send-statements-notification-facade.yaml similarity index 100% rename from tests/fixtures/drafts/send-statements-notification.yaml rename to tests/fixtures/drafts/send-statements-notification-facade.yaml diff --git a/tests/fixtures/drafts/send-statements-notification-model.yaml b/tests/fixtures/drafts/send-statements-notification-model.yaml new file mode 100644 index 00000000..5b98d952 --- /dev/null +++ b/tests/fixtures/drafts/send-statements-notification-model.yaml @@ -0,0 +1,10 @@ +controllers: + User: + store: + notify: user.email ReviewPostNotification with:post + redirect: user.show with:user.id + + Post: + store: + notify: user PublishedPostNotification + redirect: post.index diff --git a/tests/fixtures/tests/readme-example-notification.php b/tests/fixtures/tests/readme-example-notification.php index 3a6d2e1d..ae01bc9d 100644 --- a/tests/fixtures/tests/readme-example-notification.php +++ b/tests/fixtures/tests/readme-example-notification.php @@ -75,8 +75,8 @@ public function store_saves_and_redirects() $response->assertRedirect(route('post.index')); $response->assertSessionHas('post.title', $post->title); - Notification::assertSent(ReviewNotification::class, function ($notification) use ($post) { - return $notification->hasTo($post->author) && $notification->post->is($post); + Notification::assertSentTo($post->author, ReviewNotification::class, function ($notification) use ($post) { + return $notification->post->is($post); }); Queue::assertPushed(SyncMedia::class, function ($job) use ($post) { return $job->post->is($post);