Embauchez-moi

Je suis freelance ! Besoin d'un professionnel du développement web ? Pourquoi ne pas me passer un coup de fil ?

Plus d'infos sur… http://thibault.jouannic.fr

mots-cles : Ingénieur web freelance Symfony eZ Publish Solr

Mixons les formulaires avec Symfony

Lookin' through the archives
Creative Commons License photo credit : Shereen M

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  :

Symfony propel form
Creative Commons License photo credit : teeboo2734

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é  :

symfony propel form with embed
Creative Commons License photo credit : teeboo2734

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  !


13 Commentaires

  1. NiKo
    Posté le 04/11/2008 à 14:47 | Permalien

    Sympa ce petit tuto. J’éspère que d’autres suivront du même acabit :-)

  2. Posté le 05/11/2008 à 14:50 | Permalien

    A noter que depuis hier soir, le save du form principal est récursif (ce qui était déjà avant le cas avec les form doctrine je crois d’ailleurs) donc plus besoin de surcharger updateObject (a voir comment ça se goupille avec l’adress_id par contre)

    ps : une petite coquille sur le label du checkbox de ton formulaire de commentaire (  »un nouveau commetnaire  »)

  3. NiKo
    Posté le 05/11/2008 à 15:29 | Permalien

    Héhé Geoffrey m’a devancé, je venais faire la même remarque :-)

  4. Posté le 05/11/2008 à 15:35 | Permalien

    Ah, ça c’est une amélioration intéressante. Sera-t-elle présente sur la branche 1.1 ?

  5. NiKo
    Posté le 05/11/2008 à 15:37 | Permalien

    Hmmm, vraisemblablement pas, pour des raisons de compatibilité ascendante…

  6. Posté le 05/11/2008 à 15:40 | Permalien

    ça gère les propel form la 1.1 ?

  7. NiKo
    Posté le 05/11/2008 à 15:41 | Permalien

    Geoffrey> Oui :)

  8. Posté le 03/05/2009 à 23:08 | Permalien

    Dommage que ce genre de tutoriel ne soit pas étendu aux problématiques de la CREATION de nouveaux éléments !
    Merci d’avoir pris de ton temps pour ces explications en tout cas,

    bonne continuation.

  9. michael
    Posté le 24/05/2009 à 13:46 | Permalien

    Bonjour,
    Le tuto est bien, mais comme l’a mentionné Adrien il ne traite pas de la problématique de l’inscription.
    Je dispose d’un table user (id, nom, prenom, email, password) et d’une autre user_profil(id, user_id, birthday, ecole, genre, religion…)
    Je ne sais pas comment faire pour que lors de l’ajout d’un nouvel utilisateur, il me crée automatiquement une entrée avec l’id user crée dans la table profil.
    Un pti conseil à me donner ?
    Merci
    Cordialement

  10. Cyrille
    Posté le 10/09/2009 à 12:25 | Permalien

    > Création de nouveaux éléments
    Pour les objets du modèle, il suffit de mettre un $this->embedForm(…) ; et Symfony 1.2 s’occupe de tout : sfFormORM disposant de méthodes save/updateEmbeddedForms qui sont appelées automatiquement.

  11. Posté le 15/02/2010 à 19:52 | Permalien

    Dans symfony 1.4 j’obtiens :
    Unknown record property / related component «  adress  » on «  User  »

  12. Posté le 15/02/2010 à 20:22 | Permalien

    non c bon.
    Je m’étais emmêler les pédales ;-)

    Pour info :

    Une nouveauté de symfony 1.3 est la méthode sfFormDoctrine::embedRelation() qui offre au développeur la possibilité d’imbriquer automatiquement des relations n-à-plusieurs dans un formulaire.
    cf :
    http://www.symfony-project.org/more-with-symfony/1_4/fr/06-Advanced-Forms#chapter_06_imbriquer_facilement_des_formulaires_doctrine

  13. dave
    Posté le 08/06/2010 à 02:27 | Permalien

    bonjour, deux remarques

    1) il faut declarer
    public function updateObject($values = null)

    2) Class ‘BasePeer’ not found
    pourrais tu expliquer cette derniere ligne ?
    merci

One Trackback

  1. [...] Le blog d’un développeur Bonnes pratiques, astuces, culture web (non, pas 2.0), etc. Sauter vers le contenu À proposArchives « Mixons les formulaires avec Symfony [...]

Envie de vous exprimer ?

Votre email n'est jamais affiché. Votre commentaire ne sera pas affiché non plus s'il est bourré de fautes ou de liens publicitaires. Vous êtes prévenu.

*
*