cult3

Creating Registration Events in Laravel 4

Jun 16, 2014

Table of contents:

  1. Why use registration events?
  2. Adding the event’s dispatcher
  3. Firing events
  4. Creating the handler classes
  5. Conclusion

Over the last couple of weeks I’ve looked into setting up a registration process for your Laravel 4 applications.

First I looked at setting up the basic foundation of a registration process to allow users to sign up with a username and password.

Next I looked at setting up authentication using a social provider such as a Twitter (Part 1, Part 2 and Part 3).

When a new user signs up for your application, you will usually want to run a couple of jobs as a consequence of that action. For example, you will probably want to send the user an email to confirm their account, or perhaps you want to set an introductory course in motion to smooth the on-boarding process.

In either case, your registration process should not be aware of what happens as a consequence of that action. This is because stuffing all of that logic code into the registration process will create a monolithic class that is really difficult to maintain.

Instead we can use events to break off the tasks into self-contained jobs that can be triggered when a new user registers.

A couple of weeks ago I covered the basics of Laravel events. In this post I’m going to look at a real-life implementation of using Laravel’s event system to ease the burden on the registration process.

Why use registration events?

Before I jump into the code, first I’ll give you a little recap on what I’m trying to achieve and why using events is a good idea.

When a new user signs up for an account on Cribbb, there are a couple of actions I want to take place.

For example, I want to send the user a welcome email and if the user was invited by another user I want to send the inviter a notification to say their friend has also joined. I also want to update the invitation in the database to show that it has been claimed.

If you imagine that the controller action for creating a new user looks like this:

public function store()
{
    $this->registrator->create(Input::all());
}

To kick off those actions I could simply shove the code into the store() method like this:

public function store()
{
    $this->registrator->create(Input::all());
    // Send welcome email
    // Send notification email to friend
    // Mark invitation as claimed
}

Whilst you could get away with this in a small project, it’s not a very good solution for a bigger application.

Typically when you have jobs taking place due to something happening in your application, that list of jobs is only ever going to grow larger. Applications tend to also evolve quickly, and so you will probably find yourself removing jobs from the list too.

However very quickly this list of actions after the user has been created will become unwieldy. This is the kind of code that becomes scary to change.

Instead we can use events. An event is simply a trigger that is broadcasted out through your application when something important happens. Each job you want to assign to an event will listen for the event taking place. When the event is triggered, the job will automatically be called.

This means you can encapsulate each individual job as it’s own class and you can add or remove jobs without the event or any of the other jobs ever knowing the difference.

If any of that doesn’t make sense, keep reading on. Events can feel a bit like magic when you start looking at them, but they are actually really simple and incredibly handy!

Adding the event’s dispatcher

As I’ve covered in Using Events in Laravel 4, Laravel ships with an excellent event system right out of the box.

In order to fire events in Laravel, we need an instance of the event dispatcher to work with.

So the first thing to do is to inject an instance of Laravel’s dispatcher into your registrator classes. In my case this is SocialProviderRegistrator and CredentialsRegistrator.

First import the namespace into the class:

use Illuminate\Events\Dispatcher;

Next set the instance as a class property:

/**
 * The events dispatcher
 *
 * @var Illuminate\Events\Dispatcher
 */
protected $events;

Finally inject the Dispatcher through the __construct() method and set it on the class property:

/**
 * Create a new instance of the CredentialsRegistrator
 *
 * @param Cribbb\Repositories\User\UserRepository $userRepository
 * @param Illuminate\Events\Dispatcher $events
 * @param array $validators
 * @return void
 */
public function __construct(UserRepository $userRepository, Dispatcher $events, array $validators)
{
    parent::__construct();

    $this->userRepository = $userRepository;
    $this->validators = $validators;
    $this->events = $events;
}

Firing events

Now that we’ve got an instance of the dispatcher in the registrator classes, we can fire the events that we will hook on to in order to process the jobs after a user registers.

Update the create() method to look like this:

/**
 * Create a new user entity
 *
 * @param array $data
 * @return Illuminate\Database\Eloquent\Model
 */
public function create(array $data)
{
    if ($this->runValidationChecks($data)) {
        $user = $this->userRepository->create($data);

        $this->events->fire('user.register', [$user]);

        return $user;
    }
}

To fire an event, simply call the fire() method on the $this->events instance and pass a name and any arguments:

$this->events->fire("user.register", [$user]);

The first event I’m firing is user.register which will pass a long an instance of the newly created $user.

Creating the handler classes

Next we need to create the handler classes that will deal with handling a specific task that should be triggered when an event is fired.

The first thing I will do is to create an Events directory under the Registrators directory.

Next I will create the first handler for sending the welcome email to the new user:

<?php namespace Cribbb\Registrators\Events;

use Illuminate\Mail\Mailer;

class WelcomeEmailHandler
{
    /**
     * The Mailer instance
     *
     * @var Illuminate\Mail\Mailer
     */
    protected $mailer;

    /**
     * Create a new instance of the WelcomeEmailHandler
     *
     * @param Illuminate\Mail\Mailer $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * Send a welcome email to the new user
     *
     * @param User $user
     * @return void
     */
    public function handle($user)
    {
        // Send the welcome email
    }

    /**
     * Register the listeners for the subscriber.
     *
     * @param Illuminate\Events\Dispatcher $events
     * @return array
     */
    public function subscribe($events)
    {
        $events->listen(
            "user.register",
            "Cribbb\Registrators\Events\WelcomeEmailHandler"
        );
    }
}

This class is injected with an instance of the Laravel’s Mailer service so that we can send the emails.

The handle() method is where the action takes place. I’ve left this blank for now as I don’t want to actually set up the emails right now. To test that you have everything working correctly but a die('hello world'); function in there so you can see that the handler is being triggered.

Finally we have the subscribe() method that listens for the event and then triggers the handler. You will notice that the second argument to the listen() method is the class. By default this will call the handle() method, but you are free to override this by using the following syntax:

"Cribbb\Registrators\Events\WelcomeEmailHandler@onRegister";

You can also add more than one job into a single class to be handled, even if it is triggered by a different event. However, I think it is better to keep the jobs separate unless they are really deeply connected and it wouldn’t make sense to keep them separate.

Finally in the RegistratorsServiceProvider you can register your event handlers in the boot() method:

/**
 * Boot the Registrator Events
 *
 * @return void
 */
public function boot()
{
    $this->app->events->subscribe(new WelcomeEmailHandler(
        $this->app['mailer'])
    );
}

Now when you sign up as a new user your application should die when the event is triggered and you should see hello world printed to the screen. If this is the case, your event has been set up correctly!

Conclusion

Monolithic processes can be very scary to change. It’s like high-stakes Jenga when all you want to do is get the job done.

The Publish-subscribe pattern is a beautiful way of handling cases where you want to trigger a series of actions based upon an event. By encapsulating each different job into it’s own class, and then independently registering those jobs to an event, you create a system that is very easy to maintain and evolve going forward.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.