Using Repository Pattern in Laravel 5
https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/
Using Repository Pattern in Laravel 5
These days there is a lot of buzz about software design patterns, and one of the most frequently asked questions is “How can I use some pattern with some technology“. In the case of Laravel and the Repository pattern, I see often questions like “How I can use repository pattern in Laravel 4” or nowadays “..in Laravel 5”. Important thing you must remember is that design patterns do not depend on specific technology, framework or programming language.
Contents [hide]
Introduction
If you have really understood Repository Pattern then it does not matter what framework or programming language you are going to use. What is important is that you understand the principle behind the Repository pattern. Then you can implement it in whatever technology you want. With that in mind, let’s start with the definition of the Repository pattern:
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes.
Repository pattern separates the data access logic and maps it to the business entities in the business logic. Communication between the data access logic and the business logic is done through interfaces.
To put it simply, Repository pattern is a kind of container where data access logic is stored. It hides the details of data access logic from business logic. In other words, we allow business logic to access the data object without having knowledge of underlying data access architecture.
The separation of data access from business logic have many benefits. Some of them are:
Centralization of the data access logic makes code easier to maintain
Business and data access logic can be tested separately
Reduces duplication of code
A lower chance for making programming errors
It’s all about interfaces
Repository pattern is all about interfaces. An interface acts like a contract which specify what an concrete class must implement. Let’s think a little bit. If we have two data objects Actor and Film, what are common set of operations that can be applied to these two data objects? In most situations we want to have the following operations:
Get all records
Get paginated set of records
Create a new record
Get record by it’s primary key
Get record by some other attribute
Update a record
Delete a record
Can you see now how much duplicated code would we have if we implement this for each data object? Sure, for small projects it’s not a big problem, but for large scale applications it’s a bad news.
Now when we have defined common operations, we can create an interface:
Directory structure
Before we continue with creating concrete repository class that will implement this interface, let’s think a bit how we want to organise our code. Usually, when I create something, I like to think component way since I want to be able to reuse that code in other projects. My simple directory structure for the repositories component looks like this:
Inside src directory I have three other directories: Contracts, Eloquent and Exceptions. As you can see, the folder names are pretty convenient for what we want to put there. In Contracts folder we put interfaces, or contracts as we call them earlier. Eloquent folder contains abstract and concrete repository class that implements contract. In Exceptions folder we put exceptions classes.
Since we are creating a package we need to create composer.json file where we define a mapping for namespaces to specific directories, package dependencies and other package metadata. Here is the content of composer.json for this package:
As you can see, we mapped namespace Bosnadev\Repository to the src directory. Another thing, before we start to implement RepositoryInterface, since it is located in the Contracts folder, we need to set correct namespace for it:
We are now ready to start with the implementation of this contract.
A Repository Implementation
Using repositories enables us to query the data source for the data, map the data to a business entity and persist changes in the business entity to the data source:
First method in our contract is conveniently named all() . It’s duty is to fetch all records for the concrete entity. It accepts only one parameter $columns which must be an array. This parameter is used, as its name suggests, to specify what columns we want to fetch from the data source, and by default we fetch them all.
For specific entity, this method could look like this:
But we want to make it generic, so we can use it wherever we want:
In this case $this->model is an instance of Bosnadev\Models\Actor . Thus, somewhere in the repository we need to create a new instance of the given model. Here is one solution how you can implement this:
Since we declared class as abstract, it means it must be extended by concrete child class. By declaring model() method as abstract we force the user to implement this method in the concrete child class. For example:
Now we can implement the rest of the contract methods:
Pretty easy, right? Only thing left now is to inject ActorRepository in the ActorsController, or our business side of application:
Criteria Queries
As you can imagine, these basic actions are just enough for simple querying. For larger applications you’ll most definitely need to make some custom queries to fetch more specific data set defined by some criteria.
To achieve this, we begin with defining what child (clients) criteria must implement. In other words, we’ll create an abstract non instantiable class with just one method in it:
This method will hold criteria query which will be applied in the Repository class on the concrete entity. We also need to extend our Repository class a bit to cover criteria queries. But first, let’s create a new contract for the Repository class:
Now we can extend functionality of our Repository class by implementing CriteriaInterface contract:
Creating A New Criteria
With criteria queries, you can now organise your repositories more easily. Your repositories do not need to be thousands of lines long.
Your criteria class can look like this:
Using Criteria In The Controller
Now when we have simple criteria, let’s see how we can use it. There is a two ways how you can apply the criteria on the repository. First is by using pushCriteria() method:
This method is useful if you need to apply multiple criteria, you can stack them as you wish. However, if you need to apply just one criteria, you can use getByCriteria() method:
Package Installation
You can install this package by adding this dependency in your composer require section:
and just run composer update afterwards.
Conclusion
Using repositories in your application have multiple benefits. From basic things like reducing code duplication and preventing you to make programming errors to making you application easier to extend, test and maintain.
From architectural point of view you managed to separate concerns. Your controller doesn’t need to know how and where you store the data. Simple and beautiful. Abstract.
You can find this package on Github, where you can check for latest updates and bug fixes. I also plan to add new features like eager loading, caching and some configs so stay tuned by staring the repository. However, if you want to contribute in the development just fork the repository and send PR.
If you have any thoughts or suggestions, please let me know in the comment section bellow. See ya.
Credits
This package is largely inspired by this great package by @andersao. Here is another package I used as reference. Also, I find these articles very helpful:
Creating flexible Controllers in Laravel 4 using Repositories Laravel Repository Pattern The Repository Pattern in Action Laravel – Using Repository Pattern
Full Stack Developer at OLXWeb Developer. Geek. Systematic. Dreamer
SHARE THIS:
LIKE THIS:
RELATED
Deploy your Laravel 4 application on HerokuSeptember 16, 2014In "Laravel"
Using Repository Pattern In Laravel 5 - Eloquent Relations And Eager LoadingMarch 26, 2015In "Laravel"
Install Elasticsearch on Laravel HomesteadSeptember 12, 2014In "Laravel"
Posted onMarch 7, 2015AuthorMirza PasicCategoriesLaravelTagsdesign patterns, laravel
Great article! I think you drew in my package http://bit.ly/1EuZWyu 😉 . I liked the changes you made .
Actually, your package was inspiration for something I didn’t add yet (not completly at least). Request criteria queries 🙂 You gave me an idea how to improve that class. I’ll be working on that in the next few days 😉
This was initial inspiration for criteria queries https://github.com/anlutro/laravel-repository/blob/master/src/Criteria/SearchCriteria.php
Thanks! Will contribute to your project. 😉
Spir
What about adding a dynamic finder to the Repository class? ie, something like this: http://laravel.io/bin/Deyv9
Andreas Baader
How do I install this ?
Alen Abdula
Mirza svaka cast!!!
Pinoy Camper
How do you cater a join table?
I’m working on the part two of this tutorial which will cover eager loading, request criteria queries etc.
Tomasz Sawicki
Thanks for the article.
To fix: – Movie vs Film – “Only thing left now is to inject ActorRepository in the ActorsController” – the screen show FilmsController, and you’re not injecting repository, but model – “Using Criteria In The Controller” – you’re not injecting repository, but model
Thanks for the reply. My logic dictated me to use Movie as name of the Model, and I did at the beginning. But later I used Sakila sample DB to test queries, and they call it Film. That’s the conflict you see 🙂 I’ll update the article accordingly 😉
kowal
What about case if we need joins? You are using relation: one eloquent model to one repo…
ovcharenkovv
Please tell me why the project is positioned as a “fresh air in the PHP” need same enterprise approaches? What benefits we get using the repository pattern with active record?
codextends
How do you deal with Aggregates ? I’ve been trying to make Eloquent fit in an abstract repository pattern for more than a year. Ended up having Service class embedding several Repository classes, one for each model, which felt quite over engineered. Never found a satisfactory solution. Repository pattern is great, Eloquent is an awesome ORM, but I just feel the two don’t fit.
Jack
TL:DR – Most Laravel implementations of the Repository pattern are fundamentally flawed, and it rarely brings any actual benefit.
Got to agree with this, I’ve always struggled with relationships when trying to use the repository pattern – Something thats easy with Eloquent out of the box.
I’ve also ended up building services utilising multiple repositories when relationships are involved. In it’s simplest form, if in your application you want to create a movie, and when when creating it, you want to add relationships to the Movie model, (adding gene, actors etc…) I end up with a MovieRepositoryService, that injects MovieRepository, ActorRepository and GeneRepository and end up using each of their individual create methods inside the MovieRepositoryService’s create method, to create what from a Domain or Business perspective is a “complete” movie.
However this always felt like reinventing the wheel.
Another issue I’ve always had with most peoples implementation of the repository pattern in Laravel is it neglects one of the main goals of the Repository Pattern. Your application logic should not be tied to your data source’s implementation.
In the package described in the article an Eloquent object, or a collection of Eloquent objects is returned on all the ‘getters’. This ties our application logic to Eloquent if we use any of it’s specific features.
What if we wanted to move to Doctrine, or one of our data sources changes to a web API supplying json or XML, or any other source not using Eloquent?
One of the goals of the repository pattern is to enable you to swap out your data source, without needing to adjust your application logic for greater flexibility. To active this you can’t return implementation specific objects. You need raw data, either an array, or better yet, explicitly defined Entities that your repository populates from it’s specific implementation.
Data source -> Repository -> Entity -> Application uses entity
At most an Entity should know what Repository to use to get any of it’s relationships, however, again, we arrive at the conclusion of reinventing the wheel, Eloquent already knows this, and can already do this – So for the most part, we’re just duplicating Eloquent’s functionality, again.
If I have an Eloquent Movie objects, I can go Movie->actors(); and Eloquent it get the actors for me, to use the repository pattern correctly, I’d have to create a MovieEntity object, which has no direct ties to the data source (Is not a Eloquent object, or contains an Eloquent object) That contains the Repository for Movie, and each of it’s relationship’s repositories, to achieve the same results as Eloquent, it’s all already there…
Eloquent also provides the equivalent of the “Criteria” concept described in the articles package in the form of query scopes. In Eloquent, I can define a scopeHighestRated() method on my Movies model, and then use it like so: $moviesEloquentObject->highestRated()->get();
So we’re reinventing existing functionality, again…
Simply put, I’ve found most peoples implementation of the repository pattern in Laravel to be a fundamentally flawed exercise in trying to shove a large square through a small round hole. A design pattern is support to work for you, and solve your specific problems. If you find yourself questioning the pattern, or failing to see how you could implement it, or what benefit it’s going to give you, then the chances are it’s not the correct solution to your specific problem.
What does implementing the repository pattern actually get you? Okay you get to resolve dependancies out of the IOC container via an Interface, which everyone is telling you is a good thing and how it should be done. By WHY is that how it should be done? Because it makes your application loosely coupled. You can change the object injected without touching the classes it’s injected to, but whats the point if the object it’s self ties you to an implementation? You’ve not loosely coupled your application at all, cause as soon as you inject anything other than an Eloquent object, and you’ve used an Eloquent specific feature anywhere in your application (Just once!), it’s broke, so what was the point?
Theres an awful lot of buzz around repositories at the moment – I’d encourage people not use it just because it’s the “in thing” to do, and actually think about what it’s going to do for you, cause in most cases, with Laravel at least, I believe the answer will: Nothing.
/rant
I find myself wondering the same.
How would you implement an actually loosely-coupled repository pattern, allowing for swapping – for an example – MySQL+Eloquent with MongoDB+?
Jack
Hi budhajeewa
Thanks for you response, whilst it’s been a while since my original comment now, my overall opinion remains the same.
Laravel as a framework is not suited to the Repository Pattern, and this basically comes down to a decision of Taylor’s when he designed and built the framework.
Eloquent is an implementation of the Active Record design pattern, and is one that does not complement the Repository Pattern by definition. An Active Record implementation tightly couples your entities with your data layer, the concept in Laravel, is that Eloquent it’s self is your abstraction layer, as it allows a common interface to several database base solutions: http://laravel.com/docs/5.1/database#configuration
The Doctrine library (Especially when bundled with Symfony2) is an excellent example of a properly implemented Repository Pattern, but this is due to the underlying differences in design. Doctrine is a Data Mapper implementation of an ORM, fundamentally different to Eloquents Active Record implementation. The Data Mapper pattern strongly complements the Repository Pattern, and Repository generation comes as standard with the Doctrine package as a result.
I’m not saying one is better than an other, they each have their pro’s and con’s, but Laravel is an opinionated framework, Taylor would be the first to say so, and it comes bundled with an Active Record ORM (An excellent one infact!) However, this is not a suitable environment for the Repository pattern. (You can however, swap it out should you really want to)
There are ton’s of useful articles on Data Mapper vs Active Record for ORM’s if you want to learn more about the differences.
Let me know if you have any more specific queries!
Guido Contreras Woda
TL;DR:
I’d strongly suggest not using RepositoryInterface nor an abstract Repository class.
Here’s why…
Let’s talk abstraction through classification first. When you declare a RepositoryInterface, you are saying that all children of that interface (either abstract or concrete) will “be a Repository”. But Repository is a pattern, not a classification. So being a pattern has no real meaning but the motivation behind the pattern itself, that will be communicated through the name or namespace anyway. In other words, “being a Repository” could be conveyed by naming a class “FilmRepository” without the need for a classification, as being a Repository is fullfilling a certain pattern’s motivation, not being part of a certain group of things.
Now, let’s talk about abstraction through generalization. The way your blog post starts, it’s clear that you don’t have a real motivation of generalizing: you start off with a general idea, an interface, but you don’t really have use cases where this general idea came from. You assume that all children of this interface will need to provide at least those behaviors, and that’s where everything starts to fall apart. Generalization – or any kind of abstraction – shouldn’t start through assumption, it should (as Kent Beck says in XP) be used when it provides simplicity. By adhering to your interface, I’m forcing myself to some specific behavior, assuming I’ll need it in every Repository I ever need, and in exactly the way you created it. The comments you received are a strong hint towards that: people start wondering how they’ll solve their specific problems whilst also adhering to your contract. That’s because they don’t really need your contract, but they’re forcing their minds to it, and that created unnecesary complexity.
Now, let’s talk about your abstract class Repository. Here you decided to provide some implementation to the mix, because Eloquent behaves pretty much the same way in every class that inherits from it. This will bring a plethora of problems, and I can speak of them with confidence: I’ve done multiple projects with base Repository abstractions and Eloquent, and I’ve moved away from both.
First off, and I think this is the most important one, you’re missing the point of using an Active Record. AR brings a lot of power, flexibility and speed at the cost of having multiple responsibilities in those objects. If you use AR in its full potential, you’ll be able to access your data in a more flexible way, and you can build over it without the need for collaborators. Now, this is not againt the Repository pattern per se, but against a generic abstract Repository that does some things out-of-the-box.
The next thing that worries me is reuse through inheritance. Although we do it in Eloquent context, I strongly advice against any code reuse through inheritance: Always reuse through composition and collaboration. What if I need to do something slightly different on find() ? You’ll probably call parent::find. Then, if you add up a layer (say, cache, for example), you may end up doing multiple parent calls, and tracing that will be horrid. And don’t get me started on testing it!
A “common way to access Eloquent models” could be abstracted out to its own class (not abstract). This could help CRUD app makers in having standard access to any Eloquent model without worrying about exactly what it does. A FilmRepository could use this CrudQueryBuilder (I’m just throwing names here) as a collaborator to have generic access, without being coupled to it (or it’s methods’ signatures) through inheritance.
I do recommend declaring interfaces for each specific repository implementation. a FilmRepository could be implemented by an EloquentFilmRepository, and declare only what the app really needs from films: maybe you don’t list them all, as that would kill your server, and you only have a paginate and a find method.
Hope it helps! Cheers 🙂
Cătălin Georgescu
Any sample code to drive your point home? You made me curious.
Guido Contreras Woda
Hey! I’m back from vacations and swamped with work. I’ll make a gist when I get some free time!
In the meantime, what are you interested in exactly? My point was that reusability through inheritance and sharing types between Repositories wasn’t necessary, but if I were to make some sample code (like suggested maybe with the CrudQueryBuilder), it would look similar to the abstract Repository class implemented here. It’s not that much about the code itself, but about how and when you choose your abstractions, and how you reuse your code.
Cheers!
Cătălin Georgescu
I’m fairly familiar with the pattern implemented here, but I would be interested in a more flexible way to do this, because being constrained to always implement the methods when you create a new repository for a new entity is not very cool, though Mirza’s implementation helps a lot. So when you have some time an example would be welcome. And thanks for answering.
Will
Just replying here so I get notified if/when you post an example, thanks 🙂
One year ago and still no example =))) Talking is always the easiest part
ghostme
lol
Nguyễn Hiệp
strongly agree =))
Aldrin Marquez
ikr lmao
Bill Garrison
I would also be extremely interested in seeing an example of this. I’m still wrapping my head around repository pattern and examples help me a LOT
Naren Chitrakar
So what you are saying basically is that the inheritance should never be used to reuse stuff and be solely used to represent something totally non-generic..in the case above..not to query database. And it seems like you are suggesting to let eloquent classes do what they do best in the concrete class in itself. But does not that makes the whole point of replacable data access layer a moot point?
Guido Contreras Woda
That’s a great question! It always depends on context. If we use inheritance to reuse code, we are coupling very hard to that code: we statically couple to a concrete class by inheriting, and we dynamically adhere to represent its parent because Liskov. How awkward could it be to receive an instance of
ActorRepository
when you were expecting aFilmRepository
? Of course this gets solved by type hinting the concrete child where you need it, which makes evident my initial point: there is no real use for the shared type.Now, when I said Eloquent does this best, what I meant is that you don’t need the common ground, because if you look at the proposed abstract
Repository
, all methods act as a proxy to the Eloquent instance. This is what I also meant when I analyzed abstraction through generalization: this is not a generalization at all. There is no real use case, you are just building Repositories imagining what you’ll probably need from them. And, guess the acronym? Right! YAGNI.If you aim to go the full RAD, fast development lane with no care for architecture and testing, then you’re better off just using Eloquent through Facades, it will get you there faster. If you aim to use the Repository pattern to abstract your data access layer, my point is: you don’t need common ground between your repositories. Implement data access methods only when you need them, and avoid overuse of inheritance when you actually want to decouple.
And, if you want to go a step beyond, check out http://www.laraveldoctrine.org and hit me at our Slack channel, maybe I get you interested in trying something different than Eloquent. 😉
rocketshipinspace
Absolutely agree with you here. I am terrible at putting my thoughts to words, but you’ve just described my personal opinion/findings perfectly.
chown
it would be good if you can show us some proper way then like how writer described his point of view in details above?
Davide Pugliese
I was looking for info on this pattern and symfony and I read this post. I partially agree with you about simplicity, the idea here however, is more to put together blocks of code that have a common topic. So we are gonna have with files FrontControllers in my case that just redirect calls from the view to the Repository and then to the Model (Entity). Now, I get your point that in some cases this might not be needed and we add one more “layer of complexity” sort to speak. However, think of an application as big as Facebook like the one I am working on right now, and I can guarantee you that having the code as well organized as possible is a desirable thing. The code ends up being more reusable. You end up with more cohesive blocks of code.
Guido Contreras Woda
Hi, Davide! This is a two year old response, so I’ll try and remember what I was thinking back then.
I would never argue against using the Repository pattern, it’s a proven practice that helps projects organize their data access while also representing something in your domain. If used correctly, a repository can actually simplify interaction between domain services and entities.
What I argued back then, and I still believe in, is that you shouldn’t take these kind of generalizations and bake them into any project. You should know about them and you should be watching over your code so you can abstract if and when you need to.
Most frameworks provide these abstractions anyway, so maybe I’m just being picky. But in my experience, I’d rather postpone writing all of this code until I need it. The best decision you can make depends on the amount of information you have, and being early in the project, you know nothing. Don’t assume you’ll need it.
Albert Cloete
The way this article explains it is very similar to how Symfony implements it out of the box for you.
I agree with not writing things until you need them, but some basic things are very unlikely not be be used, like findById(), findAll(), etc. And even if you don’t initially put them in an interface, I’d suggest putting it in the interface as soon as you do create those methods, just to make sure all your repositories stay uniform, and act predictably.
My feeling is that repositories become a mess without contracts.
Tim Sims
Hi, how can you cache result (I’m adding a Cache layer on Repository ) with criteria queues?
Without criteria, I can easily generate a cache key by the params, so next time when I pass the same params I can generate the same key to fetch data from cache
But with criteria, I can’t access to those addiction params
Hi Tim, can you provide me an example for that?
Tim Sims
Hi, not sure how to write code in Disqus, so I use gist for better explanation
https://gist.github.com/timsims/d77167794cb9c0eeef90
Spir
I’m doing almost the same thing. But I use the decorator pattern to decorate the repository with an added cache feature. Check it out: http://laravel.io/bin/Nkb4e
thbt
These kinds of articles should really start with a note that explains that the Repository pattern isn’t something everyone should use in all their projects without thinking whether it’s beneficial for their particular situation. There is a tendency for some devs to try to apply every pattern they learn to every situation.
So keep in mind that using a repository adds a significant amount of complexity above simply using something like Eloquent directly, especially if you try to completely isolate the model so that all interactions have to be done through the repository. There are situations where you need that separating barrier and you really want a layer of isolation between your business logic and the data source. And the Repository is something that web devs should at least be aware of so they know to use it when they need it. But if your app doesn’t need that separation, adding it for no reason can make a simple app far more complicated than it needs to be.
Jay Official
Very True! couldn’t agree more
Mateus Gomes
I do agree that using Eloquent directly is far more simple. But it gets really bad when you decide to change from Eloquent to another ORM or don’t use an ORM at all. Please keep that in mind. Personally I prefer make things a little bit harder first so I won’t (possibly) waste a lot of time later.
isaackearl
Hi. I’m working on an API and realized recently that we need an abstraction layer for complex queries so we don’t have to use the models directly in our controller. Your repository package has caught my eye and I’ve started to work on implementing it.
I’m wondering if there is a way with criteria to pass in parameters? I want to create criteria but I want them to be a bit more flexible, and I don’t understand from the examples if this is possible.
For example I’m currently using eloquent scope queries to handle custom criteria. I want to move that logic to my repository through criteria… and I’m not sure if it is possible.
public function scopeBeforeId($query, $id) { if ($id > 0) { return $query->where(‘id’, ‘<', $id); } return $query; }
in the example I see a way to make a similiar criteria with a hardcoded $id… is it possible to pass variables to the criteria when you apply them?
second quesiton: Is it possible to load certain criteria on every load of a particular repository? To do that do I just override the constructor and add criteria there?
thanks! and great work.
tooleks
Yes, you can pass parameters into the criteria constructor method.
class LengthOverHours implements CriteriaInterface { private $hours;
public function __construct($hours) { $this->hours = $hours; }
public function apply($model, Repository $repository) { $query = $model->where(‘length’, ‘>’, $this->hours); return $query; } }
$this->film->pushCriteria(new LengthOverHours(2)); // Passing variable into the criteria. $films = $this->film->all();
Luddinus
So, in this case, what would you need “MovieRepository” instead of use always a BaseRepository passing a model?
Sanmen1593
Hey… I’m just reading your article and trying to learn. Just have some question… The laravel ORM is not the same thing? What are the differences? What adventages que have of using this or directly use the ORM in our controllers?
Thanks!
Ngoc Phan
Perfect article
a little bug
LengthOverTwoHours implements CriteriaInterface
should be
LengthOverTwoHours extends Criteria
Teej Ten
Thank you soooooooo much Ngoc! I couldn’t get an error when I used this code just a bunch of html output gibberish so it was super hard to debug. It is so obvious now why things were not working! Thanks once again, best regards! Tim
Partoo Huang
Thanks for your great post! I use the pattern but found a weird issue,here is the link on laravel.io:
http://laravel.io/forum/06-26-2015-weird-query-results-when-using-repository-pattern
Looking forward to your reply.
jrean
Hey @mirzap:disqus, fantastic article and package implementation.
“Eloquent folder contains abstract and concrete repository class that implements contract…”
But:
“class ActorRepository extends Repository {”
Is within the “<?php namespace AppRepositories;"
It should be in "<?php namespace AppRepositoriesEloquent;" isn't it?
It's a bit confusing to me.
You can set whatever namespace you want for the child class of concrete repository implementation. It doesn’t matter. You only need to follow PSR-4 guidelines. For example, if you use this repository package in your own package, you will probably have namespace something like this: ComponentNameRepositoriesComponentRepository
Midix
Thanks, this is great, although I tend to agree with Guido Contreras Woda.
Also, I too miss request criteria helpers for cases when our business analyst goes crazy and demands that we have a filter for each column in a grid, and then also total count of records before and after filtering. This means running COUNT(*) before filtering, then applying filters dynamically, then adding one more COUNT(*) query. Yack… but business analysts are always right, therefore developers have no much choice.
Actually, I have implemented criteria builder in my controller helpers for sending simple “a = b” and “a like b” queries from Javascript JSON API and then on the server extracting query expressions from GET and dynamically adding them to Eloquent query builder, and also applying pagination over the filtered records.
But my solution seems like a quick&dirty hack. It would be great to have some out-of-the box solution done right way with repository pattern.
Cuong Duc
Just a small question: Why does your LengthOverTwoHours class does not implement all methods of CriteriaInterface?
developerbmw
“In other words, we allow business logic to access the data object without having knowledge of underlying data access architecture.”
Yet your Repositories return Eloquent objects.
Mateus Gomes
There is a difference between business logic and application logic. Could not find any business login in the above examples.
And, the Eloquent model is returned because it is injected in the repository class. It could return anything we want.
developerbmw
If your repositories return Eloquent objects then code that uses the repository can easily become reliant on Eloquent. For example (taken from the code above):
public function index() { $criteria = new LengthOverTwoHours(); return Response::json($this->film->getByCriteria($criteria)->all()); }
It calls the all() method which is Eloquent-specific. What happens if you decide to change your repository to, for example, one that does raw SQL queries and returns POPOs or arrays? Suddenly your calls to all() won’t work.
Mateus Gomes
The repository should return anything that is injected to it. If I am using Eloquent ORM, I will inject an eloquent model on it and use it within my repository.
In your example, in fact the all() method is an known method from eloquent, but I cant easily implement it on my repository so I can do this:
$this->filmRepository->getByCriteria($criteria)->all();
class FilmRepository {
public function getByCriteria() }
Nguyễn Hiệp
so if using Repository DP, we need to handle business logic completely beyond the application layer (in controller ?)
Repository::find()
returns an Eloquent model object. If I wanted to update that model, why shouldn’t I directly call-> save()
on that model? Why should I pass that model toRepository::update()
? Should we never touch Eloquent Models directly?Mateus Gomes
Yes! We should never touch Eloquent Models directly. And that’s why you pass it as dependency injection. For example:
actor = $actor;
}
public function save()
{
// here is where you implement the logic to save.
// since Eloquent ORM already does this, we’ll use it.
$actor->save();
}
}
Later on you decide that your Actor shouldn’t be save on an RDBMS using Eloquent ORM but instead, you want to save in a .txt in your system. You simple do:
class ActorRepository {
private $actor;
function __construct(Actor $actor)
{
$this->actor = $actor;
}
public function save()
{
// here is where you implement the logic to save on a .txt file
}
}
LazyLoaded
Procrastination kept me from reading this for a very long time while I built something similar, a lot of useful insights here. My approach was a little bit different though. I have this idea that REST Controllers should behave like these Repositories. I used annotations and something similar to this pattern to build a generic abstract controller that a concrete controller can extend. The abstract controller comes with generic routes like find, findById, store, count, delete, … (the usual REST actions) Like this pattern, the concrete controller is required to define a model, but in addition to that using annotations, define a url prefix. Your insights are definitely going to help me improve my implementation.
I particularly like the criteria. My approach uses url parameters to apply criteria like sort, pagination, selection. I basically use this and hack it onto the Builder class. The criteria interface looks much cleaner Cheers, great post
EDIT: One question though – How would you deal with relationships using this pattern?
Eden Chen
Hi, I’m now trying to use the repository mode in laravel, but I’m wondering what if I use multiple models in one repository, for instance, by default the PostRepository would only be related to the Post model, but if I would try to fetch a certain user’s posts in the PostRepository, clearly I would need to use the User model to make the query condition, so should I just add a function like getUserPosts() in the PostRepository class and inject the User model to achieve my purpose(fetch a certain user’s posts) or there’re other better ways?
ghostme
I think you can communicate between repositories. So basically after defining both the PostRepository and UserRepository class, you can call the getUserPost() method of the UserRepository class from the PostRepository
Janzen Zarzoso
I have questions:
1. Shouldn’t the LengthOverTwoHours extend the Criteria and not implement the CriteriaInterface because it’s defining the “apply” method from Criteria and not defining any from CriteriaInterface?
2. In Criteria you declared “apply” method with params but when declared in LengthOverTwoHours the $repository isn’t used anywhere. Shouldn’t that method omit the reference to the $repository instead? If not, why do you need the reference to that object?
Alex Muller de Jesus Sousa
I do not know if this answers one of your questions, but the ‘applyCriteria’ and ‘getByCriteria’ methods use the second parameter of the ‘apply’ method of the ‘Criteria’ classes.
P3terSoft
I want to do a query like this
Select * From Users Where state IN ‘Miami, California’ Order By id limit 12
for example, how can do with this repo in my controller ?
René
At the makeModel() method in your abstract Repository class you start typing this at some point:
return $this->model = $model->newQuery();
This doesn’t work and needs to be:
return $this->model = $model;
Like you do in the beginning. (this is also the way it is in your github package)
Jordan Plamondon
Hi, why should I use your package instead of this one? https://github.com/andersao/l5-repository
bhaskar
Hi guys,
Can we call pushCriteria() two times in controller function? I want reports filtered by message and notification respectively. function index() { $this->mnr->pushCriteria(new FilterMnrByMessage()); $msg_reports = $this->reports->all();
$this->mnr->pushCriteria(new FilterMnrByNotification()); $notification_reports = $this->reports->all(); } Thanks in advance. BJ
Liam Andrew Maddison
CHANGE TO:
function index() { $this->mnr->pushCriteria(new FilterMnrByMessage()); $this->mnr->pushCriteria(new FilterMnrByNotification()); $notification_reports = $this->reports->all(); }
Dave
I got also this problem. Don’t know the fix either.
Very nice article! But why is the $app variable needed?
Majd Alhayek
Great package! Just cerious, why would you need Repository passed to the apply method in the Criteria abstract class? Also, I recommend extracting the pagenate methout out of the RepositoryIntrface. I would make a new interface and put that method by itself in there “i.e RepositoryPagenateInterface”. Not all implementation need pagination.
Alex Muller de Jesus Sousa
Hello friends,
I followed all the steps but in the end worked only the classes of repositories. The criteria classes did not work, so I discovered that the ‘LengthOverTwoHours’ class was implementing the ‘CriteriaInterface’ interface when it should extend the ‘Criteria’ class. I noticed that the ‘Criteria’ class is an abstract class that represents the ‘Criteria’ class types that are passed by parameter in the ‘getByCriteria’ and ‘pushCriiteria’ methods of the ‘Repository’ class, so I changed the ‘LengthOverTwoHours’ class and It worked. I followed the same logic for the other criteria classes I created and everything works perfectly.
Ivana Momcilovic
Hi, I have a specific situation where I need to execute one Criteria over the Repository and after that I need to get some results and call different SearchCriteria over the same Repo. Is there any chance to add something like removeCriteria as oposite of pushCriteria?
Roman Fandeev
Thanks! Very informative!
Preetam
I have used your pattern is my project, its really very fantastic an time saving. I have a doubt, can you tell me how can i use join queries using this repository?
Manish Shrestha
You can use criteria for that.
Bishal Paudel
Do you actually require Criteria Interface and its implementations? I would go by creating a bunch of criterias which are simple functions. I may need to group them by class, which is still unnecessary, especially when PHP does not require us to be pure OO, and provides plenty of room for functional programming.
Sergej Fomin
Hello! Thank you for this article! Where can I can i read about this method “find” you are using here: “return $this->model->find($id, $columns);” ?
There are some problems with your code. The “apply()” method has been added in “Criteria” class and not in “CriteriaInterface” class, so it is not possible to implement Criteria ” class.
Could you please help me develop a relationship search for another model (related table)? I think we could do more sophisticated searches using “with ()” method, etc.
Firman Taruna Nugraha
your presentation is good man, thanks
Rob Bennett
Hi, just came across this, great pattern, thanks!
Just a quick question, shouldn’t the resetScope method of the repository set the criteria collection to an empty collection? Doing this allows for the query scope to be reset on the model instance within a session.
Do you have any more resources available to understand how eager loading works within the repository pattern?
discadaniel
class LengthOverTwoHours implements CriteriaInterface must become ===> class LengthOverTwoHours extends Criteria
Sonar
very inspired article.
But how can i do for updating multiple data with Repository Pattern ?
Hi, I translated this article into Chinese, can I post to my blog?
Deep Dhaliwal
Hello sir, I need your help,
I implemented the repository pattern like suggested in this tutorial, I am getting a problem.
$current_candidateList = $this->candidateListRepository->find($request->get(‘current_candidate_list_id’)); $new_candidateList = $this->candidateListRepository->find($request->get(‘new_candidate_list_id’)); //
* return $current_candidateList works * return $new_candidateList does not works * if i remove logic to retrieve $current_candidateList, $new_candidateList start working
In other words, find is not working once it is used twice in single method
Post navigation
Last updated