DEV Community

Cover image for The best Laravel repositories solution in 30 lines of code
Sjors van Dongen
Sjors van Dongen

Posted on • Edited on

13 6

The best Laravel repositories solution in 30 lines of code

How a 30 lines piece of script can finally provide a good solution for a repository pattern in Laravel apps.

The problem

Laravel provides a beautiful way to query your database through Eloquent, an Object Relational Mapping. The moment your project becomes larger there starts to rise a problem. The difference between entities and models start to become vague. Let me give you an example:

We have two methods, one called findByName and one called concatName.

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'products';
/**
* Find published product by name
* @param string $name
* @return Product
*/
public function findByName(string $name): Product
{
return $this->whereName($name)
->whereIsPublished(1)
->first();
}
/**
* Say we want to concat the name and a category name
* @return string
*/
public function concatName(): string
{
return $this->attributes['name'] . ' - ' . $this->attributes['category_name'];
}
}
view raw Product.php hosted with ❤ by GitHub

What we are actually doing here is mixing methods for retrieving (collections of) Products, with methods for instances of 1 Product.

A much cleaner approach would be to separate them into 2 different classes. One for obtaining instances and collections of models and one for the model definition itself. You might call this the repository pattern.

Wanted situation

There are several libraries for Laravel to start using the repository pattern. One thing that's a huge disadvantage to me is that you can't use Eloquent functions on your repositories. Since Eloquent is a huge reason why I am using Laravel I still wanted to be able to use Eloquent on repositories for obtaining models.

The solution

I created a very simple piece of code which enables all the features of Eloquent on your repositories. This way you can use the repository pattern in a way which is fun and keep your models and your repositories clean! A repository with the use of laravel-repositories would look like this:

<?php
namespace App\Repositories;
use App\Models\Product;
use MrAtiebatie\Repository;
use Illuminate\Database\Eloquent\Model;
/**
* Product repository
*/
class ProductRepository extends Model
{
use Repository;
/**
* The model being queried.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $model;
/**
* Constructor
*/
public function __construct()
{
$this->model = app(Product::class);
}
/**
* Find published products by SKU
* @param {int} $sku
* @return {Product}
*/
public function findBySku(int $sku): Product {
return this->whereIsPublished(1)
->whereSku($sku)
->first();
}
}

By the use of the Repository trait and the $model property, laravel-repositories knows which model to query and where to use Eloquent and where to use your own repository methods. 

With this definition of the repository we can now use it in our controllers, jobs, commands etc:

<?php
/**
* In your routes/web.php
*/
$router->get('/', function (\App\Repositories\ProductRepository $productRepo) {
// Use any Eloquent feature directly
$productRepo->all()->dd();
// Use your custom repository methods
echo $productRepo->findBySku(12345)->name;
// You can even query relations
echo $productRepo->first()->category;
});
view raw web.php hosted with ❤ by GitHub

Go try it out and leave me some feedback for improvement!

Link to Github repository


Tired of writing copy that doesn't convert? Try https://targetaudience.app

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (5)

Collapse
 
mratiebatie profile image
Sjors van Dongen

I’m not sure power is the right word for this. I agree that your code will be better separated when you don’t use eloquent in combination with a repository but I’ve tried a couple repository packages for Laravel which all provide some methods for basic usage. The amount of code I had to write for the options I wanted was too much for me and more of a disadvantage than advantage. Eloquent is such a great tool and it felt like I abandoned it. That’s why I came up with this.

Collapse
 
dj_syclone profile image
Dj Syclone

If you are mostly interested in creating custom functions to attach to your Models then you could just use scopes. E.g. laravel.com/docs/5.8/eloquent#loca...

Collapse
 
mratiebatie profile image
Sjors van Dongen

Scopes are a good way to create custom query functions indeed. Still, I don't think this should be part of an instance of a Product entity though. When an instance of a Product has a method called 'findByName' it is in the wrong place in my opinion.

Collapse
 
miguelf11 profile image
Miguel Figueira • Edited

This is awesome, thanks for sharing!

But I have a doubt, How you define scopes in the repository?
Or how to reuse some queries to emulate the use of scopes, I want to define this scopes inside the repository :)

Collapse
 
mratiebatie profile image
Sjors van Dongen

Thanks for your positive comment :) Unfortunately, it's currently not possible to put your scopes in your repositories. What you could do for now is put your scopes in a separate trait and include them in your model.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay