Ajouter ses propres annotations sur des Entités Doctrine : gare au Proxy !

Pour un bundle de recherche Symfony2 en cours de développement, nous avons créé un mécanisme d’annotations sur nos entités pour indiquer les champs à indexer. Ces annotations sont lues sur les entités récupérées via les événements Doctrine postPersist, postUpdate et preRemove.

Pour cela nous avons commencé avec du PHP5 natif :

new ReflectionObject($entity);

Au début, tout fonctionnait jusqu’à trouver un cas dans notre code où l’événement était bien récupéré, mais les annotations non trouvées sur l’objet.

Pourquoi ?

Nous avons fini par nous apercevoir que :

$video = $em->getRepository(‘SedonaMyBundleBundle:Video’)->find(12);
echo get_class($video); // Show :  Sedona\MyBundle\Entity\Video

MAIS

$videoFile = $em->getRepository(‘SedonaMyBundleBundle:VideoFile’)->find(18);
$video = videoFile->getVideo();
echo get_class($video); // Show :  Proxies\__CG__\Sedona\MyBundleBundle\Entity\Video

Ou même (plus subtil),  Si le video_id de VideoFile = 12, à cause du cache

$videoFile = $em->getRepository(‘SedonaMyBundleBundle:VideoFile’)->find(18);
$video = $em->getRepository(‘SedonaMyBundleBundle:Video’)->find(12);
echo get_class($video); // Show :  Proxies\__CG__\Sedona\MyBundleBundle\Entity\Video

La classe Proxy est une classe interne générée par Doctrine dans le cache qui étend la vraie classe pour y rajouter la fonctionnalité d’autoloading.
Comme elle étend la classe de base sans surcharger de méthode, c’est transparent dans 99,99% des cas, mais on tombe bien sûr dans le 0.01% restant : cette classe a tout, sauf les annotations de la classe d’origine, et c’est justement là dessus que se base notre bundle ! Il ne reconnaît alors pas la classe et ne met pas à jour l’objet.

La solution, dans le AnnotationServiceConverter, au lieu d’utiliser le ReflectionObject interne de PHP directement, il faut passer par la ClassUtils de Doctrine qui va faire la conversion automatiquement :

use Doctrine\Common\Util\ClassUtils;
$reflectionObject = ClassUtils::newReflectionObject($originalObject);

Logique, mais il faut y penser… 😉

You may also like...