Laravel – Using Repository Pattern
Laravel
is one of the most popular PHP MVC frameworks and taking the Php
community rapidly than any other frameworks probably couldn’t do and it’s because of a great combination of power, extensibility and easiness. The framework provides so many ways to a developer to develop an application using one or another, depending on the size of the project developer can use any possible way that Laravel provides and project of any size fits well in Laravel
. One of the most popular ways for building an application using Laravel
is the Repository Pattern
and use of this pattern has a lots of benefits and most of the developers follow this pattern to build an application using Laravel
.
The fundamental idea or the primary goal to use this pattern in a Laravel
application is to create a bridge or link between application models and controllers. In other words, to decouple the hard dependencies of models from the controllers. In a typical Laravel application one may use some thing like this in a controller:
class UserController extends BaseController { public function getAllUsers() { $users = User::all(); return View::make('user.index', compact('users')); } }
There is nothing new for a Laravel
user, in that controller method, the User
model is being directly used to get all the users from the database and this User
model basically extends the Laravel’s Eloquent ORM
which is a common approach in any Laravel
application because models that interact with database extends the Eloquent
.
So far, everything is good and this code is correct and will work as expected but still the code is not quite perfect and it’s using the User
model directly from the controller and manual access to the data layer from a controller violates the law of good design. It’s bad and known as anti pattern because the controller has hard dependencies on the User
model and hence it’s tightly coupled to each other and that’s why it’s hard to test (unit testing). To, overcome this problem, a repository comes in to the play and a repository is just a normal class with methods to access the data layer (User model here) and the controller uses this repository to interact with the model instead of directly using the model by itself. This is an example of a repository (UserRepository)
that the UserController
can use to interact with the User
model:
class UserRepository { public function getAllUsers() { return User::all(); } }
This repository could be injected into the UserController
during the initiation of the class using the __construct()
method and this is possible by type hinting the UserRepository
class like this:
class UserController extends BaseController { protected $user = null; public function __construct(UserRepository $user) { $this->user = $user; } public function showUsers() { $users = $this->user->getAllUsers(); return View::make('user.index', compact('users')); } }
This is the UserController
class now and it has a dependency on UserRepository and this dependency would be injected into the class during initialization by the smart IoC
container component of the framework automatically. While this will work and hard coded dependency is replaced using the repository but still now the UserController
class is indirectly dependent on the User
model because the UserRepository
class depends on that model and testing is still hard. So, to overcome this situation, an Interface
can come to the play. While the primary goal of an interface is to build a contract with the class that implements the interface
and it’s a very commonly used approach, in most cases for public API
to ensure the correctness of an application that uses the public API
where an interface defines the behavior of a class by declaring methods and the class that implements the interface must implements all the methods declared in the interface.
While this is true that an interface is like a contract but also there are other things an interface does. It is being used an abstraction layer between the consumer class (UserController that will use the interface) and the concrete class. Here, concrete class refers to the class that implements the interface (an abstraction of the concrete class). In other words an interface hides the details from the consumer class when the interface is used to type hint the data type as a dependency of the consumer class. Let’s see an example to make it clear. First, an interface for the UserRepository
class which will be implemented by the repository:
interface IUserRepository { public function getAllUsers(); public function getUserById($id); public function createOrUpdate($id = null); }
Now we need to implement this interface in the UserRepository
class using this:
// use the namespace if it has any, i.e. // namespace Repositories\User; // Same for IUserRepository interface class UserRepository implements IUserRepository { public function getAllUsers() { return User::all(); } public function getUserById($id) { return User::find($id); } public function createOrUpdate($id = null) { if(is_null($id)) { // create after validation $user = new User; $user->name = 'Sheikh Heera' $user->email = 'me@yahoo.com'; $user->password = '123456' return $user->save(); } else { // update after validation $user = User::find($id); $user->name = 'Sheikh Heera' $user->email = 'me@yahoo.com'; $user->password = '123456' return $user->save(); } } }
So, this repository will be used in the UserController
but with the type hint of the interface that this class implemented instead of the concrete impletation (this UserController class):
class UserController extends BaseController { protected $user = null; // IUserRepository is the interface public function __construct(IUserRepository $user) { $this->user = $user; } public function showUsers() { $users = $this->user->getAllUsers(); return View::make('user.index', compact('users')); } public function getUser($id) { $user = $this->user->getUserById($id); return View::make('user.profile', compact('user')); } public function saveUser($id = null) { if($id) { $this->user->createOrUpdate($id); } else { $this->user->createOrUpdate(); } // return redirect... } }
Now the UserController
depends on the IUserRepository
interface (abstract layer) instead of UserRepository
(concrete class) and not tightly coupled with the User
model so easily testable as well.
But still it’s not ready to use by the application because an interface is not a class and the IoC
container can’t make an instance of this interface and it shouldn’t but the container will try to do it because of the type hint and will fail, so it’s necessary to tell the IoC
container that, whenever the UserController
class is getting instantiated, just pass an instance of the concrete UserRepository
class which implemented the type hinted interface given in the constructor method. So, we need to bind the concrete class to the interface for the IoC
container so it can supply the class to the controller and this could be done using this:
// IuserRepository interface, UserRepository class App::bind('IUserRepository', 'UserRepository');
So, now the IoC
is able to inject an instanse of UserRepository
class into UserController
class whenever the UserController
is initialized. If the repository and interface both inside a sub-directory and has namespace something like namespace Repositories/User;
then during the binding it should be used this way:
// IuserRepository interface, UserRepository class App::bind('Repositories\\User\\IUserRepository', 'Repositories\\User\\UserRepository');
This could be placed in the global.php
file or in another file (create one as app_bindings.php
) where this code along with other code (for binding) could be placed and just include that file in the global.php
file using require '/app_bindings.php'
(assumed the file is in the same folder) or it’s also possible to create a Service Provider
so Laravel will register this at the boot up process of the framework. To create a service provider, create a file in the same folder and save it as UserServiceProvider.php
and use paste this code:
app::bind('Repositories\\User\\IUserRepository', 'Repositories\\User\\UserRepository'); } }
Then in the providers
array in the app/config/app.php
file, add this at the last:
'providers' => array( // more entries ... 'Repositories\User\UserServiceProvider' )
Using a service provider is not necessary but a good way to organize things. So, by using an interface we just not created a contract but also the detail of the implementation is hidden (abstracted) from the controller so it doesn’t know anything bout the class is being used behind the scene and this is a plus point. According to the design principles (LSP in SOLID) “objects in a program should be replaceable with instances of their subtypes
without altering the correctness of that program” which also relates to Design by contract and these rules or principles are obviously for good reason and one of the good reasons is that, now it’s possible to change the implementation of the concrete class of UserRepository
with a different one which also implements the IUserRepository
interface and hence the controller will work normally because it doesn’t know what is the concrete class is being used for that interface, it’s totally ignorant about that and that’s why we can also test our controller without using a real database because we can pass a mock object instead of original UserRepository
class, only we have to implement the same interface and the controller will think it’s using the same thing.
So, for example to use a different implementation of the interface we may implement another repository by implementing the IUserRepository interface and just can change the class/repository binding for the container and we don’t need to touch our controller code. FOR example if we implement something like this:
Now, in the service provider we only need to change the binding:
$this->app::bind('Repositories\\User\\IUserRepository', 'Repositories\\User\\MongoUserRepository');That's it, this is now more maintainable and easily testable because of the use of abstract layer (interface) instead of the concrete type.
Another thing we can do in our repository class to use the methods of
Eloquent
(User) model, for example, from ourUsercontroller
class we can't useUserRepository::with()
unless we declare that method in the class but it's possible to use those non-existing methods (in the repository) using__call()
magic method like this (also a constructor):class UserRepository implements IUserRepository { protected $user = null; public function __construct(\User $user) { $this->user = $user; } public function getAllUsers() { return $this->user->all(); } public function __call($method, $args) { return call_user_func_array([$this->user, $method], $args); } }So, now we can use all the methods of
User
model from theUserController
(if required) as if those methods are declared in theUserRepository
while they are not. For example:class UserController extends BaseController { protected $user = null; public function __construct(IUserRepository $user) { $this->user = $user; } // Can access the methods of User model public function getUserWithPostsComments() { $user = $this->user->whereEmail('me@yahoo.com') ->with('posts') ->with('comments') ->get(); } }This may not required when we use the repository pattern but it's just an idea to use model's methods easily.
So, finally, it's not important that you must follow the rules and principles in every project but it's a good practice but if you don't use it, it'll still work and it's not a sin, you should decide what to do and how to do it and by following some rules blindly doesn't makes any sense, it may create problems if applied inappropriately, so why not use your sense without blindly following some pro, some one said it's good practice so I'll do this, if this is the case then it's not good at all. I always like to find alternatives and think as
what if
, Oops! it's too much...