Architecture(s) et
application(s) Web

CSC4101 - Contrôleurs, interactions CRUD, formulaires

09/10/2024

Plan de la séquence

Objectifs de cette séquence (1h) :

  1. Rôle des contrôleurs
  2. Soumission de données à une application
  3. Gestion des formulaires HTML
  4. Programmation avec Symfony

Rappel architecture et MVC

Rappel : Architecture multi-couches

MVC-sur-archi-1.png

Rappel : Patron MVC

MVC-user.png
Figure 1 : Patron de conception MVC

Modèle et Vue

MVC-sur-archi-2.png
Figure 2 : Architecture : Modèle + Vue

Pilote des interactions : le Contrôleur

  • Contrôleur : coordonne les transitions entre vues, et les entrées de l’utilisateur, en fonction de l’état du modèle
  • Vues (pages)
  • Modèle

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.

Modèle de conception : États - Transitions

Ensemble de vues / écrans / pages

  • Naviguer dans une arborescence de pages / vues / écrans / dialogues
  • Trouver de l’information au sein d’une page
  • Widgets pour interaction : composants d’interface graphique (boutons, listes, etc.)

Un état de l’application

Une page / vue, représente l’état courant

transitions.png
Figure 3 : Transitions sur une page

Hypermedia As The Engine Of Application State (HATEOAS)

  • Une application = diagramme d’états fini
  • Transitions = hyperliens sur lesquels on clique (et autres widgets)
HATEOAS-states.png
Figure 4 : Diagramme états fini

Exemple Application Web

Point départ :

first-tweet.png
Figure 5 : Premier Tweet

(https://twitter.com/jack/status/20)

Diagramme états Web app Twitter

1 transition en entrée / 32 transitions en sortie

etats-twitter.png
Figure 6 : Diagramme états Web app Twitter

Interactions avec utilisateur

  • Affichage de pages (lecture sur des ressources)
  • Invocation de méthodes sur des ressources dédiées (y compris modification : CRUD)
  • Invocation d’APIs pour interactions fines (lecture / écriture)

Framework Web MVC

  • Modèle
  • Vue
  • Contrôleur

Le contrôleur gère les transitions HATEOAS

</hateoas>

Vers une plate-forme Web universelle

« Services Web »

  • Utiliser HTTP pour faire interagir différents programmes, sans navigateur Web, sans humain dans la boucle :
    • programme client
    • programme serveur
  • Services Web, APIs (Application Programming Interfaces)
  • Problème : HTTP est vraiment de très bas niveau, donc il faut ajouter pas mal de standardisation par-dessus, ou que les concepteurs des programmes des deux côtés se concertent.

Applications pour l’utilisateur humain

  • Utiliser les technologies du Web pour faire fonctionner des applications affichées dans un navigateur.
  • Mettre en œuvre le principe HATEOS : expérience utilisateur
  • Mécanismes de saisie de données riches (widgets affichés par le navigateur), envoyées au serveur
  • Mécanisme de traitement des données reçues, côté serveur Web

Sémantique des interactions avec un serveur HTTP

Principes REST

  • REpresentational State Transfer : passer de documents hypertextes à des applications Web
  • dans la Thèse de Roy Fielding en 2000 :
  • HTTP 1.1 pour des applications (APIs)
    • Verbes/méthodes pour une sémantique plus riche d’opérations : GET, POST + PUT ou PATCH, DELETE, OPTIONS
    • Codes de retour HTTP
  • Pas de session ?
  • The six constraints of the architectural style of REST

6 contraintes architecture REST

  1. Client-serveur
  2. Serveur sans mémoire état du client (stateless)
  3. Cache (client)
  4. Système à plusieurs couches
  5. Code sur le client on-demand (optionnel)
  6. Contrainte d’interface uniforme
    1. Identifiants de ressources,
    2. Représentations des ressources,
    3. Messages auto-descriptifs,
    4. HATEOAS : Hypermedia As The Engine Of Application State

</principes_REST>

Plan de la séquence

Objectifs de cette séquence (1h) :

  1. Rôle des contrôleurs
  2. Soumission de données à une application
  3. Gestion des formulaires HTML
  4. Programmation avec Symfony

Read + Write !

Transmission de données au serveur HTTP (par le client).

Pour quoi faire ?

CRUD :

  • création (C reate)
  • consultation (R ead)
  • modification (U pdate)
  • suppression (D elete)

Méthodes HTTP pour envoi de données à l’application

  • Différentes solutions :
    • GET
      • arguments dans URL
        ?arg1=val1&arg2=val2...
      • en-têtes
    • POST
      • arguments dans URL
      • en-têtes
      • données de contenu de requête

Exemple : ajout d’une tâche

On voudrait une page dans l’application pour ajouter une tâche, du genre :

form-screenshot-new-todo.png

Exemple : ce que le programme comprend

Le programme Symfony derrière le serveur HTTP s’attend à la transmission de données via :

  • requête POST sur ressource /todo/new
  • variables dans les données du corps :
    1. titre de la tâche (texte, obligatoire) : todo[title]
    2. statut de la tâche (booléen, optionnel) : todo[completed]

Par exemple, avec un en-tête Content-Type: application/x-www-form-urlencoded :

todo[completed]: 1
todo[title]: Apprendre+le+CSS

Utiliser un « générateur de requêtes » ?

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 ?

Installer un interface utilisateur, pour chaque 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 ?

Ajout au navigateur de mécanismes d’interface génériques

Fonctionnalités génériques dans navigateur Web, de génération d’interfaces de soumission de données :

  1. Récupère l’interface graphique propre à chaque application (HTML), transmise par le serveur
  2. Affiche l’interface à l’utilisateur
  3. Génère les bonnes requêtes qui satisferont les besoins du serveur

Envoi de l’interface par le serveur

  1. Le serveur peut envoyer aux clients une page HTML contenant l’interface particulière qui sait générer la bonne requête.
  2. La page HTML contient :
    • un formulaire qui est affichable (incluant tous ses widgets, pour chaque variable)
    • un « réflexe » qui générera une requête bien formée (POST ?) pour transmettre les données selon les conventions du serveur

</soumission_donnees>

Plan de la séquence

Objectifs de cette séquence (1h) :

  1. Rôle des contrôleurs
  2. Soumission de données à une application
  3. Gestion des formulaires HTML
  4. Programmation avec Symfony

Séquence d’interaction

Dialogue entre serveur et client

  1. Client HTTP « stupide » demande au serveur à récupérer une interface HTML pour avoir accès à une fonctionnalité de l’application
  2. Le serveur lui prépare une « jolie » page HTML contenant tous les widgets ad-hoc, et qui est prête à générer l’invocation de la méthode REST ad-hoc
  3. Le client construit une page Web utilisable par un humain : formulaires, boutons, etc. Il fait une confiance « aveugle » au serveur, concernant l’utilisabilité
  1. L’humain saisit les données requises et valide leur transmission, sans imaginer la structure et le format réel des données qui devront transiter vers le serveur via HTTP
  2. Le client transmet « stupidement » ces données au serveur avec la méthode HTTP bien formée (en principe)
  3. Le serveur réagit à cette soumission de données et confirme au client si c’est bon pour lui

Séquence HTML + HTTP

  1. Requête (méthode GET) du navigateur sur URL
  2. Réponse avec un document HTML contenant
    <form>...</form>
  3. Affichage du formulaire dans le navigateur
  4. Saisie de données dans les champs du formulaire
  5. Clic sur le bouton de soumission : nouvelle requête d’envoi des données sur l’URL cible du formulaire (souvent méthode POST)
  6. Traitement du formulaire par le serveur : code de réponse HTTP

Traitement des données transmises

  1. Le serveur reçoit les données
  2. Il décide s’il les accepte
  3. En cas de refus, le client devra :
    • ré-afficher les données pour demander de corriger
    • ou afficher une autre page (redirection)
  4. En cas d’acceptation, le client devra :
    • afficher une autre page (redirection)

Redirection

  • Évite de rejouer la requête de transmission du formulaire en cas de rechargement de la page par l’utilisateur dans son navigateur
  • Le serveur envoie :
    • code de réponse 30x
    • en-tête Location + URL de destination

</sequence_interaction>

Exemple HTML + PHP

Exemple sur l’application « fil rouge » de gestion de tâches.

1-2 : Formulaire de saisie HTML

  1. Requête : GET /todo/new
  2. 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>
    

3-4 : Gestion des contraintes de saisie du formulaire

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>).

5 : Requête transmise

  • 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
    

6: Exemple de code de gestion en PHP

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>

Version « deux en un »

<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>

Sécurité

Attention : injection de code malveillant !

  • Que se passe-t-il si $_POST[todo] contient du code HTML, qui déclenche, par exemple, l’exécution d’instructions Javascript, et qu’on fait
    echo $_POST[...] ?

</formulaires_naifs>

Plan de la séquence

Objectifs de cette séquence (1h) :

  1. Rôle des contrôleurs
  2. Soumission de données à une applicatio
  3. Gestion des formulaires HTML
  4. Programmation avec Symfony

MVC Symfony complet

Modèle des données : Doctrine

  • Classes PHP
  • Attributs Doctrine : typage des propriétés
  • Synchronisation dans la base de données relationnelle (générée)

Vues : Twig

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
          ] );
    
  • injectés dans le gabarit Twig

Routage vers les méthodes du contrôleur

#[Route('/todo/show/{id}', name: 'todo_show', methods: ['GET'])]
public function todoShow($id)
{        
    // ...
}

Cohérence des routes avec path()

  • Utilisation des routes pour les transitions dans l’application (nommées)
  • chemins « en dur ».
<a href=" {{ path('todo_show', { 'id' : todo.id }) }}">
  détails
</a>

Formulaires avec Symfony

Affichage du formulaire

Généré avec un gabarit Twig ?

Différentes façons de faire plus ou moins sophistiquées.

Gabarit d’affichage (GET)

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

Gabarit Twig de la page 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 #}

Construction du formulaire

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;
}

Gestion de la requête de soumission

Le contrôleur Symfony gère la soumission des données prévue dans le form HTML généré par le gabarit

Méthode de soumission des données du formulaire

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');
    }

    // ...

Algorithme fondamental

flowchart.png
Figure 7 : « Diagramme de décision »

Mise au point dans la barre d’outils Symfony

Historique des requêtes, dans le profiler, pour visualiser les détails de la requête POST, avant la redirection :

  1. GET (affichage d’un formulaire)
  2. POST (soumission des données du formulaire)
  3. GET (redirection suite au traitement des données soumises)

Générateur Contrôleur CRUD

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

Sécurité

  • Éviter l’injection de code malveillant
  • Éviter de rejouer les requêtes POST
    • utilisation d’un cookie CSRF (vu plus tard)
    • vérifie que le POST correspond bien au formulaire généré précédamment via le GET

</symfony>

Take away

  • principe HATEOS pour gérer les transitions entre pages
  • Formulaires HTML
  • Algorithme de gestion de la soumission des données dans les contrôleurs Symfony

Récap

  • [X] HTTP (GET)
  • [X] PHP
  • [X] Doctrine
  • [X] Routeur Symfony
  • [X] HTML
  • [X] Twig
  • [X] CSS
  • [X] Formulaires
  • [ ] Sessions

Tout ce qu’il faut pour un site interactif

Copyright

  • Document propriété de ses auteurs et de Télécom SudParis (sauf exceptions explicitement mentionnées).
  • Réservé à l’utilisation pour la formation initiale à Télécom SudParis.