Qu'est-ce qu'une procuration dans Doctrine 2?

112

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

  1. Que sont les Proxyclasses?
  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é?

Jérémy
la source

Réponses:

160

METTRE À JOUR

Cette réponse contient des informations erronées sur les différences entre les objets proxy et les objets partiels. Voir la réponse de @ Kontrollfreak pour plus de détails: https://stackoverflow.com/a/17787070/252591


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 retourne pas firstnameet lastnamepropriétés, donc vous ne pouvez pas créer Userobjet. La création d'une entité incomplète peut entraîner des erreurs inattendues.

C'est pourquoi Doctrine créera un UserProxyobjet prenant en charge le chargement différé. Lorsque vous essayez d'accéder à la firstnamepropriété (qui n'est pas chargée), il chargera d'abord cette valeur depuis 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 différé 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, pouah. 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 une perte 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;
    }
}
Crozin
la source
Merci pour votre réponse, en quoi c'est différent avec Partial Object? Je veux dire pourquoi devrais-je utiliser un proxy? Pourquoi le chargement différé ne peut pas être implémenté dans l'entité elle-même? Pouvez-vous me fournir un cas d'utilisation?
Jérémy
1
Les objets partiels et les objets proxy sont la même chose - ils peuvent être traités comme des synonymes. Pour le reste des questions, vérifiez ma réponse mise à jour.
Crozin
1
Je ne comprends pas pourquoi la doctrine ne peut pas créer l'objet s'il n'a que la moitié des propriétés. En php, je suis capable de créer un objet même si je ne définis pas toutes les propriétés.
sanders
1
C'est une réponse totalement géniale et devrait figurer dans la documentation.
Jimbo
7
Cette réponse contient de sérieuses idées fausses sur les proxys et les objets partiels. Voir ma réponse pour comprendre pourquoi.
Kontrollfreak
81

Proxies

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 du fait que le proxy étend votre classe d'entité.

Doctrine hydrate par défaut les associations en tant que proxys de chargement différé si vous ne les faites pas JOINdans votre requête ou si vous définissez le mode de récupération sur EAGER.


Maintenant, je dois ajouter ceci car 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 en $articletant qu'objet:

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

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


Objets partiels

Si vous souhaitez charger partiellement des propriétés d'entité qui ne sont pas des associations, vous devez indiquer explicitement cette 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 proxies! 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 - Documentation Doctrine 2 ORM 2

Kontrollfreak
la source
1
Merci, cela fournit beaucoup plus de détails sur la façon dont Doctrine utilise les proxys et les objets partiels que la réponse acceptée! Et la référence aux documents est également utile.
Sean the Bean
1
Aussi pour référence, voici la section de la documentation sur les objets Proxy: doctrine-orm.readthedocs.org/en/latest/reference/...
Sean the Bean
Donc, lorsque vous faites une charge impatiente, est-ce simplement ajouter des ensembles de résultats?