Mixons les formulaires avec Symfony
Depuis la version 1.1, Symfony propose un framework de génération de formulaire, respectant le modèle MVC, et bien entendu bati selon un modèle objet robuste.
Pour l’avoir un peu poussé dans ses retranchements, je dois avouer que je suis quelque peu impressionné par la qualité de l’ouvrage. Aujourd’hui, je vous propose un exemple de fonctionalité intéressante : les inclusions de formulaires.
Un formulaire peut en cacher un autre
Pour illustrer mon propos, prenons un modèle simple (dans schema.yml) :
propel: user: id: name: varchar(255) email: varchar(255) address_id: address: id: number: integer street: varchar(255) town: varchar(60)
Un utilisateur a une seule adresse, facile. Pour faire bonne mesure, j’ai quand même partitionné mes tables. Ajoutons quelques données de test dans le répertoire fixtures :
Address: a1: number: 1 street: boulevard Belleville town: Paris a2: number: 666 street: cours gambetta town: Montpellier User: robert: name: Garcin Fony email: garcin.fony@example.com address_id: a1
Puis, achevons de créer notre environnement de démonstration, en créant le modèle, chargeant les données, et préparant un module :
symfony propel:build-all-load frontend symfony generate:module frontend user
Édition d’un utilisateur
Je veux utiliser la puissance du framework de formulaires pour éditer un utilisateur. Facile, on va d’abord réaliser une action pour prendre en charge la création et la validation du formulaire :
public function executeEdit($request)
{
$user = UserPeer::retrieveByPk($request->getParameter('id'));
$this->forward404Unless($user);
$this->userForm = new UserForm($user);
if($request->isMethod('post') && $this->userForm->bindAndSave($request->getParameter('user')))
{
$this->redirect('@homepage');
}
}
Ensuite, dans mon template editSuccess.php, il ne me reste qu’à afficher le formulaire :
<form action="" method="post"> <?php echo $userForm ?> <input type="submit" value="modifier" /> </form>
Le résultat n’est pas trop mal :
Génial ! Symfony détecte automatiquement la clé étrangère, et adapte son formulaire en conséquence en me fournissant une liste déroulante des adresses (Au passage, remarquez que ce phénomène a nécessité l’ajout d’une méthode __toString dans la classe adresse, on n’a rien sans rien).
Bon, c’est bien beau, mais moi, j’espérais plutôt pouvoir éditer l’adresse en question dans le même formulaire. Pas de problèmes, nous allons modifier notre formulaire avec la fonction magique embedForm pour qu’il embarque un autre formulaire de modification de l’adresse.
La fonction embedForm permet, comme son nom l’indique, d’embarquer un formulaire dans un autre. Les validateurs sont également pris en compte. En alternative, selon les cas, on préférera la fonction mergeForm, qui permet de fusionner les formulaires.
Dans lib/form/UserForm.class.php :
class UserForm extends BaseUserForm
{
public function setup()
{
parent::setup();
$this->widgetSchema['address_id'] = new sfWidgetFormInputHidden();
$this->embedForm('address', new AddressForm($this->getObject()->getAddress()));
}
}
Et voilà ! Sans toucher le moins du monde au template, on se retrouve avec le résultat escompté :
Rhââ Lovely ! (comme dirait l’autre). Tout ça en 5 lignes de codes et 4 de templates. Bon, par contre, notre adresse n’est pas mise à jour à la soumission du formulaire. Embêtant… Il va nous falloir surcharger la méthode updateObject pour prendre en compte le formulaire embarqué. Dans la même classe :
public function updateObject()
{
parent::updateObject();
$address = $this->getObject()->getAddress();
$values = $this->getValues();
$address->fromArray($values['address'], BasePeer::TYPE_FIELDNAME);
$address->save();
return $this->object;
}
À ce moment, notre adresse sera sauvegardée en même temps que l’utilisateur. Pour la beauté de la démonstration, je n’ai pas rajouté de gestion des exceptions, mais l’idée est là.
Sur ce, je vous laisse vous amuser avec les formulaires. À la prochaine !