Symfony et les exports CSV (Doctrine)

De e-glop
Révision datée du 8 avril 2013 à 19:37 par BeTa (discussion | contributions) (apps/rp/modules/contact/templates/_csv_line.php)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)

Introduction

Considérons le modèle Contact qui est relié au modème Organism par une relation many-to-many via le modèle Professional. Nous souhaitons faire une extraction CSV des contacts avec leurs relations éventuelles.

La module correspondant à Contact est contact dans l'application rp. contact a été créé via doctrine:generate-admin.

L'idée est d'extraire les informations qui sont présentées dans la liste des enregistrements, éventuellement filtrée sur certains critères tout en profitant de l'optimisation éventuelle déjà effectuée sur la requête générique.

Préparation dans le view.yml

Dans le fichier apps/rp/modules/contact/config/view.yml,

csvSuccess:
  has_layout: off
  http_metas:
    content-type: text/comma-separated-values
    content-disposition: attachment; filename="contacts-list.csv"

L'action

Directement dans le vif du sujet

dans le fichier apps/rp/modules/actions/actions.php, créer la méthode executeCsv() de la manière suivante :

 public function executeCsv(sfWebRequest $request)
 {
   ∕/ to modify the CSV output format in case of extreme necessity
   $this->options = array('ms' => $request->hasParameter('ms'));
   
   $q = $this->buildQuery();
   $a = $q->getRootAlias();
   $q->leftJoin("$a.Organisms")
     ->select("$a.title, $a.name, $a.firstname, $a.address, $a.postalcode, $a.city, $a.country")
     ->addSelect("o.address AS organism_address, o.postalcode AS organism_postalcode, o.city AS organism_city, o.country AS organism_country");
   
   // ->fetchArray() to avoid big big memory usage
   $this->lines = $q->fetchArray();
   
   $this->outstream = 'php://output';
   $this->delimiter = $request->hasParameter('ms') ? ';' : ',';
   $this->enclosure = '"';
   $this->charset   = array('db' => 'UTF-8', 'ms' => 'WINDOWS-1252//TRANSLIT');
   
   sfConfig::set('sf_web_debug', false);
   sfConfig::set('sf_escaping_strategy', false);
   sfConfig::set('sf_charset', $this->options['ms'] ? $this->charset['ms'] : $this->charset['db']);
 }

Les subtilités

L'exemple utilisé actuellement n'est pas très très révélateur des difficultés auxquelles on peut être confronté dans le cas de schémas de données beaucoup beaucoup plus complexes. Cela dit, quelques précaution vous épargeront pas mal de temps :

  • préciser les champs du select de votre DQL
  • récupérer les résultats de la requête dans un tableau via Doctrine_Query::fetchArray() comme vu dans le code précédent

Notez également que selon la manière dont vous construirez votre Doctrine_Query dans les ->select() et ->addSelect(), vous présenterez les choses différemment dans votre fichier CSV final.

La vue (templates)

apps/rp/modules/contact/templates/csvSuccess.php

Dans le fichier apps/rp/modules/contact/templates/csvSuccess.php (que vous créerez), voilà ce qui peut convenir (il est aussi possible de créer un partial propre voire même de le placer directement dans apps/rp/templates/ pour plus de généricité) :

<?php
 $outstream = fopen($outstream, 'w');
 
 $vars = array(
   'options'   => $options,
   'delimiter' => $delimiter,
   'enclosure' => $enclosure,
   'outstream' => $outstream,
   'charset'   => $charset,
   'lines'     => $lines));
 
 // header
 if ( count($lines) > 0 )
 include_partial('contact/csv_headers',$vars);
 
 // content
 foreach ( $lines as $line )
 {
   unset($line['id']);
   include_partial('contact/csv_line',array_merge(array('line' => $line),$vars));
 }
 
 fclose($outstream);

apps/rp/modules/contact/templates/_csv_headers.php

Dans le fichier apps/rp/modules/contact/templates/_csv_headers.php

   $line = array_keys($lines[0]);
   if ( $options['ms'] )
   foreach ( $line as $key => $value )
     $line[$key] = @iconv($charset['db'], $charset['ms'], $value);
   
   fputcsv($outstream, $line, $delimiter, $enclosure);
   ob_flush();

apps/rp/modules/contact/templates/_csv_line.php

Dans le fichier apps/rp/modules/contact/templates/_csv_line.php

   if ( $options['ms'] )
   foreach ( $line as $key => $value )
     $line[$key] = @iconv($charset['db'], $charset['ms'], $value);
   
   fputcsv($outstream, $line, $delimiter, $enclosure);
   ob_flush();