The repository pattern can be very helpful to you in order to keep your code a cleaner and easy maintainable. The Repositories mediates between the data source layer and the business layers of the application. You can use the Repository pattern to have a more flexible way of abstracting the database layer of our applications. For the purposes of this post I will use a clean installation of Laravel to show how using repositories can make your controllers a bit less verbose, more loosely coupled, and easier to read.
Problem
Accessing data directly from business logic (controllers) of your application can result in the following:
- Duplicated code
- A higher potential for programming errors
- Not centralized data access
- An inability to easily test the business logic in isolation from external dependenci
- Difficulty in unit testing
Solution
Use a repository to separate the logic that retrieves the data. The repository mediates between the data source layer and the business layers of the application. The separation between the data and business tiers has following benefits:
- It centralize the data logic of the application
- It provides a substitution point for the unit tests
- It provides a flexible architecture that can be adapted
Implementation in Laravel 5
The implementation of a repository can be divided in three major steps.
Step1: Implementation of repository interface. Implementing an repositry interface allows a repository class to become more formal about the behavior it promises to provide. If you decide in some later stage to use other data source you just need to implements the repository interface in a new repsoitory.
Step2: Implementation of repository class. The repository class implements the repository interface and the methods described in it.
Step3: Binding the interface to the implementation so you can inject the repository in the controller if necessary.
Example implementation of BlogsRepositoryInterface in Laravel 5
Best practice: The best place for application interfaces is the "Contracts" folder and for repositories the "Repositories" folder inside the app folder using the naming convensions described in "How to organize your project with PHP and Laravel to get the best structure in MVC pattern".
The BlogsRepositoryInterface discribe the methods that must be implemented in the BlogsRepository
namespace App\Contracts;
interface BlogsRepositoryInterface
{
public function findOrFail($id);
public function blogsByCreatedAt($limit = null);
public function findBySlug($slug, $withTagged = false);
public function existingTags();
public function existingCategories();
public function nextPost($currentPostId);
public function previousPost($currentPostId);
}
and the BlogsRepository implements this methods.
namespace App\Repositories;
use App\Contracts\BlogsRepositoryInterface;
use App\Blog;
class BlogsRepository implements BlogsRepositoryInterface
{
public function findOrFail($id)
{
return Blog::findOrFail($id);
}
public function blogsByCreatedAt($limit = null)
{
$query = Blog::published()->with("tagged")->orderBy("created_at","desc");
if(null === $limit)
{
return $query->get();
}
return $query->limit($limit)->get();
}
...
}
We are almost ready the last thing to do is to bind the BlogsRepositoryInterface to the BlogsRepository so it can be injected in every controller.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class RepositoriesServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind( 'App\Contracts\BlogsRepositoryInterface',
'App\Repositories\BlogsRepository' );
}
}
Best practice: Create a RepositoryServiceProvider in the "Providers" folder of the app folder. This provider will bind all repository interfaces to their implementations.
Now you can inject the BlogsRepository in the controllers.
namespace App\Http\Controllers;
use App\Contracts\BlogsRepositoryInterface;
use Illuminate\Http\Request;
class ForntendController extends Controller {
/**
* @var BlogsRepositoryInterface
*/
private $repoBlogs;
public function __construct(BlogsRepositoryInterface $repoBlogs)
{
$this->repoBlogs = $repoBlogs;
}
}
Usefull example
Imagine you want to change the database storage from MySQL to MongoDB three monts later after first release of your application.
All you need to do is to write a new repositories implementing the repository interfaces
and change the binding in the RepositoryServiceProvider.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class RepositoriesServiceProvider extends ServiceProvider
{
public function register()
{
/*$this->app->bind( 'App\Contracts\BlogsRepositoryInterface',
'App\Repositories\BlogsRepository' );*/
$this->app->bind( 'App\Contracts\BlogsRepositoryInterface',
'App\Repositories\BlogsRepositoryMongoDB' );
}
}
Conclusion
Doing all this is hard work but it's worth. Repositories are a really good way to centralize the data logic and to divide the data logic from business logic in the application.