Les mécanismes internes de Doctrine expliqués Symfony2


Table des matières

Référence
Comment Doctrine conserve la trace des objets
Comment Doctrine détecte les changements
Requête internes
Les couches différente d'ORM
Hydratation
Persistants
UnitOfWork
resultSetMapping
Parser DQL
SQLWalker
EntityManager
ClassMetadataFactory

Référence

http://docs.doctrine-project.org/en/latest/reference/unitofwork.html

Object Relational Mapping est un sujet suffisamment complexe pour comprendre comment fonctionne les mécanismes en interne de Doctrine et vous permettre d'utiliser sa pleine puissance.

Comment Doctrine conserve la trace des objets

Doctrine utilise le modèle de la carte d'identité pour suivre des objets. Chaque fois que vous chercher un objet à partir de la base de données, Doctrine conservera une référence à cet objet à l'intérieur de son UnitOfWork. Le tableau contenant toutes les références d'entité est de deux niveaux de profondeur et a les clefs "root entity name" et "id". Depuis Doctrine permet d'avoir des clés composites id est une liste triée, version sérialisée de toutes les colonnes clés.

Si vous appelez la EntiyManager et demander une entité avec un ID spécifique deux fois, il aura la même instance de retour:

public function testIdentityMap()
{
    $objectA = $this->entityManager->find('EntityName', 1);
    $objectB = $this->entityManager->find('EntityName', 1);

    $this->assertSame($objectA, $objectB)
}

Une seule requête SELECT sera tiré contre la base de données ici. Dans le second appel EntityManager#find() Doctrine va vérifier la carte d'identité d'abord et n'a pas besoin de faire un aller-retour avec cette base de données .

Même si vous obtenez un objet proxy d'abord, puis aller chercher l'objet par le même identifiant vous finirez quand même avec la même référence:

public function testIdentityMapReference()
{
    $objectA = $this->entityManager->getReference('EntityName', 1);
    // check for proxyinterface
    $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA);

    $objectB = $this->entityManager->find('EntityName', 1);

    $this->assertSame($objectA, $objectB)
}

La carte d'identité en cours d'indexation par les clés primaires ne permet des raccourcis lorsque vous demandez des objets par la clé primaire. Supposons que vous ayez le tableau suivant les personnes:

id nom
1 Benjamin
2 Bud

Prenons l'exemple suivant, où deux appels consécutifs sont portés sur un dépôt pour aller chercher une entité par un ensemble de critères:

public function testIdentityMapRepositoryFindBy()
{
    $repository = $this->entityManager->getRepository('Person');
    $objectA = $repository->findOneBy(array('name' = > 'Benjamin'));
    $objectB = $repository->findOneBy(array('name' = > 'Benjamin'));

    $this->assertSame($objectA, $objectB);
}

Cette requête retournera toujours les mêmes références $objectA et $objectB . Ils ont en effet la référence au même objet. Toutefois, lors de la vérification de vos journaux SQL, vous vous rendrez compte que deux requêtes ont été exécutées dans la base de données. Doctrine sait que les objets recherché par id, ainsi q'une requête multi critère doivent repartir à la base de données, même si elle a été exécutée juste avant.

Mais au lieu de créer un second Objet Doctrine prend la clé primaire et vérifie si elle dispose déjà d'un objet à l'intérieur de UnitOfWork avec cette clé primaire. Dans notre exemple. Il trouve un objet et décide de le retourner à la place de la création d'un nouveau.

La carte d'identité a un autre cas d'utilisation. Lorsque vous appelez EntityManager#flush, Doctrine demandera la carte d'identité pour tous les objets qui sont actuellement gérés. Cela signifie que vous n'avez pas à appeler EntityManager#persist encore et encore pour passer des objets connus à l'EntityManager. Il s'agit d'une NO-OP pour les entités connues, mais conduit à beaucoup de codes écrits, ce qui est source de confusion pour les autres développeurs.

Le code suivant mettra à jour votre base de données avec les modifications apportées à l'objet Person, même si vous n'avez pas appelé EntityManager#persist

<?php
$user = $entityManager->find("Person", 1);
$user->setName("Guilherme");
$entityManager->flush();

Comment Doctrine détecte les changements

Doctrine est une banque de données du mappeur qui tente d'atteindre la persistance-ignorance (PI). Cela signifie que vous mapper des objets PHP dans une base de données relationnelle qui ne sont pas nécessairement sur la base de données du tout. Une question naturelle serait désormais, «Comment Doctrine détecte des objets qui ont changé?".

Doctrine tient une deuxième carte d'identité à l'intérieur du UnitOfWork. Chaque fois que vous chercher un objet de la base de données Doctrine conservera une copie de toutes les propriétés et les associations à l'intérieur du UnitOfWork. Parce que les variables dans le langage PHP sont soumis à "la copie sur écriture" l'utilisation de la mémoire d'une demande PHP qui lit uniquement les objets de la base de données est la même que si Doctrine n'avait pas conservée cette copie variable. Seulement si vous commencez des variables changeantes PHP va créer de nouvelles variables internes qui consommeront de la mémoire.

Maintenant, chaque fois que vous appelez EntityManager#flush Doctrine va parcourir la carte d'identité et pour chaque objet comparera la propriété d'origine et les valeurs d'association avec les valeurs qui sont actuellement définies sur l'objet. Si des changements sont détectés, puis l'objet est modifié pour une opération UPDATE SQL. Seuls les champs qui ont changé sont mises à jour.

Ce processus a un impact évident sur les performances. Plus la taille de l'UnitOfWork est élevé, plus le temps de calcul est important. Il y a plusieurs façons d'optimiser la performance de l'opération de vidage:

Requête internes

Les couches différente d'ORM

Naviguez avec un ensemble de couches Doctrine avec des responsabilités différentes. Cette section donne une brève explication de chaque couche.

Hydratation

Responsable de la création d'un résultat final d'une déclaration de base de données brut et un objet de mappage de jeu de résultats. Le développeur peut choisir le type de résultat qu'il souhaite être hydratée. Par défaut résultat-types comprennent:

L'hydratation d'entités et de tableaux est une des choses les plus complexes des algorithmes de Doctrine sages. Il peut construire résultats avec par exemple:

Persistants

En cours

UnitOfWork

En cours

resultSetMapping

En cours

Parser DQL

En cours

SQLWalker

En cours

EntityManager

En cours

ClassMetadataFactory

En cours