Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ali_rahimi_coder's avatar

Repository Pattern and How to Abstract Away Eloquent Completely

After some thought, I have come into conclusion that I need to use the repository pattern to abstract away database interactions from my Laravel application so that in the future I can:

  1. Extract services from my app and just rewrite the repositories to use an API without changing things in my actions
  2. There will come a time that I will need to cache my queries to the database, and using repository pattern can help a lot with that.

Now, I have read up on many articles on the repository pattern in Laravel, and they all seem to miss the point of the pattern! Almost all of them return Eloquent Models, which is convenient but stupid because it is just leaking the database logic to everywhere and allows for using things like $model->save() in the code base.

Now my questions:

  1. Is it a good idea to convert Eloquent Models to some DTO in the Repository? This way, I am sure that no one can use the eloquent models to perform database interactions outside the repositories.
  2. If using DTO is the way to go, how should I handle relationships?
0 likes
8 replies
ali_rahimi_coder's avatar

To illustrate my point here is some sample code:

// Models/User.php
class User
{
    protected $guarded = [];
}
// Dto/User.php
class User extends Spatie\DataTransferObject\DataTransferObject
{
    public readonly int $id
    public string $username;
    public string $password;
}
// Repositories/UserRepository.php
use Dto/User;

interface UserRepository
{
    public function getById(int $id): ?User;
}
// Repositories/UserRepositoryEloquent.php
use Dto/User;
use Models/User as UserModel;

class UserRepositoryEloquent implements UserRepository
{
    public function getById(int $id): ?User
    {
        $userModel = User::query()->where('id', $id)->first();
        return $userModel !== null ? $this->convertToDto($userModel) : null;
    }

    protected function convertToDto(UserModel $userModel): User
    {
        return new User($userModel->toArray());
    }
}

Now imagine that I have a UserSecuritySetting should I just give up on eloquent relations and foreign keys and just have a unsigned int column user_id that I will use to fetch security setting for user with specific id? If I do this, should I hydrate my User DTO with an attribute like securitySetting and attach the UserSecuritySetting DTO to it or is it clean enough to use them seperately?

martinbean's avatar

@ali_rahimi_coder If you’re going to use repositories then yes, you need to return some sort of domain object or entity, and not classes from the underlying ORM or persistence layer. For example, if you switch to Doctrine you’re no longer going to have Eloquent models. If you’re reading data from a JSON file or XML file, then you’re not going to have Eloquent models.

2 likes
ali_rahimi_coder's avatar

@martinbean Any thoughts about related data? Should I give up on trying to have for example a user object with a property orders that is a collection of orders and just use something like $orders = $orderRepository->getByUserId();? Over the past 3 years that I have been doing web development, I have been lost confidence in OOP. This is yet another situation that OOP design is not actually feasible.

martinbean's avatar

Any thoughts about related data? Should I give up on trying to have for example a user object with a property orders that is a collection of orders and just use something like $orders = $orderRepository->getByUserId();?

@ali_rahimi_coder A user repository should only be concerned with dealing with users. What objects relate to each other is a domain concern.

This is one of the downsides of the repository pattern. People like yourself (don’t worry: you’re not the first and won’t be the last) think to make “better” software they need to stick all their ORM logic in repositories, and then just waste time partly re-implementing everything an ORM like Eloquent offers them such as filtering, ordering, relations, etc. Then complain that they have repositories that aren’t quite as powerful as Eloquent but thousands of lines long because you implemented a method to get all users, then a method to get a paginated list of users, then a method to get a paginated list of users that satisfy particular conditions, then you now need to get a paginated list of users but with related records, and so on.

If you’re using Laravel, just use Eloquent. Your life will be much easier.

Over the past 3 years that I have been doing web development, I have been lost confidence in OOP. This is yet another situation that OOP design is not actually feasible.

I don’t really see how a repository not being an ORM is a problem with the entirety of OOP?

I think you really need to decide what you were problem you were trying to solve with repositories because repositories—like every other design pattern—are meant to solve problems. If you were just wanting to add a caching layer to expensive/regularly-used queries then wrap them in a query object class or something.

2 likes
ali_rahimi_coder's avatar

@Snapey Then what is the solution? Should I search for every eloquent interaction in my actions and add caching right in the action instead of repository pattern?

jlrdw's avatar

@ali_rahimi_coder you still use you model to retrieve the data, the repository just "encloses" a certain aspect of the app, i.e., a user repository. Have a look at: https://github.com/bestmomo/laravel5-5-example/blob/master/app/Repositories/UserRepository.php

But all can still be done with "fat" models.

Side note, OOP, MVC, patterns, etc is only for humans and the separating of code. The cpu doesn't care about that.

In fact you could write a whole app in a single php file verses MVC and OOP, the cpu will handle it the same. But just example.

I stopped worrying about patterns years ago in Java and just stuck to a combination of MVC and VC depending on what I was doing.

Much of a "repository" code can easily be handled with good query scopes, you just get a larger model (cpu don't care).

Bottom line is well written MVC is efficient, a repository pattern won't improve that.

2 likes

Please or to participate in this conversation.