Doctrine Lead Developer explains my WTFs

Školení, která pořádám

Some time ago, I have published the Doctrine 2 video tutorial. It is created from the point of view of a programmer who tried Doctrine for the first time after reading a tutorial and some parts of the documentation. And because I am the author of NotORM library which can solve the same task as Doctrine, the same tutorial is also created using my library.

The result of this tutorial is that I am not satisfied with Doctrine 2 and I can hardly imagine using it in my projects. Benjamin Eberlei, the Lead Developer of Doctrine found his time to answer my remarks and he has agreed with publishing the conversation.

Look at the video first or you would probably not understand what the conversation is about. You can also look at more detailed explanation of my WTFs (Czech).

1. Depends on Symfony

Benjamin: Only the usage of the Tools depend on Symfony\Component\Console, but that is shipped with the download. You can use the tools without the Symfony\Component\Console dependency. You show in the tutorial how to circumvent the console Symfony dependency.

Jakub: I know. It just seems a little bit weird to me if (a part of) some framework is bundled with Doctrine and some parts of Doctrine really depends on that framework.

Benjamin: Symfony\Component is a component framework. So you can ship your projects with only that parts.

2. Requires specifying proxy even if not used

Benjamin: Proxies are almost always used, normally you just don't realize it. We will however add a better default here using evil eval. Its not really a recommendation, its required to have properties private/protected (for lazy loading). Use __get/__set to save LOC at that point (IDEs generate getter/setter for you in no time).

3. Typo in annotation causes no error

Benjamin: Yay the /** vs /* errors are annoying. That is why I personally use XML for metadata mapping.

4. First character is missing in M:N table and column names

Benjamin: What version did you use? I have never seen this kind of bug. Did you report it?

Jakub: The bug is still there in current official release, I will report an issue.

5. New column is added at the end of the table

Benjamin: Where else would you add them? Behind the property specified by the Order of the Properties in the class? The updateSchema does an incremental update, not a regeneration. That is why its added at the end. You can recreate the whole thing and have it in order or just use schema tool to generate only the queries for you to execute (and modify them). This is all more work, in the end it doesnt really matter for rapid prototyping where the column is. You can fix it later.

Jakub: The columns should be added at the place where I specify them in the entity. Some database systems allow it (e.g. MySQL by AFTER clause in ALTER TABLE). I want to use updateSchema not only for rapid prototyping but for all changes even in production quality projects. I see updateSchema as one of the killer features of Doctrine but it is not perfect.

Benjamin: I add a feature request, sounds interesting.

6. One query for each loop iteration

Benjamin: One query per loop means you are using lazy load as you show and can easily optimize it with DQL.

Jakub: Is there a way to efficiently solve this task (print N newest articles with all their tags) in Doctrine 2? I don't see any.

Benjamin: There is the DoctrineExtensions Pagination. In 2.0 there is no other solution. But we plan to add a feature to do this in a two-step procedure. i) Load all articles and categories ii) load all tag collections that are not yet loaded in a single query. This would load all the data in two queries.

Jakub: Pagination extension is not effective. If your future solution would not read all the data but only the required rows then it would be great.

Benjamin: Why is the pagination extension not effective? For the SELECT query getting the tags along side it would only require 3 queries and load only the 10 articles from the frontpage and all their related tags. Yes its transfering more data again (join leading to multiple hydration of the same row), but the query count is still very low. The approach I have in mind works as yours in NotORM. Select all articles, then select ALL tags that are in any of the previously fetched articles using an IN query and then building the collections from there.

Jakub: Yes, the pagination extension is not effective because it transfers the same data again and again (article text for each tag). The optimal solution is provided by NotORM.

8. No LIMIT clause in DQL

Benjamin: There is an extension that allows you to efficiently paginate joined resultsets additionally we plan to add another feature (besides DQL fetch join) that allows to load several collections at once (eager) if requested, not per loop iteration.

Jakub: The eager feature will be hopefully the solution of this problem. Until that, there's no efficient solution.

9. One piece of data is transferred repetitively with DQL joins

Benjamin: That is indeed a problem, but not at all as huge as you describe. Transfering more data is still faster and more efficient than the N+1 problem. But the first usage is only the unoptimized one, since Doctrine 2 is an ORM isn't it nice something like PARTIAL exists at all? I know no other ORM that supports this. You could also do something like: $em->createQuery("SELECT a.id, a.headline, a.text FROM Article a ORDER BY a.published DESC")->getArrayResult() and just get an array. Very efficient if you need it.

Jakub: The PARTIAL feature can help to solve some issues but not all of them. I still may need to transfer one big data (e.g. article perex) and bunch of related items (e.g. article tags). Besides, I don't like PARTIAL because it requires more work from a programmer. NotORM has a much better feature - it can automatically transfer only those columns which are really used.

Benjamin: Yes thats the difference between an ORM and not an ORM. The ORM only works with full objects or in the case of Doctrine you can use the Array hydrator.

Jakub: I'm not sure if I've explained it well - NotORM has a feature which automatically transfers only the really used columns. So it's like PARTIAL in Doctrine but you don't need to write it by hand.

Benjamin: How does it work? I mean where do you know at line N that you only need columns a,b,c in the following lines N+1?

Jakub: It is described at Limiting selected columns.

10. No way to specify another column in M:N relation

Benjamin: M:N in an OO world means reference. A reference can't have additional information in the OO world. You can create a new entity for the M:N table and make it a N:1 1:M relationsship. With Doctrine 2.1 this will even have very powerful composite foreign key support.

Jakub: That's exactly what I'm talking about. One trivial change in the task means significant rewrite of the code using Doctrine. Good solution would require only a trivial modification (as in NotORM).

Benjamin: True you'd have to rewrite a lot. But normally you know up front if its a real many-to-many or a M:1 N:1.

Jakub: I'm not sure if you know it upfront, mainly with some agile form of development. For example you can create a shopping basket as M:N basket:product relation but then you realize that it would be nice to store also the number of products. Or you can store products in several categories - clearly M:N relation but then you realize that maybe some categories would be more important for a product than the others. This is quite common development pattern. With NotORM, these kind of changes are trivial. With Doctrine, it requires a massive rewrite.

Jakub Vrána, Seznámení s oblastí, 7.1.2011, comments: 8 (new: 0)

Comments

ikona Filip Procházka:

Byť porovnáváš dvě různé věci, rád vidím, že tvoje ostrá kritika přinesla podněty pro zlepšení :)

ikona Jakub Vrána OpenID:

Please write comments in English if possible.

Juraj Hríb:

Very interesting conversation. I hope it will bring some good changes into Doctrine. I found it very interesting and the features are really useful, but some bugs or missing features still make working with Doctrine not so easy. I tried NotORM too ofc - I think both of them have their pros and cons, so I just hope they both keep evolving and adding features.

dcooper:

Great stuff. Really constructive conversation.

Geoff:

Awesome.

Will be keeping a close eye on NotORM!

Jon:

Very interesting conversation, thank you both for the article. I am a fan of Propel, and am considering undertaking my next project using that software - partly because I need users to be able to build on their database, and for the ORM to generate the db-specific SQL to set up tables, constraints etc. If I were to use NotORM instead, how would you suggest I solve this problem?

Certainly, not having a class build phase is attractive, so I will have a play with NotORM. In the meantime, would you be interested in offering a comparison between your software and Propel? I've used older versions (1.2 and 1.3) for years, and so would be interested in a critical view.

ikona Jakub Vrána OpenID:

NotORM is not meant for data definition, it works just as data manipulation layer. Use direct PDO for it.

I don't plan making a comparison with Propel.

Jon:

> Use direct PDO for it.

Yes, although the create table statements for a particular schema might differ from one platform to another. I rather like the elegance of having one schema and letting the user generate the required SQL, rather than having to distribute, say, five SQL files.

Insert Comment

Input is understood as plain text but URLs will be converted to links and PHP code enclosed in <?php ?> will be highlighted.

Name: URL:

avatar © 2005-2014 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.