152
Php

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.

An interface says, “This is what all classes that implement this particular interface will look like.” Thus, any code that uses a particular interface knows what methods can be called for that interface, and that’s all. So the interface is used to establish a protocol between classes.

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 our Usercontroller class we can't use UserRepository::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 the UserController (if required) as if those methods are declared in the UserRepository 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.

The Repository pattern is not limited to Laravel or PHP but it's a widely used software architecture or design pattern used by developers using various languages.

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...

Latest Blog

0
Php

PHP – 8.0 Match Expression

In PHP 8.0 there is a new feature or I should say a new language construct (keyword) going to be introduced,  which has been implemented depending […]