Practical Versioning

Christopher Pitt
Laravel 5 Tutorials
4 min readOct 28, 2014

This is part of an upcoming tutorial on building APIs with Laravel. It assumes you know how to install PHP 5.5+ and Laravel 4.2+.

Imagine that you have just built an API and a few weeks after release, analytics show that many users cross-post their content to Medium. So the client tells you that every new user must have a link to their Medium profile.

If you alter the current API then it could break consumers. So today I’ll show you one of the most sensible aspects of building an API: practical versioning.

Container

I will not go into detail about dependency injection, except to say that it’s about providing dependencies to a class through a constructor or setters. Laravel provides an excellent tool for this, called the IoC Container, or just the Container.

IoC is short for Inversion of Control. It’s the name of a pattern which describes who controls dependencies (hint: not the class that uses them).

The Container has all the features of similar libraries. The ability to bind factories to names. The ability to treat instances as Singletons (without creating them as Singletons to begin with).

The Container also allows interfaces to be bound to implementations. It’s possible to create an interface, called UserRepository, and have it bound to UserEloquentRepository. This means you can type-hint interfaces, and Container will know which implementation you mean.

And that’s awesome!

Canon

Common wisdom dictates that Container bindings happen within a service provider. The general format is to bind a series of interfaces to implementations, within a register() method:

namespace Acme\ServiceProvider;

use Acme\Repository\Eloquent\PostEloquentRepository;
use Acme\Repository\Eloquent\UserEloquentRepository;
use Acme\Repository\PostRepository;
use Acme\Repository\UserRepository;

class RepositoryServiceProvider extends ServiceProvider
{
/**
* @var bool
*/
protected $defer = true;

/**
* @var array
*/
protected $bindings = [
PostRepository::class => PostEloquentRepository::class,
UserRepository::class => UserEloquentRepository::class
];

/**
* @return void
*/
public function register()
{
foreach ($this->bindings as $interface => $implementation) {
$this->app->bind($interface, $implementation);
}
}
}

This is concise, for the first version. Over time the API will evolve. New classes extend on the functionality of previous API versions. For the sake of backwards compatibility, it’s important to maintain the previous behaviour. Consider the following interface:

namespace Acme\Repository;

use Illuminate\Http\Request;

interface UserRepository
{
/**
* @param Request $request
*
* @return array
*/
public function getValidationRulesForAdd(Request $request);
}

…and the following implementation:

namespace Acme\Repository\Eloquent;

use Acme\Repository\UserRepository;
use Illuminate\Http\Request;

class UserEloquentRepository implements UserRepository
{
/**
* @param Request $request
*
* @return array
*/
public function getValidationRulesForAdd(Request $request)
{
return [
"name" => "required",
"slug" => "required|alpha_dash"
];
}
}

The interface defines some required validation behaviour and the implementation delivers! How those rules are used is a separate issue. The important thing here is that the first version defines behaviour we need to maintain.

Control

Then the client approaches us with the new behavioural requirements. We can’t change the old behaviour so the next best thing is to inherit:

namespace Acme\Repository\Eloquent;

use Illuminate\Http\Request;

class UserEloquentRepositoryTwo extends UserEloquentRepository
{
/**
* @param Request $request
*
* @return array
*/
public function getValidationRulesForAdd(Request $request)
{
return [
"name" => "required",
"slug" => "required|alpha_dash",
"link" => "required|url"
];
}
}

We need to provide the behaviour for both versions. Your first thought might be to alter the service provider as follows:

namespace Acme\ServiceProvider;

use Acme\Repository\Eloquent\PostEloquentRepository;
use Acme\Repository\Eloquent\PostEloquentRepositoryTwo;
use Acme\Repository\Eloquent\UserEloquentRepository;
use Acme\Repository\Eloquent\UserEloquentRepositoryTwo;
use Acme\Repository\PostRepository;
use Acme\Repository\UserRepository;

class RepositoryServiceProvider extends ServiceProvider
{
/**
* @var bool
*/
protected $defer = true;

/**
* @var array
*/
protected $bindingsOne = [
PostRepository::class => PostEloquentRepository::class,
UserRepository::class => UserEloquentRepository::class
];

/**
* @var array
*/
protected $bindingsTwo = [
PostRepository::class => PostEloquentRepositoryTwo::class,
UserRepository::class => UserEloquentRepositoryTwo::class
];

/**
* @return void
*/
public function register()
{
$bindings = $this->bindingsOne;

if ((integer) $this->app["request"]->get("version") >= 2) {
$bindings = $this->bindingsTwo;
}

foreach ($bindings as $interface => $implementation) {
$this->app->bind($interface, $implementation);
}
}
}

It’s a good plan! The trouble is we can’t depend on $this->app->make() (or array access resolution)inside register(). What you need is lazy evaluation:

namespace Acme\ServiceProvider;

use Acme\Repository\Eloquent\PostEloquentRepository;
use Acme\Repository\Eloquent\PostEloquentRepositoryTwo;
use Acme\Repository\Eloquent\UserEloquentRepository;
use Acme\Repository\Eloquent\UserEloquentRepositoryTwo;
use Acme\Repository\PostRepository;
use Acme\Repository\UserRepository;

class RepositoryServiceProvider extends ServiceProvider
{
/**
* @var bool
*/
protected $defer = true;

/**
* @var array
*/
protected $bindings = [
PostRepository::class,
UserRepository::class
];

/**
* @var array
*/
protected $bindingsOne = [
PostRepository::class => PostEloquentRepository::class,
UserRepository::class => UserEloquentRepository::class
];

/**
* @var array
*/
protected $bindingsTwo = [
PostRepository::class => PostEloquentRepositoryTwo::class,
UserRepository::class => UserEloquentRepositoryTwo::class
];

/**
* @return void
*/
public function register()
{
foreach ($this->bindings as $binding) {
$this->app->bind($binding, function($app) use ($binding) {
$bindings = $this->bindingsOne;

if ((integer) $app["request"]->get("version") >= 2) {
$bindings = $this->bindingsTwo;
}

return $app[$bindings[$binding]];
});
}
}
}

And there you have it! You can version your application behaviour with inheritance and lazy evaluation of Container bindings.

If you found this helpful and/or found an error; let me know with a comment, or on Twitter.

--

--