Beruflich Dokumente
Kultur Dokumente
CogniTIC
Plan de formation
• Pré-requis
• Tour d’horizon
• Fonctionnement de base
• Bases de données
• Template
• Les formulaires
• Validation
• Sécurité
• Traduire son interface
Tour d’horizon
Tour d’horizon
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Tour d’horizon
Introduction
Symfony est un framework (cadre de travail), une
boite à outils destinés aux développeurs.
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Tour d’horizon
Installation & configuration
• Option 1: « composer »
• Option 2: via archive
• Mettre à jour les Vendors
Tour d’horizon
Installation & configuration
Option 1: “composer”
Composer est une bibliothèque de gestion de dépendances pour PHP,
que vous pouvez utiliser pour télécharger l'Édition Standard de
symfony3.
https://getcomposer.org/Composer-Setup.exe
• Option 1: « composer »
• Option 2: via archive
• Mettre à jour les Vendors
Tour d’horizon
Installation & configuration
Option 2: via archive
Aller à l’adresse http://symfony.com/download
Cliquer sur le lien Download symfony-x.x.x
Tour d’horizon
Installation & configuration
Option 2: via archive
Dézipper l’archive dans le dossier WWW de wamp
Accéder à l’url de configuration
http://localhost/monapplicationsymfony/web/confi
g.php
Tour d’horizon
Installation & configuration
Option 2: via archive
Tour d’horizon
Installation & configuration
Option 2: via archive
Tour d’horizon
Installation & configuration
Option 2: via archive
Accéder à votre application :
http://localhost/mon-application-
symfony/web/app_dev.php/
Tour d’horizon
Installation & configuration
• Option 1: « composer »
• Option 2: via archive
• Mettre à jour les Vendors
Tour d’horizon
Installation & configuration
Mettre à jour les Vendors
A ce stade, vous avez téléchargé un projet Symfony entièrement
fonctionnel dans lequel vous allez commencer à développer votre
propre application.
Un projet Symfony dépend d'un certain nombre de bibliothèques
externes. Celles-ci sont téléchargées dans le répertoire vendor/ de
votre projet via une bibliothèque appelée Composer.
http://getcomposer.org/installer.
php installer
php composer.phar install
Tour d’horizon
Installation & configuration
Mettre à jour les Vendors
Lorsque vous exécutez php composer.phar install ou php
composer.phar update, Composer va exécuter les commandes «
install/update » pour vider le cache et installer les ressources (« assets »
en anglais).
Au lieu de copier les ressources Symfony, vous pouvez créer des liens
symboliques si votre système d'exploitation les supporte.
Tour d’horizon
Installation & configuration
Mettre à jour les Vendors
Pour créer des liens symboliques, ajoutez une entrée dans le noeud
extra de votre fichier composer.json avec la clé symfony-assets-
install et la valeur symlink :
"extra": {
"symfony-app-dir": "app",
"symfony-web-dir": "web",
"symfony-assets-install": "symlink"
}
Si vous passez relative au lieu de symlink à la commande « symfony-
assets-install », cette dernière génèrera des liens symboliques relatifs.
Tour d’horizon
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Tour d’horizon
Architecture du framework
Principaux répertoires
App
contient tout ce qui concerne l’application (config, etc.)
Bin
contient tous les exécutables utiles pour le développement (clear:cache, generate:bundle, etc.)
Src
contient tout le développement (entités, contrôleurs, vues, etc.)
Vendor
contient toutes les bibliothèques externes (Doctrine, Twig, etc.)
Web
contient tous les fichiers utilisateurs (images, css, js, contrôleur frontal (point d’entrée du site), etc.)
Tour d’horizon
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
• Exercice
Tour d’horizon
Bundle
Qu’est-ce ?
Un bundle est un ensemble structuré de fichiers (PHP,
twig, ...) qui implémentent une fonctionnalité unique
(un blog, un forum, ...) et qui peut être facilement
partagé avec d'autres développeurs.
Source : symfony.com
Tour d’horizon
Bundle
Convention
Un bundle est toujours composé d’un nom suivi du
terme Bundle.
EX : BlogBundle
Tour d’horizon
Bundle
Structure
Controller
contient toute la logique et doit retourner une réponse
DependencyInjection
contient des informations sur le bundle
Entity
contient tous vos modèles
Ressources
contient 2 répertoires (config et view) qui respectivement
permettent de configurer les routes et de définir les templates de
l’application.
…
Tour d’horizon
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Tour d’horizon
Console
Qu’est-ce ?
La console est un moyen rapide et efficace de créer
des éléments (base de données, Bundles, Entités,…)
grâce à des commandes.
Tour d’horizon
Console
Où entre-t-on nos commandes ?
Depuis l’invite de commandes (windows)
Menu Démarrer > Programmes > Accessoires > Invite de commandes.
+ R > cmd
Depuis le terminal (Linux/mac).
Ouvrir le terminal dans les applications
Tour d’horizon
Console
Configuration nécessaire
1. Allez dans les paramètres système avancés
Démarrer > Panneau de configuration > Système et sécurité > Système > Paramètres
système avancés ;
2. Cliquez sur le bouton Variables d'environnement… ;
3. Regardez dans le panneau Variables système ;
4. Trouvez l'entrée Path;
5. Double-cliquez sur l'entrée Path ;
6. Entrez votre répertoire PHP à la fin, sans oublier le point-virgule ";" auparavant. C'est le
répertoire dans lequel se trouve le fichier php.exe. Par exemple
;C:\wamp\bin\php\php5.5.12 ;
7. Confirmez en cliquant sur OK. Vous devez ensuite redémarrer l'invite de commandes
pour prendre en compte les changements.
tb_main_homepage:
path: /default/index/{name}
defaults: { _controller: TBMainBundle:Default:index }
Tour d’horizon
Console
Exemple : lister les commandes disponibles
1. Aller dans votre dossier/application symfony.
Ex : www/mon-application
2. Entrer :
php bin/console
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Tour d’horizon
En résumé
Symfony 3 :
est un « framework » qui a pour objectif d'améliorer la productivité des équipes qui l'utilisent
(Développeurs et Designers);
utilise l'architecture MVC (Modèle, Vue, Contrôleur).
utilise les espaces de noms pour éviter tout conflit entre 2 noms identiques de classe, fonction,…
est organisé en quatre répertoires : app, src (répertoire principal du développeur), vendor et
web.
se base sur des bundles (brique fonctionnelle). Un bundle concerne une fonctionnalité de
l’application. Un bundle est constitué de min. 4 répertoires (Controller, DependencyInjection,
Entity, Ressources (config et view);
Met à disposition un outil de console pour augmenter la rapidité de création de certaines
tâches (Bases de données, entités, bundle,…)
demande beaucoup d’énergie et d’étude pour une bonne maîtrise mais une fois les bases
acquises, il offre une grande souplesse, flexibilité et rapidité;
Tour d’horizon
• Introduction
• Installation & configuration
• Architecture du framework
• Bundles
• Console
• Résumé
Fonctionnement de base
Fonctionnement de base
• Routage/Routeur
• Route
• Contrôleur
• Vue (Template Twig)
• Résumé
• Exercice
Fonctionnement de base
Routage/Routeur
Le routeur Symfony3 vous laisse définir des URLs que vous faites correspondre à
différents points de votre application (contrôleur).
Le routeur permet d’obtenir des URLs propres et facilement modifiables.
Exemple de routes
url habituelle : mon-site.be/index.php?news_id=5&category=sport
url propre : mon-site.be/news/sport/5
Fonctionnement de base
Routage/Routeur
Exemple du fichier routing.yml
#app/config/routing.yml Commentaire yml
ctic_news:
resource: "@CTICNewsBundle/Resources/config/routing.yml"
prefix: /news
News_show :
path: /news/{slug}
defaults : {_controller: CTICNewsBundle:News:show}
Le nom des méthodes d’un contrôleur doit toujours être suivi du terme
Syntaxe de base :
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.titre }}</a></li>
{% endfor %}
</ul>
</body>
</html>
Fonctionnement de base
Vue (Template Twig)
Notion d’héritage
base qui contient tous les éléments communs de votre site et de définir
des blocs qui pourront être surchargés.
Fonctionnement de base
Vue (Template Twig)
Template de base
<!DOCTYPE html>
<html>
<head>
<title>Bienvenue dans Symfony3 !</title>
</head>
<body>
{% block body %}
<h1>{{ titre_page }}</h1>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.titre }}</a></li>
{% endfor %}
</ul>
{% endblock %}
</body>
</html>
Fonctionnement de base
Vue (Template Twig)
Template étendu
{% extends ‘bundleName::index.html.twig' %}
{% block body %}
<p>Tout ce qui est ici va surchargé le template de base.</p>
{% endblock %}
On remarque ici qu’il n’y a plus besoin de remettre les balises <html>,
class Product
{
protected $name;
protected $price;
protected $description;
/**
* @ORM\Entity
* @ORM\Table(name="product")
*/
class Product
{
/**
* @ORM\Column(type="string", length=100)
*/
protected $name;
}
Bases de données
ORM Doctrine2
Générer une entité
bin app/console doctrine:generate:entity
C’est l’ORM qui gère notre DB. N’allez donc pas ajouter de
foreignKey vous-même dans phpMyadmin !
Bases de données
Relation entre entités
• Introduction
• Propriétaire et inverse
• One-to-one
• Many-to-one / One-to-many
• Many-to-many
• Relations unidirectionnelle et bidirectionnelles
Bases de données
Relation entre entités
One-to-one
Faire correspondre une entité à une autre uniquement.
/**
* @ORM\ManyToOne(targetEntity=“CO\VotreBundle\Entity\Article“)
*/
private $article;
Bases de données
Relation entre entités
• Introduction
• Propriétaire et inverse
• One-to-one
• Many-to-one / One-to-many
• Many-to-many
• Relations unidirectionnelle et bidirectionnelles
Bases de données
Relation entre entités
Many-to-many
Faire correspondre plusieurs objets à plusieurs objets.
Dans ce cas, l’attribut est une liste d’objets et doit-être défini comme un
ArrayCollection (sotre de tableau) pour Doctrine. ArrayCollection est un objet
mais il se manipule comme un tableau avec quelques méthodes en plus.
/**
* @ORM\ManyToMany(targetEntity=“CO\VotreBundle\Entity\Categories“)
*/
private $categories;
Bases de données
Relation entre entités
Exemple : suite
Déclaration du constructeur
Public function __constructor($categories){
$this->categories = new ArrayCollection();
}
return $this;
}
Bases de données
Relation entre entités
• Introduction
• Propriétaire et inverse
• One-to-one
• Many-to-one / One-to-many
• Many-to-many
• Relations unidirectionnelle et bidirectionnelles
Bases de données
Relation entre entités
Uni et bidirectionnel
L’unidirectionnel signifie qu’on ne peut obtenir les
informations qu’à partir du propriétaire;
A l’inverse, une relation bidirectionnelle permettra de
retrouver les informations aussi bien à partir de l’inverse que
du propriétaire.
Bases de données
• Introduction
• ORM Doctrine2
• Entité (modèle)
• Relation entre entités
• Mapping
• Les entités et doctrine
• Les évènements et extensions Doctrine
Bases de données
Mapping
Qu’est-ce ?
C’est le fait d’associer de vos classes PHP avec des
tables de votre base, et les propriétés de ces classes
PHP avec des colonnes de la table.
Source : symfony.com
Bases de données
• Introduction
• ORM Doctrine2
• Entité (modèle)
• Relation entre entités
• Mapping
• Les entités et doctrine
• Les évènements et extensions Doctrine
Bases de données
Les entités et doctrine
• Le service Doctrine
• L’entity manager
• Les repositories
• Persister les données
• Récupérer les données
Bases de données
Les entités et doctrine
Le service Doctrine
Permet de gérer la base de données soit :
• Le service Doctrine
• L’entity manager
• Les repositories
• Persister les données
• Récupérer les données
Bases de données
Les entités et doctrine
L’entity manager
Indique à l’orm de persister un objet
Exécute les requêtes sql
• Le service Doctrine
• L’entity manager
• Les repositories
• Persister les données
• Récupérer les données
Bases de données
Les entités et doctrine
Les répositories (dépôt)
Permettent de récupérer des entités pour une classe
déterminée.
• Le service Doctrine
• L’entity manager
• Les repositories
• Persister les données
• Récupérer les données
Bases de données
Les entités et doctrine
Persister les données
Se fait depuis un contrôleur, 2 actions sont nécessaires :
1. Persister l’entité :
indique que l’objet est persisté (aucune requête n’est exécutée)
$em->persist($monEntité);
2. Flush :
exécute toutes les requêtes pour les entités persistées (enregistre en DB)
$em->flush();
Bases de données
Les entités et doctrine
• Le service Doctrine
• L’entity manager
• Les repositories
• Persister les données
• Récupérer les données
Bases de données
Les entités et doctrine
Récupérer les données
Via le Doctrine Query Language (DQL)
Simple requête sql adaptée pour l’objet
SELECT n FROM votreBundle:News n
Note: Si vous utilisez YAML ou XML pour votre mapping, ceci n’est pas nécessaire.
Bases de données
Les évènements et extensions Doctrine
Callbacks de cycle de vie
Maintenant, nous pouvons dire à Doctrine d’éxécuter une méthode à
n’importe quel évènement du cycle de vie.
Exemple: On veut définir une date created à la courante, uniquement
lorsque l’entité est ajoutée pour la première fois.
/**
* @ORM\PrePersist
*/
public function setCreated()
{
$this->created = new \DateTime();
}
Maintenant, Doctrine appelera automatiquement cette méthode.
Bases de données
Les évènements et extensions Doctrine
Un template est un simple fichier texte qui peut générer n'importe quel
format basé sur du texte (HTML, XML, CSV, LaTeX ...).
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
</body>
</html>
Template
Introduction
Twig définit deux types de syntaxe spéciale :
{{ title | upper }}
Twig est fourni avec une longue liste de tags et de filtres qui sont
disponibles par défaut.
Vous pouvez même ajouter vos propres extensions à Twig si
besoin.
Template
Introduction
Comme vous le verrez tout au long de la documentation, Twig
supporte aussi les fonctions, et de nouvelles fonctions peuvent
être ajoutées.
Par exemple, la fonction suivante utilise le tag standard for et la
fonction cycle pour écrire dix balises div en alternant les classes
odd et even :
{% for i in 0..10 %}
<div class="{{ cycle(['odd', 'even'], i) }}">
<!-- some HTML here -->
</div>
{% endfor %}
Tout au long de ce chapitre, les exemples de templates seront
donnés à la fois avec Twig et PHP.
Template
• Introduction
• Héritage de template et layouts
• Nommage de template et emplacements
• Css et Javascripts avec Twig
• Trois niveaux d’héritage
• L’échappement
Template
Héritage de template et layouts
Dans symfony3, nous abordons ce problème différemment : un
template peut être décoré par un autre.
<div id="content">
{% block body %}{% endblock %}
</div>
</body>
</html>
Template
Héritage de template et layouts
Ce template définit le squelette HTML de base d'un document
constitué simplement de deux colonnes.
Dans cette exemple, trois espaces {% block %} sont définis (title,
sidebar et body).
Chacun de ces blocs peut être soit surchargé dans un template
enfant ou soit conserver leur code d'origine.
Ce template peut aussi être rendu directement.
Dans ce cas, les blocs title, sidebar et body conserveront
simplement les valeurs par défaut utilisées dans ce template.
Template
Héritage de template et layouts
TWIG:
{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends '::base.html.twig' %}
{% block body %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
Template
Héritage de template et layouts
La clé de l'héritage de template est la balise {% extends %}.
Elle indique au moteur de template d'évaluer d'abord le
template de base, qui configure le layout et définit plusieurs
blocs.
Le template enfant est ensuite rendu.
Durant ce traitement les blocs parents title et body sont
remplacés par ceux de l'enfant.
Dépendant de la valeur de blog_entries, la sortie peut ressembler
à ceci :
Template
Héritage de template et layouts
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Mes billets de blog cools</title>
</head>
<body>
<div id="sidebar">
<ul>
<li><a href="/">Accueil</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</div>
<div id="content">
<h2>Mon premier post</h2>
<p>Le corps du premier post.</p>
<h2>Un autre post</h2>
<p>Le corps du deuxième post.</p>
</div>
</body>
</html>
Template
Héritage de template et layouts
Remarquons que comme le template enfant n'a pas défini le bloc
sidebar, la valeur du template parent est utilisée à la place.
Le contenu d'une balise {% block %} d'un template parent est toujours
utilisé par défaut.
Plus vous utilisez les balises {% block %} dans les templates, mieux
c'est. Souvenez-vous, les templates enfants ne doivent pas
obligatoirement définir tous les blocs parents, donc créez autant de
blocs que vous désirez dans le template de base et attribuez leurs
une configuration par défaut. Plus vous avez de blocs dans le
template de base, plus le layout sera flexible.
Template
Héritage de template et layouts
Si vous vous retrouvez à dupliquer du contenu dans plusieurs
templates, cela veut probablement dire que vous devriez déplacer
ce contenu dans un {% block %} d'un template parent. Dans certain
cas, la meilleure solution peut être de déplacer le contenu dans un
nouveau template et de l'inclure avec la directive``include``
{% block stylesheets %}
{{ parent() }}
{# ... #}
Template
Css et Javascripts avec Twig
Dans le template enfant, nous surchargeons simplement le bloc
stylesheets en ajoutant une nouvelle balise de feuille de style dans ce
bloc.
Bien sûr, puisque nous voulons ajouter du contenu au bloc parent (et
non le remplacer), nous devons utiliser la fonction Twig parent() pour
inclure le bloc stylesheets du template de base.
Template
Css et Javascripts avec Twig
Nous pouvons aussi inclure des ressources situées dans le dossier
Resources/public de vos bundles.
Vous devrez lancer la commande php app/console assets:install target
[--symlink] pour placer les fichiers dans le bon répertoire ("web" par
défaut)
Le résultat final est une page qui inclut à la fois la feuille de style
main.css et contact.css.
Template
• Introduction
• Héritage de template et layouts
• Nommage de template et emplacements
• Css et Javascripts avec Twig
• Trois niveaux d’héritage
• L’échappement
Template
Trois niveaux d’héritage
Une façon commune d'utiliser l'héritage est d'utiliser l'approche à trois
niveaux. Cette méthode fonctionne parfaitement avec trois différents
types de templates détaillés :
{% block body %}
<h1>Blog Application</h1>
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
Template
• Introduction
• Héritage de template et layouts
• Nommage de template et emplacements
• Css et Javascripts avec Twig
• Trois niveaux d’héritage
• L’échappement
Template
L’échappement
• introduction
• L’échappement avec Twig
• L’échappement avec PHP
Template
L’échappement
Introduction
Lors de la génération HTML d'un template, il y a toujours un risque qu'une
variable du template affiche du code HTML non désiré ou du code
dangereux côté client.
Le résultat est que le contenu dynamique peut casser le code HTML de
la page résultante ou permettre à un utilisateur malicieux de réaliser une
attaque Cross Site Scripting (XSS).
Considérons cet exemple classique :
Twig: Hello {{ name }}
PHP: Hello <?php echo $name ?>
Template
L’échappement
Introduction
Imaginons que l'utilisateur ait enregistrer le code suivant comme nom :
<script>alert('hello!')</script>
Hello <script>alert('hello!')</script>
Template
L’échappement
Introduction
Et bien que cela semble inoffensif, si un utilisateur peut aller aussi loin, ce
même utilisateur peut aussi écrire un Javascript qui réalise des actions
malicieuses dans un espace sécurisé d'un inconnu et légitime utilisateur.
Hello <script>alert('helloe')</script>
Template
L’échappement
• introduction
• L’échappement avec Twig
• L’échappement avec PHP
Template
L’échappement
L’échappement avec Twig
Si vous utilisez les templates Twig, alors l'échappement est activé par
défaut.
Ce qui signifie que vous êtes protégé immédiatement des
conséquences non intentionnelles du code soumis par l'utilisateur.
Par défaut, l'échappement suppose que le contenu est bien échappé
pour un affichage HTML.
Template
L’échappement
L’échappement avec Twig
Dans certains cas, nous aurons besoin de désactiver l'échappement de
la sortie lors du rendu d'une variable qui est sure et qui contient des
décorations qui ne doivent pas être échappées.
Supposons que des utilisateurs administrateurs sont capables décrire des
articles qui contiennent du code HTML.
Par défaut, Twig échappera le corps de l'article.
Pour le rendre normalement, il suffit d'ajouter le filtre raw :
{{ article.body | raw }}
Template
L’échappement
• introduction
• L’échappement avec Twig
• L’échappement avec PHP
Template
L’échappement
L’échappement avec PHP
L'échappement n'est pas automatique lorsque vous utilisez des
templates PHP.
Ce qui signifie que, à moins que vous ne choisissiez explicitement
d'échapper une variable, vous n'êtes pas protégé.
Pour utiliser l'échappement, utilisons la méthode spéciale escape() de
view :
• Introduction
• Créer des formulaires
• Formulaires imbriqués
Formulaires
Introduction
• Vous avez déjà créé des formulaires en HTML et PHP, vous savez donc que
c'est une vraie galère !
• À moins d'avoir créé vous-mêmes un système dédié, gérer correctement des
formulaires s'avère être un peu mission impossible.
• Par « correctement », j'entends de façon maintenable, mais surtout
réutilisable. Heureusement, le composant Form de symfony3 arrive à la
rescousse !
Note: N'oubliez pas que les composants peuvent être utilisés hors d'un projet
Symfony. Vous pouvez donc reprendre le composant Form dans votre site même
si vous n'utilisez pas Symfony.
Formulaires
• Introduction
• Créer des formulaires
• Formulaires imbriqués
Formulaires
Créer des formulaires
• Formulaires Symfony3
• Gestion basique d’un formulaire
• Gestion de la soumission d’un formulaire
• Gérer les valeurs par défaut du formulaire
• Personnaliser l’affichage
• Externaliser ses formulaires
Formulaires
Créer des formulaires
Formulaires Symfony3
Un formulaire Symfony, qu’est ce que c’est ?
• Un objet
• Un moyen pour construire un formulaire à partir de cet objet,
un FormBuilder, « constructeur de formulaire ».
Formulaires
Créer des formulaires
Gestion basique d’un formulaire
Maintenant que nous avons vus ce qu’il nous faut, passons à la
pratique avec un exemple.
On remarque que cette classe est un « bon vieil objet PHP » car jusque
ici, elle n’a rien à voir avec Symfony.
Formulaires
Créer des formulaires
Gestion basique d’un formulaire
Maintenant que vous avez créé une classe Task, la prochaine étape
est de créer et d'afficher le formulaire HTML.
Pour l’instant, tout ceci est effectué dans le contrôleur. Nous verrons
plus tard comme rendre notre formulaire « autonome ».
Formulaires
Créer des formulaires
Gestion basique d’un formulaire
Construction de notre formulaire:
// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
$form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
Formulaires
Créer des formulaires
Gestion basique d’un formulaire
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
}
}
En twig: {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{{ form(form) }}
En PHP: <?php echo $view['form']->form($form) ?>
Formulaires
Créer des formulaires
Gestion basique d’un formulaire
$form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
Formulaires
Créer des formulaires
Gestion de la soumission d’un formulaire
$form->handleRequest($request);
if ($form->isValid()) {
// fait quelque chose comme sauvegarder la tâche dans la bdd
return $this->redirect($this->generateUrl('task_success'));
}
// ...
}
Formulaires
Créer des formulaires
Gestion de la soumission d’un formulaire
Ce contrôleur suit un pattern commun dans la manière de gérer les
formulaires, et a trois scénarios possibles :
Lorsque votre formulaire contient plus d'un bouton submit, vous devrez
vérifier quel bouton a été utilisé pour adapter les actions à effectuer
dans votre contrôleur.
Note: Le support des boutons dans les formulaires est apparu avec
Symfony 2.3
Formulaires
Créer des formulaires
Gestion de la soumission d’un formulaire
Reprenons notre exemple précédent
Ajoutons un second bouton avec l'intitulé « Enregistrer et nouveau »
(save and add) à notre formulaire:
$form = $this->createFormBuilder($task)
->add('task', ‘TextType::class')
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->add('saveAndAdd', SubmitType::class)
->getForm();
Formulaires
Créer des formulaires
Gestion de la soumission d’un formulaire
Dans votre contrôleur, utilisez la méthode isClicked() du bouton pour
vérifier si un clic sur le bouton « Enregistrer et nouveau » a été fait:
if ($form->isValid()) {
// fait quelque chose comme sauvegarder la tâche dans la bdd
$nextAction = $form->get('saveAndAdd')->isClicked()
? 'task_new'
: 'task_success';
return $this->redirect($this->generateUrl($nextAction));
}
Formulaires
Créer des formulaires
• Formulaires symfony3
• Gestion basique d’un formulaire
• Gestion de la soumission d’un formulaire
• Gérer les valeurs par défaut du formulaire
• Personnaliser l’affichage
• Externaliser ses formulaires
Formulaires
Créer des formulaires
Gérer les valeurs par défaut du formulaire
L'un des besoins courants dans les formulaires, c'est de mettre des
valeurs prédéfinies dans les champs.
Cela peut servir pour des valeurs par défaut (préremplir la date, par
exemple) ou alors lors de l'édition d'un objet déjà existant (pour l'édition
d'un article, on souhaite remplir le formulaire avec les valeurs de la base
de données).
Formulaires
Créer des formulaires
Gérer les valeurs par défaut du formulaire
$article->setDate(new \Datetime());
// Et on construit le formBuilder avec cette instance d'article
$formBuilder = $this->createFormBuilder($article);
// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
// ...
}
Formulaires
Créer des formulaires
Externaliser ses formulaires
Définir la data_classe:
Chaque formulaire a besoin de savoir le nom de la classe qui
détient les données sous-jacentes (par exemple :
Acme\TaskBundle\Entity\Task).
Généralement, cette information est devinée grâce à l'objet
passé en second argument de la méthode createForm (c-a-d
$task).
Plus tard, quand vous commencerez à imbriquer des formulaires
les uns avec les autres, cela ne sera plus suffisant.
Formulaires
Créer des formulaires
Externaliser ses formulaires
Donc, bien que facultatif, c'est généralement une bonne idée
de spécifier explicitement l'option data_class en ajoutant ce qui
suit à notre classe formulaire:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
$form->get('dueDate')->getData();
• Introduction
• Créer des formulaires
• Formulaires imbriqués
Formulaires
Formulaires imbriqués
// src/Acme/TaskBundle/Entity/Category.php
namespace Acme\TaskBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class Category
{
/**
* @Assert\NotBlank()
*/
public $name;
}
Formulaires
Formulaires imbriqués
Comment imbriquer des formulaires?
Ensuite, ajoutons une nouvelle propriété category à la classe Task:
// ...
class Task
{
// ...
/**
* @Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
protected $category;
// ...
Formulaires
Formulaires imbriqués
Comment imbriquer des formulaires?
public function getCategory()
{
return $this->category;
}
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
<h3>Category</h3> <h3>Category</h3>
<div class="category"> <div class="category">
{{ form_row(form.category.name) }} <?php echo $view['form']->
</div> >row($form['category']['name']) ?>
</div>
Formulaires
Formulaires imbriqués
Comment imbriquer des formulaires?
Lorsque l'utilisateur soumet le formulaire, les données soumises pour les
champs de Category sont utilisées pour construire une instance de
Category qui est ensuite affectée au champ category de l'instance de
Task`.
symfony3 est livré avec un composant Validator qui rend cette tâche facile et
transparente.
Ce composant est basé sur la spécification JSR303. Quoi ? Une spécification Java
en PHP? Vous avez bien entendu, mais ce n'est pas aussi terrible que ça en a l'air.
Regardons comment elle peut être utilisée en PHP.
Validation
• Introduction
• Les bases de la validation
• Configuration
• Contraintes
• Objectifs des contraintes
• Groupes de Validation
• Valider des valeurs et des tableaux
Validation
Les bases de la validation
• Introduction
• Utiliser le service validator
• Validation et Formulaires
Validation
Les bases de la validation
Introduction
La meilleure façon de comprendre la validation est de la voir en
action.
Pour commencer, créons un bon vieil objet PHP:
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}
Validation
Les bases de la validation
Introduction
L'objectif de la validation est de nous dire si oui ou non les
données d'un objet sont valides.
Pour que cela fonctionne, nous allons configurer une liste de
règles (appelée constraints) que l'objet doit respecter pour être
valide.
Ces règles peuvent être spécifiées via un certain nombre de
formats (YAML, XML, les annotations, ou PHP).
Validation
Les bases de la validation
Introduction
Annotation:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
*/
public $name;
}
Validation
Les bases de la validation
• Introduction
• Utiliser le service validator
• Validation et Formulaires
Validation
Les bases de la validation
Utiliser le Service validator
Ensuite, pour vraiment valider un objet Author, utilisons la
méthode validate du service validator (class Validator).
$validator = $this->get('validator');
$errorList = $validator->validate($author);
Validation
Les bases de la validation
Utiliser le Service validator
if (count($errorList) > 0) {
return new Response(print_r($errorList, true));
} else {
return new Response('The author is valid! Yes!');
}
}
Si la propriété $name est vide, on va voir le message d’erreur suivant:
Acme\BlogBundle\Author.name:
This value should not be blank
Sinon, un message de succès va apparaître.
Validation
Les bases de la validation
Utiliser le Service validator
Nous pouvons aussi passer une collection d’erreurs à un template.
if (count($errorList) > 0) {
return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
'errorList' => $errorList,
));
} else {
// ...
}
Validation
Les bases de la validation
Utiliser le Service validator
Nous pouvons alors afficher la liste des erreurs comme suit:
Twig:
{# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}
• Introduction
• Utiliser le service validator
• Validation et Formulaires
Validation
Les bases de la validation
Validation et Formulaires
• Le service validator peut être utilisé à tout moment pour
valider n'importe quel objet.
• En réalité, nous travaillerons le plus souvent avec le validator
indirectement lorsque vous utilisez les formulaires.
• La bibliothèque de formulaires de Symfony utilise le service
validator en interne pour valider l'objet après que les valeurs
ont été soumises.
• Les violations de contraintes sur l'objet sont converties en
objets FieldError qui peuvent être facilement affichés dans
votre formulaire.
Validation
Les bases de la validation
Validation et Formulaires
Le processus de soumission d'un formulaire typique ressemble au
code suivant:
// ...
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
$form->handleRequest($request);
Validation
Les bases de la validation
Validation et Formulaires
if ($form->isValid()) {
// the validation passed, do something with the $author object
// app/config/config.php
$container->loadFromExtension('framework', array('validation' => array(
'enable_annotations' => true,
)));
Validation
• Introduction
• Les bases de la validation
• Configuration
• Contraintes
• Objectifs des contraintes
• Groupes de Validation
• Valider des valeurs et des tableaux
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Introduction
Le validator est conçu pour valider des objets selon des
contraintes (règles). Afin de valider un objet, il suffit d'associer
une ou plusieurs contraintes à sa classe et ensuite de le passer au
service validator.
Dans les coulisses, une contrainte est simplement un objet PHP
qui déclare une règle.
Dans la vraie vie, une contrainte pourrait être : « Le gâteau ne
doit pas être brûlé ».
Validation
Contraintes
Introduction
En symfony3, les contraintes sont similaires : ce sont des garanties
qu'une condition est vraie. Pour une valeur donnée, une
contrainte vous dira si cette valeur obéit aux règles ou non.
• Contraintes supportées:
symfony3 est fourni avec un grand nombre des
contraintes les plus utiles habituellement. Nous allons les
voir ensemble.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes de bases
Nom Description
NotBlank Valide qu'une valeur n'est pas vide, pas égale à une chaîne de
caractères vide, et pas égale à null.
Blank Valide qu'une valeur est vide, égale à une chaîne vide, ou égale
à null.
NotNull Valide qu'une valeur n'est strictement pas égale à null.
Null Valide qu'une valeur est exactement égale à null.
True Valide qu'une valeur est vraie (« true » en anglais).
False Valide qu'une valeur est fausse (« false » en anglais).
Type Valide qu'une valeur est d'un type spécifique.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes sur les chaînes de caractères
Nom Description
Nom Description
Range Valide qu'un nombre donné se situe entre un nombre minimum et
un nombre maximum.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes comparatives
Nom Description
EqualTo Valide si une valeur est égale à une autre, définie dans
les options.
NotEqualTo Valide si une valeur n'est pas égale à une autre, définie
dans les options.
IdenticalTo Valide si une valeur est identique à une autre, définie
dans les options.
NotIdenticalTo Valide si une valeur n'est pas identique à une autre,
définie dans les options.
Validation
Contraintes
Contraintes comparatives
Nom Description
LessThan Valide si une valeur est plus petite qu'une autre, définie
dans les options.
LessThanOrEqual Valide si une valeur est plus petite ou égale qu'une
autre, définie dans les options.
GreaterThan Valide si une valeur est plus grande qu'une autre,
définie dans les options.
GreaterThanOrEqual Valide si une valeur est plus grande ou égale qu'une
autre, définie dans les options.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes sur les dates
Nom Description
Date Valide qu'une valeur est une date valide, c'est-à-dire soit un
objet DateTime, soit une chaîne de caractères (ou un objet qui peut être
converti en chaîne de caractères) qui respecte un format valide YYYY-
MM-DD.
DateTime Valide que la valeur est un « datetime » valide, c'est-à-dire soit un
objetDateTime, soit une chaîne de caractères (ou un objet qui peut être
converti en chaîne de caractères) qui respecte un format YYYY-MM-DD
HH:MM:SS valide.
Time Valide qu'une valeur est une heure valide, c'est-à-dire soit un
objet DateTime, soit une chaîne de caractères (ou un objet converti en
chaîne de caractères) qui respecte un format « HH:MM:SS » valide.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes sur les collections
Nom Description
Choice Cette contrainte est utilisée pour s'assurer qu'une valeur donnée fait
partie d'un ensemble de choix valides.
Collection Cette contrainte est utilisée lorsque l'objet sous-jacent est une
collection (c'est-à-dire un tableau ou un objet qui
implémente Traversable et ArrayAccess), mais vous aimeriez valider
les différentes clés de cette collection de différentes manières.
Count Valide que le nombre d'éléments d'une collection (c-a-d un tableau ou
un objet qui implémente Countable) se situe entre une valeur
minimum et une valeur maximum.
UniqueEntity Valide qu'un champ particulier (ou plusieurs champs) d'une entité
Doctrine est (sont) unique.
Validation
Contraintes
Contraintes sur les collections
Nom Description
Language Valide que la valeur est un code de langue valide.
Locale Valide que la valeur est une locale valide.
Country Valide qu'une valeur est un code de pays en deux-lettres.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes sur les fichiers
Nom Description
Valide qu'une valeur est un « fichier » valide, qui peut être l'un des
formats suivants :
• Une chaîne de caractères (ou un objet avec une
File méthode __toString()) qui représente un chemin vers un fichier
existant ;
• Un objet File valide.
Validation
Contraintes
Contraintes sur les fichiers
Nom Description
La contrainte Image fonctionne exactement comme la contrainte File,
Image sauf que ses options mimeTypes et mimeTypesMessage sont
automatiquement définies pour fonctionner spécifiquement avec des
images.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Contraintes sur la finance et autres numéros
Nom Description
CardScheme Cette contrainte s'assure qu'un numéro de carte de crédit est valide
pour une banque donnée.
Luhn Cette contrainte est utilisée pour s'assurer qu'un numéro de carte de
crédit vérifie l'algorithme Luhn.
Iban Cette contrainte est utilisée pour s'assurer qu'un numéro de compte
bancaire a le bon format de numéro International Bank Account
Number (IBAN).
Isbn Cette contrainte est valide si un numéro IBSN( International Standard
Book Numbers) est soit un nombre de type ISBN-10, de type ISBN-13
ou les deux.
Issn Valide si la valeur est bien un numéro ISSN.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Autres contraintes
Nom Description
Callback Le but de la contrainte Callback est de vous permettre de créer
entièrement des règles de validation personnalisées et d'assigner des
erreurs de validation à des champs spécifiques de votre objet.
All Quand elle est appliquée à un tableau (ou un objet Traversable), cette
contrainte vous permet d'appliquer un ensemble de contraintes à
chaque élément du tableau.
UserPassword Cette contrainte valide qu'une valeur saisie est égale au mot de passe
de l'utilisateur courant.
Valid Cette contrainte est utilisée pour activer la validation sur les objets
imbriqués dans un objet qui doit être validé.
Validation
Contraintes
• Introduction
• Contraintes de bases
• Contraintes sur les chaînes de caractères
• Contraintes sur les nombres
• Contraintes comparatives
• Contraintes sur les dates
• Contraintes sur les collections
• Contraintes sur les fichiers
• Contraintes sur la finance et autres numéros
• Autres Contraintes
• Configuration de contraintes
Validation
Contraintes
Configuration de contraintes
Annotations:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice(
* choices = { "homme", "femme" },
* message = "Choose a valid gender."
*)
*/
public $gender;
}
Validation
Contraintes
Configuration de contraintes
<class name="Acme\BlogBundle\Entity\Author">
<property name="gender">
<constraint name="Choice">
<value>homme</value>
<value>femme</value>
</constraint>
</property>
</class>
</constraint-mapping>
Validation
Contraintes
Configuration de contraintes
PHP:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Choice;
class Author
{
protected $gender;
• Introduction
• Propriétés
• Getters
• Classes
Validation
Objectifs des contraintes
Introduction
Les contraintes peuvent être appliquées à une propriété de
classe (par ex. name) ou une méthode publique getter (par ex.
getFullName).
• Introduction
• Propriétés
• Getters
• Classes
Validation
Objectifs des contraintes
Propriétés
Valider des propriétés de classe est la technique de validation la
plus basique.
symfony3 vous permet de valider des propriétés privées,
protégées ou publiques.
Le prochain exemple vous montre comment configurer la
propriété $firstName d'une classe Author pour avoir au moins 3
caractères.
Validation
Objectifs des contraintes
Propriétés
YAML:
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
firstName:
- NotBlank: ~
- MinLength:
min: 3
Validation
Objectifs des contraintes
Propriétés
Annotations:
// Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
* @Assert\Length(min = "3")
*/
private $firstName;
}
Validation
Objectifs des contraintes
Propriétés
XML:
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<property name="firstName">
<constraint name="NotBlank" />
<constraint name="Length">
<option name="min">3</option>
</constraint>
</property>
</class>
Validation
Objectifs des contraintes
Propriétés
PHP:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;
class Author
{
private $firstName;
• Introduction
• Propriétés
• Getters
• Classes
Validation
Objectifs des contraintes
Getters
Les contraintes peuvent également être appliquées à la valeur
de retour d'une méthode.
symfony3 nous permet d'ajouter une contrainte à toute méthode
publique dont le nom commence par « get » ou « is ».
Dans ce cours, ces deux types de méthodes sont désignées
comme « getters ».
L'avantage de cette technique est qu'elle nous permet de
valider votre objet dynamiquement.
Validation
Objectifs des contraintes
Getters
Par exemple, nous voulons assurer qu'un champ mot de passe
ne correspond pas au prénom de l'utilisateur (pour des raisons de
sécurité). Nous pouvons le faire en créant une méthode
isPasswordLegal, puis en spécifiant que cette méthode doit
retourner true :
YAML:
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
getters:
passwordLegal:
- "True": { message: "Le mot de passe et le prénom doivent être différents" }
Validation
Objectifs des contraintes
Getters
Annotations:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\True(message = "Le mot de passe et le prénom doivent être différents")
*/
public function isPasswordLegal()
{
// return true or false
}
}
Validation
Objectifs des contraintes
Getters
XML:
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<getter property="passwordLegal">
<constraint name="True">
<option name="message">Le mot de passe et le prénom doivent être
différents</option>
</constraint>
</getter>
</class>
Validation
Objectifs des contraintes
Getters
PHP:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\True;
class Author
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addGetterConstraint('passwordLegal', new True(array(
'message' => 'Le mot de passe et le prénom doivent être différents',
)));
}
}
Validation
Objectifs des contraintes
Getters
Maintenant, créons la méthode isPasswordLegal(), et faisons ce
dont nous avons besoin:
Note: Les plus perspicaces d'entre vous auront remarqué que le préfixe du getter
(« get » ou « is ») est omis dans le mapping. Cela nous permet de déplacer la
contrainte sur une propriété du même nom plus tard (ou vice versa) sans
changer notre logique de validation.
Validation
Objectifs des contraintes
• Introduction
• Propriétés
• Getters
• Classes
Validation
Objectifs des contraintes
Classes
Certaines contraintes s'appliquent à toute la classe qui est
validée.
Par exemple, la contrainte Callback est une contrainte
générique qui s'applique à la classe elle-même.
Lorsque cette classe est validée, les méthodes spécifiées par
cette contrainte sont simplement exécutées pour personnaliser
encore plus la validation.
Validation
• Introduction
• Les bases de la validation
• Configuration
• Contraintes
• Objectifs des contraintes
• Groupes de Validation
• Valider des valeurs et des tableaux
Validation
Groupes de Validation
Jusqu'ici, nous avons été en mesure d'ajouter des contraintes à
une classe et demander si oui ou non cette classe satisfait toutes
les contraintes définies.
Dans certains cas, cependant, nous aurons besoin de valider un
objet en ne prenant en compte que certaines des contraintes
de la classe.
Pour ce faire, vous pouvez organiser chaque contrainte en un ou
plusieurs « groupes de validation », et ensuite appliquer la
validation sur un groupe de contraintes seulement.
Validation
Groupes de Validation
Par exemple, supposons que vous avez une classe User, qui est
utilisée à la fois quand un utilisateur s'enregistre et quand un
utilisateur met à jour son profil plus tard :
YAML:
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
properties:
email:
- Email: { groups: [registration] }
password:
- NotBlank: { groups: [registration] }
- Length: { min: 7, groups: [registration] }
city:
- Length:
min: 2
Validation
Groupes de Validation
Annotations: /**
// src/Acme/BlogBundle/Entity/User.php *
namespace Acme\BlogBundle\Entity; @Assert\NotBlank(groups={"registrati
use
on"})
Symfony\Component\Security\Core\User\UserInt * @Assert\Length(min=7,
erface; groups={"registration"})
use Symfony\Component\Validator\Constraints */
as Assert;
private $password;
class User implements UserInterface
{ /**
/** * @Assert\Length(min = "2")
* @Assert\Email(groups={"registration"}) */
*/
private $email;
private $city;
}
Validation
Groupes de Validation
XML:
<!--
src/Acme/BlogBundle/Resources/config/validation <constraint name="Length">
.xml --> <option name="min">7</option>
<class name="Acme\BlogBundle\Entity\User"> <option name="groups">
<property name="email"> <value>registration</value>
<constraint name="Email"> </option>
<option name="groups"> </constraint>
<value>registration</value> </property>
</option> <property name="city">
</constraint> <constraint name="Length">
</property> <option name="min">7</option>
<property name="password"> </constraint>
<constraint name="NotBlank"> </property>
<option name="groups"> </class>
<value>registration</value>
</option>
</constraint>
Validation
Groupes de Validation
PHP:
// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity; $metadata->addPropertyConstraint('password',
new Length(array(
use 'min' => 7,
Symfony\Component\Validator\Mapping\ClassMetadata;
'groups' => array('registration')
use Symfony\Component\Validator\Constraints\Email;
)));
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;
$metadata->addPropertyConstraint(
'city',
class User
Length(array("min" => 3)));
{
}
public static function
loadValidatorMetadata(ClassMetadata $metadata) }
{
$metadata->addPropertyConstraint('email', new
Email(array(
'groups' => array('registration')
)));
Validation
Groupes de Validation
Avec cette configuration, il y a deux groupes de validation :
if (count($errorList) == 0) {
// c'est une adresse valide, faire quelque chose
} else {
// ce n'est *pas* une adresse valide
$errorMessage = $errorList[0]->getMessage()
// faire quelque chose avec l'erreur
}
// ...
}
Validation
Valider des valeurs et des tableaux
En appelant validateValue du validator, nous pouvons passer
une valeur brute et l'objet contrainte dont nous voulons valider la
valeur. Une liste complète des contraintes disponibles - ainsi que
le nom de classe complet de chaque contrainte - est disponible
dans le chapitre contraintes.
access_control: Symfony\Component\Security\Core\U
- { path: ^/admin, roles: ROLE_ADMIN } ser\User: plaintext
Sécurité
Authentification HTTP
Le résultat final de cette configuration est un système de sécurité
entièrement fonctionnel, que l'on peut décrire de la manière
suivante :
Il y a 2 utilisateurs dans le système (ryan et admin);
Les utilisateurs s'authentifient grâce à une authentification
basique HTTP;
Toute URL correspondant au format /admin/* est sécurisée, et
seul l'utilisateur admin peut y accéder
Toutes les URLs qui ne correspondent pas au format /admin/*
sont accessibles par tous les utilisateurs (et l'utilisateur n'aura
pas à s'authentifier).
Sécurité
• Introduction
• Authentification HTTP
• Comment fonctionne la sécurité
• Utilisation d’un formulaire de connexion
• Autorisation
• Les utilisateurs
• Les rôles
• Se déconnecter
• Contrôle d’accès
• « Usurper l’identité »
• Authentification sans état
• Utilitaires
Sécurité
Comment fonctionne la sécurité
• Pare-feu (authentification)
• Contrôle d’accès (autorisation)
Sécurité
Comment fonctionne la sécurité
Pare-feu (authentification)
Lorsqu'un utilisateur fait une requête à une URL qui est protégée par un
pare-feu (firewall), le système de sécurité est activé.
• Pare-feu (authentification)
• Contrôle d’accès (autorisation)
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Par contre, si un utilisateur demande /admin/foo, le système se
comporte différemment.
C'est à cause de la section de la configuration access_control qui
stipule que toute requête correspondant au masque d'expression
régulière ^/admin (c'est-à-dire /admin ou tout ce qui correspond à
/admin/*) requiert le rôle ROLE_ADMIN.
Les rôles sont à la base de la plupart des mécanismes d'autorisation : un
utilisateur peut accéder à /admin/foo seulement s'il possède le rôle
ROLE_ADMIN.
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Comme précédemment, quand l'utilisateur fait une requête, le pare-feu
ne lui demande pas de s'authentifier.
Par contre, dès que la couche de contrôle d'accès refuse l'accès à
l'utilisateur (parce que l'utilisateur anonyme ne possède pas le rôle
ROLE_ADMIN), le pare-feu entre en action et initialise le processus
d'authentification.
Le processus d'authentification dépend du mécanisme
d'authentification que vous utilisez.
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Par exemple, si vous utilisez la méthode d'authentification par formulaire
de connexion, l'utilisateur sera redirigé à la page de formulaire de
connexion.
Si vous utilisez l'authentification HTTP, l'utilisateur recevra une réponse
HTTP 401 et verra donc la boite contenant les champs login et mot de
passe.
L'utilisateur a maintenant la possibilité de soumettre ses informations
d'identification à l'application.
Si ces informations sont valides, la requête initiale peut être lancée à
nouveau.
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Sécurité
Comment fonctionne la sécurité
Contrôle d’accès (autorisation)
Dans cet exemple, l'utilisateur ryan s'authentifie avec succès auprès du
pare-feu. Mais comme ryan n'a pas le rôle ROLE_ADMIN, il se verra
refuser l'accès à /admin/foo.
Enfin, cela veut dire que l'utilisateur verra un message indiquant que
l'accès lui est refusé.
// if ($request->attributes-
src/Acme/SecurityBundle/Controller/SecurityControlle >has(SecurityContext::AUTHENTICATION_ERROR)) {
r.php;
namespace Acme\SecurityBundle\Controller; $error = $request->attributes-
>get(SecurityContext::AUTHENTICATION_ERROR);
use } else {
Symfony\Bundle\FrameworkBundle\Controller\Contr $error = $session-
oller; >get(SecurityContext::AUTHENTICATION_ERROR);
use $session-
Symfony\Component\Security\Core\SecurityContex >remove(SecurityContext::AUTHENTICATION_ERRO
t; R);
}
class SecurityController extends Controller return $this-
{ >render('AcmeSecurityBundle:Security:login.html.t
public function loginAction() wig', array(
{ // last username entered by the user
$request = $this->getRequest(); 'last_username' => $session-
$session = $request->getSession(); >get(SecurityContext::LAST_USERNAME),
// get the login error if there is one 'error' => $error,
));
}
}
Sécurité
Utilisation d’un formulaire de connexion
Ne vous laissons pas impressionner par le contrôleur.
Comme nous allons voir dans un moment, lorsque l'utilisateur
soumet le formulaire, le système de sécurité prend en charge
automatiquement le formulaire soumis.
Si l'utilisateur venait à soumettre un login ou un mot de passe
invalide, ce formulaire lit les erreurs de soumission du système de
sécurité afin qu'elles soient ensuite affichées à l'utilisateur.
En d'autres termes, votre rôle est d'afficher le formulaire de
connexion et toute erreur qui aurait pu survenir, mais c'est le
système de sécurité lui-même qui prend en charge la validation
du login et du mot de passe et qui authentifie l'utilisateur.
Sécurité
Utilisation d’un formulaire de connexion
Maintenant, il ne nous reste qu’à créer le template correspondant:
TWIG:
{#
src/Acme/SecurityBundle/Resources/views/Se <input type="password" id="password"
curity/login.html.twig #} name="_password" />
{% if error %}
{#
<div>{{ error.message }}</div> Si vous voulez contrôler l'URL vers laquelle
{% endif %} l'utilisateur est redirigé en cas de succès
(plus de détails ci-dessous)
<form action="{{ path('login_check') }}" <input type="hidden" name="_target_path"
method="post"> value="/account" />
<label for="username">Login :</label> #}
<input type="text" id="username"
name="_username" value="{{ last_username }}" <button type="submit">login</button>
/> </form>
YAML:
# app/config/security.yml
security:
# ...
access_control:
- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel:
https }
Sécurité
Autorisation
• Fournisseurs d’utilisateurs
• Encoder les mots de passe
• Récupérer l’objet User
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
symfony3 comprend en standard deux des fournisseurs les plus
utilisés :
Un qui charge ses utilisateurs depuis un fichier de configuration
Un autre qui charge ses utilisateurs d'une table de base de
données..
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
Spécifier les utilisateurs dans un fichier de configuration
YAML:
# app/config/security.yml
security:
# ...
providers:
default_provider:
users:
ryan: { password: ryanpass, roles: 'ROLE_USER' }
admin: { password: kitten, roles: 'ROLE_ADMIN' }
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
XML:
<!-- app/config/security.xml -->
<config>
<!-- ... -->
<provider name="default_provider">
<user name="ryan" password="ryanpass" roles="ROLE_USER" />
<user name="admin" password="kitten" roles="ROLE_ADMIN" />
</provider>
</config>
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
PHP:
// app/config/security.php
$container->loadFromExtension('security', array(
// ...
'providers' => array(
'default_provider' => array(
'users' => array(
'ryan' => array('password' => 'ryanpass', 'roles' => 'ROLE_USER'),
'admin' => array('password' => 'kitten', 'roles' => 'ROLE_ADMIN'),
),
),
),
));
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
Charger les utilisateurs de la base de données
Si nous voulons charger vos utilisateurs depuis l'ORM Doctrine,
nous pouvons facilement le faire en créant une classe User et en
configurant le fournisseur d'entités (entity provider).
Avec cette approche, nous devons d'abord créer votre propre
classe User, qui va être sauvegardée dans la base de données.
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
// src/Acme/UserBundle/Entity/User.php
namespace Acme\UserBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class User implements UserInterface
{
/**
* @ORM\Column(type="string", length=255)
*/
protected $username;
// ...
}
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
Ensuite, il faut configurer le fournisseur d'utilisateur entity (entity
user provider), le pointer vers la classe User :
YAML:
# app/config/security.yml
security:
providers:
main:
entity: { class: Acme\UserBundle\Entity\User, property: username }
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
XML:
<!-- app/config/security.xml -->
<config>
<provider name="main">
<entity class="Acme\UserBundle\Entity\User" property="username" />
</provider>
</config>
Sécurité
Les utilisateurs
Fournisseurs d’utilisateurs
XML:
// app/config/security.php
$container->loadFromExtension('security', array(
'providers' => array(
'main' => array(
'entity' => array('class' => 'Acme\UserBundle\Entity\User', 'property' =>
'username'),
),
),
));
Sécurité
Les utilisateurs
• Fournisseurs d’utilisateurs
• Encoder les mots de passe
• Récupérer l’objet User
Sécurité
Les utilisateurs
Encoder les mots de passe
Dans notre application, nous allons toujours vouloir encoder les
mots de passe de vos utilisateurs de manière cryptés pour des
raisons de sécurité.
Ceci est facile à accomplir en mappant votre classe User avec
un des nombreux « encodeurs » intégrés.
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: sha1
iterations: 1
encode_as_base64: false
Sécurité
Les utilisateurs
Encoder les mots de passe
XML:
<!-- app/config/security.xml -->
<config>
<!-- ... -->
<provider name="in_memory">
<user name="ryan" password="bb87a29949f3a1ee0559f8a57357487151281386" roles="ROLE_USER" />
<user name="admin" password="74913f5cd5f61ec0bcfdb775414c2fb3d161b620" roles="ROLE_ADMIN" />
</provider>
• Fournisseurs d’utilisateurs
• Encoder les mots de passe
• Récupérer l’objet User
Sécurité
Les utilisateurs
Récupérer l’objet User
Après l'authentification, l'objet User correspondant à l'utilisateur
courant peut être récupéré via le service
security.context_storage.
PHP:
<p>Username: <?php echo $app->getUser()->getUsername() ?></p>
Sécurité
• Introduction
• Authentification HTTP
• Comment fonctionne la sécurité
• Utilisation d’un formulaire de connexion
• Autorisation
• Les utilisateurs
• Les rôles
• Se déconnecter
• Contrôle d’accès
• « Usurper l’identité »
• Authentification sans état
• Utilitaires
Sécurité
Les rôles
La notion de « rôle » est au centre du processus d'autorisation.
XML:
<logout />
PHP:
'logout' => array(),
Sécurité
Se déconnecter
Depuis Symfony 2.1, nous devons avoir une route qui correspond
à notre chemin de déconnexion. Sans route, nous ne pourrons
pas nous déconnecter.
YAML:
# app/config/routing.yml
logout:
pattern: /logout
PHP:
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('logout', new Route('/logout', array()));
return $collection;
Sécurité
Se déconnecter
XML:
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
</routes>
Sécurité
• Introduction
• Authentification HTTP
• Comment fonctionne la sécurité
• Utilisation d’un formulaire de connexion
• Autorisation
• Les utilisateurs
• Les rôles
• Se déconnecter
• Contrôle d’accès
• « Usurper l’identité »
• Authentification sans état
• Utilitaires
Sécurité
Contrôle d’accès
YAML:
# app/config/security.yml
security:
firewalls:
main:
// ...
switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }
Sécurité
« Usurper l’identité »
XML:
<!-- app/config/security.xml -->
<config>
<firewall>
<!-- ... -->
<switch-user role="ROLE_ADMIN" parameter="_want_to_be_this_user" />
</firewall>
</config>
Sécurité
« Usurper l’identité »
PHP:
// app/config/security.php
$container->loadFromExtension('security', array(
'firewalls' => array(
'main'=> array(
// ...
'switch_user' => array('role' => 'ROLE_ADMIN', 'parameter' =>
'_want_to_be_this_user'),
),
),
));
Sécurité
• Introduction
• Authentification HTTP
• Comment fonctionne la sécurité
• Utilisation d’un formulaire de connexion
• Autorisation
• Les utilisateurs
• Les rôles
• Se déconnecter
• Contrôle d’accès
• « Usurper l’identité »
• Authentification sans état
• Utilitaires
Sécurité
Authentification sans état
Par défaut, symfony3 s'appuie sur cookie (la Session) pour garder
le contexte de sécurité d'un utilisateur.
Mais si nous utilisons des certificats ou l'authentification HTTP par
exemple, la persistance n'est pas nécessaire, car l'identité est
disponible à chaque requête.
Dans ce cas, et si nous n'avons pas besoin de sauvegarder
quelque chose entre les requêtes, nous pouvons activer
l'authentification sans état (stateless authentication), ce qui veut
dire qu'aucun cookie ne sera jamais créé par symfony3.
Sécurité
Authentification sans état
YAML:
# app/config/security.yml
security:
firewalls:
main:
http_basic: ~
stateless: true
XML:
<!-- app/config/security.xml -->
<config>
<firewall stateless="true">
<http-basic />
</firewall>
</config>
Sécurité
Authentification sans état
PHP:
// app/config/security.php
$container->loadFromExtension('security', array(
'firewalls' => array(
'main' => array('http_basic' => array(), 'stateless' => true),
),
));
Ces utilitaires sont utilisés par Symfony, mais vous devriez aussi les utiliser
pour résoudre les problèmes qu'ils traitent.
use Symfony\Component\Security\Core\Util\StringUtils;
use Symfony\Component\Security\Core\Util\SecureRandom;
• Introduction
• Le processus de traduction
• Message avec paramètres de substitution
Traduire son interface
Traduction basique
Introduction
La traduction du texte est faite à travers le service translator
(Translator).
Pour traduire un bloc de texte (appelé un message), utilisons la
méthode trans(). Supposons, par exemple, que nous voulons
traduire un simple message dans un contrôleur :
public function indexAction()
{
$t = $this->get('translator')->trans('symfony3 is great');
• Introduction
• Le processus de traduction
• Message avec paramètres de substitution
Traduire son interface
Traduction basique
Le processus de traduction
Pour traduire le message, symfony3 utilise un processus simple :
La locale de l'utilisateur actuel, qui est stockée dans la requête (ou stockée
comme _locale en session), est déterminée ;
Un catalogue des messages traduits est chargé depuis les ressources de
traduction définies pour la locale (par ex. fr_FR). Les messages de la locale de
secours sont aussi chargés et ajoutés au catalogue s'ils n'existent pas déjà. Le
résultat final est un large « dictionnaire » de traductions.
Si le message est dans le catalogue, la traduction est retournée. Sinon, le
traducteur retourne le message original.
Lorsque vous utilisez la méthode trans(), symfony3 cherche la chaîne de
caractères exacte à l'intérieur du catalogue de messages approprié et la
retourne (si elle existe).
Traduire son interface
Traduction basique
• Introduction
• Le processus de traduction
• Message avec paramètres de substitution
Traduire son interface
Traduction basique
Message avec paramètres de substitution
Parfois, un message contenant une variable a besoin d'être
traduit :
new Response($t);
}
Traduire son interface
Traduction basique
Message avec paramètres de substitution
symfony3 va maintenant chercher une traduction du message
brut (Hello %name%) et ensuite remplacer les paramètres de
substitution avec leurs valeurs.
Créer une traduction se fait comme précédemment :
YAML:
# messages.fr.yml
'Hello %name%': Bonjour %name%
Traduire son interface
Traduction basique
Message avec paramètres de substitution
XML:
<!-- messages.fr.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Hello %name%</source>
<target>Bonjour %name%</target>
</trans-unit>
</body>
</file>
</xliff>
Traduire son interface
Traduction basique
Message avec paramètres de substitution
PHP:
// messages.fr.php
return array(
'Hello %name%' => 'Bonjour %name%',
);
Comme nous l’avons vus, créer une traduction est un processus en deux
étapes :
1. Faire abstraction du message qui a besoin d'être traduit en le
passant à travers le Translator.
2. Créer une traduction pour le message dans chaque locale que vous
avez choisi de supporter.
Traduire son interface
• Introduction
• Configuration
• Traduction basique
• Catalogues de Message
• Utiliser les Domaines de Message
• Gérer la Locale de l’Utilisateur
• Pluralisation
• Traduction dans les Templates
• Forcer la Locale du Traducteur
• Traduire les messages de contraintes
Traduire son interface
Catalogues de Message
• Introduction
• Emplacements des Traductions et conventions de
Nommage
• Créer des Traductions
Traduire son interface
Catalogues de Message
Introduction
Lorsqu'un message est traduit, symfony3 compile un catalogue de
messages pour la locale de l'utilisateur et cherche dedans une
traduction du message. Un catalogue de messages est comme un
dictionnaire de traductions pour une locale spécifique.
Par exemple, le catalogue de la locale fr_FR pourrait contenir la
traduction suivante :
symfony3 is Great => J'aime symfony3
C'est la responsabilité du développeur (ou du traducteur) d'une
application internationalisée de créer ces traductions. Les traductions
sont stockées sur le système de fichiers et reconnues par Symfony,
grâce à certaines conventions.
Traduire son interface
Catalogues de Message
• Introduction
• Emplacements des Traductions et conventions de
Nommage
• Créer des Traductions
Traduire son interface
Catalogues de Message
Emplacements des Traductions et Conventions de Nommage
Les répertoires sont listés par ordre de priorité. Cela signifie que
nous pouvons surcharger les messages de traduction d'un
bundle dans l'un des deux premiers répertoires.
Le système de surcharge se base sur les clés : seules les clés
surchargées ont besoin d'être listées dans un fichier de plus
grande priorité.
Quand une clé n'est pas trouvée dans un fichier de traductions,
le service Translator cherchera automatiquement dans les
fichiers de moindre priorité.
Traduire son interface
Catalogues de Message
Emplacements des Traductions et Conventions de Nommage
• Introduction
• Emplacements des Traductions et conventions de
Nommage
• Créer des Traductions
Traduire son interface
Catalogues de Message
Créer les traductions
Le fait de créer des fichiers de traduction est une partie
importante de la « localisation » (souvent abrégée L10n).
Les fichiers de traduction consistent en une série de paires id-
traduction pour un domaine et une locale donnés.
La source est l'identifiant de la traduction individuelle, et peut
être le message dans la locale principale (par exemple «
Symfony is great ») de notre application ou un identificateur
unique
Traduire son interface
Catalogues de Message
Créer les traductions
XML:
<!-- src/Acme/DemoBundle/Resources/translations/messages.fr.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>symfony3 is great</source>
<target>J'aime symfony3</target>
</trans-unit>
<trans-unit id="2">
<source>symfony3.great</source>
<target>J'aime symfony3</target>
</trans-unit>
</body>
</file>
</xliff>
Traduire son interface
Catalogues de Message
Créer les traductions
PHP:
// src/Acme/DemoBundle/Resources/translations/messages.fr.php
return array(
'symfony3 is great' => 'J\'aime symfony3',
'symfony3.great' => 'J\'aime symfony3',
);
Traduire son interface
Catalogues de Message
Créer les traductions
YAML:
# src/Acme/DemoBundle/Resources/translations/messages.fr.yml
symfony3 is great: J'aime symfony3
symfony3.great: J'aime symfony3
symfony3 va reconnaître ces fichiers et les utiliser lors de la
traduction de « symfony3 is great » ou de « symfony3.great » dans
une locale de langue française (par ex. fr_FR or fr_BE).
Traduire son interface
Catalogues de Message
Créer les traductions
Utiliser des mots-clés ou des messages
Cet exemple illustre les deux philosophies différentes lors de la création
des messages à traduire :
$t = $translator->trans('symfony3 is great');
$t = $translator->trans('symfony3.great');
Traduire son interface
Catalogues de Message
Créer les traductions
Dans la première méthode, les messages sont écrits dans la langue
de la locale par défaut (anglais dans ce cas). Ce message est
ensuite utilisé comme « id » lors de la création des traductions.
messages.fr.xliff
admin.fr.xliff
navigation.fr.xliff
Traduire son interface
Utiliser les Domaines de Message
Lors de la traduction de chaînes de caractères qui ne sont pas dans le
domaine par défaut (messages), vous devez spécifier le domaine
comme troisième argument de trans() :
• Introduction
• Solution de Secours et Locale par Défaut
• La locale et l’URL
Traduire son interface
Gérer la Locale de l’Utilisateur
Introduction
La locale de l'utilisateur courant est stockée dans la requête et
est accessible via l'objet request :
// Accéder à l'objet Request dans un contrôleur standard
$request = $this->getRequest();
$locale = $request->getLocale();
$request->setLocale('en_US');
Traduire son interface
Gérer la Locale de l’Utilisateur
Introduction
Il est aussi possible de stocker la locale en session plutôt qu'en
requête. Si vous faites cela, toutes les requêtes auront la même
locale.
$this->get('session')->set('_locale', 'en_US');
Traduire son interface
Gérer la Locale de l’Utilisateur
• Introduction
• Solution de Secours et Locale par Défaut
• La locale et l’URL
Traduire son interface
Gérer la Locale de l’Utilisateur
Solution de Secours et Locale par Défaut
Si la locale n'a pas été explicitement définie dans la session, le
paramètre de configuration fallback_locale va être utilisé par le
Translator.
Le paramètre est défini comme en par défaut (voir Configuration).
Alternativement, nous pouvons garantir que la locale soit définie dans
chaque requête de l'utilisateur en définissant le paramètre
default_locale du framework :
YAML:
# app/config/config.yml
framework:
default_locale: en
Traduire son interface
Gérer la Locale de l’Utilisateur
Solution de Secours et Locale par Défaut
XML:
<!-- app/config/config.xml -->
<framework:config>
<framework:default-locale>en</framework:default-locale>
</framework:config>
PHP:
// app/config/config.php
$container->loadFromExtension('framework', array(
'default_locale' => 'en',
));
Traduire son interface
Gérer la Locale de l’Utilisateur
• Introduction
• Solution de Secours et Locale par Défaut
• La locale et l’URL
Traduire son interface
Gérer la Locale de l’Utilisateur
La locale et l’URL
Puisque nous pouvons stocker la locale de l'utilisateur dans la session, il
peut être tentant d'utiliser la même URL pour afficher une ressource
dans de nombreuses langues différentes en se basant sur la locale de
l'utilisateur.
Par exemple, http://www.example.com/contact pourrait afficher le
contenu en anglais pour un utilisateur, et en français pour un autre
utilisateur.
Malheureusement, cela viole une règle fondamentale du Web qui dit
qu'une URL particulière retourne la même ressource indépendamment
de l'utilisateur. Pour enfoncer encore plus le clou, quelle version du
contenu serait indexée par les moteurs de recherche ?
Traduire son interface
Gérer la Locale de l’Utilisateur
La locale et l’URL
Une meilleure politique est d'inclure la locale dans l'URL. Ceci est
entièrement pris en charge par le système de routage en utilisant le
paramètre spécial _locale :
YAML:
contact:
pattern: /{_locale}/contact
defaults: { _controller: AcmeDemoBundle:Contact:index, _locale: en }
requirements:
_locale: en|fr|de
Traduire son interface
Gérer la Locale de l’Utilisateur
La locale et l’URL
XML:
<route id="contact" pattern="/{_locale}/contact">
<default key="_controller">AcmeDemoBundle:Contact:index</default>
<default key="_locale">en</default>
<requirement key="_locale">en|fr|de</requirement>
</route>
Traduire son interface
Gérer la Locale de l’Utilisateur
La locale et l’URL
PHP:
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('contact', new Route('/{_locale}/contact', array(
'_controller' => 'AcmeDemoBundle:Contact:index',
'_locale' => 'en',
), array(
'_locale' => 'en|fr|de'
)));
return $collection;
Traduire son interface
Gérer la Locale de l’Utilisateur
La locale et l’URL
Lorsque nous utilisons le paramètre spécial _locale dans une route, la
locale correspondante sera automatiquement définie dans la session de
l'utilisateur.
En d'autres termes, si un utilisateur visite l'URI /fr/contact, la locale fr sera
automatiquement définie comme la locale de sa session.
• Introduction
• Intervalle Explicite de Pluralisation
Traduire son interface
Pluralisation
Introduction
La pluralisation des messages est un sujet difficile, car les règles peuvent
être assez complexes. Par exemple, voici la représentation
mathématique des règles de la pluralisation russe :
Comme vous pouvez le voir, en russe, vous pouvez avoir trois différentes
formes de pluriel, chacune donnant un index de 0, 1 ou 2. Pour chaque
forme, le pluriel est différent, et ainsi la traduction est également
différente.
Traduire son interface
Pluralisation
Introduction
Quand une traduction a des formes différentes dues à la pluralisation,
nous pouvons fournir toutes les formes comme une chaîne de
caractères séparée par un pipe (|):
'There is one apple|There are %count% apples‘
• Introduction
• Intervalle Explicite de Pluralisation
Traduire son interface
Pluralisation
Intervalle Explicite de Pluralisation
La meilleure façon de pluraliser un message est de laisser symfony3
utiliser sa logique interne pour choisir quelle chaîne utiliser en se basant
sur un nombre donné.
Parfois, vous aurez besoin de plus de contrôle ou vous voudrez une
traduction différente pour des cas spécifiques (pour 0, ou lorsque le
nombre est négatif, par exemple).
Pour de tels cas, vous pouvez utiliser des intervalles mathématiques
explicites :
'{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf]
There are many apples'
Traduire son interface
Pluralisation
Intervalle Explicite de Pluralisation
Les intervalles suivent la notation ISO 31-11 . La chaîne de caractères ci-
dessus spécifie quatre intervalles différents : exactement 0, exactement
1, 2-19, et 20 et plus.
Vous pouvez également mélanger les règles mathématiques explicites
et les règles standards.
Dans ce cas, si le nombre ne correspond pas à un intervalle spécifique,
les règles standards prennent effet après la suppression des règles
explicites :
'{0} There are no apples|[20,Inf] There are many apples|There is one
apple|a_few: There are %count% apples'
Traduire son interface
Pluralisation
Intervalle Explicite de Pluralisation
Par exemple, pour 1 pomme (« apple »), la règle standard There is one
apple va être utilisée.
Pour 2-19 pommes (« apples »), la seconde règle standard There are
%count% apples va être sélectionnée.
{1,2,3,4}
Traduire son interface
Pluralisation
Intervalle Explicite de Pluralisation
Ou des nombres entre deux autres nombres :
[1, +Inf[
]-1,2[
Le délimiteur gauche peut-être [ (inclusif) ou ] (exclusif).
Le délimiteur droit peut-être [ (exclusif) ou ] (inclusif).
En sus des nombres, vous pouvez utiliser -Inf and +Inf pour l'infini.
Traduire son interface
• Introduction
• Configuration
• Traduction basique
• Catalogues de Message
• Utiliser les Domaines de Message
• Gérer la Locale de l’Utilisateur
• Pluralisation
• Traduction dans les Templates
• Forcer la Locale du Traducteur
• Traduire les messages de contraintes
Traduire son interface
Traductions dans les Templates
• Templates Twig
• Templates PHP
Traduire son interface
Traductions dans les Templates
Templates Twig
symfony3 fournit des balises Twig spécialisées (trans et transchoice) pour
aider à la traduction des blocs statiques de texte :
{% trans %}Hello %name%{% endtrans %}
{% transchoice count %}
{0} There is no apples|{1} There is one apple|]1,Inf] There are %count% apples
{% endtranschoice %}
La balise transchoice prend automatiquement la variable %count%
depuis le contexte actuel et la passe au traducteur.
Ce mécanisme fonctionne seulement lorsque vous utilisez un paramètre
de substitution suivi du pattern %var%.
Traduire son interface
Traductions dans les Templates
Templates Twig
Vous pouvez également spécifier le domaine de messages et passer
quelques variables supplémentaires :
{% trans with {'%name%': 'Fabien'} from "app" into "fr" %}Hello %name%{% endtrans
%}
{{ message|transchoice(5) }}
• Templates Twig
• Templates PHP
Traduire son interface
Traductions dans les Templates
Templates PHP
Le service de traduction est accessible dans les templates PHP à travers
l'outil d'aide translator :
<?php echo $view['translator']->trans('symfony3 is great') ?>
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}
Traduire son interface
Traduire les messages de contraintes
Ajoutez des contraintes avec l'une des méthodes supportées.
Définissez le texte source à traduire dans l'option message.
Par exemple, pour garantir qu'une propriété $name n'est pas vide,
ajoutez le code suivant :
YAML:
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
name:
- NotBlank: { message: "author.name.not_blank" }
Traduire son interface
Traduire les messages de contraintes
Annotations:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank(message = "author.name.not_blank")
*/
public $name;
}
Traduire son interface
Traduire les messages de contraintes
XML:
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="Acme\BlogBundle\Entity\Author">
<property name="name">
<constraint name="NotBlank">
<option name="message">author.name.not_blank</option>
</constraint>
</property>
</class>
</constraint-mapping>
Traduire son interface
Traduire les messages de contraintes
Créons un fichier de traduction pour le catalogue validators pour les
messages de contraintes, typiquement dans le répertoire
Resources/translations/ du bundle.
XML:
<!-- validators.en.xliff -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>author.name.not_blank</source>
<target>Saisissez un nom</target>
</trans-unit>
</body>
</file>
</xliff>
Traduire son interface
Traduire les messages de contraintes
PHP:
// validators.en.php
return array(
'author.name.not_blank' => 'Saisissez un nom.',
);
YAML:
# validators.en.yml
author.name.not_blank: Saisissez un nom.
Références
• http://symfony.com/fr/doc/current/book/index.html
• http://uploads.siteduzero.com/pdf/517569-developpez-votre-
site-web-avec-le-framework-symfony3.pdf
• http://en.wikipedia.org/wiki/Symfony