Présenté par : Ahmed Zaki JENHANI
Symfony et les fondamentaux HTTP
Fondamentaux HTTP
Envoi de la requête
GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)
Méthodes HTTP
● GET
– Récupère une ressource à partir du serveur
● POST
– Crée une ressource dans le serveur
● PUT
– Met à jour une ressource dans le serveur
● DELETE
– Supprime une ressource dans le serveur
● Exemple de demande de suppression de ressource
DELETE /blog/15 HTTP/1.1
Réponse du Serveur WEB
Réponse du Serveur WEB
HTTP/1.1 200 OK
Date: Sat, 02 Apr 2011 21:05:05 GMT
Server: lighttpd/1.4.19
Content-Type: text/html
<html>
<!-- ... HTML for the xkcd comic -->
</html>
Requêtes et réponses en PHP
<?php
$uri = $_SERVER['REQUEST_URI'];
$foo = $_GET['foo'];
header('Content-Type: text/html');
echo 'The URI requested is: '.$uri;
?><br/><?php
echo 'The value of the "foo"
parameter is: '.$foo;
Requêtes en Symfony
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
// the URI being requested (e.g. /about) minus any query parameters
$request->getPathInfo();
// retrieve GET and POST variables respectively
$request->query->get('foo');
$request->request->get('bar', 'default value if bar does not exist');
// retrieve SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by foo
$request->files->get('foo');
// retrieve a COOKIE value
$request->cookies->get('PHPSESSID');
// retrieve an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content_type');
$request->getMethod(); // GET, POST, PUT, DELETE, HEAD
$request->getLanguages();// an array of languages the client accepts
Réponses en Symfony
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent('<html><body><h1>Hello world!
</h1></body></html>');
$response->setStatusCode(Response::HTTP_OK);
$response->headers->set('Content-Type', 'text/html');
// prints the HTTP headers followed by the content
$response->send();
The Front Controller
(Contrôleur en Avant)
● /index.php: Exécute index.php
● /index.php/contact: Exécute index.php
● /index.php/blog: Exécute index.php
● Au lieu d'exécuter pour chaque URL une page PHP
● On Exécute toujours le même fichier PHP
– La route (ce qui vient après le nom du fichier PHP) est
utiliser pour exécuter la partie souhaité de l'application
The Front Controller
(Contrôleur en Avant)
// index.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // the URI path being
requested
if (in_array($path, array('', '/'))) {
$response = new Response('Welcome to the
homepage.');
} elseif ('/contact' === $path) {
$response = new Response('Contact us');
} else {
$response = new Response('Page not found.',
Response::HTTP_NOT_FOUND);
}
$response->send();
Flux de l'application Symfony
Une requête Symfony
● Routing
app/config/routing.yml
contact:
path: /contact
defaults:{ _controller:AppBundle:Main:contact }
● Controlleur
// src/AppBundle/Controller/MainController.php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class MainController {
public function contactAction() {
return new Response('<h1>Contact us!</h1>');
}
}
Arborescence des répertoires
● app/
– Configuration de l'application, templates and traductions.
● bin/
– Fichiers exécutables (exp : bin/console).
● src/
– Les codes sources des projets
● tests/
– Tests automatiques (exp: Unit tests).
● var/
– Fichiers générés (cache, logs, etc.).
● vendor/
– Dépendances tiers
● web/
– Le répertoire web racine
RoadMap: Stabilité vs Nouveautés
Atelier
Installation de Symfony
Architecture Symfony
Comprendre les fondamentaux
Advertisement
● Objectifs :
– Code organisé
– Evolution simple est rapide
– Éviter le mélange des balises Appels bases de données,
HTML et Code PHP (métier) dans le même script
● Le code est organisé et définit comme :
– actions et contrôleurs: Le code métier
– routing: La correspondance entre les requêtes
utilisateurs et le code (actions).
– template: Générer la réponse HTML
Actions et Contrôleurs
● src/AppBundle/Controller/DefaultController.php
class DefaultController extends Controller {
/**
- @Route("/", name="homepage")
*/
public function indexAction() {
return $this->render('default/index.html.twig');
}
}
● Controllers: Des classes PHP finissants par Controller
● Actions: Méthodes définies dans les controlleurs finissants
par Action. Généralement associé à une URL.
L'Object «Request» (1/2)
● Lorsque l'utilisateur demande une ressource (URL),
Symfony crée un objet «Request» encapsulant les
informations relatives à cette demande.
– Langue préférée
– Adresse IP
– Paramètres URL
● Dans ce cas, un argument de type «Request» est
ajouté aux argument de l'action
L'Object «Request» (2/2)
● // src/AppBundle/Controller/DefaultController.php
class DefaultController extends Controller {
public function indexAction(Request $request) {
// is it an Ajax request?
$isAjax = $request->isXmlHttpRequest();
// what's the preferred language of the user?
$language = $request-
>getPreferredLanguage(array('en', 'fr'));
// get the value of a $_GET parameter
$pageName = $request->query->get('page');
// get the value of a $_POST parameter
$pageName = $request->request->get('page');
}
}
● Au niveaux des templates, cet objet est aussi accessible comme suit:
– {{ app.request.query.get('page') }}
– {{ app.request.request.get('page') }}
L'Objet «Response»
● Symfony définit un framework pour la gestion des
requêtes (et des réponses).
● Un objet «Response» encapsulant les informations
relatives à la réponse est retourné à Symfony.
● L'objet est utilisé pour générer le contenu
(généralement HTML) renvoyé à l'utilisateur
● $this->render() est une méthode prédéfinie au
niveau des contrôleurs qui retourne une réponse
générée
Sessions (1/2)
● Le Protocol HTTP est sans état ;
● Symfony offre un objet représentant le client (une
personne réelle, un robot, ou un web service);
● Entre deux requêtes, Symfony enregistre des
attributs dans un cookie en utilisant les session
natives PHP;
● L'enregistrement ou la demande des informations de
session peuvent se faire au niveau de tout
contrôleur;
Sessions (2/2)
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request) {
$session = $request->getSession();
// store an attribute for reuse during a
later user request
$session->set('foo', 'bar');
// get the value of a session attribute
$foo = $session->get('foo');
// use a default value if the attribute
doesn't exist
$foo = $session->get('foo', 'default_value');
}
Routing
● Chaque requête (URL), est routé vers l'action qui lui
est associé.
● Le commentaire du «DefaultController» est la
définition de la route de l'action index.
● @Route est une annotation PHP
● Le block (commentaire) de configuration commence
par «/**»
● L'annotation «@Route("/", name="homepage")»
ajoute une route nommé «homepage», qui permet à
symfony d'exécuter l'action «index» du contrôleur
«Default»
Paramètre «route»
● L'URL de la plus part des applications incluent des
«parties variables». Ce sont des paramètres:
– Identifiant;
– Motif de recherche, etc...
● Route: /article/consulter/{identifiant}
– /article/consulter/5
– /article/consulter/10
Paramètre «route»
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller {
/**
- @Route("/hello/{name}", name="hello")
*/
public function helloAction($name) {
return $this->render('default/hello.html.twig', array(
'name' => $name
));
}
}
Redirection
// src/AppBundle/Controller/DefaultController.php
class DefaultController extends Controller {
/**
- @Route("/", name="homepage")
*/
public function indexAction() {
return $this->redirectToRoute('hello',
array('name' => 'Fabien'));
}
Advertisement
}
● Arguements de «redirectToRoute()»
– Nom de la route :
● «hello»
– Paramètres de la route dans un tableau
● «array('name' => 'Fabien')»
Templates
● return $this→render('default/index.html.twig');
● Méthode qui génère la vue (réponse html) à partir du
fichier twig «default/index.html.twig».
● Par défauts les templates sont localisés dans
«app/Resources/views/»
● «default/index.html.twig» correspond donc au
template
«app/Resources/views/default/index.html.twig»
Twig
● Twig is a modern template engine for PHP
– http://twig.sensiolabs.org/documentation
● Un template twig est un fichier texte qui peut générer tout
type de documents:
– HTML
– CSS
– JavaScript
– XML
– CSV
– LaTeX, etc...
Twig
● {{ ... }}
– Affiche le contenu d'une variable
– Affiche le résultat d'une expression
● {% ... %}
– Controle la logique du template
– Boucles, conditions, etc...
● {# ... #}
– Commentaires
– Ne sont pas inclus dans le document généré
Twig – Génération de document
● Pour générer un document avec twig, utilisez la
méthode render du contrôleur (la classe PHP)
$this→render(
'default/index.html.twig',
array(
'variable_name' => 'variable_value',
)
);
Accès aux variables et attributs
● Les variable passé au template peuvent être tableaux, objets, chaîne de
caractères, etc.
● Accès à une variables simple
– {# $this->render('template.html.twig', array(
'name' => 'Fabien')
) #}
– {{ name }}
● Tableaux
– {# $this->render('template.html.twig', array(
'user' => array('name' => 'Fabien'))
) #}
– {{ user.name }}
– {{ user['name'] }}
● Objets
– {# $this->render('template.html.twig', array(
'user' => new User('Fabien'))
) #}
– {{ user.name }}
– {{ user.getName }}
– {{ user.name() }}
– {{ user.getName() }}
Décoration (1/2)
● {# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock
%}</title>
{% block stylesheets %}{% endblock %}
<link rel="icon" type="image/x-icon"
href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
Décoration (2/2)
● {# app/Resources/views/default/index.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<h1>Welcome to Symfony</h1>
{# ... #}
{% endblock %}
Liens hypertexte
● Les URL ne sont pas écrite «hardcoded», mais
générées
– <a href="{{ path('homepage') }}">
Return to homepage
</a>
– <a href="{{ path('hello',{'name':'Fabien'})
}}">
Return to homepage
</a>
Inclure des «assets»
● Toute ressource «statique» est qui n'est pas un
programme (PHP) est considérée comme asset
– Images ;
– CSS ;
– Javascript, etc.
● <link href="{{asset('css/blog.css')}}"
rel="stylesheet" type="text/css" />
● <img src="{{asset('images/logo.png')}}" />
Activités
● Activité 0: Comprendre le Bundle «AppBundle»
● Activité 1: Une première page et décoration
● Activité 2: Deuxième page
● Activité 3: Liens hypertextes
Formulaires
Formulaires
● Symfony intègre un composant Formulaire pour
faciliter leur gestion
● Dans le contrôleur, la création d'un objet «Form»
permet de:
– Faciliter son affichage (génération avec twig)
– Récupérer les données saisie (après soumissions)
– Vérifier la validité des données
– Appliquer d'autre tests et validation
Entité Tast
namespace AppBundle\Entity;
class Task {
Advertisement
protected $task;
protected $dueDate;
public function getTask() {
return $this->task;
}
public function setTask($task) {
$this->task = $task;
}
public function getDueDate() {
return $this->dueDate;
}
public function setDueDate(\DateTime $dueDate = null) {
$this->dueDate = $dueDate;
}
}
Construction du Formulaire
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
class DefaultController extends Controller{
public function newAction(Request $request) {
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->getForm();
return $this->render('default/new.html.twig', array(
'form' => $form->createView(),
));
}
}
Affichage du formulaire
● form_start(form)
– Génère le début du formulaire
● Balise ouvrante
● enctype correspondant...
● form_widget(form)
– Génère tout les champs «input»
– Label
– Messages de validation
● form_end(form)
– Génère la fin du formulaire
– Champs cachés «hidden»
– Protection CSRF
● Twig
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Récupération du formulaire
public function newAction(Request $request) {
$task = new Task();
$form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->redirectToRoute('task_success');
}
return $this->render('default/new.html.twig', array(
'form' => $form->createView(),
));
}
Soumission avec plusieurs boutons
● $form = $this->createFormBuilder($task)
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Task'))
->add('saveAndAdd', SubmitType::class, array('label' => 'Save and
Add'))
→getForm();
● if ($form->isValid()) {
$nextAction = $form->get('saveAndAdd')->isClicked()
? 'task_new'
: 'task_success';
return $this->redirectToRoute($nextAction);
}
Validation des Formulaires
● Validation est effectuée en ajoutant un ensemble de règles (appelées
contraintes) à une classe.
● use Symfony\Component\Validator\Constraints as Assert;
class Task{
/**
- @Assert\NotBlank()
*/
public $task;
/**
- @Assert\NotBlank()
- @Assert\Type("\DateTime")
*/
protected $dueDate;
}
Type d'attributs (formulaire)
● Text Fields
– TextType, TextareaType, EmailType, IntegerType, MoneyType, NumberType,
PasswordType, PercentType, SearchType, UrlType, RangeType
● Choice Fields
– ChoiceType, EntityType, CountryType, LanguageType, LocaleType, TimezoneType,
CurrencyType
● Date and Time Fields
– DateType, DateTimeType, TimeType, BirthdayType
● Autres
– CheckboxType, FileType, RadioType,
● Champs composés
– CollectionType, RepeatedType
● Hidden Fields
– HiddenType
● Buttons
– ButtonType, ResetType, SubmitType
Options pour un champs
->add('dueDate', 'date', array(
'widget' => 'single_text',
'required' => false
))
Quelques options du champs
«TextType»
● data: Données par défaut
● disabled: true/false, Valeur non éditable
● empty_data: Valeur à renvoyer en cas d'absence de
données
● label : Nom à afficher pour ce champs
● required: true/false, champs obligatoire
Affichage de chaque champs à part
{{ form_start(form) }}
{{ form_errors(form) }}
Advertisement
<div>
{{ form_label(form.task) }}
{{ form_errors(form.task) }}
{{ form_widget(form.task) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
<div>
{{ form_widget(form.save) }}
</div>
{{ form_end(form) }}
Activités
● Activité 4: Création d'un Bundle
● Activité 5: Utilisation d'un formulaire
Bases de données et Doctrine
Configuration de la base de donnée
app/config/parameters.yml
parameters:
database_driver: pdo_mysql
database_host: localhost
database_name: symfony
database_user: root
database_password: null
Création de la base de donnée
● Création de la base:
– php bin/console doctrine:database:create
● Suppression de la base:
– php bin/console doctrine:database:drop --force
● Création du schéma
– php bin/console doctrine:schema:create
● Suppression du schéma
– php bin/console doctrine:schema:drop --force
● Mise à jour du schéma
– php bin/console doctrine:schema:update --force
● Validation du schéma
– php bin/console doctrine:schema:validate
Génération des entités
● Génération des Entités
– php bin/console doctrine:generate:entity
● Génération des Getters et de Setters
– php bin/console doctrine:generate:entities
{nom_entité}
Informations de «Mapping» (1/2)
● Quelle table pour un objet
● Quelle colonne pour un attributs
● Quelles propriété pour les colonnes
Informations de «Mapping» (2/2)
// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
- @ORM\Entity
- @ORM\Table(name="product")
*/
class Product{
/**
- @ORM\Column(type="integer")
- @ORM\Id
- @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
- @ORM\Column(type="string", length=100)
*/
protected $name;
/**
- @ORM\Column(type="decimal", scale=2)
*/
protected $price;
/**
- @ORM\Column(type="text")
*/
protected $description;
}
Persistance
$product = new Product();
$product->setName('A Foo Bar');
$product->setPrice('19.99');
$product->setDescription('Lorem ipsum dolor');
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
Chercher un Objet
● Recherche par identifiant:
– $product = $this->getDoctrine()
->getRepository('AppBundle:Product')
-> find ($id) ;
● Recherche un objet par le nom et le prix
– $product = $repository->findOneBy(
array('name' => 'foo', 'price' => 19.99)
);
● Recherche plusieurs objets par le nom ordonnés par le prix:
– $products = $repository->findBy(
array('name' => 'foo'),
array('price' => 'ASC')
);
Mise à jour d'un objet
$em = $this->getDoctrine()->getManager();
$product = $em
->getRepository('AppBundle:Product')
->find($id);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
$product->setName('New product name!');
$em->flush();
Suppression d'un objet
$em->remove($product);
$em->flush();
Langage d'interrogation (DQL)
$em = $this→getDoctrine()
->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();
Activités
● Activité 6: Génération des Entités Doctrine et CRUD