/ Laravel

The way I implement repository pattern in Laravel

Repository pattern is the design pattern that a lot of developers want to implement into their Laravel application, because the repository pattern is easy to implement. Here's couple of examples:

The repository classes these developers built is, in my opinion, completely wrong. Here's the general algorithm that most of the developers use:

  • Create a repository interface
  • Create a base repository class that implements that interface
    • This is usually abstract class
  • Create a subclass or that base repository for each entity in your application
  • Delegate various methods in your base class such as all(), count(), find(), ...

This is wrong on so many levels. Please note that this is the way I like to create repositories, which may not work for you at all.

General rules

Don't implement an interface

The reason Laravel (Illuminate) components implement various interfaces (or as they like to call them, Contracts) is because you can swap any concrete class implementation with your own and still resolve it with the same key, thus making Laravel really customizable. For example, you can bind ImageResizer interface with GDImageResizer implementation to the Container and use that GD-specific driver with app(ImageResizer::class). If later down the road, you need to swap GD with Imagick, just bind the ImagickImageResizer (that implemented the interface) instead of GDImageResizer and you can still resolve it with the same interface. So in essence, Laravel does not care about your implementation, it cares about the bound interface. If we inherit each entity-specific repository class from one superclass, what is the point of interface? Drop it and let Composer autoloader do it's magic.

Don't delegate Eloquent

If you ever tried to implement repository library into your app, you probably saw something like this:

class PostsRepository extends BaseRepository
{
    public function all()
    {
        return $this->model->all();
    }

    public function count()
    {
        return $this->model->count();
    }

    public function find($id)
    {
        return $this->model->find($id);
    }

    ...
}

What problem are you really solving by creating a class that delegates to the Eloquent methods? There is general rule of thumb to abstract the code from the framework, but if you're going to switch frameworks during development, this will be the least of your problems. The only time I find this type of class acceptable is when you need to scale and Eloquent is causing you headaches so you need to migrate to raw queries for performance or for more complex database calls. But if you're ever at that scale, you probably know what to do next, but don't optimize prematurely. There is no need to use DigitalOcean's server for $320/month if you're going to have 10 people using your site, right? You'll optimize your code when your server can't handle that amount of requests and data.

My way

The way I like to create a repository class is by creating a simple class that contains all the Eloquent queries with different constraints. Examples of that are: give me all paginated posts ordered by the published date, give me all drafted days within the last 3 days, etc. No inheritance, no interface implementations, no funny business. If I really need any dependencies, I can simply use dependency injection to load all necessary dependencies.

class Posts
{
    public function countWithinLastMinute()
    {
        return Post::where('ip_address', request()->ip())
            ->where('created_at', '>=', now()->subMinute())
            ->count();
    }

    public function getPendingWithNoUserVote()
    {
        return Post::pending()
            ->when(Auth::check(), function ($query) {
                $query->whereDoesntHave('votes', function ($query) {
                    $query->where('user_id', Auth::id());
                });
            })
            ->latest('created_at')
            ->first();
    }

    ...
}

I try to never delegate the Eloquent methods, because if I need ALL posts, I can simply call Post::all() and be done with it. No need to create a separate class for such a simple call. However, if I need to add some custom, more advanced constraints to the query, I will put them in a separate class. By doing it this way, I have a class (usually called Users, or Posts) that handles a collection of records. Also, that keeps my models pretty clean since I don't fill them up with random queries. Because of that, my models usually contain custom scopes, relationships, checkers such as ->isConfirmed(), ->isPublished() and methods that operate on a single model instance, such as $user->publish(Post $post), $user->confirm($token), ... Collection is handled at a different place and keeps my code tidy and clean. No interfaces, no fuss, no abstract classes. If some day I need to scale, I can unit-test a repository class, refactor and I'm done!

The way I implement repository pattern in Laravel
Share this

Subscribe to Josip Crnković