Short story: How I started with Laravel
I used to spend many years with Wordpress because my clients wanted a low-cost solution with the ability to update the site themselves. For small companies that is a typical requirement.
However, as Wordpress was becoming bigger and more popular, more people were building their own plugins, the development started to be unbearable. The sites became unstable, insecure, huge and slow.
One day, after spending hours trying to find an error in one of my sites, I said to myself, ENOUGH! I did some research about other options and I found several popular frameworks. I almost immediately liked Laravel and started to learn a completely new approach to web development.
The last straw that broke the camel’s back
One of the main OOP principles is SRP — Single Responsibility Principle. Well, Wordpress uses SoC at “level expert”. Sarcasm obviously!
One of the websites I managed, but did not create, used a testimonial plugin. The author of the plugin thought it would be a good idea to connect groups of testimonials with the article categories. What happened after deleting the category? The whole home page collapsed in a very strange way. All the blocks were placed one over another on the right side. One simple change in categories destroyed the whole website. Amazing code architecture of Wordpress!
Well designed code vs poor code. Money — time — clients
Many programmers feel a lot of pressure from their clients. Obviously, a client wants the job to be quick and cheap. The lower the price, the better but only at the beginning. What is the cost of poorly written code? It can be actually huge! You, as a professional have to be able to explain it better to your client or employer.
This image shows a typical comparison between poor code and well-designed code.
Well, thought-through code requires much more work and thinking at the beginning. The initial results are not that quick and the price is higher. However, the proper code will be easy to extend or update. It is worth it for the programmer and also for the client in a long term.
An experience with poorly designed code
I already experienced the “red situation” several times. A client asked me to update some else’s messy code. It was clear to me, that it just wasn’t possible. The programmer himself told the client that he was not able to update it anymore and he had given up.
Extremely long functions without proper naming conventions. Ifs inside fors inside other whiles inside more ifs. How much time would a new programmer need to understand such function? It is just not worth it. We had to start all over on the green line.
Recently, I have started to work on a new project with similar bad concepts. PHP, SQL calls, Html, all in one file. Separation of Concerns died for this project. The guys who created the code don’t even bother separating the HTML header. Each page has its own header. Now, if a client asks to change the favicon, they would end up updating 30+ files. Have they never heard of the DRY principle?
I talked about it with the client, and he said that he knew the programmers, and they had been in business for a long time, so they couldn’t be bad. Well, if a programmer keeps doing the same mistakes over and over, it doesn’t really matter if his experience counts in years or decades. The code is bad, period.
OOP rules will help you create better code
The OOP rules are proved to work well, and they truly help you write better code. However, it is very difficult to learn how to actually apply them. The principles are quite clear, but unfortunately, it is often very unintuitive how to follow them. A friend of mine, who works as a senior programmer at Microsoft has been the most important source of knowledge and tips. The internet was not helpful enough anymore. A person who can advise directly on your use case is way more valuable.
We won’t explain the main principles here, but rather show some examples on how to actually use OOP effectively.
Here are a few most important terms:
Some of these terms obviously overlap, but they will help you search for deeper knowledge. Especially the design patterns require some experience if and where to use them. You, as a programmer, have to decide for the proper compromise among many criteria such as complexity, time, code readability, program speed, a pace of the development, team cooperation etc.
Start your design with layers; your life will become much easier!
One of the most important programming principles is an architectural pattern (1, 2) — simply Layers. Let’s think of what each layer we typically need does. Is there any good approach that fits most standard situations of a typical web application?
Possible layers of a typical web application.
1. Routing. A user or an API client calls a certain url and expects something to happen. Our application has to recognize the url. That is done by a routing class. In Laravel, there is a great routing system with a lot of helpful features, 1. VueJs also includes a great routing mechanism, 1.
2. The application checks important properties of the call. Does it have proper headers? Is the session active? Is the user logged in? Is the call authorized? Does the user have proper permissions to use this call?
All kinds of call validations are managed by middleware. In Laravel, you can create as many middleware’s as needed. Many are prepared for you right away.
3. Laravel creates a Request object encapsulating all the input data. Laravel sends the Request object into a Controller.
Now, we are in a Controller, which is a kind of an entry point of our application. At the end of the controller’s function, the app is supposed to return some data. Between the entry point and the return statement, we have a business logic doing some magic which our client wants.
Note: Controller is a place where most of the tutorials for Laravel end. For trivial examples, it doesn’t make sense to build more layers. However, because of that, new programmers have a very hard time learning something more advanced. I went through this struggle myself as well.
4. A controller is supposed to validate the incoming data, call a service doing some business logic, and then form a response in the desired format (e.g. a json or a blade view).
5. Data validation is an extremely important task. Many programmers don’t pay enough attention to it creating insecure applications. SQL injection is just one of the well-known security threats.
Remember that you should never trust the client’s data. Always validate them, even when they come from your own front-end client. Remember that the API may be used by other applications.
6. After the data is validated, we are ready to call a service. The business logic should be separated from the controller and if possible, also separated from the framework itself. It is easier to unit test your app if classes do not depend on the framework. An example of the recommendation is to avoid using resolve (‘ClassName’); Prefer inversion of control or passing dependencies to the lower layers manually.
What is business logic? 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.
7. 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
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.
8. 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. E.g. UserDataMapper needs functions such as getByUserName(), isRoot(), etc.
9. 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 #1
We 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!