philthathril's avatar

Models, repository pattern and Eloquent

Hi. Long time PHP-developer, but fairly new to Laravel and using the repository pattern. I have read plenty of articles, conversations and blogs about repositories. What I just don't seem to understand is when not to use them.

For example, I have a User model with a UserRepository interface and an EloquentUserRepository implementation of that interface.

// model
class User extends Eloquent {
    public function someMethod() {...}
}

// controller
$user = $this->userRepository->findByUsername(...);
$user->someMethod();

What I don't understand is if the User model is really an Eloquent model, but I'm abstracting with an extra layer (my repository), is the above call to someMethod() breaking that pattern? If the repository is essentially hiding the "ORM" portion, should I be calling Eloquent/ORM-related methods on model?

Any tips on how to interact with my models and repositories would be appreciated.

0 likes
46 replies
michaeldyrynda's avatar

If you're going to use repositories, you can still have methods on the model, but you shouldn't be accessing them directly on the model - rather accessing them via the repository.

You don't have to expose someMethod on the repository, though. You may have a method that calls someMethod on the model in order to give you some other result. The repository should just return a collection, so you could return the result of someMethod in that collection.

Have a read over Shawn's excellent post about the repository pattern, see if that helps provide some clarity.

2 likes
philthathril's avatar

@deringer Thanks for the quick reply. I have actually read that article (several times to wrap my head around it). I think you touched upon something that may help me understand what the controller knows. Lemme confirm. Basically at the controller level, should the only thing it knows about the object returning from the repository is the data structure?

Also, in Shawn's post, he provides this example:

$member = Member::register($email, $password);

In this case, is Member not the model? If it is the model, isn't it breaking the pattern?

Also, he mentioned specifically that the repository shouldn't have "create" functionality because it's just a collection, so how is that handled? He gave an example of possibly using a factory, but would that be on the model or the repository or elsewhere?

jekinney's avatar

I love the pattern and almost always use it. Yes I had the same question at first too.

I look at it as the model is your database connection with maybe a couple of attributes. Your queries and depending on if your not using other patterns like command bus, control the business logic. Your interface is the contract ensuring methods will be available no matter what type of database and repository is used.

It can be more lines of code up front depending on your scope of the project, no doubt about that. For your user model example I generally utilize at least two repositories. One for authentication like loging in, creating accounts etc, and a general user repository for permission checks and lists etc.

Also creating a abstract base repository for your generic queries allows a more rapid development.

1 like
Appkr's avatar

According to my humble experience, by leveraging Repositories, I was able to decouple Controllers from Models. Now the Repositories depends on Eloquent Models. And then I was able to decouple Repositories from Eloquent Models by leveraging Interface.

9 likes
jekinney's avatar

To answer your create method question, utilizing repository pattern and the command bus pattern together. The command bus is to perform the create and update implementation and execution. So in your controllers you execute the command instead of using creating that method in the repository. If implemented per the structure which isn't laravel specific it will in effect perform the create and or update method.

3 likes
jekinney's avatar

To answer your model question the typical use is using a constructor to inject the model. It shouldn't matter if it's a Eloquent model or a Mongo model or a file model. Just so it has access to a model to get an object or collection. So if eloquent doesn't support mongo (which it doesn't) you'll only have to code the model to interact with your current repository. Remember Eloquent is extended into the typical laravel model that behind the scenes give you the query builder. So for the mongo implementation you would need to set that up in a base model, contracts and interfaces much like Eloquent (or a third party package).

1 like
JeffreyWay's avatar

Or, if your project isn't too complex, don't use repositories. People act like, if Eloquent shows its face in your controller, you're going to hell. :) Not true.

25 likes
MrMoto9000's avatar

I think this answer pretty much says everything you need to know. SOLID is a set of principles, not laws. You won't always be able to adhere to them, and you're not supposed to. People can get over zealous trying to keep the SOLID gods happy, and even get angry when others don't.

If you know why you're not adhering to specific principles of SOLID (for example, you know that your persistence later isn't going to change) then using the benefits of Eloquent's active record pattern is fine.

It's about making the right decision for your project.

Edit: I see Jeffrey has summed up this point very succinctly here, too. I completely agree:

https://laracasts.com/lessons/the-repository-trap-and-other-ramblings

You shouldn't be creating Repositories just because you've read that's the right thing to do. The repository pattern is a very powerful pattern, and has some great benefits, but it's not always needed. You should educate yourself on the pros and cons of approaches, and make an informed decision on a per project basis.

Clausi's avatar

Hi,

I do have exactly the same question. Could someone elaborate on the

$member = Member::register($email, $password);

in Shawn's post? Looks like a named constructor (using late static binding) to me. With Shawn's strategy, if Member is an Eloquent model, would I implement such a function in the model to instantiate a new Member?

pmall's avatar

If the repository is essentially hiding the "ORM" portion, should I be calling Eloquent/ORM-related methods on model?

Yes because with other implementations you will use another orm so you can use its methods. A repository method is like a black box nobody care what happens inside the only matter is its returning consistent data whatever the implementation is.

3 likes
jekinney's avatar

@Clausi Yes you can if you want. In this case for a create method is probably being handled in the model under the register method. So I would "assume" the create logic is being performed in the model.

Shawn's Post is great, but he does contradict himself by throwing that in example of using the register method when he states in his opinion a repository should only return objects and/or collections and not (basically) write data to the database. So a one way street of only receiving.

As @JeffreyWay stated though it always depends on your needs and scope.

I agree with Shawn's statement of swapping out databases is what people focus on as a benefit and not other benefits. Many interviews I have had people always ask that question on how I would build a project to allow for this. I believe that question is generally dumb. While I am sure it has happened, I have yet to have a client ever call me up and ask to move 10's of thousands of rows of data from MySql to MsSql (which would be pretty simple in Eloquent really, change the driver and migrate). If things work and meet expectations changes to the core of the project aren't cost effective when it works.

philthathril's avatar

@JeffreyWay As of right this moment, my current project is not that large, but I'm wanting to develop it in anticipation that it will grow large. I want to decouple my controllers and models, not so that I can swap out the persistence storage (because that will probably never happen), but because I want it to remain flexible and easily testable.

I've seen some examples where people define the structures that come back from repositories - essential data transfer objects. But I've not really seen examples of this on the laravel site or this site. Does anyone have opinions for whether repositories return DTOs so that you're truly committing to a specific contract instead of just what eloquent returns? Obviously, this means that you'd always have to convert all your returned objects to these custom objects, but I've written a mechanism before that's done that.

Thanks to all for the great input so far!

1 like
philthathril's avatar

@jekinney Yes, it does map your database columns to object properties. However, if I'm wanting to add a layer of abstraction so that I can guarantee a structure - even if the backend object changes - then I don't really want to rely on that implementation. Or, if I don't want to couple my results with my business models, then I'd want to use an agreed-upon contract (DTO)..... I think. Am I over-thinking this? I'm wanting my application to use a restful design, so I'm being cautious about tying my business models to my api results.

1 like
nolros's avatar

@philthathril

A simple way to think about the need for the two is as follows:

A model really is about data manipulation and management to ensure your data is stored and retrieved in the optimal format for you application i.e. “models” the data.

Whereas a repository is like a controller for your model abstracting logic that has nothing to do with data manipulation.

EDIT

I forgot to add that you can many more abstraction layers between your model and your controller depending on the size of your dev team, application, etc. Example you could have specification patterns, value objects, entity objects, assertion, commands, etc. so there is no absolute boundaries, really depends on what you developing, how large. In the case of DDD it will had a lot more classes, complexity and abstraction, but will create a highly modular OO application.

The example that resonates with me the the following:

// model 
$cabinet = new Cabinet ();

// this would be a bad model call as a cabinet does not know or should not know how to open
// it does not describe the cabinet but instead we are now bleeding the operation of the cabinet and how it operates 
// you might be tempted to say yes a cabinet should know this functionality, but think about it for a second
// if you ordered a cabinet would you have a checkbox for openDrawer ... nope you would have size, # drawer, etc.
$cabinet->openDrawer();

// openDrawer(); could be a repository method as we are now adding smarts to the model 
$this->cabinetRepository->openDrawer();

// Cabinet Repository example
public function openDrawer()
{
    if($cabinet->status === true)
    {
        return ‘cabinet is open ’
    }
}


// in cabinet model you could assert the sku pattern using a Sku value object type assertion 
/**
 * @param $sku
 * @return mixed
 */
public function getSkuAttribute($sku )
{
    return Sku::fromNative( $sku )->toString();
}
    
/**
 * @param Sku $sku
 */
public function setSkuAttribute(Sku $sku )
{
    $this->attributes['sku'] = $sku->toString();
} 

btw, if you look at the industry pundits description of models and repository patterns you will see that differ from Eloquent and Laravel therefore some of the confusion about lines demarcation. In DDD models are closer to business logic / representation vs. data mapper.

Final example - email address:

User model would be concerned about the format, assertion, validation of email i.e. xxx#xxx.com

whereas reporistory would be concerned about richer level logic i.e.

userOfEmail($email);

authUsingEmail($email);

rolesOfEmail($email); // questionable user repo, but you get the idea

2 likes
nolros's avatar

@JarekTkaczyk it could be in your model for a smaller application, but here is why I don’t think it should be or at least why I wouldn’t put it in my model and again just my thinking.

First, model should be persistence logic i.e. refers to the characteristic of state that outlives the process that created it. In this example, the cabinet might care about which drawer is open or close i.e. state, but the action of opening or closing is not a persistence state, but rather a richer level of logic that would be suited elsewhere i.e. something that can open and close drawers e.g. a repo, command or some sort of service. Example:

I would have state in my model

$getState = Cabinet::drawState($model, $whichDrawer); /// open or close 

// or a mutator of some sort
 protected function setTopDrawerStateAttribute($drawer )
{
        $this->attributes[‘top_draw_state’] = ($drawer === ‘open’ ) ? true : false;
}

and like I said the standard cabinet does not open it itself, but also even if it did how would I store openDrawer()? As it leads to a state of persistence but in itself is not a state. Logical read would be something along the lines of:

$result = UserActionService::openDrawer($cabinetModel, $whichDrawer)->cabinet();

//I could have some machine that opens drawers, but even then it would be another product / model:

$actionResult = Robot2020::openDrawer($cabinetModel, $whichDrawer)->cabinet();

//OR in my CabinetRepo, but even then I would most likely place it in a repo of a user or 
// something that can perform the action
$result = $this->cabinetRepo->openDrawer($model, $whichDrawer);

3 likes
JarekTkaczyk's avatar

@nolros I'm not refering to Eloquent right now, abut general principles.

What would this method look like:

UserActionService::openDrawer($cabinetModel, $whichDrawer)
->cabinet(); // and what's this?
nolros's avatar

@JarekTkaczyk then I'm not following you? the post question was models vs. repo so I was talking Eloquent. Maybe I'm missing something, what is your question ?

JarekTkaczyk's avatar

@nolros Oh, yes, original question was about model vs repo. But in the meantime you gone far from it with your examples, so I wasn't referring to the original q anymore :)

I wonder about the way you see it. Your examples and ideas are pretty much always original and a bit extraordinary, thus I'm interested.

nolros's avatar

@JarekTkaczyk lol you are kind man for wording it the way you did :) The reality is I'm always learning and listening and often post thoughts and opinions to get feedback. Also "original" often equals inexperienced and complex, please don't take it as arrogance on my part. In my opinion the only way to learn is to put stuff out there and have people critique or guide me. My attitude is be humble and keep learning as long as people want to teach me :)

1 like
cm's avatar

I spend the whole day trying to wrap my head around repositories in Laravel and just when I thought I understood them, I found another point of criticism. I read Shawn's post, this discussion here, and for sure, watched the videos on Laracasts.

Let me make a bold statement:

The original idea of repositories is pointless within Laravel, if you use Eloquent for all your models.

In the video "Repositories Simplified", the idea is to separate the controller from the ORM. So instead of doing

$orders = Order::all();

You'd do:

$orders = $orderRepository->all();

If I use an interface, I can now implement my DbRepository or ArrayRepository, RedisRepository or whatnot. But what happens inside your repository? You call:

$this->model->all();

Such a repository requires an Eloquent model and won't work without one. So, if you truly want to swap out the implementation details of your persistence layer, you also need a model that doesn't depend on Eloquent.

But if you use a model without Eloquent, why would you use Laravel at all? It seems like L5's Model class makes heavy use of Eloquent features, although it's probably possible to create your own base model.

Anyways, try a thought experiment. If things like these:

Order::all();
Order::find(1);
Order::create(...);

were just a different way to write

$orderRepository->all();
$orderRepository->find(1);
$orderRepository->create(...);

all of a sudden, Eloquent just looks like a repository. Also, try to read Shawn's post again with this though experiment in mind:"Using the static function of a model is the same as a repository, e. g. it means we're working with the collection of this model". Then suddenly, it seems that there's no need to use repositories at all as long as your models rely on Eloquent.

For sure, this doesn't mean that one shouldn't keep his controllers super-slim, but the same can be achieved with Command Bus or Services.

Again, my point is: If your models all extend L5's Model class or use Eloquent, a repository is redundant.

Or to put it another way: If you don't separate your models from Eloquent, your repositories don't achieve any abstractions at all, because the dependency is still there.

Comments?

10 likes
cm's avatar

To go back to the original question, with my above post in mind, I'd answer this as follows @philthathril :

For example, I have a User model with a UserRepository interface and an EloquentUserRepository implementation of that interface.

With my above thinking, you actually have a UserEloquent model as you stated.

What I don't understand is if the User model is really an Eloquent model, but I'm abstracting with an extra layer (my repository), is the above call to someMethod() breaking that pattern?

No, it's not breaking the pattern, as long as someMethod() doesn't use Eloquent functions. Think about it this way: Shawn talks about a repository being a collection of entities.

Now it depends: Is someMethod() part of that collection? If so, it goes to the repository (or you make a static function, which would be the same as a repository, see above post).

If someMethod() operates on a single object, like $person->getHairColor(); or $math->calculateResult(), this would still be a function of the model.

Also, in Shawn's post, he provides this example: $member = Member::register($email, $password); In this case, is Member not the model? If it is the model, isn't it breaking the pattern?

It is the model and he is not breaking the pattern. Why? Because a Member knows how to create itself. Or to put it another way: You create a member and then add it to the collection. The collection (= repository) is not responsible for creating members, because it's not a factory.

Also, he mentioned specifically that the repository shouldn't have "create" functionality because it's just a collection, so how is that handled? He gave an example of possibly using a factory, but would that be on the model or the repository or elsewhere?

You simply create the member like so: $member = new Member(). Member::register() is a bit confusing, because it uses a static function. This is how Jeff does it in many tutorials. With my above post in mind, it would be contradictory (if you always treat static functions like Member::whatever() as "working with the collection").

But I've not really seen examples of this on the laravel site or this site. Does anyone have opinions for whether repositories return DTOs so that you're truly committing to a specific contract instead of just what eloquent returns?

If you want real abstraction, you probably have to use something like DTOs or DAOs. You probably need one model to work with the ORM and one which contains the business logic. Otherwise, as stated before, your repository depends on Eloquent.

1 like
Clausi's avatar

@cm In your first post I don't quite understand why the repository would require an Eloquent model. You should be able to implement the repository with any Laravel Collection object, which gives you the $this->object->all(). For testing you might not be interested in persistence, and a collection based on arrays might suffice. But as this is very new to me I might be missing something.

cm's avatar

@Clausi Ah that's where the dynamic ->all(); comes from.

Anyways: My point isn't that it's impossible to write a repository without Eloquent, but merely that all models usually extend Eloquent (L4) or extend Model (L5) and that the repositories I've seen in the tutorials use Eloquent features of the model.

Now, let's assume a repository doesn't use Eloquent and only uses the Query Builder. Your repositories can now be swapped out for another implementation. Thing is, your models cannot be exchanged that easily, because usually they extend Eloquent.

My point is: If your models use Eloquent, the abstraction you wanted to achieve through a repository isn't necessary, because your models are still tied to the ORM. If you want to use a repository, you need to get rid of the Eloquent dependency in your models as well. Otherwise, repositories are pointless.

1 like
JarekTkaczyk's avatar

@cm I think you misunderstand the repository idea. If you use models (in Laravel context, that is Eloquent models) then you have EloquentRepository that wraps the model and implements the interface you can rely on. But you can use Redis, Query\Builder or whatever else instead, and you will have RedisRepository, DbRepository, WhaeverRepository implementing the same interface.

If you have models, then you use Eloquent - no other way. But you don't need to. You can use only Query\Builder, or anything else, without touching Eloquent.

In fact Eloquent itself could be used as a repository-like instance - just implemnt the same interface in your model and use it directly. I'm not saying it's a way of handling things according to the coding principles, but Eloquent is an anti-pattern anyway :)

Also, the repository might work for you by wrapping your domain needs and translating them into data storage language. For example you can define in your repository methods like findUserByEmailOrPhone, getInactiveUsers, findLatestMessageForUser and so on.

cm's avatar

@JarekTkaczyk Thanks for tuning in.

For example you can define in your repository methods like findUserByEmailOrPhone, getInactiveUsers, findLatestMessageForUser and so on.

This is actually a very legitimate use. Make similar requests over and over again and wrap them in a function. But why would I use a repository class for that if I could make a static function on my Eloquent model and use it like so User::findUserByEmailOrPhone();?

If you use models (in Laravel context, that is Eloquent models) then you have EloquentRepository that wraps the model and implements the interface you can rely on. But you can use Redis, Query\Builder or whatever else instead, and you will have RedisRepository, DbRepository, WhaeverRepository implementing the same interface.

Yes, I understood that, but my point is different: I don't criticize the repository pattern, but the use of Eloquent together with the repository pattern, which doesn't give any benefit.

Example:

You use a FileRepository, which is not database related. It offers the same stuff like your other repositories and does some nasty file crunching under the hood.

Now, you have User extends Eloquent as your model. Now what? You tried to use repositories to get rid of the database, but then your entire model still depends on the database. What have you gained?

JarekTkaczyk's avatar

@cm That's what I said above - you can use Eloquent directly as your repository creature, just implement the interface in your SomeModel extends Eloquent.

But this Now, you have User extends Eloquent as your model. Now what? make no sense. What model do you mean? Eloquent is Active Record, not modelling tool for describing your domain. It may work like this in mot cases, because most likely your app depends on the DB. Otherwise there is no single reason for using Eloquent.

cm's avatar

@JarekTkaczyk

you can use Eloquent directly as your repository creature

I will probably do this, because I don't see a point in repositories and I won't stop using Eloquent, because I always will use a DB.

But this whole discussion is about when to choose the repository pattern at all. Let me try it this way:

Why does Jeff use a repository in Larabook, e. g. for the users (https://github.com/foxted/Larabook/tree/master/app/Larabook/Users) if his User model extends Eloquent?

Next

Please or to participate in this conversation.