cult3

Creating a basic user registration system in Laravel 4

May 19, 2014

Table of contents:

  1. A quick recap
  2. The Register Route, Controller and View
  3. Creating a Registrator Service
  4. Validation in a Service class
  5. The Registrators Service Provider
  6. Injecting the Service into the Controller
  7. Conclusion

Over the last couple of weeks I’ve looked into implementing an invitation system for a Laravel 4 application.

First I looked at a basic foundation for requesting a new invitation and requiring a user to have a valid invitation to access the registration form.

Next I looked at creating an Invitation service class and automatically generating invitation codes through model events.

Then I looked at allowing existing users to invite their friends and the all important test to ensure everything is working correctly.

And finally I looked at creating a referral scheme to encourage invited users to also invite their friends and therefore jump the queue to get access to the application.

Now that I’ve got the invitation process in place, I can start to look at the registration process to allow the creation of new users for Cribbb.

In this week’s tutorial I’m going to be looking at building a basic user registration system.

A quick recap

If you haven’t already read the tutorials from the last couple of weeks, you should go and do that now.

Here is a quick recap of the process so far.

A user will land on the Cribbb homepage and request an invitation. The user will receive an email to register with a URL that contains their unique invitation code. When the user lands on the registration page, the invitation code will give them access to the registration form.

Requesting an invitation

The first step in the process is when a new potential users lands on the homepage of Cribbb and requests an invite through the invitation request form.

when the user submits a valid email address, a new invitation will be created in the invites.

In the InviteController I will finish off the store() method to return the correct response depending on whether the invitation was created, or not:

/**
 * Create a new invite
 *
 * @return Response
 */
public function store()
{
    $invite = $this->requester->create(Input::all(), Session::get('referral', null));

    if ($invite) {
        return Redirect::route('home.index')->with('message', 'Thank you for requesting an invite to Cribbb!');
    }

    return Redirect::route('home.index')->withInput()->withErrors($this->requester->errors());
}

In the home.index view file I can now update the form to display the response feedback. If the success $message has been set, I can display it to the user. If the user has not entered an email address that passes all of the validation rules, I can iterate through them and display an unordered list or errors:

{{ Form::open(array('route' => 'invite.store')) }}

@if (Session::has('message'))
<div>{{ Session::get('message') }}</div>
@endif

@if ($errors->any())
<div>
<ul>
{{ implode(",", $errors->all('<li>:message</li>'))}}
</ul>
</div>
@endif

<div>
{{ Form::label('email', 'Email Address') }}
{{ Form::text('email') }}
</div>

{{ Form::submit('Submit') }}

{{ Form::close() }}

Invitation filter

In a previous tutorial I created a filter to check to see if an invitation code has been included in the query string. This is great for checking a single route when a user clicks an invitation link, but what about the other routes in the registration process? I wouldn’t want someone to gain access to one of the other routes of the registration process without an invite.

To fix this problem I will add the code to the Session if it is valid. I can then check to see if the code is part of the query string or if it has already been set in the session:

Route::filter("invite", function () {
    if (!Input::has("code") and !Session::has("invitation_code")) {
        return App::abort(404);
    }

    if (Input::has("code")) {
        $repository = App::make("Cribbb\Repositories\Invite\InviteRepository");

        if (!$repository->getValidInviteByCode(Input::get("code"))) {
            return App::abort(404);
        }

        Session::put("invitation_code", Input::get("code"));
    }
});

The Register Route, Controller and View

In order to process new registrations, I will need a RegisterController and a couple of routes. The first route and method to create will be for displaying the initial step in the registration process:

/**
 * Register
 *
 * Sign up as a new user
 */
Route::get("register", [
    "uses" => "RegisterController@index",
    "as" => "register.index",
]);

The RegisterController looks like this:

class RegisterController extends BaseController
{
    /**
     * Create a new instance of the RegisterController
     *
     * @return void
     */
    public function __construct()
    {
        $this->beforeFilter("invite");
    }

    /**
     * Display the form for creating a new user
     *
     * @return View
     */
    public function index()
    {
        return View::make("register.index");
    }
}

Notice how I’ve set the beforeFilter() in the __construct() method? This will ensure the request has either got an invite code in the query string of the URL or it has already been set on the session.

Finally a basic registration View would look like this:

{{ Form::open(array('route' => 'register.store')) }}

@if ($errors->any())
<ul>
{{ implode(",", $errors->all('<li>:message</li>'))}}
</ul>
@endif

<div>
{{ Form::label('email', 'Email Address') }}
{{ Form::text('email') }}
</div>

<div>
{{ Form::label('username', 'Username') }}
{{ Form::text('username') }}
</div>

<div>
{{ Form::label('password', 'Password') }}
{{ Form::text('password') }}
</div>

{{ Form::submit('Complete') }}

{{ Form::close() }}

In this form I’m requesting an email address, username and password. Notice how I’m not requesting the user to confirm their password and I’m not using the password input type. This is by choice as I believe it makes the user experience much better.

Creating a Registrator Service

As you might have been able to guess from the title of this post, the Cribbb registration process won’t be your usual basic registration process.

Normally to register a new user at this stage you would simply accept the POST data from the form and create a new User entity as long as the data was valid.

However, for Cribbb, I want to have the option of many different possibilities of registration. There are many reasons behind this decision, but an important one is, it makes for a more interesting tutorials.

So instead of simply creating a new user using the UserRepository as you normally would. I’m going to instead create a new Service class for handling registrations.

As with every other component of Cribbb, I’m going to create a new namespace for this service under Cribbb\Registrators.

Setting the Interface

As there are going to be multiple ways to register for Cribbb, I will create an interface that each class will implement:

<?php namespace Cribbb\Registrators;

interface Registrator
{
    /**
     * Create a new user entity
     *
     * @param array $data
     * @return Illuminate\Database\Eloquent\Model
     */
    public function create(array $data);
}

This class simply ensures that there will be a create() method and it accepts an array.

Defining an Abstract class

Next I will define an abstract class that each of my registrator implementations can extend and inherit from:

<?php namespace Cribbb\Registrators;

use Illuminate\Support\MessageBag;

abstract class AbstractRegistrator
{
    /**
     * The errors MesssageBag instance
     *
     * @var Illuminate\Support\MessageBag
     */
    protected $errors;

    /**
     * Create a new instance of Illuminate\Support\MessageBag
     * automatically when the child class is created
     *
     * @return void
     */
    public function __construct()
    {
        $this->errors = new MessageBag();
    }

    /**
     * Return the errors MessageBag
     *
     * @return Illuminate\Support\MessageBag
     */
    public function errors()
    {
        return $this->errors;
    }
}

So far this abstract class is simply setting a new instance of Illuminate\Support\MessageBag on the $errors class property and defining an errors() method to return that property.

As I think I’ve mentioned in the past, I prefer to have an empty MessageBag, rather than a null value when there are no errors and I think of the MessageBag as an enhancement to the class, rather than a dependency, so I’m not worried about injecting it through the constructor. The MessageBag is never something that I’m going to need to mock.

Finally I can create the first implementation, CredentialsRegistrator:

<?php namespace Cribbb\Registrators;

class CredentialsRegistrator extends AbstractRegistrator implements Registrator
{
    /**
     * Create a new instance of the CredentialsRegistrator
     *
     * @return void
     */
    public function __construct()
    {
        parent::__constuct();
    }

    /**
     * Create a new user entity
     *
     * @param array $data
     * @return Illuminate\Database\Eloquent\Model
     */
    public function create(array $data)
    {
        // Create
    }
}

As you can see, this is fairly simple so far. All I’m doing here is defining the __construct() method and calling parent::__construct() to set the MessageBag. I’ve also defined the create() method to satisfy the Registrator interface contract.

Validation in a Service class

Validation is an interesting aspect of this Service class because I’ll have a set of rules for all registrator implementations, and some rules will only be valid for certain implementations.

In much the same was as creating the Inviter service, I can create a number of individual validation classes and then inject them into the service as an array to be iterated.

This means that should any of the validation rules change, I won’t have to rewrite the service class, and I will be able to reuse the same basic validation classes across all of my registrator implementations.

Under Cribbb\Registrators create a new directory called Validators.

The first validation rule I will implement will be that all usernames should be unique and alpha_dash:

<?php namespace Cribbb\Registrators\Validators;

use Cribbb\Validators\Validable;
use Cribbb\Validators\LaravelValidator;

class UsernameValidator extends LaravelValidator implements Validable
{
    /**
     * Validation rules
     *
     * @var array
     */
    protected $rules = [
        "username" => "required|alpha_dash|unique:users",
    ];
}

As with the Inviters service, I can add a method to the AbstractRegistrator class to iterate through the array of validation classes:

/**
 * Run the validation checks on the input data
 *
 * @param array $data
 * @return bool
 */
public function runValidationChecks(array $data)
{
    foreach ($this->validators as $validator) {
        if ($validator instanceof Validable) {
            if (! $validator->with($data)->passes()) {
                $this->errors = $validator->errors();
            }
        } else {
            throw new Exception("{$validator} is not an instance of Cribbb\Validiators\Validable");
        }
    }

    if ($this->errors->isEmpty()) {
        return true;
    }
}

I’ll be injecting the array through the __construct() method:

/**
 * An array of Validable classes
 *
 * @param array
 */
protected $validators;

/**
 * Create a new instance of the CredentialsRegistrator
 *
 * @return void
 */
public function __construct(array $validators)
{
    parent::__construct();

    $this->validators = $validators;
}

And I will also need an instance of UserRepository to actually create the new entity in the database:

/**
 * The UserRepository
 *
 * @param Cribbb\Repositories\User\UserRepository
 */
protected $userRepository;

/**
 * An array of Validable classes
 *
 * @param array
 */
protected $validators;

/**
 * Create a new instance of the CredentialsRegistrator
 *
 * @return void
 */
public function __construct(UserRepository $userRepository, array $validators)
{
    parent::__construct();

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

Finally I can implement the create() method to spin through the $validators and then create the new user entity:

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

The Registrators Service Provider

Now that I’ve got the basic outline of the CredentialsRegistrator class chalked out, I can set up a Service Provider to create the object and bind it to Laravel’s IoC Container:

<?php namespace Cribbb\Registrators;

use Illuminate\Support\ServiceProvider;
use Cribbb\Registrators\Validators\EmailValidator;
use Cribbb\Registrators\Validators\UsernameValidator;

class RegistratorsServiceProvider extends ServiceProvider
{
    /**
     * Register the binding
     *
     * @return void
     */
    public function register()
    {
        $this->registerCredentialsRegistrator();
    }

    /**
     * Register the CredentialsRegistrator service
     *
     * @return void
     */
    public function registerCredentialsRegistrator()
    {
        $this->app->bind(
            "Cribbb\Registrators\CredentialsRegistrator",
            function ($app) {
                return new CredentialsRegistrator(
                    $this->app->make("Cribbb\Repositories\User\UserRepository"),
                    [
                        new UsernameValidator($app["validator"]),
                        new EmailValidator($app["validator"]),
                    ]
                );
            }
        );
    }
}

As with the Inviters service classes, here I’m basically just injecting the correct dependencies into the class. Notice how the second dependency is an array of new Validator objects.

Next add 'RegistratorsServiceProvider' to app/config/app.php to register the Service Provider.

Injecting the Service into the Controller

Now that we’ve got the basic service set up, we can inject it into the Controller to take it for a spin:

use Cribbb\Registrators\CredentialsRegistrator;

class RegisterController extends BaseController
{
    /**
     * Create a new instance of the RegisterController
     *
     * @return void
     */
    public function __construct(CredentialsRegistrator $registrator)
    {
        $this->beforeFilter("invite");
        $this->registrator = $registrator;
    }

    /**
     * Display the form for creating a new user
     *
     * @return View
     */
    public function index()
    {
        return View::make("register.index");
    }

    /**
     * Create a new user
     *
     * @return Redirect
     */
    public function store()
    {
        $user = $this->registrator->create(Input::all());

        echo "<pre>";
        dd($user);
    }
}

As I’ve shown you many times in this series, here I’m injecting the service into the __construct() method of the Controller. The create() method accepts an array so we can just dump the Input::all() straight in there.

If all goes well you should be presented with a dump of a new User object.

To finish off the store() method we can add in the redirect to return the register form if there was an error, or authenticate the user and redirect to the main screen of the application if a new user was successfully created:

/**
 * Create a new user
 *
 * @return Redirect
 */
public function store()
{
    $user = $this->registrator->create(Input::all());

    if ($user) {
        Auth::login($user);

        return Redirect::route('home.index');
    }

    return Redirect::route('register.index')->withInput()
        ->withErrors($this->registrator->errors());
}

Conclusion

A lot of the code in this tutorial will probably be overkill if you are only ever going to offer a single type of registration. It’s usually a bad decision to overcomplicate something just for the sake of it.

However, if you do want to offer a couple of different types of registration, creating a service that can handle each different type is a great solution.

In this tutorial I’ve set up the first basic registrator service that can be used to allow a new user to sign up with a email, username and password. If I decide to change the validation rules around this registration process, all I have to do is to update the Service Provider to inject different Validator instances. And if I decide I want to add a different form of registration all together, I’ve already got the separate building blocks to create a new service, rather than trying to hack at existing code.

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.