この記事のタイトルはトローリングに似ていることを知っています。 しかし、これはそうではなく、単なる事実の表明です。 私はDoctrineが悪いライブラリであるとか、使用すべきではないと言っているのではありません。 PHPには適していないと言っているだけです。この点を無視して誤って使用すると、深刻な問題が発生する可能性があります。
Hibernate ORMに触発されたDoctrine
2000年代に戻りましょう。 Javaは非常に人気があり、最もよく使用されるJavaライブラリの1つはORM Hibernateです。 独自のHQLクエリ言語がバンドルされており、Javaコミュニティに愛されていました。 Hibernateは、Javaオブジェクトとリレーショナルテーブルの共存を支援しました。
DoctrineはHibernateの概念に触発され、それらをPHPの世界に持ち込みたいと考えました。
JavaとPHPの違い
しかし、PHPはJavaではありません。このことから非常に重要な結論を導き出すことができます。 Javaアプリケーションは、PHPリクエストよりもずっと長く生きます。
ORMは、すべてのユーザー間のデータの整合性を考慮する必要があります。 時間の経過とともに、データベースの各変更はすべてのオブジェクトに反映されるはずです。 これが、ORMが非常に複雑な理由の1つです。
そして、それがORMパターンがPHPでほとんど必要ない理由です。 HTTPプロトコルはステートレスプロトコルであるため、すべての呼び出し間でデータの一貫性を維持する必要はありません。
セッションの問題
もちろん、これは真実ではないと言うことができます。 セッションを使用してリクエスト間でオブジェクトを保存できます。その後、オブジェクトの整合性を維持する方法が必要です。 これは合理的な議論です。 しかし、Doctrineでのエンティティのシリアル化は非常にトリッキーであり、深刻な問題につながる可能性があります。
IDマップはステートレス環境では役に立たない
アイデンティティマップはエンティティの一意性を維持するDoctrineの一部です。 たとえば、ID 4のエンティティを2回リクエストすると、同じオブジェクトを2回取得します。
一見、素晴らしいアイデアのように見えます。 しかし、孤立した実行の本質は何ですか?
- コードが適切に構成されている場合、同じエンティティを2回クエリする必要はありません。 代わりに、依存性注入を使用します。
- データを変更するのは、POST要求を受け取ったためです。 POSTリクエストを受信した場合、すぐにリダイレクトを実行することをお勧めします。 オブジェクトを「更新」する必要はありません。
ドクトリンアイデンティティマップは、デザインが不十分な場合にのみ有用であるように思えます。 または非常にまれで特別な場合。
UnitOfWorkが複雑すぎます
UnitOfWorkはDoctrine ORMの主要部分の1つです。 役に立たないと言っているわけではありませんが、複雑すぎます。 セッションとシリアル化の問題についてはすでに説明しました。 エンティティ管理は複雑なものであり、実装の複雑さに我慢できます。 この点を実現するのはかなり困難です。
しかし、私が我慢できないのは、「遅延」ロードおよび変更追跡ポリシーのためにほとんどの困難が生じたという事実です。
遅延読み込みは無意味です
「ステートレス」環境では、遅延読み込みは悪い習慣です。 これに追加するものは何もありません。 これによりパフォーマンスがわずかに向上する場合がありますが、これは非常にまれです。 では、なぜこれがDoctrine ORMの中心概念の1つなのでしょうか?
私がDoctrineを使用してチームと話すたびに、彼らはスタートアップの悪用が原因で問題が発生したことを認めています。 そして、これは経験豊富な開発者でも起こります。
私のクライアントの1人に対して、遅延読み込みの使用を検出して削除するロガーを作成しました。 これは無価値の露骨な例です。
変更追跡ポリシー
データの整合性を確保するために、このような複雑なシステムが必要なのはなぜですか? 私たちは「状態の保存なし」に囲まれています! アプリケーションのアーキテクチャが優れている場合、データはランダムな場所で変更されません。 まれなケース(ロギング、最後の接続時間の更新など)を除き、POST要求に従ってデータを変更するだけです。 その後、すぐにユーザーを別のページにリダイレクトします。
では、なぜこのような複雑なシステムが必要なのでしょうか? 設計が不十分なアプリケーションを非表示にするには?
EntityManagerにはあまりにも多くの魔法があります
グローバルEntityManagerは、他のORMオブジェクトと連携するFacadeパターンのように動作します。 これは、コード内のどこからでも呼び出すことができる非常に強力なツールです。
ただし、適切に設計されたアプリケーションでは、EntityManagerは次の3つの場合にのみ使用する必要があると強く信じています。
- 構成のためにロードするとき
- ファクトリー、Service ManagerまたはDependency Injectorで、必要なオブジェクトを初期化します。
- 一部のリポジトリでは、SQLクエリを作成する必要がある場合
他の場所では、使用する必要はありません。
まだ興味深く強力なライブラリ
明確にするために、もう一度繰り返します。DoctrineORMは役に立たないと言っているのではありません。 私が一番気になるのは、図書館が悪い習慣を課していることです。
- エンティティマップを使用すると、開発者は依存関係の注入に応じて、エンティティインスタンスをずさんにできます。
- 遅延読み込みは魔法のように機能しすぎて、手遅れになるまでパフォーマンスの問題を隠します。 これは、経験の少ない開発者には特に当てはまります。 しかし、経験豊富な開発者もこのトラップに陥ることがあります。これは、フェッチ結合を使用しないように誘惑することがあるためです。
- EntityManagerを使用すると、どこでも何でもできます。 便利ですが、良い習慣とはほど遠いです。
次は?
元の記事の著者は、Doctrineについてのいくつかの記事の資料を持っています。 たとえば、ODM拡張機能やジェネレーターについて。 さらに、彼はコメントの申請を受け入れます。