formulaire
Pour un développeur Web, le traitement des formulaires HTML est l'une des tâches les plus courantes et les plus difficiles. Symfony intègre un composant Form pour faciliter le traitement des formulaires. Dans ce chapitre, vous créerez un formulaire complexe à partir de zéro et découvrirez les fonctionnalités importantes de la bibliothèque de formulaires.
Le composant Form de Symfony est une bibliothèque de classes indépendante que vous pouvez utiliser en dehors de votre projet Symfony. Reportez-vous à la Documentation du composant Form pour en savoir plus.
Créez un formulaire simple ¶
Supposons que vous construisiez une simple liste de tâches pour afficher certaines "tâches". Vous devez créer un formulaire pour permettre à vos utilisateurs de modifier et de créer des tâches. Avant cela, jetons un coup d'œil à la classe Task
, qui peut restituer et stocker des données pour une seule tâche. Task
类,它可呈现和存储一个单一任务的数据。
// src/AppBundle/Entity/Task.phpnamespace AppBundle\Entity; class Task{ 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; }}
这是一个原生的PHP对象类,因为它没有和Symfony互动也没有引用其它类库。它是非常简单的一个PHP对象类,直接解决了 你 程序中的 task
(任务)之数据问题。当然,在本章的最后,你将能够通过HTML表单把数据提交到一个 Task
实例,验证它的值,并把它持久化到数据库。
构建表单 ¶
现在你已经创建了一个 Task
// src/AppBundle/Controller/DefaultController.phpnamespace AppBundle\Controller; use AppBundle\Entity\Task;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\Form\Extension\Core\Type\TextType;use Symfony\Component\Form\Extension\Core\Type\DateType;use Symfony\Component\Form\Extension\Core\Type\SubmitType; class DefaultController extends Controller{ public function newAction(Request $request) { // create a task and give it some dummy data for this example // 创建一个task对象,赋一些例程中的假数据给它 $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(), )); }}Il s'agit d'une classe d'objet PHP native, car elle n'interagit pas avec Symfony et ne fait pas référence à d'autres bibliothèques. Il s'agit d'une classe d'objets PHP très simple qui résout directement le problème de données de
task
(tâche) dans votre programme. Bien entendu, à la fin de ce chapitre, vous serez en mesure de soumettre des données à une instance Task
via un formulaire HTML, de valider sa valeur et de la conserver dans une base de données. Créez le formulaire ¶
Tâche
classe, l’étape suivante consiste à créer et à afficher un véritable formulaire HTML. Dans Symfony, cela se fait en créant un objet de formulaire et en le rendant dans le modèle. Maintenant, vous pouvez tout faire dans le contrôleur : TWIG:{# app/Resources/views/default/new.html.twig #} {{ form_start(form) }} {{ form_widget(form) }} {{ form_end(form) }}
Créer un formulaire ne nécessite pas beaucoup de code car les objets de formulaire Symfony sont créés via un « générateur de formulaire ». Le but du générateur de formulaire est de vous permettre d'écrire des "instructions" simples pour la création d'un formulaire, et toutes les tâches de "surcharge" lors de la création réelle du formulaire sont effectuées par le générateur.
Dans cet exemple, vous avez ajouté deux champs au formulaire, à savoir task
et dueDate
. Correspondant aux attributs task
et dueDate
de la classe Task
. Vous avez spécifié le "type" de FQCN (nom de classe complet/nom de classe de chemin complet) pour eux respectivement (tel que TextType
, DateType
), qui est déterminé par le tapez à générer pour le champ. Quelle balise de formulaire HTML (groupe de balises). task
和 dueDate
。对应的是 Task
类中的 task
和 dueDate
属性。你已为它们分别指定了FQCN(Full Quilified Class Name/完整路径类名)的“类型”(如 TextType
, DateType
),由类型决定为字段生成哪一种HTML表单标签(标签组)。
最后,你添加了一个带有自定义label的提交按钮以向服务器提交表单。
Symfony附带了许多内置类型,它们将被简短地介绍(见下面的内置表单类型)。
渲染表单 ¶
表单创建之后,下一步就是渲染它。这是通过传递一个特定的表单“view”对象(注意上例控制器中的 $form->createView()
方法)到你的模板,并通过一系列的表单helper function(帮助函数)来实现的。
PHP:<!-- app/Resources/views/default/new.html.php --> <?php echo $view['form']->start($form) ?> <?php echo $view['form']->widget($form) ?> <?php echo $view['form']->end($form) ?>
// ...use Symfony\Component\HttpFoundation\Request; public function newAction(Request $request){ // just setup a fresh $task object (remove the dummy data) // 直接设置一个全新$task对象(删除了假数据) $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()) { // $form->getData() holds the submitted values // but, the original `$task` variable has also been updated // $form->getData() 持有提交过来的值 // 但是,原始的 `$task` 变量也已被更新了 $task = $form->getData(); // ... perform some action, such as saving the task to the database // for example, if Task is a Doctrine entity, save it! // 一些操作,比如把任务存到数据库中 // 例如,如果Tast对象是一个Doctrine entity,存下它! // $em = $this->getDoctrine()->getManager(); // $em->persist($task); // $em->flush(); return $this->redirectToRoute('task_success'); } return $this->render('default/new.html.twig', array( 'form' => $form->createView(), ));}
本例假设你以"POST"请求提交表单,并且提交到和“表单显示(页面)”相同的URL。后面你将学习如何改变请求方法(request method)和表单提交后的目标URL。
就是这样!只需要三行就可以渲染出完整的form表单:
form_start(form)
- 渲染表单的开始标签,包括在使用文件上传时的正确enctype属性。
form_widget(form)
- 渲染出全部字段,包含字段元素本身,字段label以及字段验证的任何错误信息。
form_end(form)
Enfin, vous avez ajouté un bouton de soumission avec une étiquette personnalisée pour soumettre le formulaire au serveur. - Symfony est livré avec un certain nombre de types de formulaires intégrés, qui seront abordés brièvement (voir les types de formulaires intégrés ci-dessous ).
Rendu du formulaire ¶
Une fois le formulaire créé, l'étape suivante consiste à le rendre. Cela se fait en passant un objet "view" de formulaire spécifique (notez la méthode
$form->createView()
dans l'exemple de contrôleur ci-dessus) à votre modèle et via une série de fonctions d'assistance de formulaire ( fonction d'assistance) à réaliser. Annotations:// src/AppBundle/Entity/Task.phpnamespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; class Task{ /** * @Assert\NotBlank() */ public $task; /** * @Assert\NotBlank() * @Assert\Type("\DateTime") */ protected $dueDate;}
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Task: properties: task: - NotBlank: ~ dueDate: - NotBlank: ~ - Type: \DateTime
- 🎜
form_start(form)
🎜🎜Render la balise de début du formulaire, y compris lors de l'utilisation Corrigez l'attribut enctype lors du téléchargement de fichiers. 🎜🎜form_widget(form)
🎜🎜Rend tous les champs, y compris l'élément de champ lui-même, l'étiquette du champ et toute information d'erreur pour la validation du champ. 🎜🎜form_end(form)
🎜🎜Lorsque vous générez chaque champ manuellement, il peut restituer la balise de fin du formulaire et tous les champs du formulaire qui n'ont pas encore été rendus. Ceci est utile lors du rendu des champs masqués et pour profiter de la fonction automatique.
🎜🎜Protection CSRF🎜🎜 Très utile pour protéger les mécanismes. 🎜🎜🎜🎜🎜C'est aussi simple que ça, mais pas très flexible (pour l'instant). En règle générale, vous souhaitez afficher chaque champ d'un formulaire individuellement pour contrôler le style du formulaire. Vous maîtriserez cette méthode dans le prochain article 🎜Comment contrôler le rendu des formulaires🎜. 🎜🎜🎜Avant de continuer, veuillez noter pourquoi la zone de saisie task
rendue a une valeur de propriété provenant de l'objet $task
(c'est-à-dire "Écrire un article de blog"). Il s'agit de la première tâche du formulaire : extraire les données d'un objet et les convertir dans un format approprié afin qu'elles puissent être restituées dans un formulaire HTML. task
输入框中有一个来自 $task
对象的属性值(即“Write a blog post”)。这是表单的第一个任务:从一个对象中获取数据并把它转换成一种适当的格式,以便在HTML表单中被渲染。
表单系统足够智能,它们通过 getTask()
和 setTask()
方法来访问 Task
类中受保护的 task
属性。除非是public属性,否则 必须 有一个 "getter" 和 "setter" 方法被定义,以便表单组件能从这些属性中获取和写入数据。对于布尔型的属性,你可以使用一个 "isser" 和 "hasser" 方法(如 isPublished()
和 hasReminder()
)来替代getter方法(getPublished()
和 getReminder()
)。
处理表单提交 ¶
默认时,表单会把POST请求,向“渲染它的同一个控制器”提交回去。
此处,表单的第二个任务就是把用户提交的数据传回到一个对象的属性之中。要做到这一点,用户提交的数据必须写入表单对象才行。向控制器(Controller)中添加以下功能:
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8"?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Task"> <property name="task"> <constraint name="NotBlank" /> </property> <property name="dueDate"> <constraint name="NotBlank" /> <constraint name="Type">\DateTime</constraint> </property> </class></constraint-mapping>
注意 createView()
方法应该在 handleRequest
被调用 之后 再调用。否则,针对 *_SUBMIT
getTask()
et setTask( )
pour accéder à la propriété protégée task
dans la classe Task
. À moins qu'il ne s'agisse d'une propriété publique, doit avoir une méthode "getter" et "setter" définie pour que le composant de formulaire puisse obtenir et écrire des données à partir de ces propriétés. Pour les propriétés booléennes, vous pouvez utiliser une méthode "isser" et "hasser" (telle que isPublished()
et hasReminder()
) au lieu d'une méthode getter ( getPublished()
et getReminder()
). Traitement de la soumission du formulaire ¶
🎜Par défaut, le formulaire enverra la requête POST à " Render C'est le même contrôleur" soumis en retour. 🎜🎜Ici, la deuxième tâche du formulaire est de retransférer les données soumises par l'utilisateur vers les propriétés d'un objet. Pour ce faire, les données soumises par l'utilisateur doivent être écrites dans l'objet du formulaire. Ajoutez la fonctionnalité suivante au contrôleur : 🎜PHP:// src/AppBundle/Entity/Task.phpuse Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints\NotBlank;use Symfony\Component\Validator\Constraints\Type; class Task{ // ... public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('task', new NotBlank()); $metadata->addPropertyConstraint('dueDate', new NotBlank()); $metadata->addPropertyConstraint( 'dueDate', new Type('\DateTime') ); }}🎜🎜
createView()
doit être appelée dans handleRequest
Appelez à nouveau après. Dans le cas contraire, les modifications apportées aux événements du formulaire *_SUBMIT
ne seront pas appliquées à la couche de vue (comme les messages d'erreur lors de la validation). 🎜🎜Le contrôleur suit un modèle commun lors de la gestion des formulaires, qui a trois manières possibles :
Lorsque le navigateur charge initialement une page, le formulaire est créé et affiché.
handleRequest()
se rend compte que le formulaire n'a pas été soumis et ne fait rien. Si le formulaire n'a pas été soumis,isSubowned()
renvoie false;handleRequest()
意识到表单没有被提交进而什么都不做。如果表单未被提交,isSubmitted()
返回false;当用户提交表单时,
handleRequest()
会识别这个动作并立即将提交的数据写入到$task
对象的task
anddueDate
属性。然后该对象被验证。如果它是无效的(验证在下一章),isValid()
会返回false
,进而表单被再次渲染,只是这次有验证错误;当用户以合法数据提交表单的时,提交的数据会被再次写入到表单,但这一次
isValid()
返回true
。在把用户重定向到其他一些页面之前(如一个“谢谢”或“成功”的页面),你有机会用$task
handleRequest() code> reconnaîtra cette action et écrira immédiatement les données soumises dans la tâche
de l'objet $task
et dueDate
. L'objet est ensuite validé. S'il n'est pas valide (la vérification est dans le chapitre suivant), isValid()
renverra false
et le formulaire sera rendu encore une fois, mais il y a une erreur de validation cette fois ;Lorsque l'utilisateur soumet le formulaire avec des données légales, les données soumises seront à nouveau écrites dans le formulaire, mais cette fois isValid()< /a> code> renvoie true
. Avant de rediriger l'utilisateur vers une autre page (telle qu'une page "Merci" ou "Succès"), vous avez la possibilité d'utiliser l'objet $task
pour effectuer certaines opérations ( comme le conserver dans la base de données).
Si vous avez besoin de contrôler précisément quand un formulaire est soumis ou quelles données sont transmises au formulaire, vous pouvez utiliser submit(). Pour plus d'informations, veuillez vous référer à Appel manuel de Form::submit().
Validation du formulaire ¶
Dans la section précédente, vous avez appris comment un formulaire contenant des données valides ou invalides est soumis. Dans Symfony, le processus de vérification est effectué dans l'objet sous-jacent (tel que Task
). En d'autres termes, le problème n'est pas de savoir si le "formulaire" est valide, mais si l'objet $task
est valide après "les données soumises sont appliquées au formulaire". L'appel de $form->isvalid()
est un raccourci pour demander à l'objet $task
sous-jacent s'il a obtenu des données valides. Task
)。换句话说,问题不在于“表单”是否有效,而是 $task
对象在“提交的数据应用到表单”之后是否合法。调用 $form->isvalid()
是一个快捷方式,询问底层 $task
对象是否获得了合法数据。
验证(validation)是通过把一组规则(称之为“constraints/约束”)添加到一个类中来完成的。我们给 Task
类添加规则和约束,使task属性不能为空, duDate
字段不空且必须是一个有效的DateTime对象。
PHP:<!-- app/Resources/views/default/new.html.php --><?php echo $view['form']->form($form, array( 'attr' => array('novalidate' => 'novalidate'),)) ?>
Twig:{# app/Resources/views/default/new.html.twig #} {{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
1
->add('dueDate', DateType::class, array('widget' => 'single_text'))
就是这样!如果你现在重新以非法数据提交表单,你将会看到相应的错误被输出到表单。
验证是Symfony一个非常强大的功能,它拥有自己的专属章节。