The new Analytics module

People following the development of Fork CMS probably noticed that we recently introduced a new Analytics module. We started from scratch to be able to use all the most recent Fork Features in this module.

This blog post will go trough the most notable changes with the new module.

Read More

Using Symfony's Event Dispatcher in Fork CMS

Symfony's Event Dispatcher has been around in Fork CMS for some time now, but is only recently being integrated in our modules. It's not hard to use it though, and it's a great tool to decouple code in your modules. This post will explain in small steps how you can use it in your own Fork CMS modules.

What are events?

To fully understand Symfony's Event Dispatcher, you need to grasp some more abstract concepts. The first thing you need to understand are events. In programming, an event can be seen as a message that represents something that happened in your code. An example of this could be a "ProfileRegistered" event.

In our code, we will represent this event using an object. This object contains data about the event that has happened. For a ProfileRegistered event, this will possibly contain some data about the registered profile, or even the full profile object.

What is an event dispatcher?

An event dispatcher is a service that will send your events to possible listeners. In our example, it will receive the "ProfileRegistered" event and call all hooked listeners. The amount of listeners can be anything. It's possible to have zero listeners, but you can also have multiple listeners for the same event. This makes it easy to add or remove extra code without needing to change existing classes.

How can we implement this in Fork?

Since we use Symfony's Event Dispatcher component, the implementation in Fork is almost exactly the same as in Symfony. The only difference is where we locate our objects. We use modules instead of bundles, so we're putting our events and listeners in there.

1. The event

The first thing we need to create is our event. This will just be a plain php object that receives some data and can return it back to the event listeners. For our ProfileRegistered event, it can look something like this:

// src/Frontend/Modules/Profiles/Event/ProfileRegistered.php

namespace Frontend\Modules\Profiles\Event;

use Symfony\Component\EventDispatcher\Event;
use Frontend\Modules\Profiles\Engine\Profile;

 * This event will contain all data about a registration
final class ProfileRegistered extends Event
     * @var Profile
    protected $profile;

     * @param Profile $profile
    public function __construct(Profile $profile)
        $this->profile = $profile;

     * @return Profile
    public function getProfile()
        return clone $this->profile;

Note that we clone our profile event object in the getProfile method. We do this to make sure the listeners can't edit the profile object before it is passed to another listener. We don't want to have a listener that changes the data of the newly registered profile. They can only use the data, but not change it.

2. Dispatching our event

We have created our event object, but it's just an unused class in our directory structure now. We want to dispatch this event whenever a profile is registered. This event can happen in two different places in our applications: a visitor can register in the frontend, or an admin can add a profile from the backend. We'll add this code on these two places:

// src/Frontend/Modules/Profiles/Actions/Register.php

use src/Frontend/Modules/Profiles/Event/ProfileRegistered;

// add this code after the user has been registered/added
// and you have fetched the Profile object
    new ProfileRegistered($profile)

As you can see, the event_dispatcher is a service in our service container (you can read more about service containers in Fork here:

We call the "dispatch" method on it and pass it two parameters. The first one is the name of your event. This will couple your event to the listeners. In this case, listeners will need to listen to the "profiles.profile_registered" event to be executed and to receive the ProfileRegistered object.
The second argument is our ProfileRegistered object. We insert our profile in there, to make sure the listeners can use it.

3. Creating an event listener

We now have an event that gets dispatched, but we aren't doing anything with it. In most cases, you will create at least one listener for an event. In this case, we willl add a listener that sends a confirmation email to the visitor that registered a new profile. Other use cases could be: sending push notifications, adding a search index, notifying an external api, ... The possibilities are endless.

First, we will create a listener class. This could also be called a "subscriber" in some other languages/frameworks. A listener class will need a method that will be called when the event occurs. I will name the class "RegistrationConfirmationMailListener" and name the method "onProfileRegistered". These names can be everything you want, but in most cases, you want to explain what happens in the class name and add a reference to the dispatched event in the method name. Our example class looks like this:

// src/Frontend/Modules/Profiles/EventListener/RegistrationConfirmationMailListener.php

namespace Frontend\Modules\Profiles\EventListener;

use Swift_Mailer;
use Swift_Message;
use Frontend\Modules\Profiles\Event\ProfileRegistered;

class RegistrationConfirmationMailListener
     * @var Swift_Mailer
    private $mailer;

     * @var Swift_Mailer $mailer
    public function __construct(Swift_Mailer $mailer)
        $this->mailer = $mailer;

     * @var ProfileRegistered $event
    public function onProfileRegistered(ProfileRegistered $event)
        $profile = $event->getProfile();

        // build an email message.
        $message = Swift_Message::newInstance('Subject');
        $message->setTo(array($profile->getUserName(), $profile->getEmail()));

        // ... You'll probably want to add content too

We pass our mailer class in our constructor using dependency injection. The onProfileRegistered method contains all the logic for this listener.

4. Hook your listener to the event

To make our listener really listen to our event, we still have to add the listener to our configuration. If you module contains a module specific services.yml file (in Backend/Modules/YourModule/Resources/config/services.yml), you can add it there, if this is not the case, you can use the app/config/config.yml file. You will need to add a part that looks like this:

# app/config/config.yml

# You should add it under the services: key. Note: indentation is important!
        class: Frontend\Modules\Profiles\EventListener\RegistrationConfirmationMailListener
            - @mailer
            - { name: kernel.event_listener, event: profiles.profile_registered, method: onProfileRegistered }

This registers our listener in the service container. More info about building services can be found in this blogpost:
To convert our service in an event listener, we add a tag with the name 'kernel.event_listener'. The event key contains the name of the event we're listening to and the method is used to set the called method.

Wrap up

It may seem like some work to set this up for your modules, but when you start using it, you will definitly see that it is totally worth it. It makes adding/removing functionality so much easier. You can now change what happens after events without the need to touch your actual action classes. This way, you're sure you're not introducing bugs in your action class, for functionality that does not even belong there. It keeps your classes smaller and more understandable. If you want to, you can also easily create unit tests for your event listener class!

The FormBuilder module already uses this functionality in Fork, for example to send notification emails to the admin when a form has been submitted. If you're implementing this, and you're stuck, you can always take a look in there to get some inspiration.

Have fun creating your own events and making your modules expandable!

Read More

Module specific services

Since Fork CMS version 3.9.2, our Fork modules look a little more like Symfony Bundles. They can now contain a so called "module extension" class that can be used to load module specific configuration or services.

This blogpost explains how you can start using this "module extensions" to write cleaner decoupled code in Fork CMS.

Read More

Fork Meetup 26/03/2015

the public

Yesterday, thursday 26 March 2015, we finally organized another event. The previous real event was somewhat about 2 years ago. This meetup was Symfony-flavoured, as many people wanted an update about the move towards Symfony. So yesterday we gathered in the Combell-offices in Ghent, to talk about Fork and Symfony. We had one intro-talk and two "real" talks:

  • Update on Fork and the Symfony integration by Tijs Verkoyen
  • Effectively using the Symfony Components in Fork by Wouter Sioen
  • Having fun with the Symfony Container by Bram Van der Sype

Update on Fork and the Symfony integration

Tijs talked briefly about which components are already integrated into Fork and the future. The future will hold more meetups, more Core members, more ... So if you are interested in participating as a Core member, send us an email (info [at]

See for the slides.

Effectively using the Symfony Components in Fork

Wouter talked about how you can use some of the components already available in Fork in more detail. He explained Routing, DependencyInjection, Console, EventDispatcher. And last but not the least he discussed the Functional tests.

See for the slides.

Having fun with the Symfony Container

bram talking about the DIC Bram, who works at SparkCentral, showed us some nice tricks with the Symfony Container: tagging and Compiler Passes. It showed us some features most of us didn't know about, but will be handy in the future development of Fork.

See for the slides.


We would like to thank all the speakers: Wouter, Bram and Tijs, and Combell for sponsoring the location, drinks and food. If you would like to keep up with new announcements for events you can check our MeetUp-page.

Read More

New GitHub labels

You may have noticed that we cleaned up the labels we use for issues/pull requests on GitHub. We now follow the same structure as the Symfony repository.

Read More