Most of the time, your application will need CRUD operations.
The service class will take the incoming data and save it to the database.
Or, it will read the data, put it into the desired format, and return back.
If anything more complex is needed, the service class will call other services.
If the service needs access to a database, it calls a Repository class.
Repository handles all calls for a database.
The layers above do not know how the data is stored.
You simply call$userRepository->getById(1);And that’s it.
It must not matter for the service, whether your DB is MySQL, NoSQL or data are saved somewhere in the cloud via API.
Laravel native models are very powerful and can save some time for creating simple applications.
However, they violate several OOP rules.
For this reason, I decided to create my own system, and completely get rid of the Active Record (anti) pattern.
I will explain this idea later in the article.
A repository calls a DataMapper that knows how to get data from the database.
The DataMapper class is database-specific.
It has to be created separately for each type of DB (mySql, noSql, files).
However, its interface should be exactly the same.
My DataMappers always have these common public functions:public function getById($id, $attributes = ‘*’);public function getBySlug($slug, $attributes = ‘*’);public function patchById (IModel $model, $selectedAttributes);public function deleteById ($id);public function search (array $criteria, $attributes = ‘*’);public function store (IModel &$model);If any DataMapper require some special functions, they are implemented within the inhereted class.
UserDataMapper needs functions such as getByUserName(), isRoot(), etc.
DataMapper prepares data for the Laravel Query Builder.
This is the lowest layer of the application.
The query builder returns data or throws an exception if something fails.
Important rule: No class or function should skip layers!.It creates tight coupling, and ultimately leads to problems with testing and distributing work among your teammates.
This rule is really important, however, even Laravel supports breaking it in a horrible way.
Example: Laravel Validator is a powerful tool.
No question about it.
However, it allows you to create a validation rule like this:‘state’ => ‘exists:states’.
It looks simple and elegant.
However, from the OOP rules perspective, it is a horrible concept.
It connects your top layer with the bottom layer being the SQL database.
On top of that, it connects your validation with the actual name of the database column.
This is extremely tight coupling that will ultimately kick you in the back.
Once you go this path, you almost certainly end up painfully rewriting a huge portion of your code in the future.
On top of that, you are allowed to do even a worse thing with this concept.
You can validate user’s data like this (pseudo code):IF Validator( ‘email’ => ‘exists:emails’) == False THEN createNewUser()Why is this code dangerous?.What if the email appears in the DB before your code reaches the save() function?.It will most likely not happen 99% of the time, but when your app gets busier serving more clients, you have to expect this kind of situation happens.
Your app would crash without warning.
The validation passes, the email is not taken, but the data will not be saved, the email is already there.
Conclusions for Part #1We have introduced important basic concepts of the object-oriented programming for PHP/Laravel.
The concepts are however of a general nature and can be applied in any type of code.
Following a few basic OOP rules will help you write much better code, better in terms of readability, security, and extensibility.
See you next time at Part #2!.