Générer un document au format PDF avec la librairie FPDF
Partons d’un site de e-commerce classique (bien sûr réalisé avec CakePHP), sur lequel nous souhaitons offrir au client la possibilité de télécharger sa facture au format PDF. Pour générer cette dernière, nous allons utiliser la librairie FPDF, qui s’avère être simple à utiliser et présente l’avantage d’être libre. Comment allons-nous procéder pour intégrer la librairie FPDF à notre application CakePHP ?
Chaque application CakePHP dispose d’un dossier vendor, qui permet d’inclure des librairies extérieures au sein d’une application, par simple appel de la fonction vendor(‘ma_librairie’) dans un Contrôleur. Nous aurions pu également choisir d’utiliser le dossier vendor qui se trouve dans le cœur de CakePHP, afin de partager une librairie entre plusieurs applications.
1. Téléchargement et installation de la librairie FPDF
- Téléchargement : http://www.fpdf.org/fr/download.php
- Manuel : http://www.fpdf.org/fr/doc/
Une fois l’archive téléchargée, nous copions le fichier fpdf.php ainsi que l’intégralité du dossier fonts dans un nouveau répertoire nommé pdf, que nous plaçons dans le dossier vendor de notre application :
{app}/vendors/pdf/fpdf.php
{app}/vendors/pdf/fonts/
2. Modèle de données
Pour les besoins de ce tutoriel, réduisons le modèle de données de notre site à ce qui nous intéresse uniquement :
User hasMany Order (Un utilisateur dispose de plusieurs commandes)
Order belongsTo User (Une commande appartient à un et un seul utilisateur)
Order hasMany OrderLine (Une commande dispose de plusieurs lignes de commande)
Note : Le modèle OrderLine (ligne de commande) sert à conserver toutes les informations de la commande au moment de son enregistrement, pour qu’un changement sur les produits achetés n’ait pas d’impact sur le contenu de la commande.
3. Génération du fichier PDF
3.1. L’action download du Contrôleur
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // app/controllers/orders_controller.php function download($id = null) { if(null === $id) { $this->redirect($this->referer()); } $order = $this->Order->findById(intval($id)); if(empty($order)) { $this->redirect($this->referer()); } if($order["User"]["id"] !== $this->loggedUserId) { $this->redirect($this->referer()); } vendor("pdf/fpdf"); $this->layout = "pdf_layout"; $this->set("order", $order); } |
Quelques précisions sur le code ci-dessus :
2 3 4 5 | if(null === $id) { $this->redirect($this->referer()); } |
Nous ne voulons pas que les utilisateurs accèdent à l’action sans passer un ID de commande.
8 9 10 11 12 13 | $order = $this->Order->findById(intval($id)); if(empty($order)) { $this->redirect($this->referer()); } |
Si la commande n’existe pas, ce qui est peu probable, nous redirigeons l’utilisateur d’où il vient.
13 14 15 16 | if($order["User"]["id"] !== $this->loggedUserId) { $this->redirect($this->referer()); } |
Si l’auteur de la commande n’est pas l’utilisateur actuellement connecté, nous redirigeons l’utilisateur.
Note : Depuis la version 1.2.0.6311, le troisième argument de la méthode redirect vaut true par défaut. La fonction exit() est donc appelée après chaque exécution de la méthode, il n’est plus nécessaire de le spécifier.
19 | $this->layout = "pdf_layout"; |
Nous forçons CakePHP à utiliser le layout spécial PDF au lieu du layout par défault du site.
3.2. Un layout pour la sortie PDF
Nous avons besoin d’un layout particulier qui informera le navigateur que nous lui enverrons des données au format PDF. Dans {app}/views/layouts, nous créons le fichier pdf_layout.ctp :
1 2 3 | // app/views/layout/pdf_layout.ctp header("Content-type: application/pdf"); echo $content_for_layout; |
3.3. La vue de l’action download
Nous allons maintenant créer la vue de l’action download. Nous n’utiliserons pas un fichier avec des balises (x)HTML classiques, mais des fonctions de la librairie FPDF pour formater les informations de la commande. Nous avons volontairement simplifié à l’extrême la mise en page de la facture, nous laissons au lecteur le bon soin de parcourir le manuel pour parvenir à un résultat satisfaisant.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // app/views/orders/download.ctp $pdf = new FPDF(); $pdf->SetMargins(7, 7, 7); $pdf->AliasNbPages(); $pdf->AddPage(); $pdf->SetFont("arial", 'B', 10); $pdf->SetTextColor(0, 0, 0); foreach($orders as $order) { $pdf->Cell(20, 20, $order["OrderLine"]["description"]." ".$order["OrderLine"]["price"]); } $pdf->Output($order["Order"]["id"] . ".pdf", "D"); |
3.4. Lien vers le téléchargement de la facture PDF
Nous allons lancer la génération du fichier PDF à partir d’un simple lien pointant vers l’action download du contrôleur OrderController.
Depuis l’historique des commandes d’un client, nous parcourons la liste des commandes et générons pour chacune un lien vers le téléchargement du document PDF.
1 2 3 4 5 6 7 8 9 10 11 12 | // app/views/users/profile.ctp foreach($orders as $order) { e( $html->link( "Télécharger la facture au format PDF", "/orders/download/" . $order["Order"]["id"], array("target" => "_blank") ) ); } |
Commentaires
1 février 2008 à 11:41
hello,
Article édifiant (surtout quand on vient de mettre la même chose en place sur une application développée SANS framework
)
Juste une remarque / demande d’avis : fpdf est-il toujours le bon choix ? En particulier dans le cadre d’une application multilingue, où des classes dérivées de fpdf semblent plus avancées (je pense entre autres à tcpdf, que je n’ai pas encore eu l’occasion d’évaluer).
(merci pour votre blog !)
1 février 2008 à 19:29
Salut,
Pour des besoins complexes, TCPDF (qui est lui-même basé sur FPDF) peut s’avérer en effet plus complet dans certains cas. Cependant il est tout à fait possible, par le biais de CakePHP, de créer un helper à partir de la librairie de ton choix (FPDF, TCPDF, oPDF .. ) pour repondre à certains besoin propre à ton application (En-tête personalisée au couleur de l’entreprise, encodage des caractères en sortie .. ).
18 février 2008 à 15:26
Bonjour je tente de mettre en place
ce systeme. je rencontre cependant un problème de headers, voici le message d’erreur :
Warning: Cannot modify header information - headers already sent by (output started at /monchemin/www/views/helpers/fpdf.php:35) in /monchemin/www/vendors/fpdf/fpdf.php on line 1037
FPDF error: Some data has already been output to browser, can’t send PDF file
(j’ai remplacé volontairement l’acces par /monchemin/ :p)
et voici la ligne en question :
header(’Content-Type: application/octet-stream’);
je ne vois pas trop comment faire.
par avance merci de vos conseils!
18 février 2008 à 15:32
hm autant pour moi, j’avais un reste de helper de pdf qui trainait, désolé pour le dérrangement ca marche niquel maintenant !
merci pour le tuto et votre site, je reviendrais :p
bonne continuation