Formulaire imbriqué sous symfony
Dans votre administration, sous symfony, vous créez un module pour chaque modèle. Ne vous êtes vous jamais demandé si vous pouviez tous mettre à jour en un seul formulaire? Et bien c’est réalisable et pas si compliqué que cela.
Je vais donc vous montrer comment imbriqué différents formulaire pour mettre à jour plusieurs modèles en même temps!
Pour l’exemple, je vais prendre le schéma suivant : Création et un-ou-plusieurs LienCreation pour chaque Création.
Creation:
actAs: {Timestampable: ~ }
columns:
titre:
type: string(255)
est_visible:
type: boolean
notnull: true
default: 1
LienCreation:
actAs: { Timestampable: ~ }
columns:
creation_id:
type: integer
url:
type: string(255)
relations:
Creation:
foreignType: many
foreignAlias: LiensCreation
local: creation_id
Jusque là pas de surprise, on à deux modèles très pauvres en données. Pour avoir un rendu immédiat, on va créer un petit /data/fixtures/fixture.yml pour avoir tous les cas possibles.
Creation:
crea_1:
titre: creation n°1
est_visible: true
crea_2:
titre: creation n°2
est_visible: true
crea_3:
titre: creation n°3
est_visible: true
LienCreation:
link1:
Creation: crea_1
url: test1.com
link2:
Creation: crea_1
url: test2.com
link3:
Creation: crea_1
url: test3.com
link4:
Creation: crea_2
url: test4.com
On build tous ça, on charge les données dans la base de données et on crée notre module Création dans l’application backend (je suppose que vous avez déjà créé une application backend.
$ ./symfony doctrine:build --all --no-confirmation --and-load $ ./symfony doctrine:generate-admin backend Creation $ ./symfony plugin:publish-assets
Afficher les liens de la création dans le même formulaire
Alors pour commencer, on va éditer le fichier /lib/form/doctrine/CreationForm.php. C’est dans ce fichier que nous allons imbriquer le ou les formulaires supplémentaires. C’est également dans ce fichier que nous allons surcharger les règles de validation des formulaires imbriqués, nous verrons pourquoi en temps voulu.
Dans cette première partie, nous voulons donc afficher tous les liens de la création choisi. On se place donc dans le cadre de l’édition d’une création déjà existante.
Dans le fichier /lib/form/doctrine/CreationForm.php
public function configure() {
// autre code
$this->embedRelation('LiensCreation');
}
Toute la magie se trouve dans cette méthode embedRelation(). Cette méthode va générer au sein de votre formulaire de mise à jour de la Création, autant de formulaire que de relations présentes dans la base de données. Vous pourrez alors modifier TOUS les liens en même temps que la création correspondante!
Afficher également un formulaire de nouveau lien
Nous avons vu comment afficher les liens déjà reliés à la création, et bien nous allons voir comment rajouter un formulaire pour générer un nouveau lien en le liant automatiquement à la Création en cours d’édition ou en cours de création.
Dans le fichier /lib/form/doctrine/CreationForm.php on vient remodifier la méthode configure()!
public function configure() {
// Formulaire vide pour ajouter un lien
$newLienForm = new LienCreationForm();
$this->embedForm('new', $newLienForm);
// Formulaire pour les liens existants
// On utilise la valeur donné à ForeignAlias dans le schema.yml
$this->embedRelation('LiensCreation');
}
En effectuant les actions précédentes, même les formulaires vides seront pris en compte, il faut donc remédier à cela. Pour ce faire, nous allons créer un validateur personnalisé. Nous allons donc créer le fichier/lib/validator/LienCreationValidatorSchema.class.php et le remplir comme ci-dessous:
class LienCreationValidatorSchema extends sfValidatorSchema {
protected function configure($options = array(), $messages = array()) {
// Ici on viendra rajouter/editer ou supprimer les différents messages d'erreurs
$this->addMessage('url', 'La description est requise.');
}
protected function doClean($values) {
$errorSchema = new sfValidatorErrorSchema($this);
foreach($values as $key => $value) {
$errorSchemaLocal = new sfValidatorErrorSchema($this);
// Si il n'y a pas l'url de remplit ou si elle est vide, on évite l'insertion d'un lien "vide"
if (!$value['url'] || empty($value['url'])) {
unset($values[$key]);
}
// Si il y a des erreurs ont les traitent ici
if (count($errorSchemaLocal)) {
$errorSchema->addError($errorSchemaLocal, (string) $key);
}
}
// throws the error for the main form
if (count($errorSchema)) {
throw new sfValidatorErrorSchema($this, $errorSchema);
}
return $values;
}
}
Une fois ce validateur personnalisé, on va l’appeler et s’en servir! On va pas s’amuser à écrire du code pour rien quand même !!! =D
Dans le fichier /lib/form/LienCreationForm.class.php on va venir ajouter ce validateur en rajoutant dans la méthode configure() la ligne suivante :
$this->mergePostValidator(new LienCreationValidatorSchema());
Cela évite de créer des enregistrements vides dans la base de donnée. Cela arrive si par exemple l’utilisateur veut seulement ajouter une Création sans liens, ou quand il édite une création avec déjà des liens existants.
Vous pouvez maintenant tester l’ajout de nouveau lien relié avec la création en cours d’édition ou de création directement depuis ce formulaire.
Bon dév’ :D












Article très clair et intéressant, j’ai une petite question.
Dans le sous formulaire il y a un select pour choisir la création, y’a un moyen d’automatiser la relation entre la création et ces liens (et donc de cacher le select) ?
Merci pour cet article qui m’a fait faire un grand pas en avant !
Il subsiste pourtant un problème. Cette méthode renvoie le html suivant où la balise sensée contenir deux input text est vide :
Entrees
Il génère pourtant bien les champs cachés. Et si j’essaie d’afficher le formulaire imbriqué directement via son module (non imbriqué), j’ai bien tous les champs… J’avoue être un peu coincé là :(
Mais merci quand même
Bonjour,
Merci pour cet article, c’est exactement ce que je cherchais.
J’ai une table Film, une table Realisateur, et une table de jointure Realisateur_has_film.
L’administration de la table Film et de la table Realisateur est faite. Mais j’aimerais pouvoir lorsque je crée un film, lui attribuer un réalisateur (déjà existant ou que je crée).
J’essaye donc en faisant :
$this->embedRelation('RealisateurHasFilm');
$newRealisateurHasFilm = new RealisateurHasFilmForm();
$this->embedForm('new', $newRealisateurHasFilm);
Néanmoins j’ai le même problème que Jekif.. Le formulaire ne s’affiche pas. Si je regarde le code source, les champs sont cachés. :(
Bonjour,
Merci beaucoup pour votre aide.
J’ai donc mis dans RealisateurHasFilmFormClass.php:
$this->widgetSchema['realisateur_id_fk'] = new sfWidgetFormDoctrineChoice(array(
‘model’ => ‘Realisateur’,
‘add_empty’ => true
));
$this->setDefault(‘id_realisateur_fk’, $this->getOption(‘id_realisateur_fk’));
$form = new RealisateurForm();
$this->embedForm(‘nom_realisateur’, $form);
$this->widgetSchema->setLabel(‘id_realisateur_fk’, ‘Choisir…’);
$this->widgetSchema->setLabel(‘nom_realisateur’, ‘ou créer.’);
et dans FilmForm.class.php :
$idProd = $this->getObject()->isNew() ? NULL : $this->getObject()->getIdProdFk();
$form = new RealisateurHasFilmForm(array(), array(‘id_prod_fk’ => $idProd));
$this->embedForm(‘Realisateur’, $form);
$this->setValidator(‘id_realisateur_fk’, new sfValidatorNumber(array(‘required’ => true)));
Mais lorsque je veux créer un nouveau film, il me dit “Unexpected extra form field named “auteur_id_fk”.” De plus, si j’essaye d’éditer un film, la liste déroulante ne récupère pas l’id du réalisateur.
Je sais pas trop comment faire..
bonjour ,
merci pour cet article , il ma aidé de faire un pas , je suis debutant en dev Web , je suis coincé sur un point de multi upload des photos du projet watchmydesk du site la fermeduweb, si vous avez une idée , ??
Ouaaaaoouu!!! Trop bien, c’est vraiment puissant, merci pour cette astuce !!
Bonjour,
j’essaie également de faire un formulaire imbriqué.
J’ai un classe Demande et une classe Message qui contient une relation vers une demande.
Dans demandeForm.class methode configure j’ai ajouté:
$message= new messageForm();
$this->embedForm(‘message’, $message);
$this->embedRelation(“message”);
$this->mergePostValidator(new sfValidatorMessage());
$this->widgetSchema->setLabels(array(
‘message’=>”Description”
));
et dans messageForm.class methode configure j’ai ajouté:
$this->useFields(array(
‘text’,
‘envoyePar_id’
));
$this->widgetSchema['demande_id'] = new sfWidgetFormInputHidden();
dans mon _form de demande j’ai ajouté:
renderLabel() ?>:
renderError() ?>
mon problème est que quand j’affiche mon formulaire pour une nouvelle demande, j’ai bien le label “Description” mais a coté c’est un message d’erreur VIDE qui s’affiche au lieu des différents champs que j’attendais..
est-ce que j’ai mal compris les explications si-dessus? est-ce que j’ai oublié quelque chose?
merci pour votre aide
léa
Merci pour cet article qui m’a bien aiguillé dans mon développement. J’ai réussi à résoudre tout mes problèmes grâce à lui et cet excellent plugin: http://www.symfony-project.org/plugins/ahDoctrineEasyEmbeddedRelationsPlugin/1_4_4
Encore merci !
ce que je cherchais, merci
au niveau du code validator y a une erreur au niveau du boucle foreach($values as $key =>; $value) pas de point virgule aprs$key=>
Merci j’ai pu créer mes forms imbriqués
Merci, c’est corrigé!
Bonjour, désolé de répondre si tard, mais mon alternance me prend plus de temps que prévu!
Pour ce qui est de votre question, il y a bien un moyen mais je pense qu’il faut le faire à la main.
Je vais prendre comme exemple des Créations, qui imbriquent des Images
* Il faut spécifier les champs à utiliser dans le form imbriqué, ici dans lib/form/doctrine/CreationForm.class.php.
public function configure() {
$this->useFields(array('description', 'image', 'est_visible'));
// Ton code
}
Pour ma part, il met automatiquement à jour le champs et ne l’affiche pas!
En espérant que cela t’aide!
Bon dév’
Salut à toi!
Désolé de répondre si tard.
Pour effectuer soit la sélection soit l’ajout d’un réalisateur, il ne suffit pas de ces trois lignes!
Dans le FilmForm.class.php:
public function configure() {
$realisateurId = $this->getObject()->isNew() ? NULL : $this->getObject()->getRealisateurId();
$form = new FilmRealisateurForm(array(), array('realisateur_id' => $realisateurId));
$this->embedForm('Realisateur', $form);
}
Cela a pour effet de créer le formulaire de la table de jointure avec l’id d’un réalisateur ou non si celui ci est spécifié.
Ensuite, il faut modifier l’action de FilmRealisateurForm.class.php:
public function configure() {
$this->widgetSchema['realisateur_id'] = new sfWidgetFormDoctrineChoice(array(
'model' => 'Realisateur',
'add_empty' => true
));
$this->setDefault('realisateur_id', $this->getOption('realisateur_id'));
$form = new RealisateurForm();
$this->embedForm('new_realisateur', $form);
$this->widgetSchema->setLabel('realisateur_id', 'Choisir…');
$this->widgetSchema->setLabel('new_realisateur', 'ou créer.');
}
L’affichage du formulaire affichera quelques chose comme le screen ci-dessous!
@lakhcim: Bonjour, est-ce que tu pourrais être un peu plus précis sur ton problème? Merci.
@Pomme: Bonjour,
Et non je n’ai pas oublié de répondre, mais il faut que je prenne un peu de temps, je réponds dès que je peux.
Bonne journée!
Content de l’aide que ça t’as apporté!
A l’occasion, je fouinerai un peu ce plugin.
Bon dév’