| 2 | | ====================================== |
|---|
| 3 | | |
|---|
| 4 | | Dans un projet web, la plupart des formulaires sont utilisés pour créer ou modifier des objets du modèle. Ces objets sont généralement sérialisés dans une base de données grâce à un ORM. Le système de formulaires de Symfony offre une couche supplémentaire pour l'interfaçage avec Doctrine, l'ORM intégré à symfony, rendant l'implémentation de formulaires basés sur ces objets modèles plus aisée. |
|---|
| 5 | | |
|---|
| 6 | | Ce chapitre détaille comment intégrer des formulaires avec les modèles objet Doctrine. Il est hautement recommandé d'être déjà familier avec Doctrine et son intégration dans symfony. Si ce n'est pas le cas, référez-vous au livre "[The symfony and Doctrine book](http://www.symfony-project.org/doctrine/1_2/)". |
|---|
| | 2 | ======================================= |
|---|
| | 3 | |
|---|
| | 4 | Dans un projet web, la plupart des formulaires sont utilisés pour créer ou modifier des |
|---|
| | 5 | objets du modèle. Ces objets sont généralement sérialisés dans une base de données grâce |
|---|
| | 6 | à un ORM. Le système de formulaires de Symfony offre une couche supplémentaire pour |
|---|
| | 7 | l'interfaçage avec Doctrine, l'ORM intégré à symfony, rendant l'implémentation de formulaires |
|---|
| | 8 | basés sur ces objets modèles plus aisée. |
|---|
| | 9 | |
|---|
| | 10 | Ce chapitre détaille comment intégrer des formulaires avec les modèles objet Doctrine. |
|---|
| | 11 | Il est hautement recommandé d'être déjà familier avec Doctrine et son intégration dans |
|---|
| | 12 | symfony. Si ce n'est pas le cas, référez-vous au |
|---|
| | 13 | livre "[The symfony and Doctrine book](http://www.symfony-project.org/doctrine/1_2/)". |
|---|
| | 239 | } |
|---|
| | 240 | |
|---|
| | 241 | public function executeCreate(sfWebRequest $request) |
|---|
| | 242 | { |
|---|
| | 243 | $this->forward404Unless($request->isMethod('post')); |
|---|
| | 244 | |
|---|
| | 245 | $this->form = new AuthorForm(); |
|---|
| | 246 | |
|---|
| | 247 | $this->processForm($request, $this->form); |
|---|
| | 248 | |
|---|
| | 249 | $this->setTemplate('new'); |
|---|
| | 250 | } |
|---|
| | 251 | |
|---|
| | 252 | public function executeEdit(sfWebRequest $request) |
|---|
| | 253 | { |
|---|
| | 254 | $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id')), sprintf('Object author does not exist (%s).', $request->getParameter('id'))); |
|---|
| | 255 | $this->form = new AuthorForm($author); |
|---|
| | 256 | } |
|---|
| | 257 | |
|---|
| | 258 | public function executeUpdate(sfWebRequest $request) |
|---|
| | 259 | { |
|---|
| | 260 | $this->forward404Unless($request->isMethod('post') || $request->isMethod('put')); |
|---|
| | 261 | $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id')), sprintf('Object author does not exist (%s).', $request->getParameter('id'))); |
|---|
| | 262 | $this->form = new AuthorForm($author); |
|---|
| | 263 | |
|---|
| | 264 | $this->processForm($request, $this->form); |
|---|
| 234 | | public function executeEdit($request) |
|---|
| 235 | | { |
|---|
| 236 | | $this->form = $this->getAuthorForm($request->getParameter('id')); |
|---|
| 237 | | } |
|---|
| 238 | | |
|---|
| 239 | | public function executeUpdate($request) |
|---|
| 240 | | { |
|---|
| 241 | | $this->forward404Unless($request->isMethod('post')); |
|---|
| 242 | | |
|---|
| 243 | | $this->form = $this->getAuthorForm($request->getParameter('id')); |
|---|
| 244 | | |
|---|
| | 269 | public function executeDelete(sfWebRequest $request) |
|---|
| | 270 | { |
|---|
| | 271 | $request->checkCSRFProtection(); |
|---|
| | 272 | |
|---|
| | 273 | $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id')), sprintf('Object author does not exist (%s).', $request->getParameter('id'))); |
|---|
| | 274 | $author->delete(); |
|---|
| | 275 | |
|---|
| | 276 | $this->redirect('author/index'); |
|---|
| | 277 | } |
|---|
| | 278 | |
|---|
| | 279 | protected function processForm(sfWebRequest $request, sfForm $form) |
|---|
| | 280 | { |
|---|
| | 281 | $form->bind($request->getParameter($form->getName())); |
|---|
| | 282 | if ($form->isValid()) |
|---|
| | 283 | { |
|---|
| | 284 | $author = $form->save(); |
|---|
| | 285 | |
|---|
| | 286 | $this->redirect('author/edit?id='.$author->getId()); |
|---|
| | 287 | } |
|---|
| | 288 | } |
|---|
| | 289 | } |
|---|
| | 290 | |
|---|
| | 291 | Dans ce module, le cycle de vie du formulaire est géré par trois méthodes : `create`, |
|---|
| | 292 | `edit`, `update` et `processForm`. Il est aussi possible de faire cela d'une manière |
|---|
| | 293 | moins longue en déplaçant ces 4 tâches dans une seule méthode, le listing 4-5 montre |
|---|
| | 294 | un exemple simplifié de ceci. |
|---|
| | 295 | |
|---|
| | 296 | Listing 4-5 - Le cycle de vie du formulaire de la classe `authorActions` après quelques |
|---|
| | 297 | refactorisation |
|---|
| | 298 | |
|---|
| | 299 | [php] |
|---|
| | 300 | // In authorActions, replacing the create, edit, update and processForm methods |
|---|
| | 301 | public function executeEdit($request) |
|---|
| | 302 | { |
|---|
| | 303 | $this->form = new AuthorForm(Doctrine::getTable('Author')->find($request->getParameter('id'))); |
|---|
| | 304 | |
|---|
| | 305 | if ($request->isMethod('post')) |
|---|
| | 306 | { |
|---|
| 252 | | |
|---|
| 253 | | $this->setTemplate('edit'); |
|---|
| 254 | | } |
|---|
| 255 | | |
|---|
| 256 | | public function executeDelete($request) |
|---|
| 257 | | { |
|---|
| 258 | | $this->forward404Unless($author = $this->getAuthorById($request->getParameter('id'))); |
|---|
| 259 | | |
|---|
| 260 | | $author->delete(); |
|---|
| 261 | | |
|---|
| 262 | | $this->redirect('author/index'); |
|---|
| 263 | | } |
|---|
| 264 | | |
|---|
| 265 | | private function getAuthorTable() |
|---|
| 266 | | { |
|---|
| 267 | | return Doctrine::getTable('Author'); |
|---|
| 268 | | } |
|---|
| 269 | | |
|---|
| 270 | | private function getAuthorById($id) |
|---|
| 271 | | { |
|---|
| 272 | | return $this->getAuthorTable()->find($id); |
|---|
| 273 | | } |
|---|
| 274 | | |
|---|
| 275 | | private function getAuthorForm($id) |
|---|
| 276 | | { |
|---|
| 277 | | $author = $this->getAuthorById($id); |
|---|
| 278 | | |
|---|
| 279 | | if ($author instanceof Author) |
|---|
| 280 | | { |
|---|
| 281 | | return new AuthorForm($author); |
|---|
| 282 | | } |
|---|
| 283 | | else |
|---|
| 284 | | { |
|---|
| 285 | | return new AuthorForm(); |
|---|
| 286 | | } |
|---|
| 287 | | } |
|---|
| 288 | | } |
|---|
| 289 | | |
|---|
| 290 | | Dans ce module, le cycle de vie du formulaire est géré par trois méthodes : `create`, `edit` et `update`. Il est aussi possible de demander à `doctrine:generate-crud` de générer une seule méthode couvrant les fonctionnalités des trois précédentes méthodes avec l'option `--non-atomic-actions` : |
|---|
| 291 | | |
|---|
| 292 | | $ ./symfony doctrine:generate-crud frontend author Author --non-atomic-actions |
|---|
| 293 | | |
|---|
| 294 | | Le code généré en utilisant `--non-atomic-actions` (Listing 4-5) est plus concis et moins verbeux. |
|---|
| 295 | | |
|---|
| 296 | | Listing 4-5 - La classe `authorActions` générée avec l'otion `--non-atomic-actions` |
|---|
| 297 | | |
|---|
| 298 | | [php] |
|---|
| 299 | | class authorActions extends sfActions |
|---|
| 300 | | { |
|---|
| 301 | | public function executeIndex() |
|---|
| 302 | | { |
|---|
| 303 | | $this->authorList = $this->getAuthorTable()->findAll(); |
|---|
| 304 | | } |
|---|
| 305 | | |
|---|
| 306 | | public function executeEdit($request) |
|---|
| 307 | | { |
|---|
| 308 | | $this->form = new AuthorForm(Doctrine::getTable('Author')->find($request->getParameter('id'))); |
|---|
| 309 | | |
|---|
| 310 | | if ($request->isMethod('post')) |
|---|
| 311 | | { |
|---|
| 312 | | $this->form->bind($request->getParameter('author')); |
|---|
| 313 | | if ($this->form->isValid()) |
|---|
| 314 | | { |
|---|
| 315 | | $author = $this->form->save(); |
|---|
| 316 | | |
|---|
| 317 | | $this->redirect('author/edit?id='.$author->getId()); |
|---|
| 318 | | } |
|---|
| 319 | | } |
|---|
| 320 | | } |
|---|
| 321 | | |
|---|
| 322 | | public function executeDelete($request) |
|---|
| 323 | | { |
|---|
| 324 | | $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id'))); |
|---|
| 325 | | |
|---|
| 326 | | $author->delete(); |
|---|
| 327 | | |
|---|
| 328 | | $this->redirect('author/index'); |
|---|
| 329 | | } |
|---|
| 330 | | } |
|---|
| 331 | | |
|---|
| 332 | | La tâche génère également deux templates, `indexSuccess` et `editSuccess`. Le template `editSuccess` a été généré sans la déclaration `<?php echo $form ?>`. Nous pouvons modifier ce comportement en utilisant `--non-verbose-templates` : |
|---|
| | 313 | } |
|---|
| | 314 | } |
|---|
| | 315 | |
|---|
| | 316 | >**NOTE** |
|---|
| | 317 | >Les exemples qui suivent utilisent la valeur par défaut, le style plus verbeux donc vous aurez |
|---|
| | 318 | >besoin de faire des ajustements en conséquence si vous souhaitez suivre l'approche dans le Listing |
|---|
| | 319 | >4-5. Par exemple, dans votre modèle de formulaire, vous n'aurez besoin que de faire pointer le |
|---|
| | 320 | >formulaire sur l'action edit indépendamment du fait que l'objet est nouveau ou ancien. |
|---|
| | 321 | |
|---|
| | 322 | La tâche génère également trois templates et un partial, `indexSuccess`, |
|---|
| | 323 | `editSuccess`, `newSuccess` et `_form`. Le template `_form` a été généré |
|---|
| | 324 | sans la déclaration `<?php echo $form ?>`. Nous pouvons modifier ce comportement, |
|---|
| | 325 | en utilisant `--non-verbose-templates` : |
|---|
| 338 | | Listing 4-6 - Le template `editSuccess` |
|---|
| 339 | | |
|---|
| 340 | | [php] |
|---|
| 341 | | // apps/frontend/modules/author/templates/editSuccess.class.php |
|---|
| 342 | | <?php $author = $form->getObject() ?> |
|---|
| 343 | | <h1><?php echo $author->isNew() ? 'New' : 'Edit' ?> Author</h1> |
|---|
| 344 | | |
|---|
| 345 | | <form action="<?php echo url_for('author/edit'.(!$author->isNew() ? '?id='.$author->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>> |
|---|
| | 331 | Listing 4-6 - Le template `_form` |
|---|
| | 332 | |
|---|
| | 333 | [php] |
|---|
| | 334 | // apps/frontend/modules/author/templates/_form.php |
|---|
| | 335 | <?php include_stylesheets_for_form($form) ?> |
|---|
| | 336 | <?php include_javascripts_for_form($form) ?> |
|---|
| | 337 | |
|---|
| | 338 | <form action="<?php echo url_for('author/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '?id='.$form->getObject()->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>> |
|---|
| | 339 | <?php if (!$form->getObject()->isNew()): ?> |
|---|
| | 340 | <input type="hidden" name="sf_method" value="put" /> |
|---|
| | 341 | <?php endif; ?> |
|---|
| 365 | | >L'option `--with-show` nous permet de générer une action et un template utilisables pour voir un objet (en lecture seule). |
|---|
| 366 | | |
|---|
| 367 | | Vous pouvez maintenant ouvrir l'URL `/frontend_dev.php/author` dans un navigateur pour voir le module généré (Figure 4-1 et Figure 4-2). Grâce au module généré, vous pouvez lister les auteurs, en ajouter de nouveaux, les éditer, les modifier et même les supprimer. Vous noterez également que les règles de validation fonctionnent. |
|---|
| | 361 | >L'option `--with-show` nous permet de générer une action et un template utilisables pour |
|---|
| | 362 | >voir un objet (en lecture seule). |
|---|
| | 363 | |
|---|
| | 364 | Vous pouvez maintenant ouvrir l'URL `/frontend_dev.php/author` dans un navigateur pour voir le |
|---|
| | 365 | module généré (Figure 4-1 et Figure 4-2). Prenez le temps de jouer avec l'interface. Grâce au |
|---|
| | 366 | module généré, vous pouvez lister les auteurs, en ajouter un nouveau, éditer, modifier et |
|---|
| | 367 | éventuellement supprimer. Vous remarquerez également que les règles de validation sont |
|---|
| | 368 | opérationnelles. Notez que dans les figures suivantes, nous avons choisi de supprimer |
|---|
| | 369 | le champ "actif". |
|---|
| 379 | | $ ./symfony doctrine:generate-crud frontend article Article --non-verbose-templates --non-atomic-actions |
|---|
| 380 | | |
|---|
| 381 | | Le code généré est assez similaire à celui de la classe `Author`. Cependant, si vous essayez de créer un nouvel article, le code va lancer une erreur fatale comme vous pouvez le voir en Figure 4-3. |
|---|
| 382 | | |
|---|
| 383 | | Figure 4-3 - Les tables liées doivent définir la méthode `__toString()` |
|---|
| 384 | | |
|---|
| 385 | | ` method") |
|---|
| 386 | | |
|---|
| 387 | | Le formulaire `ArticleForm` utilise le widget `sfWidgetFormDoctrineSelect` pour représenter la relation entre l'objet `Article` et l'objet `Author`. Ce widget crée une liste déroulante avec les auteurs. Durant l'affichage, les objets auteur sont convertis en chaîne de caractères grâce à la méthode magique `__toString()`, qui doit être définie dans la classe `Author`, comme montré en Listing 4-7. |
|---|
| | 381 | $ ./symfony doctrine:generate-crud frontend article Article --non-verbose-templates |
|---|
| | 382 | |
|---|
| | 383 | Le formulaire `ArticleForm` utilise le widget `sfWidgetFormDoctrineSelect` pour représenter |
|---|
| | 384 | la relation entre l'objet `Article` et l'objet `Author`. Ce widget crée une liste déroulante |
|---|
| | 385 | avec les auteurs. Durant l'affichage, les objets auteur sont convertis en chaîne de caractères |
|---|
| | 386 | grâce à la méthode magique `__toString()`, qui doit être définie dans la classe `Author`, |
|---|
| | 387 | comme montré en Listing 4-7. |
|---|
| 403 | | >sfDoctrineRecord va essayer de deviner la valeur de __toString() si vous ne la spécifiez pas vous-même. Elle regarde les colonnes nommées titre, nom, sujet, etc. pour les utiliser comme représentation de chaîne. |
|---|
| | 404 | >sfDoctrineRecord va essayer de deviner la valeur de __toString() si vous ne la |
|---|
| | 405 | >spécifiez pas vous-même. Elle regarde les colonnes nommées 'name', 'title', 'description', |
|---|
| | 406 | >'subject', 'keywords' et enfin 'id' pour les utiliser comme représentation de chaîne. Si |
|---|
| | 407 | >l'un de ces champs n'est pas trouvé, Doctrine retournera une chaîne d'alerte par défaut. |
|---|
| | 408 | |
|---|
| | 409 | - |
|---|
| 465 | | Le validateur `sfValidatorDoctrineUnique` est un `postValidator` s'exécutant sur toutes les données après les validations individuelles des champs. Afin de valider l'unicité de `slug`, le validateur doit être capable d'accéder non seulement à la valeur de `slug`, mais également à la valeur de(s) clé(s) primaire(s). Les règles de validation sont ainsi différentes entre la création et l'édition, puisque le slug peut rester le même lors de la mise à jour d'un article. |
|---|
| 466 | | |
|---|
| 467 | | Personnalisons maintenant le champ `active` de la table `author`, utilisé pour savoir si un auteur est actif. Le Listing 4-10 montre comment exclure les auteurs inactifs du formulaire `AuthorForm`, en modifiant l'option `query` du widget `DoctrineSelect` connecté au champ `author_id`. L'option `query` accepte un objet Doctrine Query, permettant de réduire les options disponibles dans la liste déroulante. |
|---|
| | 476 | Le validateur `sfValidatorDoctrineUnique` est un `postValidator` s'exécutant sur toutes |
|---|
| | 477 | les données après les validations individuelles des champs. Afin de valider l'unicité de |
|---|
| | 478 | `slug`, le validateur doit être capable d'accéder non seulement à la valeur de `slug`, mais |
|---|
| | 479 | également à la valeur de(s) clé(s) primaire(s). Les règles de validation sont ainsi |
|---|
| | 480 | différentes entre la création et l'édition, puisque le slug peut rester le même lors |
|---|
| | 481 | de la mise à jour d'un article. |
|---|
| | 482 | |
|---|
| | 483 | Personnalisons maintenant le champ `active` de la table `author`, utilisé pour savoir |
|---|
| | 484 | si un auteur est actif. Le Listing 4-10 montre comment exclure les auteurs inactifs |
|---|
| | 485 | du formulaire `ArticleForm`, en modifiant l'option `query` du widget `FormDoctrineSelect` |
|---|
| | 486 | connecté au champ `author_id`. L'option `query` accepte un objet Doctrine Query, |
|---|
| | 487 | permettant de réduire les options disponibles dans la |
|---|
| | 488 | liste déroulante. |
|---|
| 533 | | >Tout comme le widget `sfWidgetFormDoctrineSelect` et le validateur `sfValidatorDoctrineChoice` représentent une relation 1-n entre deux tables, le widget `sfWidgetDoctrineSelectMany` et le validateur `sfValidatorDoctrineChoiceMany` représentent une relation n-n et acceptent les mêmes options. Dans le formulaire `ArticleForm`, ces classes sont utilisées pour représenter une relation entre la table `article` et la table `tag`. |
|---|
| | 556 | >Tout comme le widget `sfWidgetFormDoctrineSelect` et le |
|---|
| | 557 | >validateur `sfValidatorDoctrineChoice` représentent une relation 1-n entre deux |
|---|
| | 558 | >tables, le widget `sfWidgetDoctrineSelectMany` et le |
|---|
| | 559 | >validateur `sfValidatorDoctrineChoiceMany` représentent une relation n-n et acceptent |
|---|
| | 560 | >les mêmes options. Dans le formulaire `ArticleForm`, ces classes sont utilisées pour |
|---|
| | 561 | >représenter une relation entre la table `article` et la table `tag`. |
|---|
| 568 | | L'exemple précédent n'est pas parfait, car si nous décidons de modifier par la suite la longueur du champ `email` dans le schéma de base de données, nous devrons penser à le faire également dans le formulaire. Au lieu de remplacer le validateur généré, il est préférable d'en ajouter un, comme le montre le Listing 4-15. |
|---|
| | 602 | L'exemple précédent n'est pas parfait, car si nous décidons de modifier par la suite la longueur |
|---|
| | 603 | du champ `email` dans le schéma de base de données, nous devrons penser à le faire |
|---|
| | 604 | également dans le formulaire. Au lieu de remplacer le validateur généré, il est |
|---|
| | 605 | préférable d'en ajouter un, comme le montre le Listing 4-15. |
|---|
| 586 | | Dans le schéma de base de données, le champ `status` de la table `article` stocke les statuts d'articles dans une chaine de caractères. Les valeurs possibles ont été définies dans la classe `ArticlePeer`, comme montré dans le Listing 4-16. |
|---|
| 587 | | |
|---|
| 588 | | Listing 4-16 - Définition des statuts possibles dans la classe `ArticlePeer` |
|---|
| 589 | | |
|---|
| 590 | | [php] |
|---|
| 591 | | class ArticlePeer extends BaseArticlePeer |
|---|
| | 623 | Dans le schéma de base de données, le champ `status` de la table `article` stocke les |
|---|
| | 624 | statuts d'articles dans une chaine de caractères. Les valeurs possibles ont été définies |
|---|
| | 625 | dans la classe `ArticleTable`, comme montré dans le Listing 4-16. |
|---|
| | 626 | |
|---|
| | 627 | Listing 4-16 - Définition des statuts possibles dans la classe `ArticleTable` |
|---|
| | 628 | |
|---|
| | 629 | [php] |
|---|
| | 630 | class ArticleTable extends Doctrine_Table |
|---|
| 635 | | La table `article` a tres colonnes spéciales, `created_at`, `updated_at` et `published_at`, dont la mise à jour est automatiquement gérée par Doctrine. Nous devons donc les supprimer du formulaire comme le montre le Listing 4-19, pour éviter que les utilisateurs les modifient. |
|---|
| | 680 | La table `article` a trois colonnes spéciales, `created_at`, `updated_at` |
|---|
| | 681 | et `published_at`. Les deux premiers sont automatiquement gérée par Doctrine comme |
|---|
| | 682 | un comportement `timestampable`, le troisième que nous traiterons plus tard dans notre |
|---|
| | 683 | propre code. Nous devons donc les supprimer du formulaire comme le montre le Listing 4-19, |
|---|
| | 684 | pour éviter que les utilisateurs les modifient. |
|---|
| 710 | | Utiliser `doctrine:build-forms` permet de générer la plupart des éléments en introspectant le modèle objet. Cette automatisation est utile pour plusieurs raisons : |
|---|
| 711 | | |
|---|
| 712 | | * Cela rend la vie plus facile au développeur, lui épargnant un travail répétitif et redondant. Il peut alors se concentrer sur la personnalisation des validateurs et des widgets en fonction des règles métier spécifiques au projet. |
|---|
| 713 | | |
|---|
| 714 | | * De plus, lorsque le schéma de base de données est mis à jour, les formulaires générés seront automatiquement mis à jour. Le développeur aura juste à adapter ses personnalisations. |
|---|
| 715 | | |
|---|
| 716 | | La prochaine section décrira la personnalisation des actions et des templates générés par la tâche `doctrine:generate-crud`. |
|---|
| | 766 | Utiliser la tâche `doctrine:build-forms` permet de générer la plupart |
|---|
| | 767 | des éléments en introspectant le modèle objet. Cette automatisation |
|---|
| | 768 | est utile pour plusieurs raisons : |
|---|
| | 769 | |
|---|
| | 770 | * Cela rend la vie plus facile au développeur, lui épargnant un travail répétitif et |
|---|
| | 771 | redondant. Il peut alors se concentrer sur la personnalisation des validateurs et des |
|---|
| | 772 | widgets en fonction des règles métier spécifiques au projet. |
|---|
| | 773 | |
|---|
| | 774 | * De plus, lorsque le schéma de base de données est mis à jour, les formulaires |
|---|
| | 775 | générés seront automatiquement mis à jour. Le développeur aura juste à adapter |
|---|
| | 776 | ses personnalisations. |
|---|
| | 777 | |
|---|
| | 778 | La prochaine section décrira la personnalisation des actions et des templates |
|---|
| | 779 | générés par la tâche `doctrine:generate-crud`. |
|---|
| 725 | | **Une instance de formulaire Doctrine est toujours reliée à un objet Doctrine**. L'objet Doctrine lié appartient toujours à la classe retournée par la méthode `getModelName()`. Par exemple, le formulaire `AuthorForm` peut seulement être lié à des objets appartenant à la classe `Author`. Ces objets sont soit des objets vides (une instance vide de la classe `Author`), soit les objets passés en premier argument au constructeur. Alors que le constructeur d'un formulaire « normal » prend un tableau de valeurs en premier argument, le constructeur d'un formulaire Doctrine prend un objet Doctrine. Cet objet est utilisé pour définir la valeur par défaut de chaque champ du fomulaire. La méthode `getObject()` retourne l'objet relié à l'instance courante, et la méthode `isNew()` permet de savoir si l'objet a été créé par le constructeur : |
|---|
| | 791 | **Une instance de formulaire Doctrine est toujours reliée à un objet Doctrine**. |
|---|
| | 792 | L'objet Doctrine lié appartient toujours à la classe retournée par la méthode |
|---|
| | 793 | `getModelName()`. Par exemple, le formulaire `AuthorForm` peut seulement être |
|---|
| | 794 | lié à des objets appartenant à la classe `Author`. Ces objets sont soit |
|---|
| | 795 | des objets vides (une instance vide de la classe `Author`), soit les objets passés |
|---|
| | 796 | en premier argument au constructeur. Alors que le constructeur d'un formulaire |
|---|
| | 797 | « normal » prend un tableau de valeurs en premier argument, le constructeur |
|---|
| | 798 | d'un formulaire Doctrine prend un objet Doctrine. Cet objet est utilisé pour définir |
|---|
| | 799 | la valeur par défaut de chaque champ du fomulaire. La méthode `getObject()` retourne |
|---|
| | 800 | l'objet relié à l'instance courante, et la méthode `isNew()` permet de savoir si |
|---|
| | 801 | l'objet a été créé par le constructeur : |
|---|
| 743 | | Comme nous l'avons observé au début de ce chapitre, l'action `edit`, montrée dans le listing 4-23, gère le cycle de vie. |
|---|
| 744 | | |
|---|
| 745 | | Listing 4-23 - La méthode `executeEdit` du module `author` |
|---|
| | 819 | Comme nous l'avons observé au début de ce chapitre, les actions `new`, `edit` et |
|---|
| | 820 | `create`, montrée dans le listing 4-23, gèrent le cycle de vie du formulaire. |
|---|
| | 821 | |
|---|
| | 822 | Listing 4-23 - Les méthodes `executeNew`, `executeEdit`, `executeCreate` et |
|---|
| | 823 | `processForm` du module `author` |
|---|
| 752 | | |
|---|
| 753 | | public function executeEdit($request) |
|---|
| 754 | | { |
|---|
| 755 | | $author = Doctrine::getTable('Author')->find($request->getParameter('id')); |
|---|
| | 830 | public function executeNew(sfWebRequest $request) |
|---|
| | 831 | { |
|---|
| | 832 | $this->form = new AuthorForm(); |
|---|
| | 833 | } |
|---|
| | 834 | |
|---|
| | 835 | public function executeCreate(sfWebRequest $request) |
|---|
| | 836 | { |
|---|
| | 837 | $this->forward404Unless($request->isMethod('post')); |
|---|
| | 838 | |
|---|
| | 839 | $this->form = new AuthorForm(); |
|---|
| | 840 | |
|---|
| | 841 | $this->processForm($request, $this->form); |
|---|
| | 842 | |
|---|
| | 843 | $this->setTemplate('new'); |
|---|
| | 844 | } |
|---|
| | 845 | |
|---|
| | 846 | public function executeEdit(sfWebRequest $request) |
|---|
| | 847 | { |
|---|
| | 848 | $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id')), sprintf('Object author does not exist (%s).', $request->getParameter('id'))); |
|---|
| 845 | | La méthode `save()` met automatiquement à jour les objets Doctrine, mais ne peut pas gérer les autres éléments tels que les envois de fichiers. |
|---|
| 846 | | |
|---|
| 847 | | Voyons comment attacher un fichier à chaque article. Les fichiers sont enregistrés dans le répertoire `web/uploads` et une référence vers le fichier est gardée dans le champ `file` de la table `article`, comme le montre le Listing 4-24. |
|---|
| | 945 | La méthode `save()` met automatiquement à jour les objets Doctrine, mais ne peut pas |
|---|
| | 946 | gérer les autres éléments tels que les envois de fichiers. |
|---|
| | 947 | |
|---|
| | 948 | Voyons comment attacher un fichier à chaque article. Les fichiers sont enregistrés dans |
|---|
| | 949 | le répertoire `web/uploads` et une référence vers le fichier est gardée dans le champ |
|---|
| | 950 | `file` de la table `article`, comme le montre le Listing 4-24. |
|---|
| 881 | | Comme pour chaque formulaire permettant l'envoi d'un fichier, n'oubliez pas d'ajouter l'attribut `enctype` à la balise `form` dans le template (voyez le Chapitre 2 pour plus d'informations concernant la gestion d'envois de fichiers). |
|---|
| | 986 | Comme pour chaque formulaire permettant l'envoi d'un fichier, n'oubliez pas d'ajouter |
|---|
| | 987 | l'attribut `enctype` à la balise `form` dans le template (voyez le Chapitre 2 pour |
|---|
| | 988 | plus d'informations concernant la gestion d'envois de fichiers). |
|---|
| | 989 | |
|---|
| | 990 | >**TIP** |
|---|
| | 991 | >Lorsque vous créez votre modèle de formulaire, vous pouvez vérifier si le formulaire |
|---|
| | 992 | >contient des champs de fichier, et ajouter l'attribut `enctype` automatiquement : |
|---|
| | 993 | > |
|---|
| | 994 | > [PHP] |
|---|
| | 995 | > <?php if ($form->isMultipart() echo 'enctype="multipart/form-data" '; ?> |
|---|
| | 996 | > |
|---|
| | 997 | >Ce code est automatiquement ajoutée lorsque votre formulaire est créé par la tâche generate-crud. |
|---|
| 947 | | >La logique que vous pourriez ajouter dans l'action `edit`, particulièrement durant la sérialisation du formulaire, doit souvent être déplacée dans les classes du modèle ou dans les classes du formulaire. |
|---|
| 948 | | > |
|---|
| 949 | | >Nous avons juste vu un exemple de refactorisation dans la classe formulaire afin d'effectuer un stockage de fichier. Prenons un autre exemple lié au modèle. Le formulaire `ArticleForm` a un champ `slug`. Nous avons vu que ce champ devrait être calculé automatiquement à partir du champ `title`, et qu'il pourrait éventuellement être spécifié par l'utilisateur. Cette logique ne dépend pas du formulaire. Elle appartient donc au modèle, comme le montre le code suivant : |
|---|
| | 1063 | >La logique que vous pourriez ajouter dans l'action `edit`, particulièrement durant la sérialisation du |
|---|
| | 1064 | >formulaire, doit souvent être déplacée dans les classes du modèle ou dans les classes du formulaire. |
|---|
| | 1065 | > |
|---|
| | 1066 | >Nous avons juste vu un exemple de refactorisation dans la classe formulaire afin |
|---|
| | 1067 | >d'effectuer un stockage de fichier. Prenons un autre exemple lié au modèle. Le |
|---|
| | 1068 | >formulaire `ArticleForm` a un champ `slug`. Nous avons vu que ce champ devrait |
|---|
| | 1069 | >être calculé automatiquement à partir du champ `title`, et qu'il pourrait |
|---|
| | 1070 | >éventuellement être spécifié par l'utilisateur. Cette logique ne dépend pas du |
|---|
| | 1071 | >formulaire. Elle appartient donc au modèle, comme le montre le code suivant : |
|---|