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


112

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

  1. Proxyクラスとは?
  2. エンティティに対していつ使用する必要がありますか?

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

回答:


160

更新

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


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

@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

あなたが見ることができるように、このクエリは返さないfirstnamelastname性質は、したがって、あなたが作成することはできません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;
    }
}

Partial Objectとの違いは何ですか?なぜプロキシを使用する必要があるのですか?Entitiy自体に遅延読み込みを実装できないのはなぜですか?ユースケースを教えてもらえますか?
ジェレミー

1
部分オブジェクトとプロキシオブジェクトは同じものです。同義語として扱うことができます。残りの質問については、私の最新の回答を確認してください。
Crozin

1
doctrineが半分のプロパティしか持っていないのになぜオブジェクトを作成できないのか理解できません。PHPでは、すべてのプロパティを設定しなくてもオブジェクトを作成できます。
サンダー2011

1
これは完全に素晴らしい答えであり、ドキュメントにあるはずです。
神保

7
この回答には、プロキシと部分オブジェクトに関するいくつかの深刻な誤解が含まれています。その理由を理解するために私の答えを見ください。
Kontrollfreak 2013年

81

プロキシ

Doctrineプロキシは、エンティティクラスを拡張して遅延読み込みを提供するラッパーです。

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

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

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


どこにでもコメントできる評判がないので、これを追加する必要があります。

残念ながら、クロジンの回答には誤った情報が含まれています。

次のような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

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


部分オブジェクト

アソシエーションではないエンティティプロパティを部分的にロードしたい場合、このDoctrineに明示的に伝える必要があります:

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

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

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


1
おかげで、これは受け入れられた答えよりもDoctrineがプロキシと部分オブジェクトをどのように使用するかについてより多くの詳細を提供します!そして、ドキュメントへの参照も役に立ちます。
Sean the Bean

1
また、参考のために、プロキシオブジェクトに関するドキュメントのセクションを以下に示します。doctrine
Sean the Bean

したがって、熱心なロードを実行する場合、それは基本的に結果セットを追加するだけですか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.