
Gérer la soumission du formulaire dans le contrôleur (sans persistance de données)
Apprenez à gérer la soumission d'un formulaire Symfony dans un contrôleur : utiliser `handleRequest`, vérifier `isSubmitted` et `isValid`, récupérer les données avec `getData`, et simuler une action sans persistance.
Le cycle de vie d'un formulaire Symfony : De l'affichage à la soumission
La gestion d'un formulaire dans Symfony suit un cycle bien défini. Initialement, lorsque l'utilisateur accède à la page contenant le formulaire (via une requête GET), le contrôleur instancie le formulaire et le passe au template Twig pour affichage. L'utilisateur remplit ensuite les champs et clique sur le bouton de soumission, ce qui déclenche une nouvelle requête HTTP vers le serveur, généralement une requête POST (bien que les formulaires puissent aussi utiliser GET).
C'est cette seconde requête qui nous intéresse ici. Le contrôleur doit intercepter cette requête, lier les données soumises au formulaire, vérifier si les données sont valides, et enfin, effectuer une action en fonction de ces données. Pour ce chapitre, nous nous concentrerons sur la gestion de la soumission et la récupération des données, sans encore implémenter la persistance réelle de ces données (par exemple, dans une base de données).
Utilisation de `handleRequest()` pour lier les données de la requête au formulaire
Lorsque la requête de soumission du formulaire arrive au contrôleur, la première étape cruciale est de demander à l'objet formulaire de traiter cette requête. Cela se fait à l'aide de la méthode handleRequest($request) de l'objet formulaire. Cette méthode inspecte l'objet Request (qui doit être injecté dans votre méthode de contrôleur) pour voir si le formulaire a été soumis. Si c'est le cas, elle récupère les données envoyées par le client et les utilise pour hydrater l'objet ou le tableau de données qui a été initialement lié au formulaire lors de sa création (le second argument de $this->createForm()).
Reprenons notre méthode newTask() dans TaskController et intégrons cette gestion :
namespace App\Controller;
use App\Form\TaskType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; // Important : injecter l'objet Request
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class TaskController extends AbstractController
{
// Notre 'fausse' base de données pour la démonstration
// Dans un vrai projet, ce serait géré par la session ou une base de données
private static array $simulatedTasksStorage = [];
// ... (méthodes index, show)
#[Route('/tache/nouvelle', name: 'app_task_create', methods: ['GET', 'POST'])]
public function newTask(Request $request): Response
{
$taskData = []; // Données initiales pour le formulaire (sera un tableau associatif)
$form = $this->createForm(TaskType::class, $taskData);
// Etape clé : le formulaire traite la requête entrante
$form->handleRequest($request);
// La suite de la logique de traitement vient ici...
return $this->render('task/new.html.twig', [
'taskForm' => $form->createView(),
]);
}
// Pour la démonstration, ajoutons une méthode pour afficher nos tâches simulées
#[Route('/taches-simulees', name: 'app_task_simulated_list')]
public function showSimulatedTasks(): Response
{
return $this->render('task/simulated_list.html.twig', [
'tasks' => self::$simulatedTasksStorage
]);
}
}L'injection de Request $request comme argument de la méthode newTask permet à Symfony de fournir automatiquement l'objet Request actuel, qui contient toutes les informations sur la requête HTTP (y compris les données POST du formulaire).
Vérification de la soumission et de la validité avec `isSubmitted()` et `isValid()`
Après avoir appelé $form->handleRequest($request), nous devons vérifier deux choses :
- Le formulaire a-t-il été soumis ? La méthode
$form->isSubmitted()retournetruesi le formulaire a été soumis (c'est-à-dire si la requête contient des données correspondant à ce formulaire), etfalsesinon (par exemple, lors du premier affichage de la page via une requête GET). - Les données soumises sont-elles valides ? La méthode
$form->isValid()retournetruesi le formulaire a été soumis ET si toutes les données passent les règles de validation. Ces règles peuvent provenir des types de champs (par exemple, un champEmailTypevérifiera le format de l'email), des options de champ (comme'required' => true), ou des contraintes de validation explicites que vous auriez ajoutées (via des attributs sur une entité ou directement dans leFormType). Pour l'instant, la validation se basera principalement sur les options `required` et les types de champs de base.
Ces deux vérifications sont généralement combinées dans une instruction if :
// Suite de la méthode newTask() dans TaskController
// ... après $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Le formulaire a été soumis et les données sont considérées comme valides.
// Nous pouvons maintenant récupérer les données et effectuer une action.
// Récupération des données soumises et validées
// $form->getData() retourne les données dans le format de $taskData initial
// (ici, un tableau associatif car $taskData était un tableau vide).
$submittedData = $form->getData();
// Action à effectuer avec les données (pour l'instant, sans persistance)
// Par exemple, affichons-les pour déboguer ou simulons un ajout.
// Option 1: Afficher les données (pour le débogage)
// dump($submittedData);
// return new Response('<html><body>Données soumises et valides : <pre>' . htmlspecialchars(print_r($submittedData, true)) . '</pre></body></html>');
// Option 2: Simulation d'un ajout à notre 'fausse' base de données
// Pour que cela ait un semblant de persistance au sein d'une même session PHP (très basique),
// il faudrait utiliser la session de Symfony ou une propriété statique (pour la démo ici).
$newId = count(self::$simulatedTasksStorage) > 0 ? max(array_column(self::$simulatedTasksStorage, 'id')) + 1 : 1;
$newTaskEntry = [
'id' => $newId,
'title' => $submittedData['title'], // Accès aux données par leur nom de champ
'description' => $submittedData['description'],
'completed' => false, // Par défaut, une nouvelle tâche n'est pas complétée
];
self::$simulatedTasksStorage[] = $newTaskEntry;
$this->addFlash(
'success',
sprintf('La tâche "%s" a été ajoutée à la liste simulée (ID: %d) !', $submittedData['title'], $newId)
);
// Il est crucial de rediriger après un traitement POST réussi
// pour éviter la resoumission du formulaire en cas de rafraîchissement (Pattern Post/Redirect/Get).
return $this->redirectToRoute('app_task_simulated_list'); // Redirection vers une page qui affiche les tâches simulées
}
// Si le formulaire n'est pas soumis (requête GET initiale) ou n'est pas valide,
// on affiche simplement le formulaire (avec les erreurs de validation s'il y en a).
return $this->render('task/new.html.twig', [
'taskForm' => $form->createView(),
]);
}Si $form->isValid() retourne false (et que le formulaire a été soumis), Symfony aura automatiquement attaché les messages d'erreur aux champs concernés. Ces erreurs seront affichées dans le template Twig grâce aux fonctions comme form_errors() si vous les avez incluses.
Récupération des données avec `getData()` et simulation d'une action
Lorsque la condition $form->isSubmitted() && $form->isValid() est vraie, cela signifie que les données saisies par l'utilisateur sont prêtes à être utilisées. Vous pouvez les récupérer en appelant la méthode $form->getData(). Le format des données retournées par getData() dépend de ce que vous avez passé comme second argument à $this->createForm() :
- Si vous avez passé un objet (par exemple, une instance d'entité Doctrine),
getData()retournera cet objet, mis à jour avec les données soumises. - Si vous avez passé un tableau (comme notre
$taskData = []),getData()retournera un tableau associatif où les clés sont les noms des champs du formulaire et les valeurs sont les données soumises. C'est notre cas ici.
Dans notre exemple, $submittedData = $form->getData(); nous donnera un tableau comme ['title' => 'Titre saisi', 'description' => 'Description saisie'].
Pour la "simulation" d'une action sans persistance réelle :
- Nous avons créé une propriété statique
private static array $simulatedTasksStorage = [];dans notreTaskController. Attention : L'utilisation de propriétés statiques pour stocker des données de cette manière est une très mauvaise pratique dans les applications réelles car cela crée un état global et des problèmes de concurrence. C'est utilisé ici uniquement pour une démonstration simplifiée de la persistance des données au sein d'une même exécution de script PHP ou entre requêtes si le serveur PHP garde le processus en vie (ce qui n'est pas garanti). Pour une persistance temporaire correcte entre requêtes, il faudrait utiliser le service de Session de Symfony. - Nous calculons un nouvel ID simple et ajoutons la nouvelle tâche à ce tableau statique.
- Nous utilisons
$this->addFlash()pour préparer un message de succès qui sera affiché après la redirection. - Nous redirigeons vers une nouvelle route
app_task_simulated_listqui affichera le contenu de$simulatedTasksStorage. Il faudra créer un template simpletemplates/task/simulated_list.html.twigpour cela :
{# templates/task/simulated_list.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}Liste des tâches simulées{% endblock %}
{% block body %}
<div class="container mt-4">
<h1>Tâches simulées (non persistantes)</h1>
{% if tasks is not empty %}
<ul class="list-group">
{% for task in tasks %}
<li class="list-group-item">
<strong>ID {{ task.id }}: {{ task.title }}</strong> - {{ task.description }}
</li>
{% endfor %}
</ul>
{% else %}
<p>Aucune tâche simulée pour le moment.</p>
{% endif %}
<p class="mt-3">
<a href="{{ path('app_task_create') }}" class="btn btn-primary">Ajouter une autre tâche simulée</a>
<a href="{{ path('app_task_list') }}" class="btn btn-secondary">Retour à la vraie liste (en dur)</a>
</p>
</div>
{% endblock %}Ce flux complet – affichage, soumission, traitement, récupération des données, et action (même simulée) – constitue le coeur de la gestion des formulaires dans Symfony. La prochaine étape logique dans un projet réel serait d'intégrer la persistance des données avec Doctrine ORM.