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 .
Proxy
?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é?
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;
}
}
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
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.
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