Générer un PDF dans un module PrestaShop : guide complet
Découvrez comment générer des PDF personnalisés dans un module PrestaShop : contrôleur dédié, classe PDF, erreurs AJAX courantes et bonnes pratiques.
En bref : Pour générer un PDF dans PrestaShop, évitez AJAX et utilisez une URL directe vers un contrôleur dédié qui envoie le fichier avec les bons en-têtes HTTP. Créez un ModuleFrontController qui récupère l'ID commande via Tools::getValue(), vérifie les droits d'accès, puis appelle la classe PDF native ou TCPDF pour le rendu.
Générer un PDF dans un module PrestaShop : guide complet
La génération de documents PDF — factures personnalisées, bons de livraison, attestations ou tout autre document métier — est un besoin récurrent dans l'écosystème PrestaShop. Pourtant, de nombreux développeurs se heurtent à des PDF qui ne se génèrent tout simplement pas, souvent à cause d'un mauvais choix d'architecture pour déclencher la génération.
Cet article détaille les bonnes pratiques pour créer un système de génération PDF fiable dans un module PrestaShop, en évitant les pièges classiques liés à AJAX et aux en-têtes HTTP.
Pourquoi la génération PDF échoue souvent via AJAX
L'erreur la plus fréquente consiste à vouloir générer un PDF via une requête AJAX. Le problème est fondamental : un PDF est un fichier binaire que le navigateur doit recevoir avec les bons en-têtes HTTP (Content-Type: application/pdf, Content-Disposition) pour déclencher le téléchargement ou l'affichage.
Or, une requête AJAX intercepte la réponse en JavaScript. Le navigateur ne sait pas qu'il doit ouvrir un fichier — il reçoit du binaire brut dans un callback JS, ce qui produit :
- Un fichier corrompu si on tente de le reconstruire côté client
- Une page blanche si la réponse n'est pas traitée
- Des erreurs `json_decode` si le contrôleur attend du JSON mais reçoit du binaire
La solution : une requête HTTP classique
Pour générer un PDF, il suffit de passer par une URL directe ou un formulaire HTML classique. Le navigateur reçoit alors les en-têtes corrects et propose le téléchargement nativement.
// ❌ Mauvaise approche : AJAX
$.ajax({
url: baseUrl + 'module/monmodule/generatepdf',
data: { id_order: orderId },
success: function(response) {
// Le PDF binaire arrive ici... inutilisable
}
});
// ✅ Bonne approche : redirection directe
window.location.href = baseUrl + 'module/monmodule/generatepdf?id_order=' + orderId;
Créer un contrôleur dédié à la génération PDF
La méthode recommandée est de créer un ModuleFrontController (ou ModuleAdminController) qui reçoit l'identifiant de la commande via l'URL et génère le PDF.
Structure du module
monmodule/
├── monmodule.php
├── controllers/
│ └── front/
│ └── generatepdf.php
└── classes/
└── MonModulePDFGenerator.php
Le contrôleur front (PrestaShop 1.7 / 8.x)
<?php
/**
* Contrôleur de génération PDF
*/
class MonModuleGeneratepdfModuleFrontController extends ModuleFrontController
{
public function initContent()
{
parent::initContent();
// Récupérer l'ID commande depuis l'URL
$id_order = (int) Tools::getValue('id_order');
if (!$id_order) {
Tools::redirect('index.php?controller=history');
}
// Vérifier que la commande appartient au client connecté
$order = new Order($id_order);
if (!Validate::isLoadedObject($order)
|| $order->id_customer !== (int) $this->context->customer->id
) {
Tools::redirect('index.php?controller=history');
}
// Générer le PDF
$this->generateOrderPDF($order);
}
protected function generateOrderPDF(Order $order)
{
// Utiliser la classe PDF native de PrestaShop
$pdf = new PDF($order, PDF::TEMPLATE_INVOICE, $this->context->smarty);
$pdf->render();
exit; // Important : stopper l'exécution après l'envoi du PDF
}
}
Appel depuis un template Smarty
<a href="{$link->getModuleLink('monmodule', 'generatepdf', ['id_order' => $order.id_order])}"
class="btn btn-primary"
target="_blank">
Télécharger le PDF
</a>
Alternativement, un formulaire HTML classique fonctionne tout aussi bien :
<form method="GET" action="{$link->getModuleLink('monmodule', 'generatepdf')}" target="_blank">
<input type="hidden" name="id_order" value="{$order.id_order}">
<button type="submit" class="btn btn-primary">Générer le PDF</button>
</form>
Créer un PDF personnalisé avec TCPDF
PrestaShop embarque TCPDF. Pour des documents sur mesure (attestation, certificat, bon de garantie), on peut s'en servir directement.
<?php
class MonModulePDFGenerator
{
public static function generate(Order $order)
{
// TCPDF est chargé par PrestaShop
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8');
$pdf->SetCreator('Ma Boutique');
$pdf->SetAuthor('Ma Boutique');
$pdf->SetTitle('Document #' . $order->reference);
// Supprimer en-tête/pied par défaut
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$pdf->SetFont('helvetica', 'B', 16);
$pdf->Cell(0, 15, 'Document personnalisé', 0, 1, 'C');
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Commande : ' . $order->reference, 0, 1);
$pdf->Cell(0, 10, 'Date : ' . Tools::displayDate($order->date_add), 0, 1);
// Contenu HTML si besoin
$html = '<table border="1" cellpadding="5">';
$html .= '<tr><th>Produit</th><th>Quantité</th><th>Prix</th></tr>';
$products = $order->getProducts();
foreach ($products as $product) {
$html .= '<tr>';
$html .= '<td>' . htmlspecialchars($product['product_name']) . '</td>';
$html .= '<td>' . (int) $product['product_quantity'] . '</td>';
$html .= '<td>' . Tools::displayPrice($product['total_price_tax_incl']) . '</td>';
$html .= '</tr>';
}
$html .= '</table>';
$pdf->writeHTML($html, true, false, true, false, '');
// Envoyer le PDF au navigateur
$pdf->Output('document-' . $order->reference . '.pdf', 'D');
}
}
Côté back-office : AdminController
Pour la génération depuis le back-office, le principe est identique. On utilise un ModuleAdminController avec un token CSRF vérifié automatiquement par PrestaShop :
<?php
class AdminMonModulePdfController extends ModuleAdminController
{
public function postProcess()
{
if (Tools::isSubmit('generateCustomPdf')) {
$id_order = (int) Tools::getValue('id_order');
$order = new Order($id_order);
if (Validate::isLoadedObject($order)) {
MonModulePDFGenerator::generate($order);
exit;
}
$this->errors[] = $this->l('Commande introuvable.');
}
}
}
Gérer le cas où AJAX est incontournable
Si votre UX impose vraiment une requête AJAX (par exemple pour afficher un loader pendant la génération), la solution consiste à séparer la logique en deux étapes :
- **Requête AJAX** : prépare le PDF et le stocke temporairement sur le serveur
- **Redirection** : redirige vers une URL de téléchargement
// Étape 1 : préparer le PDF via AJAX
fetch(baseUrl + 'module/monmodule/preparepdf', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'id_order=' + orderId + '&ajax=1'
})
.then(response => response.json())
.then(data => {
if (data.success && data.download_url) {
// Étape 2 : télécharger le PDF préparé
window.location.href = data.download_url;
}
});
Côté PHP, le contrôleur de préparation génère le fichier, le stocke dans un dossier temporaire, et renvoie l'URL :
public function displayAjaxPreparePdf()
{
$id_order = (int) Tools::getValue('id_order');
$order = new Order($id_order);
// Générer et stocker le PDF
$filename = 'doc_' . $order->reference . '_' . time() . '.pdf';
$filepath = _PS_MODULE_DIR_ . 'monmodule/tmp/' . $filename;
// ... logique de génération avec TCPDF ::Output($filepath, 'F') ...
$download_url = $this->context->link->getModuleLink(
'monmodule', 'downloadpdf', ['file' => $filename, 'token' => $this->generateSecureToken($filename)]
);
die(json_encode([
'success' => true,
'download_url' => $download_url
]));
}
Sécurité : ne jamais exposer directement le chemin du fichier. Utilisez un token temporaire pour valider le téléchargement et supprimez le fichier après un délai raisonnable via un cron.
Checklist de débogage PDF
Si votre PDF ne se génère pas, vérifiez ces points dans l'ordre :
Questions fréquentes
Tout ce que vous devez savoir sur ce sujet.
Un projet PrestaShop ?
Discutons-en directement.
193 projets livrés
Lire sur le blog

Alexandre Carette
Expert PrestaShop & Architecture E-commerce
Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.