Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
bonjour à tous,
Dans mon application j'aurai besoin d'une fonctionnalité d'export de données en CSV :
l'utilisateur clique sur un lien "exporter" et le navigateur doit lui proposer le téléchargement d'un fichier CSV contenant les données d'une table de la base de données. Et ceci pour quasiment chaque table de ma base.
Le problème est que je ne vois pas du tout comment procéder.
Dois-je créer une aide d'action, ZF propose-t-il cette fonctionnalité d'export ? pour l'instant j'avoue être un peu perdu.
Dernière modification par lefafa (07-04-2010 14:16:13)
Hors ligne
Salut,
je me suis penché sur ce problème la semaine dernière.
Pour ma part j'ai créé une action dans un de mes contrôleurs. Voici comment j'ai procédé :
- création du fichier destination avec fopen
- insertion d'une ligne de titre de colonne avec la fonction fputcsv si besoin
- parcours du tableau de données puis insertion de chaque ligne avec fputcsv
- fermeture du fichier avec fclose
- insertion des headers qui vont bien
- lecture du fichier généré avec la fonction readfile
- désactiver le renderer de ton action ainsi que le layout
Maintenant, si au travers d'un lien tu fais appel à cette action, tu devrais avoir une fenêtre te permettant d'ouvrir ou d'enregistrer le fichier csv.
N'hésite pas pour plus d'infos.
Hors ligne
bonjour
pas une réponse directe à ce sujet mais quelque chose qui le touche de loin
une classe pour parser les fichiers csv qui prend en compte le fait qu'un champ peut contenir un séparateur (de champs ou de fin de ligne s'il est délimité
<?php /** * Lecture de fichier CVS prenant en compte le fait qu'un séparateur peut êtr eprésent dans un champs s'il est délimité. * * @package Fast * @copyright ftgroup * @author Jean-Yves Terrien * @package Fast_CVS */ /* sample $cvs = new Fast_Csv_Parser('file.csv'); $row = $cvs->getRow(); while (!$cvs->eof()) { print_r($row); $row = $cvs->getRow(); } $cvs->close(); */ class Fast_Csv_Parser { /** * @param string $filename nom du fichier à parser * @param string $separator column separator char * @param string $enclosure column enclosure char */ public function __construct($filename, $separator = ';',$enclosure='"') { $this->separator =$separator; $this->enclosure =$enclosure; $this->filename = $filename; $this->resource = fopen($filename,'r'); } // replace double enclosure mark by single enclosure mark function cleanField($field) { return preg_replace('/'.$this->enclosure.$this->enclosure.'/', $this->enclosure, $field); } // Verify if is not truncated field function isCompletField(&$field) { if (($this->enclosure == substr($field, 0, 1)) && ($this->enclosure == substr($field, -1, 1)) && (0 == substr_count($field, $this->enclosure)%2)) { $field = substr($field, 1, -1); } return (0 == substr_count($field, $this->enclosure)%2); //nombre pair de $this->enclosure } // get next row in cvs file public function getRow() { $line = $this->getLine(); if (null != $line) { $this->row = array(); $complete = false; $incompletField = false; //while incomplet row and not eof while (!$complete && null != $line) { $list = explode ($this->separator, $line); foreach ($list as $key=>$value) { $value = trim($value,"\r\n"); // if have an incomplet fiel try to complet it if ($incompletField) { $value = $incompletField . $break . $value; } if ($this->isCompletField($value)) //nombre pair de $this->enclosure { $complete = ($key == count($list)-1); $this->row[] = $this->cleanField($value); $incompletField = false; } else { $incompletField = $value; $break = $this->separator; } } if (!$complete && ($key == count($list)-1)) { $line = $this->getLine(); $break = "\n"; } } } else { $this->row = null; } return $this->row; } // is end of file ? public function eof() { return (null === $this->row); } // close cvs file public function close () { if ($this->resource) { fclose($this->resource); } } public function getLine() { $line = null; if ($this->resource) { $line = fgets($this->resource); } return $line; } }
a+jyt
Hors ligne
Personnellement j'utiliserai plus l'helper ContextSwitch
http://framework.zend.com/manual/fr/zen … textswitch
Hors ligne
merci à tous pour vos réponses rapides
Pour la création du fichier CSV ça fonctionne. J'ai bien un fichier temporaire dans mon répertoire contenant les données de ma table et parfaitement formaté.
Par contre lorsque le navigateur me demande de l'ouvrir ou de l'enregistrer (grâce à readfile) j'obtiens un fichier corrompu qui n'est même plus un csv et qui contient du code de mon layout !
@Godzinho : je pense que ça vient de mes headers, pourrais-tu me montrer lesquels tu utilises STP.
Dernière modification par lefafa (01-04-2010 16:58:40)
Hors ligne
il faut désactiver le rendu
A+JYT
Hors ligne
Salut,
effectivement il faut d'une part désactiver le rendu mais j'ai du également désactiver le layout principal pour éviter d'avoir le code html du layout dans mon fichier csv lorsque je l'ouvrais alors que le fichier seul était bel et bien généré.
Voici mon code :
Zend_Layout::resetMvcInstance(); $this->_helper->viewRenderer->setNoRender(true);
Et voici mes headers :
header('Content-Type: text/csv'); header('Content-disposition: attachment; filename='. $nomFichier);
Dernière modification par Godzinho (02-04-2010 08:01:38)
Hors ligne
j'ai eu un petit pb avec IE
voici les hedaer que j'utilise
$this->getResponse()->setHeader('Pragma' , 'public', true); $this->getResponse()->setHeader('Cache-Control', 'max-age=0', true); $this->getResponse()->setHeader('Content-Type', 'application/javascript; charset=iso-8859-1; name="' . $data->site_name . '.txt"', true); $this->getResponse()->setHeader('Content-Transfer-Encoding', 'binary', true); $this->getResponse()->setHeader('Content-disposition', 'attachment; filename="'.$data->site_name.'.txt"', true);
mieux vaut utiliser setHeader de ZF que header
en effet header envois imédiatement le header au client alors que setHeader prépare tous les headers qui seront envoyé avant le rendu
du coup si dans un script (appel de fonction etc..) un même header est positioné avec des valeur différentes seul le dernier est envoyé alors qu'avec header ce sera le premier
A+JYT
Dernière modification par sekaijin (02-04-2010 09:19:05)
Hors ligne
Je viens d'essayer de changer mon appel à la fonction header php par un appel via le ZF, mais lorsque je veux ouvrir mon fichier excel, il ne sait pas que c'est un fichier csv et je n'ai pas le nom voulu.
Hors ligne
Encore merci à tous
grâce à vous j'ai enfin réussi à faire fonctionner mon export CSV
je vous donne le code :
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); //on récupère les données à insérer puis formatage pour le csv //.... $fichier = fopen("/tmp/nom_fichier.csv", "w+"); fwrite($fichier, utf8_decode($data)); fclose($fichier); header("Content-Type: application/csv-tab-delimited-table"); header("Content-disposition: filename=nom_fichier.csv"); readfile("/tmp/nom_fichier.csv"); unlink("/tmp/nom_fichier.csv");
Hors ligne
De rien lefafa et merci pour le disableLayout() que je n'avais pas touvé.
Hors ligne
Perso, j'aurais aussi utilisé comme "ndesaleux" contextSwitch, qui se charge tout seul de desactiver le rendu, placé les entetes, ...
Hors ligne
lefafa a écrit:
Encore merci à tous
grâce à vous j'ai enfin réussi à faire fonctionner mon export CSV
je vous donne le code :Code:
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); //on récupère les données à insérer puis formatage pour le csv //.... $fichier = fopen("/tmp/nom_fichier.csv", "w+"); fwrite($fichier, utf8_decode($data)); fclose($fichier); header("Content-Type: application/csv-tab-delimited-table"); header("Content-disposition: filename=nom_fichier.csv"); readfile("/tmp/nom_fichier.csv"); unlink("/tmp/nom_fichier.csv");
il n'est pas nécessaire de passer par un fichier.
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); header("Content-Type: application/csv-tab-delimited-table"); header("Content-disposition: filename=nom_fichier.csv"); //on récupère les données à insérer puis formatage pour le csv //.... print(utf8_decode($data));
j'insiste n'utilise pas header mais $this->getResponse()->setHeader(...)
enfin pour garantir que cela fonctionne avec tout type de client pas seulement FF
je te conseille l'ensemble des headers que j'ai donné plus haut
A+JYT
Hors ligne
@ sekaijin
merci pour tous ces conseils
j'ai donc créé une aide d'action en utilisant tes headers avec :
$this->getActionController()->getResponse()->setHeader
ainsi je peux utiliser n'importe où dans mon appli l'export CSV.
Hors ligne
sekaijin a écrit:
lefafa a écrit:
Encore merci à tous
grâce à vous j'ai enfin réussi à faire fonctionner mon export CSV
je vous donne le code :Code:
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); //on récupère les données à insérer puis formatage pour le csv //.... $fichier = fopen("/tmp/nom_fichier.csv", "w+"); fwrite($fichier, utf8_decode($data)); fclose($fichier); header("Content-Type: application/csv-tab-delimited-table"); header("Content-disposition: filename=nom_fichier.csv"); readfile("/tmp/nom_fichier.csv"); unlink("/tmp/nom_fichier.csv");il n'est pas nécessaire de passer par un fichier.
Code:
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->layout->disableLayout(); header("Content-Type: application/csv-tab-delimited-table"); header("Content-disposition: filename=nom_fichier.csv"); //on récupère les données à insérer puis formatage pour le csv //.... print(utf8_decode($data));j'insiste n'utilise pas header mais $this->getResponse()->setHeader(...)
enfin pour garantir que cela fonctionne avec tout type de client pas seulement FF
je te conseille l'ensemble des headers que j'ai donné plus haut
A+JYT
Ces header ne sont pas suffisants sous ie quand le site est en https. Malheureusement, je ne trouve pas quels header pour les fichiers csv, alors que j'ai trouvé pour PDF
Hors ligne