Laravel : Repositories the right way

If you want to make your application testable you should really use Repositories in order to perform unit tests without touching the database.

In my case I was programming a Fantasy Football game and I will stick with the Team model.

The Team model has some attributes eg : Name, Colors and Formation which was an enumerator of various Formations.

On your app if you accessed the model directly on your Controller than testing the controller and trying to bypass a database call would be impossible and database calls are expensive. Imagine if you had a gulp task on elixir to perform unit tests on ‘watch’, every time you saved something that would do a call to database eg :

<?php namespace App\Http\Controllers;

use Offside\Models\Team;

class HomeController extends Controller {

protected $team;
public function __construct(Team $team){
$this->team = $team;
}

/**
* Show the application dashboard to the user.
*
*
@return Response
*/
public function getPlayers()
{
return $this->team->players;
}

}

Everytime you hit getPlayers() you would make a call to database.

To avoid that you should create a repository and inject it on your controller by passing it to the Controller constructor.

You need to create an interface so it can be mocked eg :

<?php namespace Offside\Repo;

interface TeamRepositoryInterface {
public function getPlayers();
}

//And then you should create a concrete implementation of that interface eg :

<?php namespace Offside\Repo;

use Offside\Models\Team;
use Offside\Random;

class TeamRepository implements TeamRepositoryInterface{

protected $team = null;
public function __construct(Team $team){
$this->team = $team;
}

/**
* Return complete list of players of a team
*
*
@return mixed
*/
public function getPlayers()
{
return $this->team->players;
}
}

The implementation creates a new Team model and it acts as an adapter between your controller and the Model itself.

In order to be explicit about your implementation you need to tell Laravel how to initialize it by using a service provider eg :

<?php namespace Offside\Repo;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Offside\Models\Team;

class TeamRepositoryServiceProvider extends ServiceProvider{

public function register()
{
$this->app->bind("Offside\Repo\TeamRepositoryInterface",function(){
if(Auth::user()){
$team = Auth::user()->team;
return new TeamRepository($team);
}else{
return new TeamRepository(new Team());
}
});
}
}

This way you can bind your concrete implementation of TeamRepository to anything that accepts a TeamRepositoryInterface by using Depedency Injection.

Laravel uses it’s IoC container to resolve/initialize anything which is non-concrete such an Interface or Abstract class, where you previously just told what to initialize in the TeamRepositoryInterface particular case.

Now on your controller you can accept a TeamRepositoryInterface which Laravel will initialize for you eg :

<?php namespace App\Http\Controllers;

use Offside\Repo\TeamRepositoryInterface;

class HomeController extends Controller {

protected $team;
public function __construct(TeamRepositoryInterface $team){
$this->team = $team;
}

/**
* Show the application dashboard to the user.
*
*
@return Response
*/
public function getPlayers()
{
return $this->team->getPlayers();
}

}

In this case you can perform any unit test on this controller, without touching the database, by Mocking the TeamRepository object and pass it to controller. The mocked version of TeamRepository will just acknowledge the interpreter/compiler that I exist, I have a specific method and I return an expected result and this is because right now you just need to make sure the Controller getPlayers(); method is called as the TeamRepository had it’s own tests and you don’t want to test it again.

<?php

class TeamTest extends TestCase {

public function tearDown()
{
Mockery::close(); //don't forget to tell Mockery to place everything as it was
}

public function testIfPlayersAreThere(){
$team = Mockery::mock("Offside\Repo\TeamRepository"); //Mock the TeamRepository object

$players = $team->shouldReceive("getPlayers")->andReturn([]); //tell the mocked object that you expect from him this that getPlayers() method will be called and it will return an empty array

$this->assertTrue([],$players);
}

}

This way your code is chunked into smaller parts where you can write tests for all of them independently.

And the problem begun with my TeamRepository. The Team model has an attribute called formation, which is an enumerator (Value Object) and depend on that I had a factory which produced some objects anyone with it’s unique behavior based on team formation.

In the Football game logic a formation is the team structure of players on their position, so if you have a formation of 522 it means 5 players on defense, 2 players on midfield and 2 others on attack.

My bad practice was that every method which was related to Team Formation I placed on TeamRepository forgetting completely that Formation acts as an independent entity.

This way I overloaded my Repository and just screwed their purpose by making the testing part a nightmare.

So my conclusion is, if you have another Entity or Value Object on your model which looks like an attribute you should split that into another Repository and it is just fine to have more than one Repository which points to a model.