diff --git a/_build/redirection_map b/_build/redirection_map index 5d44f67b39d..e446f199ff9 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -479,3 +479,4 @@ /components/translation/usage /translation /components/translation/custom_formats https://github.com/symfony/translation /components/translation/custom_message_formatter https://github.com/symfony/translation +/components/notifier https://github.com/symfony/notifier diff --git a/components/notifier.rst b/components/notifier.rst deleted file mode 100644 index 29fac4f81f0..00000000000 --- a/components/notifier.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. index:: - single: Notifier - single: Notifications - single: Components; Notifier - -The Notifier Component -====================== - - The Notifier component sends notifications via one or more channels - (email, SMS, Slack, ...). - -.. versionadded:: 5.0 - - The Notifier component was introduced in Symfony 5.0 as an - :doc:`experimental feature `. - -Installation ------------- - -.. code-block:: terminal - - $ composer require symfony/notifier - -.. include:: /components/require_autoload.rst.inc - - -Usage ------ - -.. caution:: - - We're still working on the docs of this component. Check this page again - in a few days. diff --git a/index.rst b/index.rst index ac293625cb9..423b4407cc5 100644 --- a/index.rst +++ b/index.rst @@ -47,6 +47,7 @@ Topics mercure messenger migration + notifier performance profiler routing diff --git a/mailer.rst b/mailer.rst index d407a0568c6..8b57e872ec8 100644 --- a/mailer.rst +++ b/mailer.rst @@ -12,6 +12,9 @@ integration, CSS inlining, file attachments and a lot more. Get them installed w $ composer require symfony/mailer + +.. _mailer-transport-setup: + Transport Setup --------------- diff --git a/notifier.rst b/notifier.rst new file mode 100644 index 00000000000..8bae301a2ec --- /dev/null +++ b/notifier.rst @@ -0,0 +1,568 @@ +.. index:: + single: Notifier + +Creating and Sending Notifications +================================== + +.. versionadded:: 5.0 + + The Notifier component was introduced in Symfony 5.0 as an + :doc:`experimental feature `. + +Current web applications use many different channels to send messages to +the users (e.g. SMS, Slack messages, emails, push notifications, etc.). The +Notifier component in Symfony is an abstraction on top of all these +channels. It provides a dynamic way to manage how the messages are send. +Get the Notifier installed using: + +.. code-block:: terminal + + $ composer require symfony/notifier + +Channels: Chatters, Texters, Email and Browser +---------------------------------------------- + +The notifier component can send notifications to different channels. Each +channel can integrate with different providers (e.g. Slack or Twilio SMS) +by using transports. + +The notifier component supports the following channels: + +* `SMS `_ sends notifications to phones via SMS messages +* `Chat `_ sends notifications to chat services like Slack + and Telegram; +* `Email `_ integrates the :doc:`Symfony Mailer `; +* Browser uses :ref:`flash messages `. + +.. tip:: + + Use :doc:`secrets ` to securily store your + API's tokens. + +.. _notifier-texter-dsn: + +SMS Channel +~~~~~~~~~~~ + +The SMS channel uses :class:`Symfony\\Component\\Notifier\\Texter` classes +to send SMS messages to mobile phones. This feature requires subscribing to +a third-party service that sends SMS messages. Symfony provides integration +with a couple popular SMS services: + +======= =========================== ======================================== +Service Package DSN +======= =========================== ======================================== +Twilio ``symfony/twilio-notifier`` ``twilio://SID:TOKEN@default?from=FROM`` +Nexmo ``symfony/nexmo-notifier`` ``nexmo://KEY:SECRET@default?from=FROM`` +======= =========================== ======================================== + +To enable a texter, add the add the correct DSN in your ``.env`` file and +configure the ``texter_transports``: + +.. code-block:: bash + + # .env + TWILIO_DSN=twilio://SID:TOKEN@default?from=FROM + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + texter_transports: + twilio: '%env(TWILIO_DSN)%' + + .. code-block:: xml + + + + + + + + + %env(TWILIO_DSN)% + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'texter_transports' => [ + 'twilio' => '%env(TWILIO_DSN)%', + ], + ], + ]); + +.. _notifier-chatter-dsn: + +Chat Channel +~~~~~~~~~~~~ + +The chat channel is used to send chat messages to users by using +:class:`Symfony\\Component\\Notifier\\Chatter` classes. Symfony provides +integration with these chat services: + +======== ============================= ============================================ +Service Package DSN +======== ============================= ============================================ +Slack ``symfony/slack-notifier`` ``slack://TOKEN@default?channel=CHANNEL`` +Telegram ``symfony/telegram-notifier`` ``telegram://TOKEN@default?channel=CHAT_ID`` +======== ============================= ============================================ + +Chatters are configured using the ``chatter_transports`` setting: + +.. code-block:: bash + + # .env + SLACK_DSN=slack://TOKEN@default?channel=CHANNEL + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + chatter_transports: + slack: '%env(SLACK_DSN)%' + + .. code-block:: xml + + + + + + + + + %env(SLACK_DSN)% + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'chatter_transports' => [ + 'slack' => '%env(SLACK_DSN)%', + ], + ], + ]); + +Email Channel +~~~~~~~~~~~~~ + +The email channel uses the :doc:`Symfony Mailer ` to send +notifications using the special +:class:`Symfony\\Bridge\\TwigBridge\\Mime\\NotificationEmail`. It is +required to install the Twig bridge along with the Inky and CSS Inliner +Twig extensions: + +.. code-block:: terminal + + $ composer require symfony/twig-pack twig/cssinliner-extra twig/inky-extra + +After this, :ref:`configure the mailer `. You can +also set the default "from" email address that should be used to send the +notification emails: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/mailer.yaml + framework: + mailer: + dsn: '%env(MAILER_DSN)%' + envelope: + sender: 'notifications@example.com' + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + # config/packages/mailer.php + $container->loadFromExtension('framework', [ + 'mailer' => [ + 'dsn' => '%env(MAILER_DSN)%', + 'envelope' => [ + 'sender' => 'notifications@example.com', + ], + ], + ]); + +Configure to use Failover or Round-Robin Transports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Besides configuring one or more separate transports, you can also use the +special ``||`` and ``&&`` characters to implement a failover or round-robin +transport: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + chatter_transports: + # Send notifications to Slack and use Telegram if + # Slack errored + main: '%env(SLACK_DSN)% || %env(TELEGRAM_DSN)%' + + # Always send notifications to both Slack and Telegram + all: '%env(SLACK_DSN)% && %env(TELEGRAM_DSN)%' + + .. code-block:: xml + + + + + + + + + + %env(SLACK_DSN)% || %env(TELEGRAM_DSN)% + + + + + %env(SLACK_DSN)% && %env(TELEGRAM_DSN)% + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'chatter_transports' => [ + // Send notifications to Slack and use Telegram if + // Slack errored + 'main' => '%env(SLACK_DSN)% || %env(TELEGRAM_DSN)%', + + // Always send notifications to both Slack and Telegram + 'all' => '%env(SLACK_DSN)% && %env(TELEGRAM_DSN)%', + ], + ], + ]); + +Creating & Sending Notifications +-------------------------------- + +To send a notification, autowire the +:class:`Symfony\\Component\\Notifier\\NotifierInterface` (service ID +``notifier``). This class has a ``send()`` method that allows you to send a +:class:`Symfony\\Component\\Notifier\\Notification\\Notification` to a +:class:`Symfony\\Component\\Notifier\\Recipient\\Recipient`:: + + // src/Controller/InvoiceController.php + namespace App\Controller; + + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\NotifierInterface; + use Symfony\Component\Notifier\Recipient\AdminRecipient; + + class InvoiceController extends AbstractController + { + /** + * @Route("/invoice/create") + */ + public function create(NotifierInterface $notifier) + { + // ... + + // Create a Notification that has to be sent + // using the "email" channel + $notification = (new Notification('New Invoice', ['email'])) + ->content('You got a new invoice for 15 EUR.'); + + // The receiver of the Notification + $recipient = new AdminRecipient( + $user->getEmail(), + $user->getPhonenumber() + ); + + // Send the notification to the recipient + $notifier->send($notification, $recipient); + + // ... + } + } + +The ``Notification`` is created by using two arguments: the subject and +channels. The channels specify which channel (or transport) should be used +to send the notification. For instance, ``['email', 'sms']`` will send +both an email and sms notification to the user. It is required to specify +the transport when using chatters (e.g. ``['email', 'chat/telegram']``). + +The default notification also has a ``content()`` and ``emoji()`` method to +set the notification content and icon. + +Symfony provides three types of recipients: + +:class:`Symfony\\Component\\Notifier\\Recipient\\NoRecipient`` + This is the default and is useful when there is no need to have + information about the receiver. For example, the browser channel uses + the current requests's :ref:`session flashbag `; + +:class:`Symfony\\Component\\Notifier\\Recipient\\Recipient`` + This contains only the email address of the user and can be used for + messages on the email and browser channel; + +:class:`Symfony\\Component\\Notifier\\Recipient\\AdminRecipient``. + This can contain both email address and phonenumber of the user. This + recipient can be used for all channels (depending on whether they are + actually set). + +Configuring Channel Policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of specifying the target channels on creation, Symfony also allows +you to use notification importance levels. Update the configuration to +specify what channels should be used for specific levels (using +``channel_policy``): + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + # ... + channel_policy: + # Use SMS, Slack and email for urgent notifications + urgent: ['sms', 'chat/slack', 'email'] + + # Use Slack for highly important notifications + high: ['chat/slack'] + + # Use browser for medium and low notifications + medium: ['browser'] + low: ['browser'] + + .. code-block:: xml + + + + + + + + + + + + sms + chat/slack + email + + + chat/slack + + + browser + browser + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + // ... + 'channel_policy' => [ + // Use SMS, Slack and email for urgent notifications + 'urgent' => ['sms', 'chat/slack', 'email'], + + // Use Slack for highly important notifications + 'high' => ['chat/slack'], + + // Use browser for medium and low notifications + 'medium' => ['browser'], + 'low' => ['browser'], + ], + ], + ]); + +Now, whenever the notification's importance is set to "high", it will be +sent using the Slack transport:: + + // ... + class InvoiceController extends AbstractController + { + /** + * @Route("/invoice/create") + */ + public function invoice(NotifierInterface $notifier) + { + // ... + + $notification = (new Notification('New Invoice')) + ->content('You got a new invoice for 15 EUR.') + ->importance(Notification::IMPORTANCE_HIGH); + + $notifier->send($notification, new Recipient('wouter@wouterj.nl')); + + // ... + } + } + +Customize Notifications +----------------------- + +You can extend the ``Notification`` or ``Recipient`` base classes to +customize their behavior. For instance, you can overwrite the +``getChannels()`` method to only return ``sms`` if the invoice price is +very high and the recipient has a phonenumber:: + + namespace App\Notifier; + + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\Recipient\Recipient; + + class InvoiceNotification extends Notification + { + private $price; + + public function __construct(int $price) + { + $this->price = $price; + } + + public function getChannels(Recipient $recipient) + { + if ( + $this->price > 10000 + && $recipient instanceof AdminRecipient + && null !== $recipient->getPhone() + ) { + return ['sms', 'email']; + } + + return 'email'; + } + } + +Customize Notification Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each channel has its own notification interface that you can implement to +customize the notification message. For instance, if you want to modify the +message based on the chat service, implement +:class:`Symfony\\Component\\Notifier\\Notification\\ChatNotificationInterface` +and its ``asChatMessage()`` method:: + + // src/Notifier/InvoiceNotification.php + namespace App\Notifier; + + use Symfony\Component\Notifier\Message\ChatMessage; + use Symfony\Component\Notifier\Notification\ChatNotificationInterface; + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\Recipient\Recipient; + + class InvoiceNotification extends Notification implements ChatNotificationInterface + { + private $price; + + public function __construct(int $price) + { + $this->price = $price; + } + + public function asChatMessage(Recipient $recipient, string $transport = null): ?ChatMessage + { + // Add a custom emoji if the message is sent to Slack + if ('slack' === $transport) { + return (new ChatMessage('You\'re invoiced '.$this->price.' EUR.')) + ->emoji('money'); + } + + // If you return null, the Notifier will create the ChatMessage + // based on this notification as it would without this method. + return null; + } + } + +The +:class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface` +and +:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface` +also exists to modify messages send to those channels. + +.. TODO + - Using the message bus for asynchronous notification + - Describe notifier monolog handler + - Describe notification_on_failed_messages integration + +Learn more +---------- + +.. toctree:: + :maxdepth: 1 + :glob: + + notifier/* diff --git a/notifier/chatters.rst b/notifier/chatters.rst new file mode 100644 index 00000000000..4850c96e0e8 --- /dev/null +++ b/notifier/chatters.rst @@ -0,0 +1,92 @@ +.. index:: + single: Notifier; Chatters + +How to send Chat Messages +========================= + +.. versionadded:: 5.0 + + The Notifier component was introduced in Symfony 5.0 as an + :doc:`experimental feature `. + +The :class:`Symfony\\Component\\Notifier\\ChatterInterface` class allows +you to sent messages to chat services like Slack or Telegram:: + + // src/Controller/CheckoutController.php + namespace App\Controller; + + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\NotifierInterface; + use Symfony\Component\Notifier\Recipient\AdminRecipient; + + class CheckoutController extends AbstractController + { + /** + * @Route("/checkout/thankyou") + */ + public function thankyou(ChatterInterface $chatter) + { + $message = (new ChatMessage('You got a new invoice for 15 EUR.')) + // if not set explicitly, the message is send to the + // default transport (the first one configured) + ->transport('slack'); + + $chatter->send($message); + + // ... + } + } + +.. seealso:: + + Read :ref:`the main Notifier guide ` to see how + to configure the different transports. + +Adding Interactions to a Slack Message +-------------------------------------- + +With a Slack message, you can use the +:class:`Symfony\\Component\\Notifier\\Bridge\\Slack\\SlackOptions` to add +some interactive options called `Block elements`_:: + + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackActionsBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackDividerBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackImageBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackSectionBlock; + use Symfony\Component\Notifier\Bridge\Slack\SlackOptions; + use Symfony\Component\Notifier\Message\ChatMessage; + + $chatMessage = new ChatMessage('Contribute To Symfony'); + + // Create Slack Actions Block and add some buttons + $contributeToSymfonyBlocks = (new SlackActionsBlock()) + ->button( + 'Improve Documentation', + 'https://symfony.com/doc/current/contributing/documentation/standards.html', + 'primary' + ) + ->button( + 'Report bugs', + 'https://symfony.com/doc/current/contributing/code/bugs.html', + 'danger' + ); + + $slackOptions = (new SlackOptions()) + ->block((new SlackSectionBlock()) + ->text('The Symfony Community') + ->accessory( + new SlackImageBlockElement( + 'https://example.com/symfony-logo.png', + 'Symfony' + ) + ) + ) + ->block(new SlackDividerBlock()) + ->block($contributeToSymfonyBlocks); + + // Add the custom options to the chat message and send the message + $chatMessage->options($slackOptions); + + $chatter->send($chatMessage); + +.. _`Block elements`: https://api.slack.com/reference/block-kit/block-elements diff --git a/notifier/texters.rst b/notifier/texters.rst new file mode 100644 index 00000000000..12505cacfa1 --- /dev/null +++ b/notifier/texters.rst @@ -0,0 +1,41 @@ +.. index:: + single: Notifier; Texters + +How to send SMS Messages +======================== + +.. versionadded:: 5.0 + + The Notifier component was introduced in Symfony 5.0 as an + :doc:`experimental feature `. + +The :class:`Symfony\\Component\\Notifier\\TexterInterface` class allows +you to sent SMS messages:: + + // src/Controller/SecurityController.php + namespace App\Controller; + + class SecurityController + { + /** + * @Route("/login/success") + */ + public function loginSuccess(TexterInterface $texter) + { + $sms = new SmsMessage( + // the phone number to send the SMS message to + '+1411111111', + // the message + 'A new login was detected!' + ); + + $texter->send($sms); + + // ... + } + } + +.. seealso:: + + Read :ref:`the main Notifier guide ` to see how + to configure the different transports.