From a0208f7a2906d6ab8ed53d797ec69dfe241adec6 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 8 Mar 2016 14:30:25 +0100 Subject: [PATCH 1/8] Added a mechanism that sends reminder notifications to the todos assignee. includes worker script, Command, Event and a new projection --- config/autoload/dependencies.global.php | 1 + config/autoload/prooph.global.php | 4 ++ migrations/Version20160308110616.php | 31 +++++++++ scripts/send_reminder_notification.php | 35 ++++++++++ .../Todo/RemindTodoAssigneeHandlerFactory.php | 26 +++++++ src/Model/Todo/Command/RemindTodoAssignee.php | 39 +++++++++++ .../Todo/Event/TodoAssigneeWasReminded.php | 68 +++++++++++++++++++ src/Model/Todo/Exception/InvalidReminder.php | 9 +++ .../Handler/RemindTodoAssigneeHandler.php | 65 ++++++++++++++++++ src/Model/Todo/Todo.php | 42 ++++++++++-- src/Projection/Todo/TodoFinder.php | 12 ++++ src/Projection/Todo/TodoProjector.php | 18 +++++ 12 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 migrations/Version20160308110616.php create mode 100644 scripts/send_reminder_notification.php create mode 100644 src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php create mode 100644 src/Model/Todo/Command/RemindTodoAssignee.php create mode 100644 src/Model/Todo/Event/TodoAssigneeWasReminded.php create mode 100644 src/Model/Todo/Handler/RemindTodoAssigneeHandler.php diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 369dbac..1d5c533 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -34,6 +34,7 @@ \Prooph\ProophessorDo\Model\Todo\Handler\ReopenTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\ReopenTodoHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\Handler\AddDeadlineToTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\AddDeadlineToTodoHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\Handler\AddReminderToTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\AddReminderToTodoHandlerFactory::class, + \Prooph\ProophessorDo\Model\Todo\Handler\RemindTodoAssigneeHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\RemindTodoAssigneeHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\TodoList::class => \Prooph\ProophessorDo\Container\Infrastructure\Repository\EventStoreTodoListFactory::class, // Projections \Prooph\ProophessorDo\Projection\User\UserProjector::class => \Prooph\ProophessorDo\Container\Projection\User\UserProjectorFactory::class, diff --git a/config/autoload/prooph.global.php b/config/autoload/prooph.global.php index a8b04aa..359bc4a 100644 --- a/config/autoload/prooph.global.php +++ b/config/autoload/prooph.global.php @@ -53,6 +53,7 @@ \Prooph\ProophessorDo\Model\Todo\Command\ReopenTodo::class => \Prooph\ProophessorDo\Model\Todo\Handler\ReopenTodoHandler::class, \Prooph\ProophessorDo\Model\Todo\Command\AddDeadlineToTodo::class => \Prooph\ProophessorDo\Model\Todo\Handler\AddDeadlineToTodoHandler::class, \Prooph\ProophessorDo\Model\Todo\Command\AddReminderToTodo::class => \Prooph\ProophessorDo\Model\Todo\Handler\AddReminderToTodoHandler::class, + \Prooph\ProophessorDo\Model\Todo\Command\RemindTodoAssignee::class => \Prooph\ProophessorDo\Model\Todo\Handler\RemindTodoAssigneeHandler::class, ], ], ], @@ -83,6 +84,9 @@ \Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo::class => [ \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class, ], + \Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded::class => [ + \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class, + ], ], ], ], diff --git a/migrations/Version20160308110616.php b/migrations/Version20160308110616.php new file mode 100644 index 0000000..e2cdfff --- /dev/null +++ b/migrations/Version20160308110616.php @@ -0,0 +1,31 @@ +getTable(Table::TODO); + $todo->addColumn('reminded', 'boolean', ['default' => 0, 'notnull' => false, 'length' => 1]); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $todo = $schema->getTable(Table::TODO); + $todo->dropColumn('reminded'); + } +} diff --git a/scripts/send_reminder_notification.php b/scripts/send_reminder_notification.php new file mode 100644 index 0000000..567a3d0 --- /dev/null +++ b/scripts/send_reminder_notification.php @@ -0,0 +1,35 @@ +get(CommandBus::class); + $todoFinder = $container->get(TodoFinder::class); + + $todos = $todoFinder->findByOpenReminders(); + + if (!$todos) { + echo "Nothing todo. Exiting.\n"; + exit; + } + + foreach ($todos as $todo) { + echo "Send reminder for Todo with id {$todo->id}.\n"; + $commandBus->dispatch(RemindTodoAssignee::forTodo(TodoId::fromString($todo->id))); + } + + echo "Done!\n"; +} diff --git a/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php b/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php new file mode 100644 index 0000000..0293d32 --- /dev/null +++ b/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php @@ -0,0 +1,26 @@ + + */ +class RemindTodoAssigneeHandlerFactory +{ + /** + * @param ContainerInterface $container + * @return RemindTodoAssigneeHandlerFactory + */ + public function __invoke(ContainerInterface $container) + { + return new RemindTodoAssigneeHandler($container->get(TodoList::class), $container->get(TodoFinder::class)); + } +} diff --git a/src/Model/Todo/Command/RemindTodoAssignee.php b/src/Model/Todo/Command/RemindTodoAssignee.php new file mode 100644 index 0000000..7363c1a --- /dev/null +++ b/src/Model/Todo/Command/RemindTodoAssignee.php @@ -0,0 +1,39 @@ + + */ +final class RemindTodoAssignee extends Command implements PayloadConstructable +{ + use PayloadTrait; + + /** + * + * @param TodoId $todoId + * @return RemindTodoAssignee + */ + public static function forTodo(TodoId $todoId) + { + return new self([ + 'todo_id' => $todoId->toString(), + ]); + } + + + /** + * @return TodoId + */ + public function todoId() + { + return TodoId::fromString($this->payload['todo_id']); + } +} diff --git a/src/Model/Todo/Event/TodoAssigneeWasReminded.php b/src/Model/Todo/Event/TodoAssigneeWasReminded.php new file mode 100644 index 0000000..ef7d4c8 --- /dev/null +++ b/src/Model/Todo/Event/TodoAssigneeWasReminded.php @@ -0,0 +1,68 @@ + + */ +final class TodoAssigneeWasReminded extends AggregateChanged +{ + /** + * @var TodoId + */ + private $todoId; + + /** + * @var UserId + */ + private $userId; + + /** + * @param TodoId $todoId + * @param UserId $userId + * @return DeadlineWasAddedToTodo + */ + public static function forAssignee(TodoId $todoId, UserId $userId) + { + $event = self::occur($todoId->toString(), [ + 'todo_id' => $todoId->toString(), + 'user_id' => $userId->toString(), + ]); + + $event->todoId = $todoId; + $event->userId = $userId; + + return $event; + } + + /** + * @return UserId + */ + public function todoId() + { + if (!$this->todoId) { + $this->todoId = TodoId::fromString($this->payload['todo_id']); + } + + return $this->todoId; + } + + /** + * @return UserId + */ + public function userId() + { + if (!$this->userId) { + $this->userId = UserId::fromString($this->payload['user_id']); + } + + return $this->userId; + } +} diff --git a/src/Model/Todo/Exception/InvalidReminder.php b/src/Model/Todo/Exception/InvalidReminder.php index 97e3d17..0b24dee 100644 --- a/src/Model/Todo/Exception/InvalidReminder.php +++ b/src/Model/Todo/Exception/InvalidReminder.php @@ -40,4 +40,13 @@ public static function reminderInThePast(TodoReminder $reminder) $reminder->createdOn() )); } + /** + * @return InvalidReminder + */ + public static function alreadyReminded() + { + return new self(sprintf( + 'The assignee was already reminded.' + )); + } } diff --git a/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php b/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php new file mode 100644 index 0000000..acaaf88 --- /dev/null +++ b/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php @@ -0,0 +1,65 @@ + + */ +final class RemindTodoAssigneeHandler +{ + /** + * @var TodoList + */ + private $todoList; + /** + * @var TodoFinder + */ + private $todoFinder; + + /** + * @param TodoList $todoList + * @param TodoFinder $todoFinder + */ + public function __construct(TodoList $todoList, TodoFinder $todoFinder) + { + $this->todoList = $todoList; + $this->todoFinder = $todoFinder; + } + + /** + * @param RemindTodoAssignee $command + */ + public function __invoke(RemindTodoAssignee $command) + { + $todo = $this->todoList->get($command->todoId()); + if (!$todo) { + throw TodoNotFound::withTodoId($command->todoId()); + } + + $todoData = $this->todoFinder->findById($command->todoId()->toString()); + + if ($todoData->reminded) { + var_dump('do nothing, assignee already reminded'); + + return; + } + + $reminder = new \DateTimeImmutable($todoData->reminder, new \DateTimeZone('UTC')); + $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); + + if ($reminder > $now) { + var_dump('do nothing, reminder in future'); + + return; + } + + $todo->remindAssignee(); + } +} diff --git a/src/Model/Todo/Todo.php b/src/Model/Todo/Todo.php index 1f0b1ff..ddb5bd0 100644 --- a/src/Model/Todo/Todo.php +++ b/src/Model/Todo/Todo.php @@ -10,14 +10,15 @@ */ namespace Prooph\ProophessorDo\Model\Todo; -use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; -use Prooph\ProophessorDo\Model\Todo\Event\TodoWasReopened; -use Prooph\ProophessorDo\Model\User\UserId; use Assert\Assertion; use Prooph\EventSourcing\AggregateRoot; +use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Model\Todo\Event\DeadlineWasAddedToTodo; -use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; +use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasMarkedAsDone; +use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; +use Prooph\ProophessorDo\Model\Todo\Event\TodoWasReopened; +use Prooph\ProophessorDo\Model\User\UserId; /** * Class Todo @@ -57,6 +58,11 @@ final class Todo extends AggregateRoot */ private $reminder; + /** + * @var bool + */ + private $reminded = false; + /** * @param string $text * @param UserId $assigneeId @@ -68,6 +74,7 @@ public static function post($text, UserId $assigneeId, TodoId $todoId) $self = new self(); $self->assertText($text); $self->recordThat(TodoWasPosted::byUser($assigneeId, $text, $todoId, TodoStatus::open())); + return $self; } @@ -130,6 +137,14 @@ public function addReminder(UserId $userId, TodoReminder $reminder) $this->recordThat(ReminderWasAddedToTodo::byUserToDate($this->todoId, $this->assigneeId, $reminder)); } + public function remindAssignee() + { + if ($this->reminded) { + throw Exception\InvalidReminder::alreadyReminded(); + } + $this->recordThat(TodoAssigneeWasReminded::forAssignee($this->todoId, $this->assigneeId)); + } + public function reopenTodo() { if (!$this->status->isDone()) { @@ -155,6 +170,14 @@ public function reminder() return $this->reminder; } + /** + * @return bool + */ + public function assigneeWasReminded() + { + return $this->reminded; + } + /** * @return TodoId */ @@ -223,7 +246,6 @@ protected function whenDeadlineWasAddedToTodo(DeadlineWasAddedToTodo $event) $this->deadline = $event->deadline(); } - /** * @param ReminderWasAddedToTodo $event * @return void @@ -231,6 +253,16 @@ protected function whenDeadlineWasAddedToTodo(DeadlineWasAddedToTodo $event) protected function whenReminderWasAddedToTodo(ReminderWasAddedToTodo $event) { $this->reminder = $event->reminder(); + $this->reminded = false; + } + + /** + * @param TodoAssigneeWasReminded $event + * @return void + */ + protected function whenTodoAssigneeWasReminded(TodoAssigneeWasReminded $event) + { + $this->reminded = true; } /** diff --git a/src/Projection/Todo/TodoFinder.php b/src/Projection/Todo/TodoFinder.php index d9ef707..b658e8c 100644 --- a/src/Projection/Todo/TodoFinder.php +++ b/src/Projection/Todo/TodoFinder.php @@ -33,6 +33,7 @@ final class TodoFinder public function __construct(Connection $connection) { $this->connection = $connection; + $this->connection->setFetchMode(\PDO::FETCH_OBJ); } /** @@ -74,4 +75,15 @@ public function findById($todoId) $stmt->execute(); return $stmt->fetch(); } + + + /** + * @return \stdClass[] of todoData + */ + public function findByOpenReminders() + { + $stmt = $this->connection->prepare(sprintf("SELECT * FROM %s where reminder < NOW() AND reminded = 0", Table::TODO)); + $stmt->execute(); + return $stmt->fetchAll(); + } } diff --git a/src/Projection/Todo/TodoProjector.php b/src/Projection/Todo/TodoProjector.php index dbdd3b3..c9d5bdb 100644 --- a/src/Projection/Todo/TodoProjector.php +++ b/src/Projection/Todo/TodoProjector.php @@ -10,6 +10,7 @@ */ namespace Prooph\ProophessorDo\Projection\Todo; +use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Model\Todo\Event\DeadlineWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; @@ -109,6 +110,23 @@ public function onReminderWasAddedToTodo(ReminderWasAddedToTodo $event) Table::TODO, [ 'reminder' => $event->reminder()->toString(), + 'reminded' => 0, + ], + [ + 'id' => $event->todoId()->toString(), + ] + ); + } + /** + * @param TodoAssigneeWasReminded $event + * @return void + */ + public function onTodoAssigneeWasReminded(TodoAssigneeWasReminded $event) + { + $this->connection->update( + Table::TODO, + [ + 'reminded' => 1, ], [ 'id' => $event->todoId()->toString(), From 52b9179cda177616a9067d0ba894742e8cdfd79d Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 8 Mar 2016 14:31:40 +0100 Subject: [PATCH 2/8] Added a subscriber that listens to TodoAssigneeWasReminded events and echos out that it sent an email --- config/autoload/dependencies.global.php | 2 + config/autoload/prooph.global.php | 1 + .../Mail/SendTodoReminderMailSubscriber.php | 44 +++++++++++++++++++ .../SendTodoReminderMailSubscriberFactory.php | 26 +++++++++++ 4 files changed, 73 insertions(+) create mode 100644 src/App/Mail/SendTodoReminderMailSubscriber.php create mode 100644 src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 1d5c533..e27b0c5 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -41,6 +41,8 @@ \Prooph\ProophessorDo\Projection\User\UserFinder::class => \Prooph\ProophessorDo\Container\Projection\User\UserFinderFactory::class, \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoProjectorFactory::class, \Prooph\ProophessorDo\Projection\Todo\TodoFinder::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoFinderFactory::class, + // Subscriber + \Prooph\ProophessorDo\App\Mail\SendTodoReminderMailSubscriber::class => \Prooph\ProophessorDo\Container\App\Mail\SendTodoReminderMailSubscriberFactory::class, ], ], ]; diff --git a/config/autoload/prooph.global.php b/config/autoload/prooph.global.php index 359bc4a..21d0e3f 100644 --- a/config/autoload/prooph.global.php +++ b/config/autoload/prooph.global.php @@ -86,6 +86,7 @@ ], \Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded::class => [ \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class, + \Prooph\ProophessorDo\App\Mail\SendTodoReminderMailSubscriber::class, ], ], ], diff --git a/src/App/Mail/SendTodoReminderMailSubscriber.php b/src/App/Mail/SendTodoReminderMailSubscriber.php new file mode 100644 index 0000000..2e5f247 --- /dev/null +++ b/src/App/Mail/SendTodoReminderMailSubscriber.php @@ -0,0 +1,44 @@ + + */ +class SendTodoReminderMailSubscriber +{ + /** + * @var UserFinder + */ + private $userFinder; + /** + * @var TodoFinder + */ + private $todoFinder; + + /** + * @param UserFinder $userFinder + * @param TodoFinder $todoFinder + */ + public function __construct(UserFinder $userFinder, TodoFinder $todoFinder) + { + $this->userFinder = $userFinder; + $this->todoFinder = $todoFinder; + } + + public function __invoke(TodoAssigneeWasReminded $event) + { + $user = $this->userFinder->findById($event->userId()->toString()); + $todo = $this->todoFinder->findById($event->todoId()->toString()); + + $mail = "Hello {$user->name}. This a reminder for {$todo->text}"; + echo "Sending mail to {$user->email}\n"; + echo " {$mail}\n\n"; + } +} diff --git a/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php new file mode 100644 index 0000000..21aae9e --- /dev/null +++ b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php @@ -0,0 +1,26 @@ + + */ +class SendTodoReminderMailSubscriberFactory +{ + /** + * @param ContainerInterface $container + * @return SendTodoReminderMailSubscriber + */ + public function __invoke(ContainerInterface $container) + { + return new SendTodoReminderMailSubscriber($container->get(UserFinder::class), $container->get(TodoFinder::class)); + } +} From d31b22babb9b4c1bc5ca954155edd03b465a21c1 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 15 Mar 2016 14:41:57 +0100 Subject: [PATCH 3/8] Added distinct read model for TodoReminders. The read model is used by the send_reminder_notification worker. --- config/autoload/dependencies.global.php | 24 ++-- config/autoload/prooph.global.php | 2 + migrations/Version20160308110616.php | 17 ++- scripts/send_reminder_notification.php | 20 ++-- .../Todo/TodoReminderFinderFactory.php | 32 +++++ .../Todo/TodoReminderProjectorFactory.php | 23 ++++ src/Model/Todo/Command/AddReminderToTodo.php | 3 +- src/Model/Todo/Command/RemindTodoAssignee.php | 14 ++- .../Todo/Event/ReminderWasAddedToTodo.php | 5 +- .../Todo/Event/TodoAssigneeWasReminded.php | 30 ++++- src/Model/Todo/Exception/InvalidReminder.php | 32 ++++- src/Model/Todo/Exception/TodoNotOpen.php | 16 ++- .../Handler/RemindTodoAssigneeHandler.php | 45 +++++--- src/Model/Todo/Todo.php | 28 +++-- src/Model/Todo/TodoReminder.php | 61 +++++++--- src/Model/Todo/TodoReminderStatus.php | 96 +++++++++++++++ src/Projection/Table.php | 1 + src/Projection/Todo/TodoProjector.php | 22 +--- src/Projection/Todo/TodoReminderFinder.php | 46 ++++++++ src/Projection/Todo/TodoReminderProjector.php | 80 +++++++++++++ tests/Model/Todo/TodoReminderTest.php | 31 ++++- tests/Model/Todo/TodoTest.php | 109 +++++++++++++++++- 22 files changed, 625 insertions(+), 112 deletions(-) create mode 100644 src/Container/Projection/Todo/TodoReminderFinderFactory.php create mode 100644 src/Container/Projection/Todo/TodoReminderProjectorFactory.php create mode 100644 src/Model/Todo/TodoReminderStatus.php create mode 100644 src/Projection/Todo/TodoReminderFinder.php create mode 100644 src/Projection/Todo/TodoReminderProjector.php diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index e27b0c5..bc5239f 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -21,26 +21,28 @@ Helper\UrlHelper::class => Helper\UrlHelperFactory::class, 'doctrine.connection.default' => \Prooph\ProophessorDo\Container\Infrastructure\DoctrineDbalConnectionFactory::class, // Action middleware - \Prooph\ProophessorDo\App\Action\Home::class => \Prooph\ProophessorDo\Container\App\Action\HomeFactory::class, - \Prooph\ProophessorDo\App\Action\UserList::class => \Prooph\ProophessorDo\Container\App\Action\UserListFactory::class, + \Prooph\ProophessorDo\App\Action\Home::class => \Prooph\ProophessorDo\Container\App\Action\HomeFactory::class, + \Prooph\ProophessorDo\App\Action\UserList::class => \Prooph\ProophessorDo\Container\App\Action\UserListFactory::class, \Prooph\ProophessorDo\App\Action\UserRegistration::class => \Prooph\ProophessorDo\Container\App\Action\UserRegistrationFactory::class, - \Prooph\ProophessorDo\App\Action\UserTodoList::class => \Prooph\ProophessorDo\Container\App\Action\UserTodoListFactory::class, - \Prooph\ProophessorDo\App\Action\UserTodoForm::class => \Prooph\ProophessorDo\Container\App\Action\UserTodoFormFactory::class, + \Prooph\ProophessorDo\App\Action\UserTodoList::class => \Prooph\ProophessorDo\Container\App\Action\UserTodoListFactory::class, + \Prooph\ProophessorDo\App\Action\UserTodoForm::class => \Prooph\ProophessorDo\Container\App\Action\UserTodoFormFactory::class, // Model \Prooph\ProophessorDo\Model\User\Handler\RegisterUserHandler::class => \Prooph\ProophessorDo\Container\Model\User\RegisterUserHandlerFactory::class, - \Prooph\ProophessorDo\Model\User\UserCollection::class => \Prooph\ProophessorDo\Container\Infrastructure\Repository\EventStoreUserCollectionFactory::class, - \Prooph\ProophessorDo\Model\Todo\Handler\PostTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\PostTodoHandlerFactory::class, - \Prooph\ProophessorDo\Model\Todo\Handler\MarkTodoAsDoneHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\MarkTodoAsDoneHandlerFactory::class, - \Prooph\ProophessorDo\Model\Todo\Handler\ReopenTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\ReopenTodoHandlerFactory::class, + \Prooph\ProophessorDo\Model\User\UserCollection::class => \Prooph\ProophessorDo\Container\Infrastructure\Repository\EventStoreUserCollectionFactory::class, + \Prooph\ProophessorDo\Model\Todo\Handler\PostTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\PostTodoHandlerFactory::class, + \Prooph\ProophessorDo\Model\Todo\Handler\MarkTodoAsDoneHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\MarkTodoAsDoneHandlerFactory::class, + \Prooph\ProophessorDo\Model\Todo\Handler\ReopenTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\ReopenTodoHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\Handler\AddDeadlineToTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\AddDeadlineToTodoHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\Handler\AddReminderToTodoHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\AddReminderToTodoHandlerFactory::class, \Prooph\ProophessorDo\Model\Todo\Handler\RemindTodoAssigneeHandler::class => \Prooph\ProophessorDo\Container\Model\Todo\RemindTodoAssigneeHandlerFactory::class, - \Prooph\ProophessorDo\Model\Todo\TodoList::class => \Prooph\ProophessorDo\Container\Infrastructure\Repository\EventStoreTodoListFactory::class, + \Prooph\ProophessorDo\Model\Todo\TodoList::class => \Prooph\ProophessorDo\Container\Infrastructure\Repository\EventStoreTodoListFactory::class, // Projections \Prooph\ProophessorDo\Projection\User\UserProjector::class => \Prooph\ProophessorDo\Container\Projection\User\UserProjectorFactory::class, - \Prooph\ProophessorDo\Projection\User\UserFinder::class => \Prooph\ProophessorDo\Container\Projection\User\UserFinderFactory::class, + \Prooph\ProophessorDo\Projection\User\UserFinder::class => \Prooph\ProophessorDo\Container\Projection\User\UserFinderFactory::class, \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoProjectorFactory::class, - \Prooph\ProophessorDo\Projection\Todo\TodoFinder::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoFinderFactory::class, + \Prooph\ProophessorDo\Projection\Todo\TodoFinder::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoFinderFactory::class, + \Prooph\ProophessorDo\Projection\Todo\TodoReminderFinder::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoReminderFinderFactory::class, + \Prooph\ProophessorDo\Projection\Todo\TodoReminderProjector::class => \Prooph\ProophessorDo\Container\Projection\Todo\TodoReminderProjectorFactory::class, // Subscriber \Prooph\ProophessorDo\App\Mail\SendTodoReminderMailSubscriber::class => \Prooph\ProophessorDo\Container\App\Mail\SendTodoReminderMailSubscriberFactory::class, ], diff --git a/config/autoload/prooph.global.php b/config/autoload/prooph.global.php index 21d0e3f..86d6ff4 100644 --- a/config/autoload/prooph.global.php +++ b/config/autoload/prooph.global.php @@ -83,9 +83,11 @@ ], \Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo::class => [ \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class, + \Prooph\ProophessorDo\Projection\Todo\TodoReminderProjector::class, ], \Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded::class => [ \Prooph\ProophessorDo\Projection\Todo\TodoProjector::class, + \Prooph\ProophessorDo\Projection\Todo\TodoReminderProjector::class, \Prooph\ProophessorDo\App\Mail\SendTodoReminderMailSubscriber::class, ], ], diff --git a/migrations/Version20160308110616.php b/migrations/Version20160308110616.php index e2cdfff..56c9f57 100644 --- a/migrations/Version20160308110616.php +++ b/migrations/Version20160308110616.php @@ -11,21 +11,18 @@ */ class Version20160308110616 extends AbstractMigration { - /** - * @param Schema $schema - */ public function up(Schema $schema) { - $todo = $schema->getTable(Table::TODO); - $todo->addColumn('reminded', 'boolean', ['default' => 0, 'notnull' => false, 'length' => 1]); + $user = $schema->createTable(Table::TODO_REMINDER); + + $user->addColumn('todo_id', 'string', ['length' => 36]); + $user->addColumn('reminder', 'string', ['length' => 30]); + $user->addColumn('status', 'string', ['length' => 10]); + $user->setPrimaryKey(['todo_id']); } - /** - * @param Schema $schema - */ public function down(Schema $schema) { - $todo = $schema->getTable(Table::TODO); - $todo->dropColumn('reminded'); + $schema->dropTable(Table::TODO_REMINDER); } } diff --git a/scripts/send_reminder_notification.php b/scripts/send_reminder_notification.php index 567a3d0..e48b83f 100644 --- a/scripts/send_reminder_notification.php +++ b/scripts/send_reminder_notification.php @@ -6,7 +6,8 @@ use Prooph\ProophessorDo\Model\Todo\Command\RemindTodoAssignee; use Prooph\ProophessorDo\Model\Todo\TodoId; - use Prooph\ProophessorDo\Projection\Todo\TodoFinder; + use Prooph\ProophessorDo\Model\Todo\TodoReminder; + use Prooph\ProophessorDo\Projection\Todo\TodoReminderFinder; use Prooph\ServiceBus\CommandBus; chdir(dirname(__DIR__)); @@ -17,18 +18,21 @@ $container = require 'config/container.php'; $commandBus = $container->get(CommandBus::class); - $todoFinder = $container->get(TodoFinder::class); + $todoReminderFinder = $container->get(TodoReminderFinder::class); - $todos = $todoFinder->findByOpenReminders(); + $todoReminder = $todoReminderFinder->findOpen(); - if (!$todos) { - echo "Nothing todo. Exiting.\n"; + if (!$todoReminder) { + echo "Nothing to do. Exiting.\n"; exit; } - foreach ($todos as $todo) { - echo "Send reminder for Todo with id {$todo->id}.\n"; - $commandBus->dispatch(RemindTodoAssignee::forTodo(TodoId::fromString($todo->id))); + foreach ($todoReminder as $reminder) { + echo "Send reminder for Todo with id {$reminder->todo_id}.\n"; + $commandBus->dispatch( + RemindTodoAssignee::forTodo( + TodoId::fromString($reminder->todo_id), TodoReminder::fromString($reminder->reminder, $reminder->status) + )); } echo "Done!\n"; diff --git a/src/Container/Projection/Todo/TodoReminderFinderFactory.php b/src/Container/Projection/Todo/TodoReminderFinderFactory.php new file mode 100644 index 0000000..092795a --- /dev/null +++ b/src/Container/Projection/Todo/TodoReminderFinderFactory.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 5/4/15 - 8:53 PM + */ +namespace Prooph\ProophessorDo\Container\Projection\Todo; + +use Interop\Container\ContainerInterface; +use Prooph\ProophessorDo\Projection\Todo\TodoReminderFinder; + +/** + * Class TodoFinderFactory + * + * @package Prooph\ProophessorDo\Projection\Todo + * @author Roman Sachse + */ +final class TodoReminderFinderFactory +{ + /** + * @param ContainerInterface $container + * @return TodoReminderFinder + */ + public function __invoke(ContainerInterface $container) + { + return new TodoReminderFinder($container->get('doctrine.connection.default')); + } +} diff --git a/src/Container/Projection/Todo/TodoReminderProjectorFactory.php b/src/Container/Projection/Todo/TodoReminderProjectorFactory.php new file mode 100644 index 0000000..984490e --- /dev/null +++ b/src/Container/Projection/Todo/TodoReminderProjectorFactory.php @@ -0,0 +1,23 @@ + + */ +final class TodoReminderProjectorFactory +{ + /** + * @param ContainerInterface $container + * @return TodoReminderProjector + */ + public function __invoke(ContainerInterface $container) + { + return new TodoReminderProjector($container->get('doctrine.connection.default')); + } +} diff --git a/src/Model/Todo/Command/AddReminderToTodo.php b/src/Model/Todo/Command/AddReminderToTodo.php index 1b4cca5..53f67b0 100644 --- a/src/Model/Todo/Command/AddReminderToTodo.php +++ b/src/Model/Todo/Command/AddReminderToTodo.php @@ -15,6 +15,7 @@ use Prooph\Common\Messaging\PayloadTrait; use Prooph\ProophessorDo\Model\Todo\TodoId; use Prooph\ProophessorDo\Model\Todo\TodoReminder; +use Prooph\ProophessorDo\Model\Todo\TodoReminderStatus; use Prooph\ProophessorDo\Model\User\UserId; /** @@ -48,6 +49,6 @@ public function todoId() */ public function reminder() { - return TodoReminder::fromString($this->payload['reminder']); + return TodoReminder::fromString($this->payload['reminder'], TodoReminderStatus::OPEN); } } diff --git a/src/Model/Todo/Command/RemindTodoAssignee.php b/src/Model/Todo/Command/RemindTodoAssignee.php index 7363c1a..127e7fa 100644 --- a/src/Model/Todo/Command/RemindTodoAssignee.php +++ b/src/Model/Todo/Command/RemindTodoAssignee.php @@ -5,6 +5,7 @@ use Prooph\Common\Messaging\PayloadConstructable; use Prooph\Common\Messaging\PayloadTrait; use Prooph\ProophessorDo\Model\Todo\TodoId; +use Prooph\ProophessorDo\Model\Todo\TodoReminder; /** * Class RemindTodoAssignee @@ -19,12 +20,15 @@ final class RemindTodoAssignee extends Command implements PayloadConstructable /** * * @param TodoId $todoId + * @param TodoReminder $todoReminder * @return RemindTodoAssignee */ - public static function forTodo(TodoId $todoId) + public static function forTodo(TodoId $todoId, TodoReminder $todoReminder) { return new self([ 'todo_id' => $todoId->toString(), + 'reminder' => $todoReminder->toString(), + 'reminder_status' => $todoReminder->status()->toString() ]); } @@ -36,4 +40,12 @@ public function todoId() { return TodoId::fromString($this->payload['todo_id']); } + + /** + * @return TodoReminder + */ + public function reminder() + { + return TodoReminder::fromString($this->payload['reminder'], $this->payload['reminder_status']); + } } diff --git a/src/Model/Todo/Event/ReminderWasAddedToTodo.php b/src/Model/Todo/Event/ReminderWasAddedToTodo.php index 489a4b4..63917d0 100644 --- a/src/Model/Todo/Event/ReminderWasAddedToTodo.php +++ b/src/Model/Todo/Event/ReminderWasAddedToTodo.php @@ -3,8 +3,9 @@ namespace Prooph\ProophessorDo\Model\Todo\Event; use Prooph\EventSourcing\AggregateChanged; -use Prooph\ProophessorDo\Model\Todo\TodoReminder; use Prooph\ProophessorDo\Model\Todo\TodoId; +use Prooph\ProophessorDo\Model\Todo\TodoReminder; +use Prooph\ProophessorDo\Model\Todo\TodoReminderStatus; use Prooph\ProophessorDo\Model\User\UserId; /** @@ -81,7 +82,7 @@ public function userId() public function reminder() { if (!$this->reminder) { - $this->reminder = TodoReminder::fromString($this->payload['reminder']); + $this->reminder = TodoReminder::fromString($this->payload['reminder'], TodoReminderStatus::OPEN); } return $this->reminder; diff --git a/src/Model/Todo/Event/TodoAssigneeWasReminded.php b/src/Model/Todo/Event/TodoAssigneeWasReminded.php index ef7d4c8..56bb9ac 100644 --- a/src/Model/Todo/Event/TodoAssigneeWasReminded.php +++ b/src/Model/Todo/Event/TodoAssigneeWasReminded.php @@ -4,10 +4,11 @@ use Prooph\EventSourcing\AggregateChanged; use Prooph\ProophessorDo\Model\Todo\TodoId; +use Prooph\ProophessorDo\Model\Todo\TodoReminder; use Prooph\ProophessorDo\Model\User\UserId; /** - * Class AssigneeWasNotified + * Class TodoAssigneeWasReminded * * @package Prooph\ProophessorDo\Model\Todo\Event * @author Roman Sachse @@ -24,26 +25,35 @@ final class TodoAssigneeWasReminded extends AggregateChanged */ private $userId; + /** + * @var TodoReminder + */ + private $reminder; + /** * @param TodoId $todoId * @param UserId $userId - * @return DeadlineWasAddedToTodo + * @param TodoReminder $reminder + * @return TodoAssigneeWasReminded */ - public static function forAssignee(TodoId $todoId, UserId $userId) + public static function forAssignee(TodoId $todoId, UserId $userId, TodoReminder $reminder) { $event = self::occur($todoId->toString(), [ 'todo_id' => $todoId->toString(), 'user_id' => $userId->toString(), + 'reminder' => $reminder->toString(), + 'reminder_status' => $reminder->status()->toString() ]); $event->todoId = $todoId; $event->userId = $userId; + $event->reminder = $reminder; return $event; } /** - * @return UserId + * @return TodoId */ public function todoId() { @@ -65,4 +75,16 @@ public function userId() return $this->userId; } + + /** + * @return TodoReminder + */ + public function reminder() + { + if (!$this->reminder) { + $this->reminder = TodoReminder::fromString($this->payload['reminder'], $this->payload['reminder_status']); + } + + return $this->reminder; + } } diff --git a/src/Model/Todo/Exception/InvalidReminder.php b/src/Model/Todo/Exception/InvalidReminder.php index 0b24dee..b469ef1 100644 --- a/src/Model/Todo/Exception/InvalidReminder.php +++ b/src/Model/Todo/Exception/InvalidReminder.php @@ -35,18 +35,42 @@ public static function userIsNotAssignee(UserId $user, UserId $assigneeId) public static function reminderInThePast(TodoReminder $reminder) { return new self(sprintf( - 'Provided reminder %s is in the past from %s', - $reminder->toString(), - $reminder->createdOn() + 'Provided reminder %s is in the past', + $reminder->toString() )); } + + /** + * @param TodoReminder $reminder + * @return InvalidReminder + */ + public static function reminderInTheFuture(TodoReminder $reminder) + { + return new self(sprintf( + 'Provided reminder %s is in the future', + $reminder->toString() + )); + } + /** * @return InvalidReminder */ public static function alreadyReminded() + { + return new self('The assignee was already reminded.'); + } + + /** + * @param TodoReminder $expected + * @param TodoReminder $actual + * @return InvalidReminder + */ + public static function reminderNotCurrent(TodoReminder $expected, TodoReminder $actual) { return new self(sprintf( - 'The assignee was already reminded.' + 'Notification for reminder %s can not be send, because %s is the current one.', + $actual->toString(), + $expected->toString() )); } } diff --git a/src/Model/Todo/Exception/TodoNotOpen.php b/src/Model/Todo/Exception/TodoNotOpen.php index 9de4bda..ca07123 100644 --- a/src/Model/Todo/Exception/TodoNotOpen.php +++ b/src/Model/Todo/Exception/TodoNotOpen.php @@ -59,7 +59,21 @@ public static function triedToAddDeadline(TodoDeadline $deadline, TodoStatus $st public static function triedToAddReminder(TodoReminder $reminder, TodoStatus $status) { return new self(sprintf( - 'Tried to reminder %s to a todo with status %s.', + 'Tried to add reminder %s to a todo with status %s.', + $reminder->toString(), + $status->toString() + )); + } + + /** + * @param TodoReminder $reminder + * @param TodoStatus $status + * @return TodoNotOpen + */ + public static function triedToSendReminder(TodoReminder $reminder, TodoStatus $status) + { + return new self(sprintf( + 'Tried to send a reminder %s for a todo with status %s.', $reminder->toString(), $status->toString() )); diff --git a/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php b/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php index acaaf88..3d16bb6 100644 --- a/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php +++ b/src/Model/Todo/Handler/RemindTodoAssigneeHandler.php @@ -3,8 +3,9 @@ use Prooph\ProophessorDo\Model\Todo\Command\RemindTodoAssignee; use Prooph\ProophessorDo\Model\Todo\Exception\TodoNotFound; +use Prooph\ProophessorDo\Model\Todo\Todo; use Prooph\ProophessorDo\Model\Todo\TodoList; -use Prooph\ProophessorDo\Projection\Todo\TodoFinder; +use Prooph\ProophessorDo\Model\Todo\TodoReminder; /** * Class RemindTodoAssigneeHandler @@ -18,19 +19,13 @@ final class RemindTodoAssigneeHandler * @var TodoList */ private $todoList; - /** - * @var TodoFinder - */ - private $todoFinder; /** * @param TodoList $todoList - * @param TodoFinder $todoFinder */ - public function __construct(TodoList $todoList, TodoFinder $todoFinder) + public function __construct(TodoList $todoList) { $this->todoList = $todoList; - $this->todoFinder = $todoFinder; } /** @@ -43,23 +38,35 @@ public function __invoke(RemindTodoAssignee $command) throw TodoNotFound::withTodoId($command->todoId()); } - $todoData = $this->todoFinder->findById($command->todoId()->toString()); - - if ($todoData->reminded) { - var_dump('do nothing, assignee already reminded'); + $reminder = $todo->reminder(); - return; + if ($this->reminderShouldBeProcessed($todo, $reminder)) { + $todo->remindAssignee($reminder); } + } - $reminder = new \DateTimeImmutable($todoData->reminder, new \DateTimeZone('UTC')); - $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); + /** + * @param Todo $todo + * @param TodoReminder $reminder + * @return bool + */ + private function reminderShouldBeProcessed(Todo $todo, TodoReminder $reminder) + { + // drop command, wrong reminder + if (!$todo->reminder()->equals($reminder)) { + return false; + } - if ($reminder > $now) { - var_dump('do nothing, reminder in future'); + // drop command, reminder is closed + if (!$reminder->isOpen()) { + return false; + } - return; + // drop command, reminder is in future + if ($reminder->isInTheFuture()) { + return false; } - $todo->remindAssignee(); + return true; } } diff --git a/src/Model/Todo/Todo.php b/src/Model/Todo/Todo.php index ddb5bd0..37bd11e 100644 --- a/src/Model/Todo/Todo.php +++ b/src/Model/Todo/Todo.php @@ -12,9 +12,9 @@ use Assert\Assertion; use Prooph\EventSourcing\AggregateRoot; -use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Model\Todo\Event\DeadlineWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; +use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasMarkedAsDone; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasReopened; @@ -54,7 +54,7 @@ final class Todo extends AggregateRoot private $deadline; /** - * @var \DateTimeImmutable + * @var TodoReminder */ private $reminder; @@ -137,12 +137,25 @@ public function addReminder(UserId $userId, TodoReminder $reminder) $this->recordThat(ReminderWasAddedToTodo::byUserToDate($this->todoId, $this->assigneeId, $reminder)); } - public function remindAssignee() + public function remindAssignee(TodoReminder $reminder) { - if ($this->reminded) { + if ($this->status->isDone()) { + throw Exception\TodoNotOpen::triedToAddReminder($reminder, $this->status); + } + + if (!$this->reminder->equals($reminder)) { + throw Exception\InvalidReminder::reminderNotCurrent($this->reminder, $reminder); + } + + if (!$this->reminder->isOpen()) { throw Exception\InvalidReminder::alreadyReminded(); } - $this->recordThat(TodoAssigneeWasReminded::forAssignee($this->todoId, $this->assigneeId)); + + if ($reminder->isInTheFuture()) { + throw Exception\InvalidReminder::reminderInTheFuture($reminder); + } + + $this->recordThat(TodoAssigneeWasReminded::forAssignee($this->todoId, $this->assigneeId, $reminder->close())); } public function reopenTodo() @@ -163,7 +176,7 @@ public function deadline() } /** - * @return \DateTimeImmutable + * @return TodoReminder */ public function reminder() { @@ -253,7 +266,6 @@ protected function whenDeadlineWasAddedToTodo(DeadlineWasAddedToTodo $event) protected function whenReminderWasAddedToTodo(ReminderWasAddedToTodo $event) { $this->reminder = $event->reminder(); - $this->reminded = false; } /** @@ -262,7 +274,7 @@ protected function whenReminderWasAddedToTodo(ReminderWasAddedToTodo $event) */ protected function whenTodoAssigneeWasReminded(TodoAssigneeWasReminded $event) { - $this->reminded = true; + $this->reminder = $event->reminder(); } /** diff --git a/src/Model/Todo/TodoReminder.php b/src/Model/Todo/TodoReminder.php index 1adc321..64be6d7 100644 --- a/src/Model/Todo/TodoReminder.php +++ b/src/Model/Todo/TodoReminder.php @@ -16,26 +16,39 @@ final class TodoReminder private $reminder; /** - * @var \DateTimeImmutable + * @var TodoReminderStatus */ - private $createdOn; + private $status; /** * @param string $reminder + * @param string $status * @return TodoReminder */ - public static function fromString($reminder) + public static function fromString($reminder, $status) { - return new self($reminder); + return new self( + new \DateTimeImmutable($reminder, new \DateTimeZone('UTC')), + TodoReminderStatus::fromString($status) + ); } /** * @param string $reminder + * @param TodoReminderStatus $status + */ + private function __construct($reminder, TodoReminderStatus $status) + { + $this->reminder = $reminder; + $this->status = $status; + } + + /** + * @return bool */ - private function __construct($reminder) + public function isOpen() { - $this->reminder = new \DateTimeImmutable($reminder, new \DateTimeZone('UTC')); - $this->createdOn = new \DateTimeImmutable("now", new \DateTimeZone('UTC')); + return $this->status->isOpen(); } /** @@ -43,22 +56,44 @@ private function __construct($reminder) */ public function isInThePast() { - return $this->reminder < $this->createdOn; + return $this->reminder < new \DateTimeImmutable("now", new \DateTimeZone('UTC')); } /** - * @return string + * @return bool */ - public function toString() + public function isInTheFuture() { - return $this->reminder->format(\DateTime::ATOM); + return $this->reminder > new \DateTimeImmutable("now", new \DateTimeZone('UTC')); + } + + /** + * @return TodoReminderStatus + */ + public function status() + { + return $this->status; + } + + + /** + * @return TodoReminder + */ + public function close() + { + return new self($this->reminder, TodoReminderStatus::closed()); } /** * @return string */ - public function createdOn() + public function toString() + { + return $this->reminder->format(\DateTime::ATOM); + } + + public function equals(TodoReminder $todoReminder) { - return $this->createdOn->format(\DateTime::ATOM); + return $this->toString() === $todoReminder->toString(); } } diff --git a/src/Model/Todo/TodoReminderStatus.php b/src/Model/Todo/TodoReminderStatus.php new file mode 100644 index 0000000..1e236f3 --- /dev/null +++ b/src/Model/Todo/TodoReminderStatus.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 5/4/15 - 5:16 PM + */ +namespace Prooph\ProophessorDo\Model\Todo; + +use Assert\Assertion; + +/** + * Class TodoReminderStatus + * + * @package Prooph\ProophessorDo\Model\Todo + * @author Roman Sachse + */ +final class TodoReminderStatus +{ + const OPEN = "open"; + const CLOSED = "closed"; + + /** + * @var string + */ + private $status; + + /** + * @return TodoReminderStatus + */ + public static function open() + { + return new self(self::OPEN); + } + + /** + * @return TodoReminderStatus + */ + public static function closed() + { + return new self(self::CLOSED); + } + + /** + * @param string $status + * @return TodoReminderStatus + */ + public static function fromString($status) + { + return new self($status); + } + + /** + * @param string $status + */ + private function __construct($status) + { + Assertion::inArray($status, [self::OPEN, self::CLOSED]); + $this->status = $status; + } + + /** + * @return TodoReminderStatus + */ + public function close() + { + return new self(self::CLOSED); + } + + /** + * @return bool + */ + public function isOpen() + { + return $this->status !== self::CLOSED; + } + + /** + * @return bool + */ + public function isClosed() + { + return $this->status === self::CLOSED; + } + + /** + * @return string + */ + public function toString() + { + return $this->status; + } +} diff --git a/src/Projection/Table.php b/src/Projection/Table.php index 132cf63..c471d85 100644 --- a/src/Projection/Table.php +++ b/src/Projection/Table.php @@ -20,4 +20,5 @@ final class Table { const USER = 'read_user'; const TODO = "read_todo"; + const TODO_REMINDER = "read_todo_reminder"; } diff --git a/src/Projection/Todo/TodoProjector.php b/src/Projection/Todo/TodoProjector.php index c9d5bdb..65a029c 100644 --- a/src/Projection/Todo/TodoProjector.php +++ b/src/Projection/Todo/TodoProjector.php @@ -10,14 +10,13 @@ */ namespace Prooph\ProophessorDo\Projection\Todo; -use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; +use Doctrine\DBAL\Connection; use Prooph\ProophessorDo\Model\Todo\Event\DeadlineWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; -use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasMarkedAsDone; +use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasReopened; use Prooph\ProophessorDo\Projection\Table; -use Doctrine\DBAL\Connection; /** * Class TodoProjector @@ -110,23 +109,6 @@ public function onReminderWasAddedToTodo(ReminderWasAddedToTodo $event) Table::TODO, [ 'reminder' => $event->reminder()->toString(), - 'reminded' => 0, - ], - [ - 'id' => $event->todoId()->toString(), - ] - ); - } - /** - * @param TodoAssigneeWasReminded $event - * @return void - */ - public function onTodoAssigneeWasReminded(TodoAssigneeWasReminded $event) - { - $this->connection->update( - Table::TODO, - [ - 'reminded' => 1, ], [ 'id' => $event->todoId()->toString(), diff --git a/src/Projection/Todo/TodoReminderFinder.php b/src/Projection/Todo/TodoReminderFinder.php new file mode 100644 index 0000000..5431dd4 --- /dev/null +++ b/src/Projection/Todo/TodoReminderFinder.php @@ -0,0 +1,46 @@ + + */ +final class TodoReminderFinder +{ + /** + * @var Connection + */ + private $connection; + + /** + * @param Connection $connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + $this->connection->setFetchMode(\PDO::FETCH_OBJ); + } + + /** + * @return \stdClass[] of todoData + */ + public function findOpen() + { + $stmt = $this->connection->prepare( + sprintf( + "SELECT * FROM %s where reminder < NOW() AND status = '%s'", + Table::TODO_REMINDER, + TodoReminderStatus::OPEN + ) + ); + $stmt->execute(); + + return $stmt->fetchAll(); + } +} diff --git a/src/Projection/Todo/TodoReminderProjector.php b/src/Projection/Todo/TodoReminderProjector.php new file mode 100644 index 0000000..5da05e6 --- /dev/null +++ b/src/Projection/Todo/TodoReminderProjector.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 5/4/15 - 5:44 PM + */ +namespace Prooph\ProophessorDo\Projection\Todo; + +use Doctrine\DBAL\Connection; +use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; +use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; +use Prooph\ProophessorDo\Projection\Table; + +/** + * Class TodoReminderProjector + * + * @package Prooph\ProophessorDo\Projection\Todo + * @author Roman Sachse + */ +final class TodoReminderProjector +{ + /** + * @var Connection + */ + private $connection; + + /** + * @param Connection $connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * @param ReminderWasAddedToTodo $event + * @return void + */ + public function onReminderWasAddedToTodo(ReminderWasAddedToTodo $event) + { + // remove other reminder for todo first + $this->connection->delete( + Table::TODO_REMINDER, + [ + 'todo_id' => $event->todoId()->toString(), + ] + ); + + $reminder = $event->reminder(); + $this->connection->insert( + Table::TODO_REMINDER, + [ + 'todo_id' => $event->todoId()->toString(), + 'reminder' => $reminder->toString(), + 'status' => $reminder->status()->toString(), + ] + ); + } + + /** + * @param TodoAssigneeWasReminded $event + * @return void + */ + public function onTodoAssigneeWasReminded(TodoAssigneeWasReminded $event) + { + $this->connection->update( + Table::TODO_REMINDER, + [ + 'status' => $event->reminder()->status()->toString(), + ], + [ + 'todo_id' => $event->todoId()->toString(), + ] + ); + } +} diff --git a/tests/Model/Todo/TodoReminderTest.php b/tests/Model/Todo/TodoReminderTest.php index f92fd62..2ecfe6e 100644 --- a/tests/Model/Todo/TodoReminderTest.php +++ b/tests/Model/Todo/TodoReminderTest.php @@ -3,6 +3,7 @@ namespace ProophTest\ProophessorDo\Model\Todo; use Prooph\ProophessorDo\Model\Todo\TodoReminder; +use Prooph\ProophessorDo\Model\Todo\TodoReminderStatus; use ProophTest\ProophessorDo\TestCase; /** @@ -21,13 +22,14 @@ final class TodoReminderTest extends TestCase */ public function it_correctly_validates_the_reminder($reminder, $inThePast) { - $reminder = TodoReminder::fromString($reminder); - $reminderInThePast = $reminder->isInThePast(); + $reminder = TodoReminder::fromString($reminder, TodoReminderStatus::OPEN); if ($inThePast) { - $this->assertTrue($reminderInThePast); + $this->assertTrue($reminder->isInThePast()); + $this->assertFalse($reminder->isInTheFuture()); } else { - $this->assertFalse($reminderInThePast); + $this->assertFalse($reminder->isInThePast()); + $this->assertTrue($reminder->isInTheFuture()); } } @@ -38,4 +40,25 @@ public function getReminders() ['1947-01-01 10:00:00', true], ]; } + + /** + * @test + */ + public function it_knows_about_its_status() + { + $reminder = TodoReminder::fromString('2047-02-01 10:00:00', TodoReminderStatus::OPEN); + $this->assertTrue($reminder->isOpen()); + + $reminder = TodoReminder::fromString('2047-02-01 10:00:00', TodoReminderStatus::CLOSED); + $this->assertFalse($reminder->isOpen()); + } + + public function it_returns_a_new_reminder_with_status_closed_when_closed() + { + $reminder = TodoReminder::fromString('2047-02-01 10:00:00', TodoReminderStatus::OPEN); + $reminderClosed = $reminder->close(); + + $this->assertNotEquals($reminder, $reminderClosed); + $this->assertFalse($reminderClosed->isOpen()); + } } diff --git a/tests/Model/Todo/TodoTest.php b/tests/Model/Todo/TodoTest.php index bc812ea..f9742ea 100644 --- a/tests/Model/Todo/TodoTest.php +++ b/tests/Model/Todo/TodoTest.php @@ -12,6 +12,7 @@ use Prooph\ProophessorDo\Model\Todo\Event\DeadlineWasAddedToTodo; use Prooph\ProophessorDo\Model\Todo\Event\ReminderWasAddedToTodo; +use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasMarkedAsDone; use Prooph\ProophessorDo\Model\Todo\Event\TodoWasPosted; use Prooph\ProophessorDo\Model\Todo\Exception\InvalidReminder; @@ -20,6 +21,8 @@ use Prooph\ProophessorDo\Model\Todo\TodoDeadline; use Prooph\ProophessorDo\Model\Todo\TodoId; use Prooph\ProophessorDo\Model\Todo\TodoReminder; +use Prooph\ProophessorDo\Model\Todo\TodoReminderStatus; +use Prooph\ProophessorDo\Model\Todo\TodoStatus; use Prooph\ProophessorDo\Model\User\UserId; use ProophTest\ProophessorDo\TestCase; @@ -101,7 +104,7 @@ public function it_adds_a_deadline_to_todo() { $todoId = TodoId::generate(); $userId = UserId::generate(); - $deadline = TodoDeadline::fromString('2047-12-31 12:00:00', '2047-12-01 12:00:00'); + $deadline = TodoDeadline::fromString('2047-12-31 12:00:00'); $todo = Todo::post('Do something tomorrow', $userId, $todoId); $this->assertNull($todo->deadline()); @@ -192,7 +195,7 @@ public function it_adds_a_reminder_to_todo() { $todoId = TodoId::generate(); $userId = UserId::generate(); - $reminder = TodoReminder::fromString('2047-12-31 12:00:00'); + $reminder = TodoReminder::fromString('2047-12-31 12:00:00', TodoReminderStatus::OPEN); $todo = Todo::post('Do something tomorrow', $userId, $todoId); $this->popRecordedEvent($todo); @@ -224,7 +227,9 @@ public function it_adds_a_reminder_to_todo() */ public function it_can_add_another_reminder_if_desired(Todo $todo) { - $todo->addReminder($todo->assigneeId(), TodoReminder::fromString('2047-12-11 12:00:00')); + $todo->addReminder( + $todo->assigneeId(), TodoReminder::fromString('2047-12-11 12:00:00', TodoReminderStatus::OPEN) + ); $events = $this->popRecordedEvent($todo); $this->assertCount(1, $events); @@ -242,7 +247,9 @@ public function it_throws_an_exception_if_reminder_is_in_the_past(Todo $todo) { $this->setExpectedException(InvalidReminder::class); - $todo->addReminder($todo->assigneeId(), TodoReminder::fromString('1980-12-11 12:00:00')); + $todo->addReminder( + $todo->assigneeId(), TodoReminder::fromString('1980-12-11 12:00:00', TodoReminderStatus::OPEN) + ); } /** @@ -254,7 +261,9 @@ public function it_throws_an_exception_if_user_who_adds_reminder_is_not_the_assi { $this->setExpectedException(InvalidReminder::class); - $todo->addReminder(UserId::generate(), TodoReminder::fromString('2047-12-11 12:00:00')); + $todo->addReminder( + UserId::generate(), TodoReminder::fromString('2047-12-11 12:00:00', TodoReminderStatus::OPEN) + ); } /** @@ -268,6 +277,94 @@ public function it_throws_an_exception_if_todo_is_closed_while_setting_a_reminde $this->setExpectedException(TodoNotOpen::class); - $todo->addReminder($todo->assigneeId(), TodoReminder::fromString('2047-12-11 12:00:00')); + $todo->addReminder( + $todo->assigneeId(), TodoReminder::fromString('2047-12-11 12:00:00', TodoReminderStatus::OPEN) + ); + } + + /** + * @test + */ + public function it_can_remind_the_assignee() + { + $todo = $this->todoWithReminderInThePast(); + $todo->remindAssignee(TodoReminder::fromString('2000-12-11 12:00:00', TodoReminderStatus::OPEN)); + + $events = $this->popRecordedEvent($todo); + + $this->assertCount(1, $events); + $this->assertInstanceOf(TodoAssigneeWasReminded::class, $events[0]); + + return $todo; + } + + /** + * @test + */ + public function it_throws_an_exception_if_todo_is_closed_while_reminding_the_assignee() + { + $todo = $this->todoWithReminderInThePast(); + + $todo->markAsDone(); + + $this->setExpectedException(TodoNotOpen::class); + + $todo->remindAssignee(TodoReminder::fromString('2000-12-11 12:00:00', TodoReminderStatus::OPEN)); + } + + /** + * @test + */ + public function it_throws_an_exception_if_assignee_should_be_notified_about_a_reminder_which_is_not_the_current_on() + { + $todo = $this->todoWithReminderInThePast(); + + $this->setExpectedException(InvalidReminder::class); + + $todo->remindAssignee(TodoReminder::fromString('2046-12-11 12:00:00', TodoReminderStatus::OPEN)); + } + + /** + * @test + */ + public function it_throws_an_exception_if_assignee_should_be_notified_about_a_reminder_in_the_future() + { + $todo = $this->todoWithReminderInThePast(); + + $reminder = TodoReminder::fromString('2046-12-11 12:00:00', TodoReminderStatus::OPEN); + $todo->addReminder($todo->assigneeId(), $reminder); + + $this->setExpectedException(InvalidReminder::class); + $todo->remindAssignee($reminder); + } + + /** + * @test + */ + public function it_throws_an_exception_if_assignee_should_be_notified_about_a_closed_reminder() + { + $todo = $this->todoWithReminderInThePast(); + + $todo->remindAssignee(TodoReminder::fromString('2000-12-11 12:00:00', TodoReminderStatus::OPEN)); + + $this->setExpectedException(InvalidReminder::class); + $todo->remindAssignee(TodoReminder::fromString('2000-12-11 12:00:00', TodoReminderStatus::OPEN)); + } + + /** + * @return Todo + */ + private function todoWithReminderInThePast() + { + $userId = UserId::generate(); + $todoId = TodoId::generate(); + $reminder = TodoReminder::fromString('2000-12-11 12:00:00', TodoReminderStatus::OPEN); + + $events = [ + TodoWasPosted::byUser($userId, 'test', $todoId, TodoStatus::open()), + ReminderWasAddedToTodo::byUserToDate($todoId, $userId, $reminder) + ]; + + return $this->reconstituteAggregateFromHistory(Todo::class, $events); } } From 37523b5ee873fb115cd02c64cb4afa167df3a8d4 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 15 Mar 2016 14:42:51 +0100 Subject: [PATCH 4/8] Wrapped the events in an ArrayIterator to comply with type hints of called method --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 474b211..a5efcd6 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -45,7 +45,7 @@ protected function reconstituteAggregateFromHistory($aggregateRootClass, array $ { return $this->getAggregateTranslator()->reconstituteAggregateFromHistory( AggregateType::fromAggregateRootClass($aggregateRootClass), - $events + new \ArrayIterator($events) ); } From 7a86383af4c401e93a02f48f74d4b0125dee5cc8 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 15 Mar 2016 15:47:01 +0100 Subject: [PATCH 5/8] Added docblocks and final --- src/App/Mail/SendTodoReminderMailSubscriber.php | 5 ++++- .../App/Mail/SendTodoReminderMailSubscriberFactory.php | 2 +- .../Model/Todo/RemindTodoAssigneeHandlerFactory.php | 2 +- src/Model/Todo/Todo.php | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/App/Mail/SendTodoReminderMailSubscriber.php b/src/App/Mail/SendTodoReminderMailSubscriber.php index 2e5f247..301f275 100644 --- a/src/App/Mail/SendTodoReminderMailSubscriber.php +++ b/src/App/Mail/SendTodoReminderMailSubscriber.php @@ -11,7 +11,7 @@ * @package Prooph\ProophessorDo\App\Mail * @author Roman Sachse */ -class SendTodoReminderMailSubscriber +final class SendTodoReminderMailSubscriber { /** * @var UserFinder @@ -32,6 +32,9 @@ public function __construct(UserFinder $userFinder, TodoFinder $todoFinder) $this->todoFinder = $todoFinder; } + /** + * @param TodoAssigneeWasReminded $event + */ public function __invoke(TodoAssigneeWasReminded $event) { $user = $this->userFinder->findById($event->userId()->toString()); diff --git a/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php index 21aae9e..0a0f20e 100644 --- a/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php +++ b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php @@ -13,7 +13,7 @@ * @package Prooph\ProophessorDo\Container\Model\Todo * @author Roman Sachse */ -class SendTodoReminderMailSubscriberFactory +final class SendTodoReminderMailSubscriberFactory { /** * @param ContainerInterface $container diff --git a/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php b/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php index 0293d32..8fc263f 100644 --- a/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php +++ b/src/Container/Model/Todo/RemindTodoAssigneeHandlerFactory.php @@ -13,7 +13,7 @@ * @package Prooph\ProophessorDo\Container\Model\Todo * @author Roman Sachse */ -class RemindTodoAssigneeHandlerFactory +final class RemindTodoAssigneeHandlerFactory { /** * @param ContainerInterface $container diff --git a/src/Model/Todo/Todo.php b/src/Model/Todo/Todo.php index 37bd11e..90a66de 100644 --- a/src/Model/Todo/Todo.php +++ b/src/Model/Todo/Todo.php @@ -137,6 +137,10 @@ public function addReminder(UserId $userId, TodoReminder $reminder) $this->recordThat(ReminderWasAddedToTodo::byUserToDate($this->todoId, $this->assigneeId, $reminder)); } + /** + * @param TodoReminder $reminder + * @throws Exception\InvalidReminder + */ public function remindAssignee(TodoReminder $reminder) { if ($this->status->isDone()) { From d193a42f85429ac23ca8bd0460c8574fd3d35406 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Tue, 15 Mar 2016 15:48:25 +0100 Subject: [PATCH 6/8] todo_id is not belonging to the payload because it is the aggregate_id which is stored anyway --- src/Model/Todo/Event/TodoAssigneeWasReminded.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Model/Todo/Event/TodoAssigneeWasReminded.php b/src/Model/Todo/Event/TodoAssigneeWasReminded.php index 56bb9ac..7788741 100644 --- a/src/Model/Todo/Event/TodoAssigneeWasReminded.php +++ b/src/Model/Todo/Event/TodoAssigneeWasReminded.php @@ -39,13 +39,11 @@ final class TodoAssigneeWasReminded extends AggregateChanged public static function forAssignee(TodoId $todoId, UserId $userId, TodoReminder $reminder) { $event = self::occur($todoId->toString(), [ - 'todo_id' => $todoId->toString(), 'user_id' => $userId->toString(), 'reminder' => $reminder->toString(), 'reminder_status' => $reminder->status()->toString() ]); - $event->todoId = $todoId; $event->userId = $userId; $event->reminder = $reminder; @@ -58,7 +56,7 @@ public static function forAssignee(TodoId $todoId, UserId $userId, TodoReminder public function todoId() { if (!$this->todoId) { - $this->todoId = TodoId::fromString($this->payload['todo_id']); + $this->todoId = TodoId::fromString($this->aggregateId()); } return $this->todoId; From a424296b2ac6fa18c11bc1a68ab1c21e3a506a7b Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Thu, 17 Mar 2016 13:21:36 +0100 Subject: [PATCH 7/8] Using Zend\Mail for sending actual mails --- composer.json | 4 +- .../autoload/smtp_connection.local.php.dist | 20 +++++ .../Mail/SendTodoReminderMailSubscriber.php | 20 ++++- .../SendTodoReminderMailSubscriberFactory.php | 73 ++++++++++++++++++- 4 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 config/autoload/smtp_connection.local.php.dist diff --git a/composer.json b/composer.json index ef6c7c7..aa0d192 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,9 @@ "zendframework/zend-expressive-helpers": "^2.0", "zendframework/zend-expressive-zendviewrenderer": "^1.0.1", "zendframework/zend-filter": "^2.6", - "zendframework/zend-servicemanager" : "^2.7.5 || ^3.0.3" + "zendframework/zend-servicemanager" : "^2.7.5 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7", + "zendframework/zend-mail": "^2.6" }, "require-dev" : { "fabpot/php-cs-fixer": "^1.9.3", diff --git a/config/autoload/smtp_connection.local.php.dist b/config/autoload/smtp_connection.local.php.dist new file mode 100644 index 0000000..6fc66ff --- /dev/null +++ b/config/autoload/smtp_connection.local.php.dist @@ -0,0 +1,20 @@ + [ + 'mail' => [ + 'transport' => 'in_memory', // smtp or in_memory (default) +// uncomment if you want to use smtp. Preset for gmail +// 'smtp' => [ +// 'name' => 'gmail.com', +// 'host' => 'smtp.gmail.com', +// 'port' => 587, +// 'connection_class' => 'plain', +// 'connection_config' => [ +// 'username' => 'YOUR_USERNAME_HERE', +// 'password' => 'YOUR_PASSWORD_HERE', +// 'ssl' => 'tls', +// ], +// ], + ] + ] +]; diff --git a/src/App/Mail/SendTodoReminderMailSubscriber.php b/src/App/Mail/SendTodoReminderMailSubscriber.php index 301f275..3fd1fbf 100644 --- a/src/App/Mail/SendTodoReminderMailSubscriber.php +++ b/src/App/Mail/SendTodoReminderMailSubscriber.php @@ -4,6 +4,8 @@ use Prooph\ProophessorDo\Model\Todo\Event\TodoAssigneeWasReminded; use Prooph\ProophessorDo\Projection\Todo\TodoFinder; use Prooph\ProophessorDo\Projection\User\UserFinder; +use Zend\Mail\Transport\TransportInterface; +use Zend\Mail; /** * Class SendTodoReminderMailSubscriber @@ -21,15 +23,21 @@ final class SendTodoReminderMailSubscriber * @var TodoFinder */ private $todoFinder; + /** + * @var TransportInterface + */ + private $mailer; /** * @param UserFinder $userFinder * @param TodoFinder $todoFinder + * @param TransportInterface $mailer */ - public function __construct(UserFinder $userFinder, TodoFinder $todoFinder) + public function __construct(UserFinder $userFinder, TodoFinder $todoFinder, TransportInterface $mailer) { $this->userFinder = $userFinder; $this->todoFinder = $todoFinder; + $this->mailer = $mailer; } /** @@ -40,8 +48,12 @@ public function __invoke(TodoAssigneeWasReminded $event) $user = $this->userFinder->findById($event->userId()->toString()); $todo = $this->todoFinder->findById($event->todoId()->toString()); - $mail = "Hello {$user->name}. This a reminder for {$todo->text}"; - echo "Sending mail to {$user->email}\n"; - echo " {$mail}\n\n"; + $mail = new Mail\Message(); + $mail->setBody("Hello {$user->name}. This a reminder for '{$todo->text}'. Don't be lazy!"); + $mail->setFrom('reminder@getprooph.org', 'Proophessor-do'); + $mail->addTo($user->email, $user->name); + $mail->setSubject('Proophessor-do Todo Reminder'); + + $this->mailer->send($mail); } } diff --git a/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php index 0a0f20e..80c40b7 100644 --- a/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php +++ b/src/Container/App/Mail/SendTodoReminderMailSubscriberFactory.php @@ -2,10 +2,18 @@ namespace Prooph\ProophessorDo\Container\App\Mail; +use Interop\Config\ConfigurationTrait; +use Interop\Config\ProvidesDefaultOptions; +use Interop\Config\RequiresConfig; +use Interop\Config\RequiresMandatoryOptions; use Interop\Container\ContainerInterface; use Prooph\ProophessorDo\App\Mail\SendTodoReminderMailSubscriber; use Prooph\ProophessorDo\Projection\Todo\TodoFinder; use Prooph\ProophessorDo\Projection\User\UserFinder; +use Zend\Mail\Transport\InMemory as InMemoryTransport; +use Zend\Mail\Transport\Smtp as SmtpTransport; +use Zend\Mail\Transport\SmtpOptions; + /** * Class RemindTodoAssigneeHandlerFactory @@ -13,14 +21,75 @@ * @package Prooph\ProophessorDo\Container\Model\Todo * @author Roman Sachse */ -final class SendTodoReminderMailSubscriberFactory +final class SendTodoReminderMailSubscriberFactory implements RequiresConfig, RequiresMandatoryOptions, ProvidesDefaultOptions { + use ConfigurationTrait; + + /** + * @inheritdoc + */ + public function vendorName() + { + return 'proophessor-do'; + } + + /** + * @inheritdoc + */ + public function packageName() + { + return 'mail'; + } + + /** + * @inheritdoc + */ + public function mandatoryOptions() + { + return ['transport']; + } + + /** + * @inheritdoc + */ + public function defaultOptions() + { + return ['transport' => 'in_memory']; + } + /** * @param ContainerInterface $container * @return SendTodoReminderMailSubscriber */ public function __invoke(ContainerInterface $container) { - return new SendTodoReminderMailSubscriber($container->get(UserFinder::class), $container->get(TodoFinder::class)); + return new SendTodoReminderMailSubscriber( + $container->get(UserFinder::class), + $container->get(TodoFinder::class), + $this->getTransport($container->get('config')) + ); + } + + /** + * @param $config + * @return SmtpTransport + */ + private function getTransport($config) + { + $config = $this->optionsWithFallback($config); + + switch ($config['transport']) { + case 'in_memory': + return new InMemoryTransport(); + + case 'smtp': + $transport = new SmtpTransport(); + $transport->setOptions(new SmtpOptions($config['smtp'])); + + return $transport; + } + throw new \InvalidArgumentException( + "Transport of type '{$config['transport']}' not known (use in_memory or smtp)" + ); } } From 728001b561523dbe78cff48a51188ba0c6c94ab2 Mon Sep 17 00:00:00 2001 From: Roman Sachse Date: Thu, 17 Mar 2016 13:27:54 +0100 Subject: [PATCH 8/8] Renamed smtp_connection.local.php.dist to mail_connection.local.php.dist --- ...p_connection.local.php.dist => mail_connection.local.php.dist} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename config/autoload/{smtp_connection.local.php.dist => mail_connection.local.php.dist} (100%) diff --git a/config/autoload/smtp_connection.local.php.dist b/config/autoload/mail_connection.local.php.dist similarity index 100% rename from config/autoload/smtp_connection.local.php.dist rename to config/autoload/mail_connection.local.php.dist