diff --git a/app/AppKernel.php b/app/AppKernel.php index 077c5db61..1129c08d5 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -22,8 +22,7 @@ public function registerBundles() new CodeExplorerBundle\CodeExplorerBundle(), new AppBundle\AppBundle(), new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), // used for initial population of non-SQLite databases in production envs - // uncomment the following line if your application sends emails - // new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), + new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), ]; // Some bundles are only used while developing the application or during diff --git a/app/Resources/translations/messages.en.xlf b/app/Resources/translations/messages.en.xlf index a41d20d13..b01531891 100644 --- a/app/Resources/translations/messages.en.xlf +++ b/app/Resources/translations/messages.en.xlf @@ -277,6 +277,15 @@ Post deleted successfully! + + notification.comment_created + Your post received a comment! + + + notification.comment_created.description + this link]]> + + help.app_description demo application built in the Symfony Framework to illustrate the recommended way of developing Symfony applications.]]> diff --git a/app/Resources/views/blog/post_show.html.twig b/app/Resources/views/blog/post_show.html.twig index c574ad115..09a1c3ee5 100644 --- a/app/Resources/views/blog/post_show.html.twig +++ b/app/Resources/views/blog/post_show.html.twig @@ -30,6 +30,7 @@ {% for comment in post.comments %}
+

{{ comment.authorEmail }} {{ 'post.commented_on'|trans }} {# it's not mandatory to set the timezone in localizeddate(). This is done to diff --git a/app/config/config.yml b/app/config/config.yml index 71d345a78..2ee3b9ccc 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -83,9 +83,9 @@ doctrine: # stores options that change the application behavior and parameters.yml # stores options that change from one server to another # -# swiftmailer: -# transport: "%mailer_transport%" -# host: "%mailer_host%" -# username: "%mailer_user%" -# password: "%mailer_password%" -# spool: { type: memory } +swiftmailer: + transport: "%mailer_transport%" + host: "%mailer_host%" + username: "%mailer_user%" + password: "%mailer_password%" + spool: { type: memory } diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml index ff9c5dfef..b26cb19b9 100644 --- a/app/config/config_dev.yml +++ b/app/config/config_dev.yml @@ -29,5 +29,5 @@ monolog: # type: chromephp # level: info -#swiftmailer: -# delivery_address: me@example.com +swiftmailer: + disable_delivery: true diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index 4cc6ec1e3..a50013b68 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -31,12 +31,9 @@ parameters: # $ php bin/console doctrine:schema:create # $ php bin/console doctrine:fixtures:load - # Uncomment these parameters if your application sends emails: - # - # mailer_transport: smtp - # mailer_host: 127.0.0.1 - # mailer_user: ~ - # mailer_password: ~ - # # If you don't use a real mail server, you can send emails via your Gmail account. # see http://symfony.com/doc/current/cookbook/email/gmail.html + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: ~ + mailer_password: ~ diff --git a/app/config/services.yml b/app/config/services.yml index bfe5ee2f7..a3c388a99 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -31,9 +31,17 @@ services: tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } - # Event subscribers are similar to event listeners but they don't need to add - # a separate tag for each listened event. Instead, the PHP class of the event - # subscriber includes a method that returns the list of listened events. + app.comment_notification: + class: AppBundle\EventListener\CommentNotificationListener + arguments: ['@mailer', '@router', '@translator', '%app.notifications.email_sender%'] + # The "method" attribute of this tag is optional and defaults to "on + camelCasedEventName" + # If the event is "comment.created" the method executed by default is "onCommentCreated()". + tags: + - { name: kernel.event_listener, event: comment.created, method: onCommentCreated } + + # Event subscribers are similar to event listeners but they don't need service tags. + # Instead, the PHP class of the event subscriber includes a method that returns + # the list of events listened by that class. # See http://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber app.console_subscriber: class: AppBundle\EventListener\ConsoleEventSubscriber diff --git a/src/AppBundle/Controller/BlogController.php b/src/AppBundle/Controller/BlogController.php index 3c107b29d..039ce867f 100644 --- a/src/AppBundle/Controller/BlogController.php +++ b/src/AppBundle/Controller/BlogController.php @@ -13,6 +13,7 @@ use AppBundle\Entity\Comment; use AppBundle\Entity\Post; +use AppBundle\Events; use AppBundle\Form\CommentType; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; @@ -20,6 +21,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -96,6 +98,20 @@ public function commentNewAction(Request $request, Post $post) $entityManager->persist($comment); $entityManager->flush(); + // When triggering an event, you can optionally pass some information. + // For simple applications, use the GenericEvent object provided by Symfony + // to pass some PHP variables. For more complex applications, define your + // own event object classes. + // See http://symfony.com/doc/current/components/event_dispatcher/generic_event.html + $event = new GenericEvent($comment); + + // When an event is dispatched, Symfony notifies it to all the listeners + // and subscribers registered to it. Listeners can modify the information + // passed in the event and they can even modify the execution flow, so + // there's no guarantee that the rest of this controller will be executed. + // See http://symfony.com/doc/current/components/event_dispatcher.html + $this->get('event_dispatcher')->dispatch(Events::COMMENT_CREATED, $event); + return $this->redirectToRoute('blog_post', ['slug' => $post->getSlug()]); } diff --git a/src/AppBundle/EventListener/CommentNotificationListener.php b/src/AppBundle/EventListener/CommentNotificationListener.php new file mode 100644 index 000000000..ff28cae0a --- /dev/null +++ b/src/AppBundle/EventListener/CommentNotificationListener.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AppBundle\EventListener; + +use Symfony\Component\EventDispatcher\GenericEvent; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Notifies post's author about new comments. + * + * @author Oleg Voronkovich + */ +class CommentNotificationListener +{ + /** + * @var \Swift_Mailer + */ + private $mailer; + + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * @var UrlGeneratorInterface + */ + private $urlGenerator; + + /** + * @var string + */ + private $sender; + + /** + * Constructor. + * + * @param \Swift_Mailer $mailer + * @param UrlGeneratorInterface $urlGenerator + * @param TranslatorInterface $translator + * @param string $sender + */ + public function __construct(\Swift_Mailer $mailer, UrlGeneratorInterface $urlGenerator, TranslatorInterface $translator, $sender) + { + $this->mailer = $mailer; + $this->urlGenerator = $urlGenerator; + $this->translator = $translator; + $this->sender = $sender; + } + + /** + * @param GenericEvent $event + */ + public function onCommentCreated(GenericEvent $event) + { + $comment = $event->getSubject(); + $post = $comment->getPost(); + + $linkToPost = $this->urlGenerator->generate('blog_post', [ + 'slug' => $post->getSlug(), + '_fragment' => 'comment_'.$comment->getId(), + ], UrlGeneratorInterface::ABSOLUTE_URL); + + $subject = $this->translator->trans('notification.comment_created'); + $body = $this->translator->trans('notification.comment_created.description', [ + '%title%' => $post->getTitle(), + '%link%' => $linkToPost, + ]); + + // Symfony uses a library called SwiftMailer to send emails. That's why + // email messages are created instantiating a Swift_Message class. + // See http://symfony.com/doc/current/email.html#sending-emails + $message = \Swift_Message::newInstance() + ->setSubject($subject) + ->setTo($post->getAuthorEmail()) + ->setFrom($this->sender) + ->setBody($body, 'text/html') + ; + + // In app/config/config_dev.yml the 'disable_delivery' option is set to 'true'. + // That's why in the development environment you won't actually receive any email. + // However, you can inspect the contents of those unsent emails using the debug toolbar. + // See http://symfony.com/doc/current/email/dev_environment.html#viewing-from-the-web-debug-toolbar + $this->mailer->send($message); + } +} diff --git a/src/AppBundle/Events.php b/src/AppBundle/Events.php new file mode 100644 index 000000000..cc504a092 --- /dev/null +++ b/src/AppBundle/Events.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AppBundle; + +/** + * This class defines the names of all the events dispatched in + * the Symfony Demo application. It's not mandatory to create a + * class like this, but it's considered a good practice. + * + * @author Oleg Voronkovich + */ +final class Events +{ + /** + * For the event naming conventions, see: + * http://symfony.com/doc/current/components/event_dispatcher.html#naming-conventions. + * + * @Event("Symfony\Component\EventDispatcher\GenericEvent") + * + * @var string + */ + const COMMENT_CREATED = 'comment.created'; +}