Setting up a Notifications System in Symfony Projects

December 15th, 2016

Symfony / Case Studies / Events / Wiki // Grossum Possum

Setting Up a Notifications System in Symfony Projects

This information was initially presented at the #SymfonyCafeKyiv in December ‘16 by Myroslav Berlad, a Grossum software developer.

Notifications are a convenient thing for any kind of service (unless the system has been created to spam and annoy everyone). It’s good for users to know what’s new, whether there are pending requests or incoming messages, etc.

As the developers’ team has been working on a project, a need for a notification system arose.

We needed four types of notifications and the team agreed that writing our own solutions would take too much time, so we decided to use proven vendor API solutions.

  1. Push notifications (a message that pops up on a mobile device): GCM (google cloud messaging) - a mobile notification service developed by Google that enables third-party application developers to send notification data or information from developer-run servers to applications.
  2. Text messages (short message service, text messaging between mobile device users ): TurboSMS - a Ukrainian SMS sender service
  3. Email (messaging between computer device users): Mandrill is an email marketing service (that was merged with MailChimp)
  4. Web notifications & dynamic interface updates (targeted website user notifications / interface updates): is a JavaScript library for real-time web applications. It enables real-time, bidirectional communication between web clients and servers. It has two parts: a client-side library that runs in the browser, and a server-side library for node.js.

How to implement a notification service?

There were two ways of doing this:

  • Symfony way: get some bundles, configure them, live happily ever after.
  • Our way: get some troubles. Cry.

How to do it Symfony-way?

A brief structure of our project’s architecture looks like this:


We decided to use RMSPushNotifications Symfony bundle for this. It allows sending notifications/messages for mobile devices and supports such platforms as iOS, Android (C2DM, GCM), Blackberry and Windows Phone (toast only).

composer require richsage/rms-push-notifications-bundle
use RMS\PushNotificationsBundle\Message\iOSMessage;
class PushDemoController extends Controller
   public function pushAction()
       $message = new iOSMessage();
       $message->setMessage('Oh my! A push notification!');
       return new Response('Push notification send!');

Another bundle that we have used is EndroidGCM, which is a Google Cloud Messaging bundle used with Symfony projects. 

composer require endroid/gcm-bundle
public function gcmSendAction()
   $client = $this->get('endroid.gcm.client');
   $registrationIds = array(
       // Registration ID's of devices to target
   $data = array(
       'title' => 'Message title',
       'message' => 'Message body',
   $response = $client->send($data, $registrationIds);


composer require kronas/smpp-client-bundle
$smpp = $this->get('kronas_smpp_client.transmitter');
$smpp->send($phone_number, $message);


composer require hipaway-travel/mandrill-bundle
$dispatcher = $this->get('hip_mandrill.dispatcher');
       $message = new Message();
           ->setFromName('Customer Care')
           ->setSubject('Some Subject')
           ->setHtml('<html><body><h1>Some Content</h1></body></html>')
       $result = $dispatcher->send($message);


composer require gos/web-socket-bundle
var webSocket = WS.connect(“ws://”);
webSocket.on(“socket/connect”, function(session){
    //session is an Autobahn JS WAMP session.
console .log(“Successfully Connected!”);
webSocket.on(“socket/disconnect”, function(error){
    //error provides us with some insight into the disconnection: error.reason and error.code
console .log(“Disconnected for ” + error.reason + “ with code ” + error.code);
    * This will receive any Subscription requests for this topic.
    * @param ConnectionInterface $connection
    * @param Topic $topic
    * @param WampRequest $request
    * @return void
   public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
       //this will broadcast the message to ALL subscribers of this topic.
       $topic->broadcast(['msg' => $connection->resourceId . “ has joined ” . $topic->getId()]);

Project architecture thoughts:

“Okay, this looks good. However, PHP is not a very good option to choose to run server daemon on. Also, it would be nice to post notifications with the same API.”

Additional parameters that we introduced for the project:

  • Reusable solution
  • Simple API for notifications
  • No PHP running as a daemon

Our way:

When we thought about the possible path for the messages to take, the solution turned out like this:

Symfony -> Rabbit -> Node -> Target


To integrate Symfony2 and Symfony3 and RabbitMQ, we used php-amqplib (formerly known as oldsound/rabbitmq-bundle)

composer require php-amqplib/rabbitmq-bundle
public function indexAction($name)
   $msg = array('user_id' => 1235, 'image_path' => '/path/to/new/pic.png');
rabbitmq-plugins enable rabbitmq_management
rabbitmqctl add_vhost SOME_VHOST
rabbitmqctl add_user SOME_USER SOME_PASS
rabbitmqctl set_permissions -p / SOME_USER “.*” “.*” “.*”
rabbitmqctl set_permissions -p SOME_VHOST SOME_USER “.*” “.*” “.*”
rabbitmqctl set_user_tags SOME_USER administrator
rabbitmqadmin declare exchange --vhost=SOME_VHOST name=send-sms type=direct -u SOME_USER -p SOME_PASS
rabbitmqadmin declare exchange --vhost=SOME_VHOST name=send-email type=direct -u SOME_USER -p SOME_PASS
rabbitmqadmin declare exchange --vhost=SOME_VHOST name=send-push type=direct -u SOME_USER -p SOME_PASS
rabbitmqadmin declare exchange --vhost=SOME_VHOST name=send-web-push type=direct -u SOME_USER -p SOME_PASS
rabbitmqadmin declare queue --vhost=SOME_VHOST name=send-sms durable=true -u SOME_USER -p SOME_PASS
rabbitmqadmin declare queue --vhost=SOME_VHOST name=send-email durable=true -u SOME_USER -p SOME_PASS
rabbitmqadmin declare queue --vhost=SOME_VHOST name=send-push durable=true -u SOME_USER -p SOME_PASS
rabbitmqadmin declare queue --vhost=SOME_VHOST name=send-web-push durable=true -u SOME_USER -p SOME_PASS
rabbitmqadmin --vhost=SOME_VHOST binding source=send-sms destination_type=queue destination=send-sms -u SOME_USER -p SOME_PASS
rabbitmqadmin --vhost=SOME_VHOST binding source=send-email destination_type=queue destination=send-email -u SOME_USER -p SOME_PASS
rabbitmqadmin --vhost=SOME_VHOST binding source=send-push destination_type=queue destination=send-push -u SOME_USER -p SOME_PASS
rabbitmqadmin --vhost=SOME_VHOST binding source=send-web-push destination_type=queue destination=send-web-push -u SOME_USER -p SOME_PASS
service rabbitmq-server restart


For the NodeJS part, we have decided to use AMQP 0-9-1 (e.g. RabbitMQ) library and client.

// Consumer
function consumer(conn) {
 var ok = conn.createChannel(on_open);
 function on_open(err, ch) {
   if (err != null) bail(err);
   ch.consume(q, function(msg) {
     if (msg !== null) {


SMPP client and server implementation in node.js.

var smpp = require('smpp');
var session = smpp.connect('smpp://');
   system_id: 'YOUR_SYSTEM_ID',
   password: 'YOUR_PASSWORD'
}, function(pdu) {
   if (pdu.command_status == 0) {
       // Successfully bound
           destination_addr: 'DESTINATION NUMBER',
           short_message: 'Hello!'
       }, function(pdu) {
           if (pdu.command_status == 0) {
               // Message successfully sent


For the email integration and notifications, we have used Mandrill (merged with Mailchimp). We used a node.js wrapper for the MailChimp API.

try {
   var api = new MailChimpAPI(apiKey, { version : '2.0' });
} catch (error) {
}'campaigns', 'list', { start: 0, limit: 25 }, function (error, data) {
   if (error)
       console.log(JSON.stringify(data)); // Do something with your data!


Finally, we have arrived at the socket settings. We used, a node.js real-time framework server for this. 

var server = require('http').createServer();
var io = require('')(server);
io.on('connection', function(client){
 client.on('event', function(data){});
 client.on('disconnect', function(){});


  • Same notification API for all cases - CHECK
  • No PHP running as a daemon - CHECK

But what about a reusable solution?

Welcome, Docker!

        build: docker/node
            - rabbitmq
- "80:80"
- "443:443"
- rabbitmq
- ./notification-server:/var/www/notification-server
working_dir: /var/www/notification-server
build: docker/rabbitmq
    - ./var/rabbitmq:/var/lib/rabbitmq
- "5672:5672"
- "15671:15671"
- "15672:15672"
docker-compose up

Using Docker, we have created a container with all the necessary settings for our project. (Read more about working with Docker and here are some practical application examples.)

Okay, now we’re meeting all three requirements.

  • Same notification API for all cases - CHECK
  • No PHP running as a daemon - CHECK
  • Reusable solution - CHECK

However, a new question arises: should a new project handle notifications in its own way?

The short answer is: No.

These thoughts have led us to the creation of a solution that can be easily shared.

We present you...

Grossum Symfony Notification Bundle

Installation is fairly simple:

composer require grossum/notification-bundle

$userNotification = new MessageNotification();

       ->setContent('You have created task to demo NotificationBundle')
       ->setTitle('You have created task to demo NotificationBundle')
       ->setCreatedAt(new \DateTime())

       new NotificationCreatedEvent($userNotification)
      - "@old_sound_rabbit_mq.send_email_producer"
    class: %grossum.notification.event_listener.email_produce.class%
      - ""
      - { name: kernel.event_listener, event: grossum.notification.event.send_email, method: produceNotifications }
   * @param NotificationSenderInterface $notificationSender
  public function __construct(NotificationSenderInterface $notificationSender)
      $this->notificationSender = $notificationSender;
interface NotificationSenderInterface
    * @param NotificationInterface $notification
   public function sendNotification(NotificationInterface $notification);
    * {@inheritdoc}
   public function sendNotification(NotificationInterface $message)
       try {
           if ($message->isValid()) {
       } catch (\Exception $e) {
           //TODO: add logging
interface NotificationInterface
   const PHONE_OS_TYPE_IOS = 'phone_ios';
   const PHONE_OS_TYPE_WINDOWS = 'phone_windows';
   const PHONE_OS_TYPE_ANDROID = 'phone_android';
    * @return array
   public function exportData();
    * @return bool
   public function isValid();

Grossum Notification Server

cp docker-compose.yml.dist docker-compose.yml docker-compose up

All requirements are met.

Our plans for the future of the bundle:

  • Write unit tests :)
  • Make flexible bundle configuration
  • Refactor NodeJS server part(simple, but can be better and cleaner)
  • Split node and docker into 2 separate repositories


  • The solutions seems to work :)
  • 3 projects use this solution, and hope more :)
  • Team received experience with RabbitMQ
  • Team received experience with NodeJS
  • Team received experience with splitting app into separate microservices
  • Team received experience with docker


You can use RabbitMQ, not as a part of GrossumNotificationServer, but you can consider it as your app message bus.

"A Message Bus is a combination of a common data model, a common command set, and a messaging infrastructure to allow different systems to communicate through a shared set of interfaces. Sending a message does not require both systems to be up and ready at the same time." Read more here.

Need help with a Symfony2 or Symfony3 project? We can help!

Author: Grossum Possum

Grossum Possum. He loves using Symfony2 for his projects and learning to implement Symfony3. He also likes developing web and mobile applications using other programming languages and frameworks, such as PHP and Java. In his free time, Grossum Possum likes to write about his experiences in the blog.

Tags Symfony Cafe Development Hacks

See all blog


Grossum Startup Guide:
Five Things to Consider Before Web & Mobile Development

Get the Guide