web-dev-qa-db-ja.com

Doctrine 2のプロキシとは何ですか?

すべてのDoctrine 2ドキュメントを読み終え、自分のサンドボックスを開始し、ほとんどの原則を理解しましたが、まだ質問があり、ドキュメントで完全な説明を見つけることができませんでした。

  1. Proxyクラスとは何ですか?
  2. エンティティ上でそれらをいつ使用する必要がありますか?

私の知る限り、プロキシクラスはレイヤを追加してエンティティに他の機能を追加できるようにしますが、エンティティクラスにメソッド自体を実装する代わりにプロキシを使用するのはなぜですか?

110
Jérémy

クエリがエンティティの作成に必要なすべてのデータを返さない場合は常に、プロキシオブジェクトが使用されます。次のシナリオを想像してください:

@Entity
class User {
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here
}

DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

ご覧のとおり、このクエリはfirstnameおよびlastnameプロパティを返さないため、Userオブジェクトを作成できません。不完全なエンティティを作成すると、予期しないエラーが発生する可能性があります。

だからこそDoctrineは遅延ロードをサポートするUserProxyオブジェクトを作成します。ロードされていないfirstnameプロパティにアクセスしようとすると、最初にロードされますデータベースからの値。


なぜプロキシを使用する必要があるのですか?

プロキシオブジェクトをまったく使用していないかのように、常にコードを記述する必要があります。それらはDoctrineによって使用される内部オブジェクトとして扱うことができます。

遅延読み込みをEntitiy自体に実装できないのはなぜですか?

技術的には、ランダムプロキシオブジェクトのクラスを見てみましょう。それは汚いコードでいっぱいです、うーん。エンティティにきれいなコードがあると便利です。

ユースケースを教えてもらえますか?

最新の25件の記事のリストを表示していて、最初の記事の詳細を表示したいとします。それぞれに大量のテキストが含まれているため、すべてのデータをフェッチするとメモリが無駄になります。そのため、不要なデータをフェッチしません。

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) {
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) {
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    }
}

更新

以下のコメントセクションには、プロキシオブジェクトと部分オブジェクトの違いに関する誤った情報があります。詳細については、@ Kontrollfreakの回答を参照してください。 https://stackoverflow.com/a/17787070/252591

159
Crozin

プロキシ

Doctrineプロキシは、エンティティクラスを拡張してレイジーロードを提供する単なるラッパーです。

既定では、エンティティマネージャーに別のエンティティに関連付けられているエンティティを要求すると、関連付けられたエンティティはデータベースから読み込まれず、プロキシオブジェクトにラップされます。その後、アプリケーションがプロパティを要求するか、このプロキシされたエンティティのメソッドを呼び出すと、Doctrineは、常にプロキシに認識されているIDを要求する場合を除き、データベースからエンティティを読み込みます。

これは、プロキシがエンティティクラスを拡張するため、アプリケーションに対して完全に透過的に行われます。

クエリでJOINを使用しない場合、またはフェッチモードをEAGERに設定しない場合、Doctrineはデフォルトで関連付けをレイジーロードプロキシとしてハイドレートします。


今、私はこれを追加する必要があります、なぜなら私はどこでもコメントするのに十分な評判がないからです:

残念ながら、Crozinの答えには誤った情報が含まれています。

次のようなDQLクエリを実行する場合

_SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id
_

(プロキシされた)エンティティオブジェクトではなく、連想配列を取得します。したがって、追加のプロパティを遅延ロードすることはできません。

これを念頭に置いて、ユースケースの例も機能しないという結論に達します。オブジェクトとして_$article_にアクセスするには、DQLを次のように変更する必要があります。

_SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25
_

また、getContent()によって返されるプロパティは、all25エンティティのコンテンツプロパティをロードしないために関連付けである必要があります。


部分オブジェクト

関連付けではないエンティティプロパティを部分的にロードする場合は、このDoctrineを明示的に指定する必要があります。

_SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id
_

これにより、部分的に読み込まれたエンティティオブジェクトが得られます。

ただし、部分オブジェクトはプロキシではないことに注意してください!遅延読み込みはそれらに適用されません。したがって、部分的なオブジェクトの使用は一般に危険であり、回避する必要があります。続きを読む: Partial Objects — Doctrine 2 ORM 2 documentation

77
Kontrollfreak