Che cos'è un proxy in Doctrine 2?


112

Ho appena finito di leggere tutta la documentazione di Doctrine 2, ho avviato la mia sandbox, ho capito la maggior parte dei principi, ma c'è ancora una domanda e non sono riuscito a trovare alcuna spiegazione completa nel documento.

  1. Cosa sono le Proxyclassi?
  2. Quando dovrei usarli sulle entità?

Per quanto ne so, le classi proxy aggiungono un livello per consentirti di aggiungere altre funzionalità alle tue entità, ma perché usare un proxy invece di implementare i metodi stessi nella classe entità?

Risposte:


160

AGGIORNARE

Questa risposta contiene informazioni errate sulle differenze tra oggetti proxy e oggetti parziali. Vedi la risposta di @ Kontrollfreak per maggiori dettagli: https://stackoverflow.com/a/17787070/252591


Gli oggetti proxy vengono utilizzati ogni volta che la query non restituisce tutti i dati necessari per creare un'entità. Immagina il seguente scenario:

@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

Come puoi vedere questa query non restituisce firstnamee lastnameproprietà, quindi non puoi creare Useroggetti. La creazione di un'entità incompleta potrebbe portare a errori imprevisti.

Ecco perché Doctrine creerà UserProxyoggetti che supportano il caricamento lento. Quando proverai ad accedere alla firstnameproprietà (che non è caricata) caricherà prima quel valore dal database.


Voglio dire, perché dovrei usare un proxy?

Dovresti sempre scrivere il tuo codice come se non usassi affatto oggetti proxy. Possono essere trattati come oggetti interni usati da Doctrine.

Perché il caricamento lento non può essere implementato nell'entità stessa?

Tecnicamente potrebbe essere solo uno sguardo alla classe di un oggetto proxy casuale. È pieno di codice sporco, ugh. È bello avere un codice pulito nelle tue entità.

Potete fornirmi un caso d'uso?

Stai visualizzando un elenco degli ultimi 25 articoli e desideri visualizzare i dettagli del primo. Ciascuno di essi contiene una grande quantità di testo, quindi recuperare tutti quei dati sarebbe uno spreco di memoria. Ecco perché non recuperi dati non necessari.

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;
    }
}

Grazie per la tua risposta, in cosa è diverso con Partial Object? Voglio dire, perché dovrei usare un proxy? Perché il caricamento lento non può essere implementato nell'entità stessa? Potete fornirmi un caso d'uso?
Jérémy

1
Gli oggetti parziali e gli oggetti proxy sono la stessa cosa: possono essere trattati come sinonimi. Per quanto riguarda il resto delle domande, controlla la mia risposta aggiornata.
Crozin

1
Non capisco perché la dottrina non possa creare l'oggetto se ha solo metà delle proprietà. In php posso creare un oggetto anche se non imposto tutte le proprietà.
sanders

1
Questa è una risposta assolutamente fantastica e dovrebbe essere nella documentazione.
Jimbo

7
Questa risposta contiene alcune idee sbagliate gravi su proxy e oggetti parziali. Vedi la mia risposta per capire perché.
Kontrollfreak

81

Proxy

Un proxy Doctrine è solo un wrapper che estende una classe entità per fornire il caricamento lento.

Per impostazione predefinita, quando chiedi a Entity Manager un'entità associata a un'altra entità, l'entità associata non verrà caricata dal database, ma racchiusa in un oggetto proxy. Quando la tua applicazione richiede una proprietà o chiama un metodo di questa entità proxy, Doctrine caricherà l'entità dal database (tranne quando richiedi l'ID, che è sempre noto al proxy).

Ciò è completamente trasparente per la tua applicazione perché il proxy estende la tua classe di entità.

Doctrine idrata per impostazione predefinita le associazioni come proxy di caricamento lento se non vengono JOINinseriti nella query o se imposti la modalità di recupero su EAGER.


Ora devo aggiungere questo perché non ho abbastanza reputazione per commentare ovunque:

Sfortunatamente, la risposta di Crozin contiene disinformazione.

Se esegui una query DQL come

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

non otterrai un oggetto entità (proxy), ma un array associativo. Quindi non è possibile caricare lazy proprietà aggiuntive.

Con questo in mente, si arriva alla conclusione che neanche l'esempio del caso d'uso funzionerà. Il DQL dovrebbe essere cambiato in qualcosa di simile per accedere $articlecome oggetto:

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

E la proprietà restituita da getContent()dovrebbe essere un'associazione per non caricare le proprietà del contenuto di tutte le 25 entità.


Oggetti parziali

Se vuoi caricare parzialmente le proprietà delle entità che non sono associazioni, devi dire esplicitamente a questa Doctrine:

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

Questo ti dà un oggetto entità parzialmente caricato.

Ma attenzione che gli oggetti parziali non sono proxy! Il caricamento lento non si applica a loro. Pertanto, l'utilizzo di oggetti parziali è generalmente pericoloso e dovrebbe essere evitato. Ulteriori informazioni: Partial Objects - Documentazione di Doctrine 2 ORM 2


1
Grazie, questo fornisce molti più dettagli su come Doctrine usa proxy e oggetti parziali rispetto alla risposta accettata! E anche il riferimento ai documenti è utile.
Sean the Bean

1
Anche per riferimento, ecco la sezione della documentazione sugli oggetti Proxy: doctrine-orm.readthedocs.org/en/latest/reference/…
Sean the Bean

Quindi, quando si esegue un carico desideroso, si tratta fondamentalmente solo di aggiungere set di risultati?
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.