Was ist ein Proxy in Lehre 2?

112

Ich habe gerade die gesamte Doctrine 2-Dokumentation gelesen, meine eigene Sandbox gestartet, die meisten Prinzipien verstanden, aber es gibt noch eine Frage, und ich konnte keine vollständige Erklärung im Dokument finden.

  1. Was sind ProxyKlassen?
  2. Wann sollte ich sie über Entitäten verwenden?

Soweit ich weiß, fügen Proxy-Klassen eine Ebene hinzu, mit der Sie Ihren Entitäten einige andere Funktionen hinzufügen können. Warum sollten Sie jedoch einen Proxy verwenden, anstatt die Methoden selbst in der Entitätsklasse zu implementieren?

Jérémy
quelle

Antworten:

160

AKTUALISIEREN

Diese Antwort enthält falsche Informationen zu Unterschieden zwischen Proxy-Objekten und Teilobjekten. Weitere Informationen finden Sie in der Antwort von @ Kontrollfreak: https://stackoverflow.com/a/17787070/252591


Proxy-Objekte werden immer dann verwendet, wenn Ihre Abfrage nicht alle Daten zurückgibt, die zum Erstellen einer Entität erforderlich sind. Stellen Sie sich folgendes Szenario vor:

@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

Wie Sie sehen, werden diese Abfragen firstnameund lastnameEigenschaften nicht zurückgegeben , daher können Sie kein UserObjekt erstellen . Die Erstellung einer unvollständigen Entität kann zu unerwarteten Fehlern führen.

Aus diesem Grund erstellt Doctrine ein UserProxyObjekt, das das verzögerte Laden unterstützt. Wenn Sie versuchen, auf eine firstnameEigenschaft zuzugreifen (die nicht geladen ist), wird dieser Wert zuerst aus der Datenbank geladen.


Ich meine, warum sollte ich einen Proxy verwenden?

Sie sollten Ihren Code immer so schreiben, als hätten Sie überhaupt keine Proxy-Objekte verwendet. Sie können als interne Objekte behandelt werden, die von Doctrine verwendet werden.

Warum kann das verzögerte Laden nicht in der Entität selbst implementiert werden?

Technisch könnte es sein, aber werfen Sie einen Blick auf die Klasse eines zufälligen Proxy-Objekts. Es ist voller schmutziger Code, ugh. Es ist schön, einen sauberen Code in Ihren Entitäten zu haben.

Können Sie mir einen Anwendungsfall geben?

Sie zeigen eine Liste der letzten 25 Artikel an und möchten Details des ersten Artikels anzeigen. Jeder von ihnen enthält eine große Textmenge, sodass das Abrufen all dieser Daten eine Verschwendung von Speicher wäre. Deshalb rufen Sie keine unnötigen Daten ab.

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;
    }
}
Crozin
quelle
Vielen Dank für Ihre Antwort, was ist mit Partial Object anders? Ich meine, warum sollte ich einen Proxy verwenden? Warum kann das verzögerte Laden nicht in der Entität selbst implementiert werden? Können Sie mir einen Anwendungsfall geben?
Jérémy
1
Teilobjekte und Proxy-Objekte sind dasselbe - sie können als Synonyme behandelt werden. Was den Rest der Fragen betrifft, überprüfe meine aktualisierte Antwort.
Crozin
1
Ich verstehe nicht, warum die Doktrin das Objekt nicht erstellen kann, wenn es nur die Hälfte der Eigenschaften hat. In PHP kann ich ein Objekt erstellen, auch wenn ich nicht alle Eigenschaften festlege.
Sanders
1
Dies ist eine absolut großartige Antwort und sollte in der Dokumentation enthalten sein.
Jimbo
7
Diese Antwort enthält einige schwerwiegende Missverständnisse von Proxies und Teilobjekten. Sehen Sie meine Antwort, um zu verstehen, warum.
Kontrollfreak
81

Proxies

Ein Doctrine-Proxy ist nur ein Wrapper, der eine Entitätsklasse erweitert, um Lazy Loading dafür bereitzustellen.

Wenn Sie den Entitätsmanager nach einer Entität fragen, die einer anderen Entität zugeordnet ist, wird die zugeordnete Entität standardmäßig nicht aus der Datenbank geladen, sondern in ein Proxy-Objekt eingeschlossen. Wenn Ihre Anwendung dann eine Eigenschaft anfordert oder eine Methode dieser Proxy-Entität aufruft, lädt Doctrine die Entität aus der Datenbank (außer wenn Sie die ID anfordern, die dem Proxy immer bekannt ist).

Dies geschieht für Ihre Anwendung vollständig transparent, da der Proxy Ihre Entitätsklasse erweitert.

Doctrine hydratisiert Assoziationen standardmäßig als Lazy-Load-Proxys, wenn Sie sie nicht JOINin Ihrer Abfrage verwenden oder den Abrufmodus auf setzen EAGER.


Jetzt muss ich das hinzufügen, weil ich nicht genug Ruf habe, um überall zu kommentieren:

Leider enthält Crozins Antwort Fehlinformationen.

Wenn Sie eine DQL-Abfrage wie ausführen

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

Sie erhalten kein (Proxy-) Entitätsobjekt, sondern ein assoziatives Array. Es ist also nicht möglich, zusätzliche Eigenschaften zu laden.

Vor diesem Hintergrund kommt man zu dem Schluss, dass das Anwendungsfallbeispiel auch nicht funktioniert. Die DQL müsste in so etwas geändert werden, um $articleals Objekt zugreifen zu können:

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

Und die von zurückgegebene Eigenschaft getContent()müsste eine Zuordnung sein, um die Inhaltseigenschaften aller 25 Entitäten nicht zu laden .


Teilobjekte

Wenn Sie Entitätseigenschaften, die keine Assoziationen sind, teilweise laden möchten, müssen Sie diese Doktrin explizit angeben:

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

Dadurch erhalten Sie ein teilweise geladenes Entitätsobjekt.

Beachten Sie jedoch, dass Teilobjekte keine Proxys sind! Lazy Loading gilt nicht für sie. Daher ist die Verwendung von Teilobjekten im Allgemeinen gefährlich und sollte vermieden werden. Lesen Sie mehr: Teilobjekte - Doctrine 2 ORM 2-Dokumentation

Kontrollfreak
quelle
1
Vielen Dank, dies liefert viel mehr Details darüber, wie Doctrine Proxies und Teilobjekte verwendet, als die akzeptierte Antwort! Auch der Verweis auf die Dokumente ist hilfreich.
Sean the Bean
1
Als Referenz finden Sie hier den Abschnitt der Dokumente zu Proxy-Objekten: doctrine-orm.readthedocs.org/en/latest/reference/…
Sean the Bean
Wenn Sie also eifrig laden, werden im Grunde nur Ergebnismengen hinzugefügt?