Elément de formulaire à la volée avec Zend Framework

Dans ce tutoriel nous allons voir comment gérer l’ajout d’un élément de formulaire à la volée, à savoir en utilisant Ajax/jQuery dans un projet Zend Framework. En PHP vanilla, c’est plutôt simple, mais avec ZF, cela se complique un peu.

Le tutoriel pré-suppose avoir les connaissances de base de ZF, si vous débutez, je vous conseille de lire le billet relatif à la mise en place d’un premier projet ZF.

Pour illustrer ce billet, imaginons une application qui gère des véhicules, nous avons une action ajouterVehiculeAction() dans un contrôlleur, comme ceci :

public function ajouterVehiculeAction()
{
	$form = new Application_Form_AjouterVehicule();

	// Vérifie si le forumulaire a été envoyé
    if ($this->getRequest()->isPost())
    {
    	// Vérifie si le formulaire est valide
    	if ($form->isValid($this->getRequest()->getPost()))
		{

			// Traitemment ici...
		}
		// Le formulaire n'est pas valide
		else
		{
			$this->view->form = $form;
		}
	}
	// Le formulaire n'est pas encore envoyé
	else
	{
		$this->view->form = $form;
	}
}

La vue correspondante qui est minimaliste :

<?php echo $this->form; ?>

Nous avons une classe de formulaire qui étend la classe Zend_Form :

class Form_AjouterVehicule extends Zend_Form
{
    public function init()
    {
		$this->setMethod('post');
		$this->setAction("add");
		$this->setName("ajoutervehicule");

		// Véhicule
		$vehicule = new Zend_Form_Element_Select('vehicule');
		$vehicule->setLabel('Véhicule :');
		// On imagine que les options sont rapatriées dans un tableau $opts
		$vehicule->addMultiOptions($opts);
		$this->addElement($vehicule);

		// Bouton submit
		$submit = new Zend_Form_Element_Submit("submit");
		$submit->setLabel("Ajouter");
		$this->addElement($submit);
    }
}

Ceci fait le job voulu, à savoir pouvoir ajouter un véhicule depuis un formulaire, mais il existe une floppée de véhicules, l’élément select peut donc être très long. D’où l’idée d’ajouter un premier élément avec la liste des types de véhicules : voitures, camions, utilitaires, bateaux, avions, etc. Lorsque l’on choisit un type, notre élément véhicule est alors alimenté via Ajax avec la liste des véhicules du type choisi.
Pour faire ceci, il va falloir ajouter trois choses : une méthode de contrôlleur qui servira à faire la requête Ajax, adapter notre classe de formulaire pour comprendre la présence de notre nouvel élément, car le gros du problème vient de là, notre formulaire étant un objet de notre classe Form_AjouterVehicule, lorsque la vue va créer le nouvel élément via jQuery, l’objet de formulaire ne l’ajoutera pas automatiquement, il va donc falloir lui dire qu’il a été alteré à la volée.

La classe de formulaire

Ajoutons notre élément type à la classe Form_AjouterVehicule :

class Form_AjouterVehicule extends Zend_Form
{
    public function init()
    {
		$this->setMethod('post');
		$this->setAction("add");
		$this->setName("ajoutervehicule");

		// Types
		$types = new Zend_Form_Element_Select('types',
								array("multiOptions" => array(
									1 => "Voitures",
									2 => "Camions",
									3 => "Utilitaires",
									4 => "Bateaux",
									5 => "Avions",
									6 => "Pigeons voyageurs"
								)));
		$types->setLabel('Types :');
		$this->addElement($types);

		// Véhicule
		$vehicule = new Zend_Form_Element_Select('vehicule', array());
		$vehicule->setLabel('Véhicule :');
		$this->addElement($vehicule);

		// Bouton submit
		$submit = new Zend_Form_Element_Submit("submit");
		$submit->setLabel("Ajouter");
		$this->addElement($submit);
    }

    // Surchage la méthode isValid
    public function isValid($valeurs)
	{
		$valeurs = $this->_modifierElements($valeurs);
	    return parent::isValid($valeurs);
	}

	// Ajoute le nouvel élément
	protected function _modifierElements($valeurs)
	{
		// Retire l'élément courant (vide)
		$this->removeElement('vehicule');

	    // Crée le nouvel élément
	    $vehicule = new Zend_Form_Element_Select('vehicule', array());
		$vehicule->setLabel('Véhicule :');
		// Encore une fois, on imagine que les options 
		// sont rapatriées dans un tableau $opts
		// contenant les véhiculent du type choisi
		$vehicule->addMultiOptions($opts);
		$this->addElement($vehicule);

		return $valeurs;
	}

	// Remplit le formulaire avec le nouvel élément
	public function populate(array $valeurs)
	{
	    $valeurs = $this->_modifyElements($valeurs);
	    return parent::populate($valeurs);
	}
}

Qui a-t-il de nouveau? On commence par ajouter l’élément qui permettra de choisir le type de véhicule, par simplicité, un simple select avec ses options en dur. Dans un projet plus sérieux, les options seraient ajoutées dynamiquement.
Ensuite, on garde l’élément véhicule, mais il est vide (on passe un tableau vide en second argument de Zend_Form_Element_Select()) et sera invisible dans la vue lorsque la page est chargée.
Puis c’est ici que ça se corse, pour prendre en compte le nouveau champ mis-à-jour par jQuery après la soumission du formulaire, il faut surcharger la méthode isValid() de Zend_Form. Celle-ci va désormais faire appel à une nouvelle méthode _modifierElements() qui se charge de mettre à jour le nouvel élément.
La dernière étape pour la classe de formulaire est de surcharger également la méthode populate() pour que le nouvel élément ait les bonnes valeurs. Voilà pour la classe Form_AjouterVehicule.

Le contrôlleur

Passons au contrôlleur en ajoutant la méthode qui va être appelée en Ajax depuis la vue pour donner à manger à l’élément vehicule :

public function jsonObtenirVehiculesAction()
{
   	// On Récupère l'id de l'espèce
   	$requete = $this->getRequest();
   	$id = $requete->getParam('id');

	// Cherche les sous-espèces
	// SousEspeces() est une classe gérant les sous-espèces
	$se = new SousEspeces();
	$sousespeces = $se->ObtenirLesSousEspecesDe($id);

	// On veut le résultat formatté JSon
	$this->_helper->json($opts);

	// Disable renderer
	$this->_helper->viewRenderer->setNoRender();

	// Disable layout
	$this->_helper->getHelper('layout')->disableLayout();
}

On peut remarquer que l’on retourne un résultat formatté Json pour alimenter les options du select.

La vue

Nous n’avons plus qu’à ajouter la partie jQuery à la vue :

<script>
$(function() {
	// Lorsque l'élément types change d'état
	// on déclenche la fonction mettreAJourElement()
    $("#types").change(function() {
        mettreAJourElement(this);
    });

    // A l'affichage de la page on cache l'élément
    // vehicule qui est pour l'instant vide
    $("#vehicule").parent().parent().css('display', 'none');
    mettreAJourElement($("#types"));
});

function mettreAJourElement(element) {
    if($(element).val() != '') {
    	// On affiche l'élément vehicule
        $("#vehicule").parent().parent().css('display', 'block');
        if ($('#vehicule').length <= 0) {
            $("#types").after('
            	<select name="vehicule" id="vehicule"></select>
            ');
        }

        $.getJSON("/controlleur/json-obtenir-vehicules",
        	{id: $(element).val(), ajax: "true"}, function(j)
        {
            var options = "";
            jQuery.each(j, function(i, val) {
                options += '<option value="' + i + '">' + val + '</option>';
            });
            $("#vehicule").html(options);
        });
    }
}
</script>

<?php echo $this->form; ?>

C’est terminé, le formulaire est maintenant modifié de façon asynchrone par une requête Ajax.

4 thoughts on “Elément de formulaire à la volée avec Zend Framework”

  1. j’ai un message d’erreur : Method _modifyElements does not exist, alors que j’ai bien la fonction.

  2. Jack et fabien, pour l’erreur Method _modifyElements does not exist il s’agit d’une petite coquille dans le code, dans la fonction populate() c’est _modifierElements() et non _modifyElements() comme le soulève l’erreur :)

Laisser un commentaire

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