I come on it, late, but I think it’s still the subject.
I tried Doctrine recently while I’m dig in Symfony. After some weeks, I see Doctrine like a shiny mess, uncomfortable as dumb.
That’s say, and because I’m not an opinionated developper (wink), let me argument :
1/ Lack of manipulation methods
The available methods to manipulate entities are the minimum vital methods.
For all the rest, you remain on the query bulder, which is very verbose.
For example, there is no method to get a list of entities by their identifiers.
You first have to know the identifier of your entity class, which is stored and accessible only in the ClassMetadata Object, which is accesible only IN the Repository, or from the EntityManager.
// Work
$repository->get(1);
// Does not work
$repository->get([1, 2]);
// Does not work, getClassMetadata is protected (but why?)
$repository->findBy([$repository->getClassMetadata()->getIdentifier() => [1, 2]]);
// So let's extend the RepositoryEntity class -> DIY
Even in the ZF1 DB there was quickest shortcut (ZF1 is from 2006).
2/ Array’s hell
Most of the basic methods never return a collection: you have to deal with arrays. It sucks.
$repository->findBy(..) // array
$repository->findAll(..) // array
Even you can use minimum-featured collections with undocumented gymnastic, there is no Entity specific collection anyway (nor in laminas-db it seems…). Modern
3/ Dangerous foundations
Everything about the ORM rely on a messy 250 lines public function createEntity(), that deal with multiple dimensionnals arrays.
The logic of Doctrine ORM anyway is to maintain an in-memory registry of all your loaded entities (using spl_object_hash/spl_object_id for object loaded in memory, or by getting their primary key(s), or by verify their persistence in the DB itself). So, once you try to deal with mass amount of data, you will have to flush Doctrine (with $em->clear()
), to make him release your memory (and so forget its own logic too).
Most of the few methods you can call do not specify what type of argument they wait for, and some accept arguments that interface shall not.
Forget about strict typing.
4/ Miss of clear hydration process
You cannot easily hydrate/populate an entity, because your entities classes extend nothing (from Doctrine). All the hydrators take only a PDO statement as argument. This is very unconfortable.
$data = [ ['name' => 'Any'], ['name' => 'Other'] ];
foreach($data as $row) {
$entity = new Entity();
// What to do ?
Because your entities extend nothing, the populate/hydrator feature use Reflexion to set the values… It’s sad but, let’s agree, it’s working. I would have preferred better non-tricky solution as the populate() method used by lamninas/db.
So, painful while you need to import data. Who doesn’t need ?
5/ Not maleable
All rely on PDO anyway. If tomorow you want to implement a relationnal database which is not suported by PDO, then you can just fork doctrine/orm and rewrite all (or, yes, fork PDO). This is not the case in laminas/db, and in most of the dbal I know.
6/ Poor care about performances
The auto-loaded relations are interesting, be very not flexibles. There is no performances concern at all (just look Doctrine make one by one request everytime you load a reference). I did not find any method to force loading all references of a loaded entity set (array), all in one request. DQL and its join trick seems not a solution and it will slow down everything once your tables grow.
7/ Founded and locked on a tiny stone base.
At the top of everything, you got the Persistence abstraction, which is like a 2nd abstraction level to define the minimum methods you can have to deal with a database, without more details, enjoy, this is all you got to manipulate a table.
Also because it’s locked, it’s a totaly useless things that delay any new feature release, and slow you down when you look at the availales methods in your IDE. And it’s completely illogical to have it while your are so dependant on things like PDO.
I think I’ve listed the most shocking points I’ve seen. There is dozen others I have after my little experience, they should be implemented but can be easily fixed without having to dig deeply (like you cannot set the identifier value you want on an auto-incremented primary field)
Beside of this, there are some good points : the annotations, the command line tools are very helpfull (schema:update feel like the light at the middle of the dark forest), the entity reference proxy.
But the good point are very small comparing to the daily hell you have with it, where you, everytime, constantly, hesitate between writing 10 lines of code that you will surely copy-paste later, or abstract/extend everything because, anyway, you will need it.
I searched, in vain, for “extensions” of Doctrine that include all the missing features that every others DBAL have (yes, really).
Doctrine 3 has been announced 2 years ago. I look at the diff between the master (v3) branch and the 2.7, and I realize there is no improvements for making it more confortable. This update will more look like a move of the classes, with few improvement, and degradation too (say good bye to entity merge feature).
From my point of view (let’s opinionated now), Doctrine is a reliable, very old, software, without being a Swiss Army knife, bogged down in its legacy. It’s a trap, and because I do not find solutions to all of this, I’m looking to run out from it.
I will really, honestly, appreciate any comment that goes to find solutions, without the need to rewrite all
Also, may I mistake in some points ?