Lorsque vous effectuez un export de données en CSV avec Laravel (ou en PHP en général), la limite de mémoire peut être atteinte en fonction de la taille des données exportées mais aussi de la méthode utilisée pour traiter les lignes récupérées depuis la base de données. Voyons ensemble les pistes d’amélioration pour soulager votre serveur et éviter que vos utilisateurs.trices n’obtiennent une erreur.
Notez que cela peut également se produire dans un simple cas de récupération de données pour de l’affichage, car c’est le traitement de récupération dans la base de données qui pose problème.
Exemple de code problématique :
$query = MyModelClass::query()->select('fld1', 'fld2', 'fld3');
$results = $query->get();
foreach ($results as $i => $row)
{
// ...
}
Contexte
Dans ce code on part du principe que notre classe MyModelClass hérite de la classe Model fournie par Laravel.
Voici ce qui se produit :
- A chaque fois qu’une ligne est retournée on initialise une instance de MyModelClass avec tous ses attributs, ses méthodes, etc.
- Toutes les lignes sont chargées en mémoire lorsqu’on utilise la méthode get().
Solution
Avant toute chose vérifiez la configuration de votre installation PHP et analysez la valeur limite définie pour la mémoire lors de l’exécution de scripts. En général, celle-ci varie entre 128 et 512 MB en fonction des applications qui tournent sur votre serveur. On va rarement au delà.
La configuration est stockée dans le fichier php.ini, malheureusement sur certains hébergements il n’est pas possible d’accéder à ce fichier ni de modifier les valeurs qui s’y trouvent.
Revenons maintenant au code : une première approche consiste à utiliser DB::table() ce qui permet d’obtenir une collection avec des objets qui ont une empreinte mémoire réduite.
Cependant ce n’est pas toujours suffisant. On peut alors remplacer la méthode get() par l’une des alternatives suivantes.
- chunk : le plus lent mais utilise le moins de mémoire ;
- cursor : rapide, mais cela peut aussi conduire à des situations où la mémoire sera insuffisante.
Il existe également une troisième méthode nommée « lazy » qui est similaire à la première méthode et ne nécessite pas de callback.
Exemple en utilisant un curseur :
$query = DB::table('myTable');
$query->select('fld1', 'fld2', 'fld3');
foreach ($query->cursor() as $row)
{
// ...
}
Références
Ce tutoriel est basé sur l’article suivant :
Il compare les différentes méthodes ainsi que leur temps d’exécution et la mémoire utilisée lors du traitement de 10 000 lignes puis 100 000.