The article is a translation of the note Why Doctrine ORM is not suited for PHP by Lucas Corbeaux.I know that the title of this article is like trolling. But it is not, it is just a statement of fact. I'm not trying to say that Doctrine is a bad library or that it should not be used. I'm just saying that it is bad for PHP, and if you do not take this moment into account and use it incorrectly, you can face serious problems.
Doctrine inspired by Hibernate ORM
Let's go back to the 2000s. Java is very popular and one of the most frequently used Java libraries is ORM Hibernate. It came with its own HQL query language and was beloved by the Java community. Hibernate helped Java objects and relational tables coexist.
Doctrine was inspired by the concepts of Hibernate and wanted to bring them into the world of PHP.
')
Differences between Java and PHP
But PHP is not Java, and one very important conclusion can be drawn from this. A Java application lives much longer than a PHP request.
The ORM must consider data integrity between all users. For a long time, every change in the database should be reflected for all objects. This is one of the reasons why ORM is so complex.
And that is why the ORM patterns are mostly not needed by PHP. Since HTTP is a stateless protocol, you don’t need to maintain data consistency between all calls.
Problem sessions
Of course, you can tell me that this is not true. You can use sessions to store objects between requests, and then you need a way to maintain their integrity. This is a reasonable argument. That's just the serialization of entities in Doctrine is pretty tricky and can lead to serious problems.
Identity Map is useless in a stateless environment.
Identity Map is part of Doctrine that supports entity uniqueness. If you, for example, request an entity with ID 4 twice, then you will get the same object both times.
At first glance it looks like a great idea. But what is the essence of isolated execution?
- If your code is well structured, then you will not need to request the same entity twice. Instead, you use Dependency Injection;
- If you change the data, it is because you received a POST request. When receiving a POST request, it is considered good practice to immediately perform a redirect. There is no need to "update" objects.
It seems to me that the doctrine Identity Map is useful only in case of a bad design. Or in a very rare and special case.
UnitOfWork too complicated
UnitOfWork is one of the main parts of the Doctrine ORM. I do not say that it is useless, but it is too complicated. I have already talked about sessions and the problem of serialization. Entity management is a complex thing, and I can accept the complexity of implementation. This moment is quite difficult to realize.
But what I cannot reconcile with is the fact that most of the difficulties arose due to the “lazy” loading and change tracking policies.
“Lazy” loading is meaningless
In a stateless environment, lazy loading is bad practice. I have nothing to add to this. It may be possible to find instances when this slightly increased productivity, but this happens very rarely. So why then is this one of the central concepts in Doctrine ORM?
Almost every time I talk to a team using Doctrine, they admit that they had problems due to autoloading abuse. And this happens, even with experienced developers.
For one of my clients, I even wrote a logger specifically to detect and delete using lazy downloads. This is just a glaring example of uselessness.
Change Tracking Policies
Why do we need such a complex system to ensure data integrity? We are surrounded by "without saving the state"! If our application has a good architecture, then the data does not change in random places. Except in rare cases (logging, updating the last connection time, etc.), we just need to change the data on the POST request. After that we immediately redirect the user to another page.
So why do we need such a complex system? To hide a poorly designed application?
EntityManager too much magic
The global EntityManager behaves like a Facade pattern that works with other ORM objects. This is a very powerful tool that can be called anywhere in the code.
However, I firmly believe that in a well-designed application, EntityManager should be used only in three cases:
- When loading for configuration
- In your "factories", Service Manager or Dependency Injector to initialize the necessary objects.
- In some repositories, if you need to create a SQL query
In other places it is not necessary to use it.
Still an interesting and powerful library
For clarity, I will repeat the second time: I am not saying that Doctrine ORM is useless or that you should not use it. What worries me most is that the library imposes bad habits.
- Entity Map allows developers to be careless with the injection of dependencies and, accordingly, instances of entities.
- Lazy loading works too magically and hides performance problems until it is too late. This is especially true for developers with a bit of experience. But experienced developers sometimes also fall into this trap, because sometimes it is so tempting not to bother using fetch-join.
- EntityManager allows you to do anything, anywhere. Convenient, but very far from good practices.
What's next?
The author of the original article has material for a few more articles about Doctrine. For example, about the ODM extension or about generators. In addition, it accepts applications in the comments .