Symfony and HTTP Fundamentals

Software Development (Web Development and Frameworks) · course

Browse all développement web documents

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