Objectifs de cette séquence (1h) :
Objectif : programmer les vues mais aussi le contrôleur, au-dessus des mécanismes de HTML et HTTP, pour offrir une bonne expérience utilisateur.
Une page / vue, représente l’état courant
Point départ :
1 transition en entrée / 32 transitions en sortie
Le contrôleur gère les transitions HATEOAS
GET
, POST
+ PUT
ou PATCH
, DELETE
, OPTIONS
Objectifs de cette séquence (1h) :
Transmission de données au serveur HTTP (par le client).
Pour quoi faire ?
CRUD :
GET
?arg1=val1&arg2=val2...
POST
On voudrait une page dans l’application pour ajouter une tâche, du genre :
Le programme Symfony derrière le serveur HTTP s’attend à la transmission de données via :
POST
sur ressource /todo/new
todo[title]
todo[completed]
Par exemple, avec un en-tête Content-Type:
application/x-www-form-urlencoded
:
todo[completed]: 1 todo[title]: Apprendre+le+CSS
L’utilisateur humain ne construit pas des requêtes HTTP (en général) : il faut une interface dans un programme.
Comment mettre cette interface à disposition de l’utilisateur : déploiement de la partie cliente de l’application ?
Client HTTP à déployer pour chaque application ?
Exemple : applications mobiles natives
Problème de déploiement !
Client HTTP universel (donc « stupide ») ?
Exemple : Navigateur FireFox
Comment rendre ce navigateur capable de parler comme il faut au serveur ?
Fonctionnalités génériques dans navigateur Web, de génération d’interfaces de soumission de données :
POST
?) pour transmettre les
données selon les conventions du serveur
Objectifs de cette séquence (1h) :
GET
) du navigateur sur URL<form>...</form>
POST
)30x
Location
+ URL de destinationExemple sur l’application « fil rouge » de gestion de tâches.
GET /todo/new
Réponse : page avec formulaire HTML « new
»
<!DOCTYPE html>
<html>
<body>
<form method="post" action="/todo/new">
Ce qu'il faut faire :<br>
<input name="todo[title]" required="required" type="text">
<br>
Déjà terminé :<br>
<input name="todo[completed]" type="checkbox">
<br><br>
<input type="submit" value="Ajouter">
</form>
</body>
</html>
Le formulaire est affiché et l’utilisateur peut saisir des données dans les champs de saisie (ou cocher les cases, pour la boîte à cocher (checkbox).
Le navigateur peut effectuer certains contrôles de saisie, par exemple
sur les champs en saisie obligatoire (attribut required
du champ <input>
).
Méthode POST
:
<form method="post" action="/todo/new">
...
Requête HTTP
soumise :
POST /todo/new HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Content-Length: 33 todo[completed]=on&todo[title]=Apprendre+le+CSS
PHP naïf :
<html>
<head>
<title>Résultats du Formulaire</title>
</head>
<body>
<p>Bonjour <?php
echo $_POST[todo][title] ." ". $_POST[todo][completed];
?>.</p>
</body>
</html>
<html> <head> <title>Résultats du Formulaire</title> </head> <body> <p>Bonjour <?php echo $_POST[todo][title] ." ". $_POST[todo][completed]; ?>.</p> <form method="post"> Ce qu il faut faire :<br> <input name="todo[title]" required="required" type="text"><br> Déjà terminé :<br> <input name="todo[completed]" type="checkbox"> <br><br> <input type="submit" value="Ajouter"> </form> </body> </html>
Attention : injection de code malveillant !
$_POST[todo]
contient du code HTML, qui
déclenche, par exemple, l’exécution d’instructions Javascript, et
qu’on fait echo $_POST[...]
?
Objectifs de cette séquence (1h) :
Une représentation de la ressource demandée dans la requête du client.
arguments de render()
:
$this->render ( 'todo.html.twig', [
'id' => $id,
'todo' => $todo
] );
#[Route('/todo/show/{id}', name: 'todo_show', methods: ['GET'])]
public function todoShow($id)
{
// ...
}
path()
<a href=" {{ path('todo_show', { 'id' : todo.id }) }}">
détails
</a>
Généré avec un gabarit Twig ?
Différentes façons de faire plus ou moins sophistiquées.
Injection d’un formulaire dans un template Twig :
#[Route('/todo/new', name: 'todo_new_get', methods: ['GET'])]
public function newGet(Request $request): Response
{
$form = $this->createNewTodoForm();
return $this->render('todo/new.html.twig', [
'formulaire' => $form->createView(),
]);
}
on verra createNewTodoForm()
un peu plus tard
new
Code HTML minimal d’un formulaire (liste pas tous champs de saisie des
propriétés d’une Todo
)
{% extends 'base.html.twig' %}
{% block title %}New Todo{% endblock %}
{% block body %}
<h1>Ajout d'une nouvelle tâche</h1>
<form action="#" method="post">
{{ form_widget(formulaire) }}
<input type="submit" name="Ajouter" />
</form>
{% endblock %} {# body #}
Formulaire câblé sur une entité du modèle de données : énumère quelles propriétés doivent être traitées dans formulaire
function createNewTodoForm()
{
$todo = new Todo();
$formbuilder =$this->createFormBuilder($todo);
$form = $formbuilder
->add('title')
->add('completed', CheckboxType::class,
array('required' => false))
->getForm();
return $form;
}
Le contrôleur Symfony gère la soumission des données prévue dans le
form
HTML généré par le gabarit
Même chemin URL (/todo/new
), mais POST
#[Route('/todo/new', name: 'todo_new_post', methods: ['POST'])]
public function newPost(Request $request, ManagerRegistry $doctrine)
{
$todo = new Todo();
$form = $this->createNewTodoForm($todo);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $doctrine->getManager();
$em->persist($todo);
$em->flush();
return $this->redirectToRoute('todo_index');
}
// ...
Historique des requêtes, dans le profiler, pour visualiser les détails de la requête POST, avant la redirection :
Encore un générateur :
symfony console make:crud Todo
Crée pour nous tous ces éléments de formulaires :
created: src/Controller/TodoController.php created: src/Form/TodoType.php created: templates/todo/_delete_form.html.twig created: templates/todo/_form.html.twig created: templates/todo/index.html.twig created: templates/todo/show.html.twig created: templates/todo/new.html.twig created: templates/todo/edit.html.twig
[X]
HTTP (GET)[X]
PHP[X]
Doctrine[X]
Routeur Symfony[X]
HTML[X]
Twig[X]
CSS[X]
Formulaires[ ]
SessionsTout ce qu’il faut pour un site interactif