Repository in PHP [Design pattern with examples]
Coding (Php 7.x)
Add some spice to the love story with your PHP framework introducing another layer.
What is the Repository Pattern?
During the last few months, I went through a few design patterns that we can use in our preferred programming language, especially if that is PHP,
The goal of using these patterns is to improve the quality of our code.
In this article, I will describe the characteristics of the Repository pattern.
The Repository pattern is used to create a middle layer between a data layer (which can be a database, a file, a CSV, etc) and your controllers.
It is mainly used on large-scale applications, where there will be dozens of references to a single data layer.
It gives the ability to change the data layer without affecting any code in your controller.
The design pattern series
This article is part of the series “Design Pattern in PHP [with examples]”
The episodes published so far are:
- Factory Method Pattern
- Decorator Design Pattern
- Observer Design Pattern
- Repository Pattern
- Strategy Pattern
Repository in MVC architectures
What is MVC?
The repository pattern probably is best known for its widespread use within MVC applications.
For this reason, make sense to give a little summary of what MVC architecture is.
Model–View–Controller is a software design pattern, it is used to separate the different logic of a web application in their own element,
If the code is separated according to what it does it will be much easier to manage it.
Using an MVC separation has proved to be one of the best ways to manage and make your application reliable.
Let’s have a look at the single elements, shall we?
Model
All the code that is related to the business logic and the data of the application resides inside the models.
If you have a database with a user table and you want to retrieve all the users that have signed up within the last month you would do the query inside the model.
View
This is quite straightforward,
Here is where your data will show up and turn visible to the users, in here you will have your HTML.
Controller
In a certain way, controllers are responsible for doing the hard work,
They are positioned right in the middle between the Model and the View and their responsibility is to process the data coming from the database (so they communicate with the Model) and send the response to the Views
If you had already a taste of PHP frameworks you would know that the majority of them follow the MVC pattern.
If instead you are new to this concept and want to step a little bit have a read at the basics of PHP.
A simple application
Now that you know what MVC stands for and what the duties of controllers and models let’s code a little example the demonstrate where you would use this pattern.
In a normal application the flow works like this:
The users reach a view or an endpoint requesting some data, this request needs to be processed by a controller that in the majority of cases will communicate with a source of data.
Usually, that would be a database.
PHP provides several ways to “talk” with a DB,
The classic one is by using the PHP command.
In this case, the simple selection will look like this
$mysqli->query("SELECT * FROM City", MYSQLI_USE_RESULT);
This method is not as effective though and you still need to take care of preparing the statement to avoid SQL injection and other security overheads you may want to avoid.
Another method to communicate with a database if you code in PHP can be to use PDO.
Now, PDO is an interface that stays in the middle between your code and the database and makes communication with the database easier even though you still need to think of the security problem that working with a database requires.
Here is how the same select statement may look using PDO
$db->query('SELECT * FROM `city`, PDO::FETCH_ASSOC);
PHP framework such as Laravel, CodeIgniter, or CakePHP provides their own helpers.
In the case of the most popular framework nowadays (Laravel), this is called Eloquent,
Eloquent is an O.R.M. (Object-relational mapping) it converts the data between different types of systems.
The benefit of working with Eloquent is that managing these communications is much easier, the databases' commands are faster to write, and all work with PDO and prepared statements in the background.
Here is how the previous query would be written using Laravel or the illuminate database:
$cites = DB::table('cities)->get();
This is the link to the GitHub repository containing the package given by the creators https://github.com/illuminate/database.
Let’s write something a bit more complex:
App\City::where('population','>=', 1000000) ->orderBy('name', 'desc') ->take(10) ->get(); ->map(function($city)){ return [ 'id' => $city->id 'name' => $city->name 'population' => $city->population ] };
In this case, the user of our application is looking at the first 10 cities in alphabetical order with a population of more than a million citizens and we are mapping the result so we show the id, the name, and the number of people living in each city.
Not so difficult but a little more time-consuming to write
The scalability problem
Imagine that this query lives in a controller,
According to an MVC application as we described above a user hit a view, that sends a request to a controller that elaborates this logic to send to the model.
Albeit this is a proven way to code there are a few issues that are brought up by this method.
What if another request sent by another user on another controller needs to perform the same exact query?
In that case, we would have duplication on our code and in case we what to edit it we must remember to edit both places (I wrote both but it can easily be more than 2,5 or 100 places).
Another problem is that if in the future we decide not to use this method again but we want to get the info via another ORM or even a CSV file it won’t be possible.
We need to edit the code hoping not to break anything while the same problem occurs next time we want to update or change the data layer.
The solution
The solution as you might have guessed by the title of this article one of the solution and the one we are going to use today is the repository pattern,
This is not like the other patterns we have discussed in this series.
Usually, when web developers talk about design patterns they must mention (you go straight to hell if you don’t) the Gang of four and their 23 patterns.
The Repository though is not a pattern included in the book.
If I have to guess a reason I would say that it is because the repository is not so easy to categorize.
It may be considered structural because of its similarity with the Facade pattern but at the same time, it is kinda creational because with some easy addition can look like a Factory.
Anyway, how do you implement it?
Let’s consider the last query we saw, it currently dwells in the controller, just in the middle between our view (or API request) and our data layer (the model).
The first thing we want to do is to remove it from there while adding a new layer to our application.
This is how it will look like
class CityRepository { public function getCrowdedCities() { return App\City::where('population','>=', 1000000) ->orderBy('name', 'desc') ->take(10) ->get(); ->map(function($city)){ return [ 'id' => $city->id 'name' => $city->name 'population' => $city->population ] }; } }
You might want to add a namespace, use referred classes on top of the declaration, add type hinting, probably inject the variable inside the method instead of hard code the number, add docblock, etc in your code.
But for now, let's simplify the snipped as much as possible.
What you would do now is edit the controller and refer the this method
class CityController { public function showCrowdedCities() { $crowdedCities = $this->CityRepository->getCrowdedCities(); return $crowdedCities; } }
If the query was on the controller it would be already improved.
The functionality now is very descriptive, the whole method is just two lines. ready to be automated tested.
What if one day I have to add this same query from another endpoint or another controller whatsoever?
class AnotherController { public function anotherMethod() { // do some stuff here $crowdedCities = $this->CityRepository->getCrowdedCities(); // do some more stuff here } }
I just copied and pasted the same line of code in another place and I am going to get the data I need.
What if, I need to show only 5 records rather than the 10 I am showing currently?
If you haven’t guessed already here is another bonus for using the repository pattern.
Let’s refactor the code inside our Repository class.
public function getCrowdedCities(int $populationAmount = 1000000, int $recordShown = 10) { return App\City::where('population','>=', $populationAmount) ->orderBy('name', 'desc') ->take($recordShown) ->get(); ->map(function($city)){ return [ 'id' => $city->id 'name' => $city->name 'population' => $city->population ] }; }
I have added variable names within the query instead of hard-code the numbers, I allowed these numbers to get in via the use of dependency injection that I have type hinted (in this case I only want integers) and add two default values as they were in the beginning.
Now I can update the controller.
public function showCrowdedCities() { $crowdedCities = $this->CityRepository->getCrowdedCities(1000000, 5); return $crowdedCities; }
What I will get from here is only five records of my 1000000-or-more cities.
What is beautiful is that the other controllers that refer to the repository will still work as usual.
There are hundreds of ways we can improve and improve the scalability of this code but that's out of the scope of this article.
Conclusion
I was at a PHP conference a couple of weeks ago (nerdy stuff I know) and one of the topics that come up in our conversation was the fact that people saw programming in different ways.
For some of them, it was the easiest thing to do, others believed that it requires special abilities to learn a skill as difficult as coding.
As always the truth is in the middle, some were born with super-developed brains, and secretly have the StackOverflow landing page as the default page when they open their browser, for the rest of us need a lot of patience and dedication to become good developers.
Reading this blog and learning more is part of this process.
Today you have unlocked and you can now implement this technique that is going to make your web development job easier.
Interested in learning more?
There are dozens and dozens of other articles available to you,
If you missed it, you can start the series from the beginning by reading about the Factory Pattern.
If you want more about core PHP you can read one of the most advanced articles about the new features of PHP 7.4.