Générer le module Game (III – Forms)

PrintFriendly and PDF

[ A+ ] /[ A- ]

Chapitre 7: Générer le module Game (III): les formulaires

 

Il est à présent de s’occuper de nos formulaires. Nous avions déjà réglé le problème spécifique aux clés étrangères dans le chapitre 4, au tout début, maintenant, nous allons voir comment personnaliser les formulaires sans aller dans la classe de base.

 

Personnaliser newSuccess.php, editSuccess.php, _form.php

 

Avant de commencer la partie sur les formulaires, nous pouvons d’ores et déjà ajouter nos slot_title() et nos classes CSS dans newSuccess.php et editSuccess.php.

Comme vous le constatez, ces deux templates appellent un partial _form, qui contient le rendu du formulaire. Gardez en tête que si deux templates ont une partie identique, il est possible de ne pas le répéter en créant un fichier unique qui sera appelé dans celles-ci. Nous utiliserons un partial plus tard, quand nous aborderons le module genre et plateforme.

Voici le code pour newSuccess.php:

 

<?php slot('title') ?>LLG: New game<?php end_slot();?>

<h1>New Game</h1>

<?php include_partial('form', array('form' => $form)) ?>

 

Voici le code pour editSuccess.php:

 

<?php slot('title')?>

<?php echo sprintf('LLG: Editing %s',$form->getOBject()->getNameGame()) ?>

<?php end_slot(); ?>

<h1>Edit <?php echo $form->getObject()->getNameGame() ?></h1>

<?php include_partial('form', array('form' => $form)) ?>

 

En fait, $form contient votre formulaire. En lui demandant de trouver l’objet auquel il est attaché, vous aurez accès aux méthodes de ce dernier. Cela vous permet de récupérer ce dont vous avez besoin en dehors de l’affichage du formulaire, comme ici, pour personnaliser le titre Edit Game en Edit + nom du jeu.

Ouvrez le fichier _form.php pour faire quatre simples modifications:

 

– changer l’url de retour de la liste en:

<a href="<?php echo url_for('@homepage') ?>">Back to list</a>

– ajouter un lien pour retourner à la fiche du jeu (à condition qu’il ne s’agit pas d’un objet « neuf » grâce à isNew()):

<?php if (!$form->getObject()->isNew()): ?>

<?php echo link_to('Delete', 'game/delete?id='.$form->getObject()->getId(), array('method' => 'delete', 'confirm' => 'Are you sure?')) ?>

<a href="<?php echo url_for('game_show_game',$form->getObject()) ?>">Back to Game</a>

<?php endif; ?>

– retirer les liens et l’input de la table pour les mettre en dessous

– ajouter ceux-ci avant la table

 

Voici donc notre code pour _form.php:

 

<?php use_stylesheets_for_form($form) ?>

<?php use_javascripts_for_form($form) ?>

<form action="<?php echo url_for('game/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '?id='.$form->getObject()->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>>

<?php if (!$form->getObject()->isNew()): ?>

<input type="hidden" name="sf_method" value="put" />

<?php endif; ?>

&nbsp;<a href="<?php echo url_for('@homepage') ?>">Back to list</a>

<?php if (!$form->getObject()->isNew()): ?>

&nbsp;<?php echo link_to('Delete', 'game/delete?id='.$form->getObject()->getId(), array('method' => 'delete', 'confirm' => 'Are you sure?')) ?>

&nbsp;<a href="<?php echo url_for('game_show_game',$form->getObject()) ?>">Back to Game</a>

<?php endif; ?>

<input type="submit" value="Save" />

<table>

<tbody>

<?php echo $form ?>

</tbody>

</table>

&nbsp;<a href="<?php echo url_for('@homepage') ?>">Back to list</a>

<?php if (!$form->getObject()->isNew()): ?>

&nbsp;<?php echo link_to('Delete', 'game/delete?id='.$form->getObject()->getId(), array('method' => 'delete', 'confirm' => 'Are you sure?')) ?>

&nbsp;<a href="<?php echo url_for('game_show_game',$form->getObject()) ?>">Back to Game</a>

<?php endif; ?>

<input type="submit" value="Save" />

</form>

 

Vous l’avez compris, nous améliorerons cela plus tard avec le fameux partial que nous créerons une fois que nous aurons fini le module genre et plateforme.

 

actions.class.php: executeNew(), executeCreate(), executeEdit, executeUpdate() et processForm()

 

Allons maintenant voir ces quatre fonctions qui régissent l’ajout et la mise à jour d’un objet.

 

executeNew() permet d’afficher le formulaire adéquat en instanciant un nouvel objet Form (que vous allez modifier dans quelques instants).

 

public function executeNew(sfWebRequest $request)

{

$this->form = new gameForm();

}

executeCreate() permet d’enregistrer le nouvel objet – en instanciant également un nouveau formulaire et en envoyant votre objet et ce dernier dans processForm:

 

public function executeCreate(sfWebRequest $request)

{

$this->forward404Unless($request->isMethod(sfRequest::POST));

$this->form = new gameForm();

$this->processForm($request, $this->form);

$this->setTemplate('new');

}

 

executeEdit() recherche d’abord le jeu concerné (comme dans executeShow()) et le met dans un formulaire:

 

public function executeEdit(sfWebRequest $request)

{

$this->forward404Unless($game = Doctrine_Core::getTable('game')->find(array($request->getParameter('id'))), sprintf('Object game does not exist (%s).', $request->getParameter('id')));

$this->form = new gameForm($game);

}

 

executeUpdate() fonctionne sur le même principe que executeCreate():

 

public function executeUpdate(sfWebRequest $request)

{

$this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT));

$this->forward404Unless($game = Doctrine_Core::getTable('game')->find(array($request->getParameter('id'))), sprintf('Object game does not exist (%s).', $request->getParameter('id')));

$this->form = new gameForm($game);

$this->processForm($request, $this->form);

$this->setTemplate('edit');

}

 

Enfin, processForm() va permettre l’enregistrement ou la mise à jour de l’objet en appelant la méthode save() qui elle-même va enchaîner les enregistrements des clés étrangères.

protected function processForm(sfWebRequest $request, sfForm $form)

{

$form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));

if ($form->isValid())

{

$game = $form->save();

$this->redirect('game/edit?id='.$game->getId());

}

}

 

Changeons la dernière ligne par:

 

$this->redirect('@homepage');

 

Personnalisation du formulaire

 

Dernière étape du chapitre, la personnalisation du formulaire, bien que nous l’aborderons partiellement, est cruciale quand certains champs ne doivent pas apparaître. Même si la classe BaseGameForm.class.php contient l’essentiel, nous n’allons pas toucher une ligne de ce fichier. Nous allons utiliser le fichier GameForm.class.php dans lib/form/doctrine à la place et remplir la fonction configure().

 

Tout d’abord, relevons ce qui nous gêne: le libellé du champ name_game, de la liste des genres et des plateformes. La présentation en liste déroulante à choix multiple est certes bien mais nous allons préférer des cases à cocher. De plus, les années couvertes par le champ de la date de sortie vont de 2005 à 2015, or les jeux vidéo existaient déjà dans les années 80 (qui ne se souvient pas de Mario, Zelda, Sonic) et les années 90 (Age of Empires, Tomb Raider, Doom, Half-Life, Colonization, etc.).

Enfin, le rendu de la zone de texte du background est un peu faible.

 

Donc commençons par comprendre ce qu’il faut faire: vous avez besoin d’un widget pour l’affichage et de son validateur au moment de l’enregistrement. Bien sûr pour certains champs, remettre le même validateur que celui déjà défini peut paraître redondant mais nous voulons être sûrs que tout soit correct.

 

Dans le cas de notre zone de texte, le textarea en lui-même ne pose pas problème, nous n’avons donc pas besoin de redéfinir le widget. Par contre, comme nous voulons l’agrandir, nous devons ajouter des attributs HTML. Avec la methode setAttributes, cela est possible:

 

//Textarea plus grand

$this->widgetSchema['background']->setAttributes(array("cols"=>'50',"rows"=>'5'));

 

Pour nos dates, nous allons définir notre propre intervalle de temps:

 

//Définit l'intervalle de temps de la date -> overrides le code initial

$years = range(1980,2015);

$this->widgetSchema['release_date'] = new sfWidgetFormDate(array('years'=>array_combine($years,$years)));

$this->validatorSchema['release_date'] = new sfValidatorDate(array('required' => false));

//Permet de changer le select en dropdown en checkboxes + Validation des données

//multiple = true dans le validateur également pour les choix multiples

$this->widgetSchema['genres_list'] = new sfWidgetFormDoctrineChoice (array('model'=> 'genre', 'multiple'=>true, 'expanded'=>true));

$this->validatorSchema['genres_list'] = new sfValidatorDoctrineChoice (array('model'=>'genre', 'multiple' => true));

$this->widgetSchema['platforms_list'] = new sfWidgetFormDoctrineChoice (array('model'=> 'platform', 'multiple'=>true, 'expanded'=>true));

$this->validatorSchema['platforms_list'] = new sfValidatorDoctrineChoice (array('model'=>'platform', 'multiple' => true));

 

Je me dois quand même de vous expliquer comment afficher soit une liste de radios, soit une liste de checkboxes (cases à cocher), soit une liste déroulante, soit un menu déroulant à choix unique. Il s’agit ici de jouer avec les paramètres expanded et multiple:

 

Ce que je veux Multiple Expanded
Liste radio false true
Liste checkbox true true
Liste déroulante false false
Liste déroulante choix mult. true false

 

N’oubliez pas non plus de rajouter le paramètre multiple dans le validateur, sinon, vous aurez un léger problème d’intolérance à l’envoi de plusieurs valeurs pour ce widget.

 

Il ne nous reste plus qu’à changer les libellés grâce à:

 

//Changement du libellé de certains champs

$this->widgetSchema->setLabels(array(

'name_game' => 'Name',

'genres_list'    => 'Genre',

'platforms_list'=> 'Platforms'

));

 

Notez qu’il est possible de ne pas afficher certains champs en utilisant la fonction unset().

 

unset($this['background']);

 

Comme ce n’est pas notre cas ici, nous n’utiliserons pas ce bout de code.

 

Fichier final

 

<?php

class GameForm extends BaseGameForm

{

public function configure()

{

//Textarea plus grand

$this->widgetSchema['background']->setAttributes(array("cols"=>'50',"rows"=>'5'));

//Définit l'intervalle de temps de la date -> overrides le code initial

$years = range(1980,2015);

$this->widgetSchema['release_date'] = new sfWidgetFormDate(array('years'=>array_combine($years,$years)));

$this->validatorSchema['release_date'] = new sfValidatorDate(array('required' => false));

//Permet de changer le select en dropdown en checkboxes + Validation des données

//multiple = true dans le validateur également pour les choix multiples

$this->widgetSchema['genres_list'] = new sfWidgetFormDoctrineChoice (array('model'=> 'genre', 'multiple'=>true, 'expanded'=>true));

$this->validatorSchema['genres_list'] = new sfValidatorDoctrineChoice (array('model'=>'genre', 'multiple' => true));

$this->widgetSchema['platforms_list'] = new sfWidgetFormDoctrineChoice (array('model'=> 'platform', 'multiple'=>true, 'expanded'=>true));

$this->validatorSchema['platforms_list'] = new sfValidatorDoctrineChoice (array('model'=>'platform', 'multiple' => true));

//Changement du libellé de certains champs

$this->widgetSchema->setLabels(array(

'name_game' => 'Name',

'genres_list'    => 'Genre',

'platforms_list'=> 'Platforms'

));

}

}

?>

 

Horreur, des points devant les checkboxes!

 

Effectivement, nous avons devant nos cases à cocher des affreuses listes à puce, du moins, pour ceux qui sont choqués par cette présentation. Voici deux pistes à suivre pour les supprimer:

 

la fonction strip_tags()

le CSS

 

En ce qui concerne strip_tags(), vous la placerez dans le partial _form.php au moment de son echo:

 

<?php echo strip_tags($form,'<a><input><tr><b><td><th><label><select><option><ul><textarea>') ?>.

 

C’est lourd à écrire, les valeurs à présent affichées horizontalement, bref, à la limite, il aurait fallu remplacer /li par des br/ et puis seulement faire un strip_tags (en ajoutant comme balise permise br/) sans oublier que $form doit rester tel quel, donc, une nouvelle variable doit naître:

 

<table>

<tbody>

<?php $form2 = str_replace("</li>", "<br/>",$form)?>

<?php echo strip_tags($form2,'<a><input><tr><b><td><th><label><select><option><ul><textarea><br/>') ?>.

</tbody>

</table>

 

L’avantage, c’est que vos balises li ne changent pas pour les autres endroits.

 

La version CSS, elle, va affecter tous vos li si vous mettez la règle dans le main.css. Par contre, si vous créez un CSS spécial pour votre formulaire, vous pourrez, grâce à use_stylesheet(), uniquement l’utiliser dans vos formulaires (du moins pour ceux qui en auront besoin).

 

Dans votre form.css, vous pouvez mettre:

 

li{

list-style-type: none;

}

 

Dans votre _form.php, vous pouvez mettre:

 

<?php use_stylesheet('form.css') ?>

 

Maintenant que nous avons appris à faire face en utilisant use_stylesheet() (et dire que c’est grâce à l’écriture de ce tutoriel que j’ai résolu les inconvénients des deux méthodes!), nous allons pouvoir passer à la génération du module genre et platform. Pour ceux qui voudraient jouer avec la fonction delete(), qu’ils lisent ce qui suit.

 

Jouer avec executeDelete()

 

Vous pouvez placer des liens Delete dans la table d’index, sous chaque fiche de jeu par exemple. Ou vous pouvez également modifier la redirection. Amusez-vous à le faire car cela fait de l’entraînement!

 

Pour placer des liens dans la liste de jeux, en face de chacun d’entre eux, il suffit de modifier votre tableau:

 

– Ajouter une entête de colonne

– Ajouter les liens dans celle-ci

– Le lien doit impérativement contenir l’id du jeu

 

Voici le code auquel vous auriez pensé:

<!--- début de la template -->

<th>Delete</th>

<!--- code --->

<?php foreach ($games as $game): ?>

<!--- colonnes -->

<td><?php echo link_to("Delete", 'game/delete?id='.$game->getId()) ?>

<!--- fin de code --->

 

En effet, nous utilisons le helper link_to en lui passant en paramètres le nom du lien et l’URL. Nous indiquons bien que l’id doit être repris.

 

Si vous avez choisi url_for, le code devait ressembler à ceci:

 

<td><a href="<?php echo url_for ('game/delete?id='.$game->getId())?>">Delete</a>

 

Mais si effectivement, vous avez mis cela, une erreur s’affichera en spécifiant que le token CRSF est requis. Si vous préférez, votre lien Delete peut être sujet à une attaque CRSF. Rappelez-vous: vous avez protégé normalement votre frontend lors de sa génération. En réalité, vous avez simplement besoin d’ajouter une demande de confirmation pour accéder au reste du code de executeDelete().

 

Voici donc le code exact à mettre:

 

<td><?php echo link_to("Delete", 'game/delete?id='.$game->getId(),array('method' => 'delete', 'confirm' => 'Are you sure?')) ?>

La méthode est bien delete et le confirm ouvre son accès si on appuie sur Oui.

Pour url_for, malheureusement, je n’ai pas la solution, après tout, je débute aussi.

Vous avez maintenant tout en main pour placer ce lien sous la fiche individuelle de chaque jeu. Il est donc inutile pour moi de mettre le code.

Si vous voulez rediriger votre utilisateur sur une autre page après la suppression, il suffit de changer l’URL dans :

$this->redirect('game/index');

 

A présent, vous retirez ces liens et commenter le code executeDelete() car nous n’en avons pas besoin pour notre application. N’oubliez pas d’aller également dans _form.php pour retirer la ligne concernant le delete(). Ne supprimez pas la condition dans laquelle elle se trouve puisqu’elle sert également pour revenir à la fiche individuelle d’un jeu en mode édition.

 

En savoir plus:

Symfony – Forms in Actions

Advanced Forms

Widgets et validateurs personnalisés


Vous avez trouvé l'article intéressant? Partagez-le!

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

onze − 11 =