Co to jest proxy w Doktrynie 2?

112

Właśnie skończyłem czytać całą dokumentację Doctrine 2, uruchomiłem własną piaskownicę, zrozumiałem większość Principes, ale wciąż jest pytanie i nie mogłem znaleźć pełnego wyjaśnienia w dokumencie.

  1. Co to są Proxyzajęcia?
  2. Kiedy należy ich używać na obiektach?

O ile rozumiem, klasy proxy dodają warstwę, aby umożliwić dodawanie innych funkcji do jednostek, ale po co używać serwera proxy zamiast implementować same metody w klasie jednostki?

Jérémy
źródło

Odpowiedzi:

160

AKTUALIZACJA

Ta odpowiedź zawiera błędne informacje o różnicach między obiektami zastępczymi a obiektami częściowymi. Zobacz odpowiedź @ Kontrollfreak, aby uzyskać więcej informacji: https://stackoverflow.com/a/17787070/252591


Obiekty proxy są używane zawsze, gdy zapytanie nie zwraca wszystkich danych wymaganych do utworzenia jednostki. Wyobraź sobie następujący scenariusz:

@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

Jak widać to zapytanie nie zwraca firstnamei nie ma lastnamewłaściwości, dlatego nie można utworzyć Userobiektu. Utworzenie niekompletnej jednostki może prowadzić do nieoczekiwanych błędów.

Dlatego Doctrine stworzy UserProxyobiekt obsługujący leniwe ładowanie. Kiedy spróbujesz uzyskać dostęp do firstnamewłaściwości (która nie jest załadowana), najpierw załaduje tę wartość z bazy danych.


Mam na myśli, dlaczego powinienem używać proxy?

Zawsze powinieneś pisać swój kod tak, jakbyś w ogóle nie używał obiektów proxy. Można je traktować jako wewnętrzne obiekty używane przez Doctrine.

Dlaczego leniwe ładowanie nie może zostać zaimplementowane w samej jednostce Entitiy?

Technicznie może to być, ale spójrz na klasę jakiegoś losowego obiektu proxy. Jest pełen brudnego kodu, ugh. Fajnie jest mieć czysty kod w swoich encjach.

Czy możesz podać mi przykład użycia?

Wyświetlasz listę ostatnich 25 artykułów i chcesz wyświetlić szczegóły pierwszego z nich. Każdy z nich zawiera dużą ilość tekstu, więc pobranie wszystkich tych danych byłoby stratą pamięci. Dlatego nie pobierasz niepotrzebnych danych.

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
źródło
Dziękuję za odpowiedź. Czym różni się obiekt częściowy? Mam na myśli, dlaczego powinienem używać proxy? Dlaczego leniwe ładowanie nie może zostać zaimplementowane w samej jednostce Entitiy? Czy możesz podać mi przykład użycia?
Jérémy
1
Obiekty częściowe i obiekty zastępcze to to samo - można je traktować jako synonimy. Jeśli chodzi o pozostałe pytania, sprawdź moją zaktualizowaną odpowiedź.
Crozin,
1
Nie rozumiem, dlaczego doktryna nie może stworzyć obiektu, jeśli ma on tylko połowę właściwości. W php mogę stworzyć obiekt, nawet jeśli nie ustawię wszystkich właściwości.
sanders
1
To jest niesamowita odpowiedź i powinna znaleźć się w dokumentacji.
Jimbo,
7
Ta odpowiedź zawiera poważne nieporozumienia dotyczące serwerów proxy i częściowych obiektów. Zobacz moją odpowiedź, aby zrozumieć, dlaczego.
Kontrollfreak
81

Proxy

Proxy Doctrine to po prostu opakowanie, które rozszerza klasę jednostki, aby zapewnić jej Lazy Loading.

Domyślnie, gdy poprosisz Entity Manager o jednostkę, która jest skojarzona z inną jednostką, skojarzona jednostka nie zostanie załadowana z bazy danych, ale zostanie opakowana w obiekt proxy. Gdy aplikacja zażąda następnie właściwości lub wywoła metodę tej jednostki proxy, Doctrine załaduje jednostkę z bazy danych (z wyjątkiem sytuacji, gdy zażądasz identyfikatora, który jest zawsze znany proxy).

Dzieje się to w pełni przezroczyste dla aplikacji ze względu na fakt, że serwer proxy rozszerza klasę jednostki.

Doctrine domyślnie nawadnia asocjacje jako serwery proxy z leniwym ładowaniem, jeśli JOINich nie uwzględnisz w zapytaniu lub ustawisz tryb pobierania na EAGER.


Teraz muszę to dodać, ponieważ nie mam wystarczającej reputacji, aby komentować wszędzie:

Niestety odpowiedź Crozina zawiera dezinformację.

Jeśli wykonujesz zapytanie DQL, takie jak

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

nie otrzymasz (zastępowanego) obiektu encji, ale tablicę asocjacyjną. Dlatego nie można leniwie ładować żadnych dodatkowych właściwości.

Mając to na uwadze, dochodzi się do wniosku, że przykład użycia również nie zadziała. Aby uzyskać dostęp $articlejako obiekt, należałoby zmienić DQL na coś takiego :

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

Właściwość zwracana przez getContent()musiałaby być asocjacją, aby nie ładować właściwości treści wszystkich 25 jednostek.


Częściowe obiekty

Jeśli chcesz częściowo załadować właściwości encji, które nie są skojarzeniami, musisz wyraźnie powiedzieć tej Doktrynie:

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

Daje to częściowo załadowany obiekt encji.

Ale uważaj, że częściowe obiekty nie są proxy! Leniwe ładowanie ich nie dotyczy. Dlatego używanie częściowych przedmiotów jest generalnie niebezpieczne i należy go unikać. Czytaj więcej: Częściowe obiekty - dokumentacja Doctrine 2 ORM 2

Kontrollfreak
źródło
1
Dzięki, zapewnia to znacznie więcej szczegółów na temat tego, w jaki sposób Doctrine wykorzystuje serwery proxy i obiekty częściowe, niż w akceptowanej odpowiedzi! Pomocne jest również odniesienie do dokumentów.
Sean the Bean
1
Również w celach informacyjnych, oto sekcja dokumentacji o obiektach Proxy: doctrine-orm.readthedocs.org/en/latest/reference/…
Sean the Bean
Czy podczas gorączkowego ładowania jest to po prostu dodawanie zestawów wyników?