Nous allons générer des fichiers PDF avec KnpSnappyBundle, ce dernier se base sur Wkhtmltopdf et nécessite Xvfb fonctionner. On part du principe qu'on est sur une Debian 7.x.
1 - Installation de Wkhtmltopdf et Xvfb
su
apt-get update
apt-get install wkhtmltopdf xvfb
2 - Configuration du wrapper
nano /usr/bin/wkhtmltopdf.sh
EOF
#!/bin/sh
xvfb-run -a wkhtmltopdf $@
Rendre le fichier exécutable :
chmod +x /usr/bin/wkhtmltopdf.sh
3 - Installation de KnpSnappyBundle
Ajout du require dans le fichier /composer.json; ajouter les lignes suivantes :
"knplabs/knp-snappy-bundle": "^1.3"
Mettre à jour le vendors :
php composer.phar update
Activation du bundle dans le fichier /app/AppKernel.php, ajouter la ligne suivante dans la méthode registerBundles() :
new Knp\Bundle\SnappyBundle\KnpSnappyBundle(),
Configuration du Bundle :
nano app/config/config.yml
knp_snappy:
pdf:
enabled: true
binary: /usr/bin/wkhtmltopdf.sh
options: []
4 - Exemple de génération de PDF
Je vais partir du projet Jobmanager pour l'exemple, le sources sont disponibles sur GitHub.
Nous allons utiliser le bundle dans un couple controller/vue qui va générer un fichier PDF à partir de la vue.
4.1 - Création de la route
nano src/Jobmanager/MainBundle/Resources/config/routing/cv.yml
jobmanager_main_bundle_cv_generate_pdf:
path: /cv/generate-pdf
defaults: { _controller: JobmanagerMainBundle:Cv:generatePdf }
requirements:
_method: GET
4.2 - Modifications du controller
Ajout d'une méthode generatePdfAction() au controller :
nano src/Jobmanager/MainBundle/Controller/CVController.php
public function generatePdfAction()
{
$em = $this->getDoctrine()->getManager();
$cvPath = '/var/www/jobmanager/web/docs/cv.pdf';
// init memcache obj
$cache = $this->get('memcache.default');
// retrieve tutorials
if ($cache->get('tutorials_list')) {
$tutorials = $cache->get('tutorials_list');
} else {
$tutorials = $em->getRepository('JobmanagerMainBundle:Tutorial')->getLastPublishedTutorialsByDate(3);
$cache->delete('tutorials_list');
$cache->set('tutorials_list', $tutorials, null, self::ONE_DAY);
}
// retrieve candidate
$candidate = $em->getRepository('JobmanagerMainBundle:Candidate')->find(1);
// retrieve candidate
$candidate = $em->getRepository('JobmanagerMainBundle:Candidate')->find(1);
// calculate age
$birthDate = $candidate->getBirthdate()->format('m/d/Y');
// explode the date to get month, day and year
$birthDate = explode("/", $birthDate);
// get age from date or birthdate
$age = (date("md", date("U", mktime(0, 0, 0, $birthDate[0], $birthDate[1], $birthDate[2]))) > date("md")
? ((date("Y") - $birthDate[2]) - 1)
: (date("Y") - $birthDate[2]));
// retrieve categories competence
$categoriesCompetence = $em->getRepository('JobmanagerMainBundle:CategoryCompetence')->getAllCategoryWithCvCompetence();
// retrieve experiences
$experiences = $em->getRepository('JobmanagerMainBundle:JobExperience')->getJobExperiencesByDate();
// remove old cv
if (file_exists($cvPath)) {
unlink($cvPath);
}
// generate pdf
$this->get('knp_snappy.pdf')->generateFromHtml(
$this->renderView('JobmanagerCmsBundle:Cv:cv-pdf.html.twig',array(
'candidate' => $candidate,
'tutorials' => $tutorials,
'age' => $age,
'categoriesCompetence' => $categoriesCompetence,
'experiences' => $experiences
)
),
$cvPath
);
// set flash bag message
$this->get('session')->getFlashBag()->add('notice', 'CV généré');
// send redirection
return $this->redirect($this->generateUrl('jobmanager_main_bundle_admin_index'));
}
NDLR : Pour l'exemple la vue du cv n'étant pas terminée, elle suffira pour l'exemple. Ne pas tenir compte de l'appel du footer et du header ;).
4.3 - Création de la vue
nano src/Jobmanager/CmsBundle/Resources/Cv/cv-pdf.html.twig
{% extends "JobmanagerCmsBundle::layout.html.twig" %}
{% block title %}CV - Pierre-Antoine Foulquier | Développeur Web - Administrateur système{% endblock %}
{% block description_keywords %}
<meta name="description" content="CV de Pierre-Antoine Foulquier, développeur web et adminisrateur système : compétences orientées dev-back, devops sur le framework PHP Symfony 2." />
<meta name="keywords" content="CV, développeur, web, administrateur système, back-end, devops, symfony 2, PHP" />
{% endblock %}
{% block fb_meta_og %}
<meta property="og:title" content="CV - Pierre-Antoine Foulquier | Développeur Web - Administrateur système"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="http://www.foulquier.info/competences/cv/">
<meta property="og:image" content="http://www.foulquier.info/images/fb-og-picto.jpg"/>
<meta property="og:description" content="CV de Pierre-Antoine Foulquier, développeur web et administrateur système : compétences orientées dev-back, devops sur le framework PHP Symfony 2."/>
<meta property="og:site_name" content="Pierre-Antoine Foulquier | Développeur Web - Administrateur système"/>
<meta property="fb:admins" content="100004078454329"/>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div id="cv-wrapper">
<section class="header-cv">
<h1 class="title-02">{{ candidate.firstname }} {{ candidate.lastname }}</h1>
<p>{{ age }} ans</p>
<p>{{ candidate.jobname }}</p>
</section>
<section class="bloc-tech-cv">
<h2 class="title-02">Compétences techniques</h2>
{% for category in categoriesCompetence %}
<div class="category-tech">
<p>
<strong>{{ category.title }} :</strong>
{% for competence in category.competences %}
{% if competence.isOnCv == true %}
{% if loop.last %}
{{ competence.name }}.
{% else %}
{{ competence.name }},
{% endif %}
{% endif %}
{% endfor %}
</p>
</div>
{% endfor %}
</section>
<section class="language">
<h2 class="title-02">Langues</h2>
<div class="bloc-language">
<p><strong>Anglais :</strong> Courant</p>
</div>
<div class="bloc-language">
<p><strong>Allemand :</strong> Bilingue</p>
</div>
</section>
<section class="formation">
<h2 class="title-02">Formations</h2>
<div class="bloc-formation">
<p><strong>2012 :</strong> Formation PHP développeur à IP FORMATION (280h) - Paris 12e</p>
</div>
<div class="bloc-formation">
<p><strong>2011 :</strong> Formation PHP développeur (niveau II) à l’IFOCOP (900h) - Paris 11e</p>
</div>
</section>
<section class="experience">
<h2 class="title-02">Expériences</h2>
{% for experience in experiences %}
<div class="bloc-experience">
<h3 class="company-name">
<strong>{{ experience.companyName }}</strong>
</h3>
<h4 class="title-experience">{{ experience.titleJob }}</h4>
<h4 class="date-experience">{{ experience.dateBegin|date('d/m/Y') }} - {{ experience.dateEnd|date('d/m/Y') }}</h4>
<p>{{ experience.description }}</p>
{% for key, project in experience.projects %}
<div class="bloc-project">
<h5 class="title-project">
<strong>Projet {{ key+1 }} : {{ project.name }}</strong>
</h5>
<div class="project-description">{{ project.description|raw }}</div>
<h6 class="sub-title-project">
<strong>Domaine d'intervention :</strong>
</h6>
<div class="domain-intervention">{{ project.domainIntervention|raw }}</div>
<h6 class="sub-title-project">
<strong>Environnement technique :</strong>
</h6>
<div class="technical-environnement">{{ project.technicalEnvironnement|raw }}</div>
{% if project.url is not null %}
<p>
<a href="{{ project.url }}" target="_blank"><strong>Lien :</strong> {{ project.url }}</a>
</p>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
</section>
</div>
</div>
</div>
{% endblock %}
On peut tester en appelant l'url /admin/cv/generate-pdf. Le résultat sera disponible à l'emplacement suivant : /var/www/jobmanager/web/docs/cv.pdf