web-dev-qa-db-fra.com

Qu'est-ce qu'un proxy dans Doctrine 2?

Je viens de terminer la lecture de toute la documentation Doctrine 2, j'ai commencé mon propre bac à sable, j'ai compris la plupart des principes, mais il y a encore une question et je n'ai trouvé aucune explication complète dans la doc .

  1. Que sont les classes Proxy?
  2. Quand dois-je les utiliser sur des entités?

Pour autant que je sache, les classes proxy ajoutent une couche pour vous permettre d'ajouter d'autres fonctionnalités à vos entités, mais pourquoi utiliser un proxy au lieu d'implémenter les méthodes elles-mêmes dans la classe d'entité?

110
Jérémy

Les objets proxy sont utilisés chaque fois que votre requête ne renvoie pas toutes les données requises pour créer une entité. Imaginez le scénario suivant:

@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

Comme vous pouvez le voir, cette requête ne renvoie pas les propriétés firstname et lastname, vous ne pouvez donc pas créer d'objet User. La création d'une entité incomplète peut entraîner des erreurs inattendues.

C'est pourquoi Doctrine créera un objet UserProxy qui prend en charge le chargement différé. Lorsque vous essaierez d'accéder à la propriété firstname (qui n'est pas chargée), elle se chargera d'abord cette valeur de la base de données.


Je veux dire pourquoi devrais-je utiliser un proxy?

Vous devez toujours écrire votre code comme si vous n'utilisiez pas du tout d'objets proxy. Ils peuvent être traités comme des objets internes utilisés par Doctrine.

Pourquoi le chargement paresseux ne peut pas être implémenté dans l'entité elle-même?

Techniquement, cela pourrait être, mais jetez un œil à la classe d'un objet proxy aléatoire. C'est plein de code sale, ugh. C'est bien d'avoir un code propre dans vos entités.

Pouvez-vous me fournir un cas d'utilisation?

Vous affichez une liste des 25 derniers articles et vous souhaitez afficher les détails du premier. Chacun d'eux contient une grande quantité de texte, donc récupérer toutes ces données serait un gaspillage de mémoire. C'est pourquoi vous ne récupérez pas de données inutiles.

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

MISE À JOUR

Dans la section des commentaires ci-dessous, il existe des informations erronées sur les différences entre les objets proxy et les objets partiels. Voir les réponses @Kontrollfreak pour plus de détails: https://stackoverflow.com/a/17787070/252591

159
Crozin

Procurations

Un proxy Doctrine n'est qu'un wrapper qui étend une classe d'entité pour lui fournir un chargement différé.

Par défaut, lorsque vous demandez au gestionnaire d'entités une entité associée à une autre entité, l'entité associée ne sera pas chargée à partir de la base de données, mais encapsulée dans un objet proxy. Lorsque votre application demande alors une propriété ou appelle une méthode de cette entité mandatée, Doctrine chargera l'entité à partir de la base de données (sauf lorsque vous demandez l'ID, qui est toujours connu du proxy).

Cela se produit de manière totalement transparente pour votre application, car le proxy étend votre classe d'entité.

Par défaut, Doctrine associera les associations d'hydrates en tant que proxys de chargement différé si vous ne les JOIN pas dans votre requête ou ne définissez pas le mode d'extraction sur EAGER.


Maintenant, je dois ajouter ceci parce que je n'ai pas assez de réputation pour commenter partout:

Malheureusement, la réponse de Crozin contient de la désinformation.

Si vous exécutez une requête DQL comme

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

vous n'obtiendrez pas un objet entité (mandaté), mais un tableau associatif. Il n'est donc pas possible de charger paresseusement des propriétés supplémentaires.

Dans cet esprit, on arrive à la conclusion que l'exemple de cas d'utilisation ne fonctionnera pas non plus. Le DQL devrait être changé en quelque chose comme ceci afin d'accéder à $article En tant qu'objet:

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

Et la propriété retournée par getContent() devrait être une association afin de ne pas charger les propriétés de contenu de all 25 entités.


Objets partiels

Si vous voulez charger partiellement des propriétés d'entité qui ne sont pas des associations, vous devez le dire explicitement Doctrine:

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

Cela vous donne un objet entité partiellement chargé.

Mais attention, les objets partiels ne sont pas des proxys! Le chargement différé ne s'applique pas à eux. Par conséquent, l'utilisation d'objets partiels est généralement dangereuse et doit être évitée. En savoir plus: Objets partiels - Doctrine 2 documentation ORM 2

77
Kontrollfreak