Concepteur / Développeur PHP5 / Symfony.
Mangeur de bières.
Technophile.
Dans le cadre de manipulation de grandes quantités de données avec doctrine, il arrive souvent que l’hydratation des objets pose problème, dans le sens ou l’occupation mémoire devient vite énorme.
Exemple :
avec ce schema :
Category:
columns:
name: { type: string(255) }
Product:
columns:
category_id: { type: integer }
name: { type: string(255) }
relations:
Category: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: Products }
et ces fixtures :
Category:
<?php for ($i = 0 ; $i < 100 ; $i++): ?>
Category_<?php echo $i ?>:
name: 'Catégorie <?php echo $i ?>'
<?php endfor ?>
Product:
<?php for ($i = 0 ; $i < 100 ; $i++): ?>
<?php for ($j = 0 ; $j < 100 ; $j++): ?>
Product_<?php echo $i ?>_<?php echo $j ?>:
Category: Category_<?php echo $i.PHP_EOL ?>
name: 'Produit <?php echo $i ?>_<?php echo $j ?>'
<?php endfor ?>
<?php endfor ?>
Soit 100 catégories contenant chacune 100 produits, ce code :
$q = Doctrine_Query::create()
->from('Product p'); foreach ($q->execute() as $product)
{
$name = $product->getName();
}
à une empreinte mémoire de plus de 30Mo. Pas vraiment problématique, mais imaginez la même chose avec des “vrais” objets, contenant autre chose qu’un simple nom…
Deux solutions à ce problème :
1er cas : Ne pas Hydrater les objets
Si vous n’utilisez pas de méthodes métier particulières, et que vous n’avez pas besoin de récuperer d’objets liés à vos produits, vous pouvez vous contenter de ne pas hydrater vos objets doctrine. Ce code :
$q = Doctrine_Query::create()
->from('Product p');
foreach ($q->execute(array(), Doctrine_Core::HYDRATE_ARRAY) as $row)
{
$name = $row['name'];
}
a une empreinte mémoire de seulement 1.9 Mo, 15 fois moins.
2ème cas : Vous avez besoin des méthode de votre objet, utilisation du poids mouche.
L’autre solution, consiste à instancier un seul objet Product, que l’on hydratera à chaque itération de la boucle, avec de nouvelles données, remplaçant les anciennes.
Ce code :
$q = Doctrine_Query::create()
->from('Product p');
$product = new Product;
foreach ($q->execute(array(), Doctrine_Core::HYDRATE_ARRAY) as $row)
{
$product->hydrate($row);
$name = $product->getName();
}
a une empreinte mémoire de 2.1Mo, c’est un peu plus que la méthode précédente, mais on garde l’accès à nos méthodes métier, et à nos relations à conditions de prévoir les jointures dans le requête.
A noter que l’execution du script est également beaucoup plus rapide