Pense bête : Gestion des erreurs avec les formulaires imbriqués sous Symfony 2.1

Lorsqu’on met en place des formulaires imbriqués avec Symfony 2.1 il ne faut pas oublier que la gestion des formulaires à légèrement été revue entre la version 2.0 et les versions 2.x, comme expliqué dans la notice prévue UPGRADE FROM 2.0 to 2.1.

Il ne faut également pas oublier de consulter la documentation Symfony officielle expliquant le fonctionnement et la mise en place des formulaires imbriqués.

Au sujet des erreurs sur les formulaire imbriqué, cette dernière nous expose comment « faire remonter » les erreurs propres au premier formulaire imbriqué dans le second. Il ne faut pas oublier de préciser au formulaire qui reçoit le premier, qu’il va devoir récupérer des erreurs autres que les siennes en précisant dans la méthode setDefaultOptions() de votre Form comme ceci :

namespace Acme\Bundle\AcmeBundle\Form;

class MyEntityType extends AbstractType
{
    public buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('myotherentity', new MyOtherEntityType());
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\Bundle\AcmeBundle\Entity\MyEntity',
            'cascade_validation' => true
        ));
    }

    ...
}

Le tout couplé avec vos « Assert » sur les champs de vos entitées et une vue exposant correctement les erreurs de vos formulaires comblera la plupart de vos besoins.

PagerFanta et l’erreur « array_merge() »

PagerFanta est un Bundle pour Symfony 2 qui permet de paginer l’affichage de collections. Il est assez simple d’utilisation, mais il est courant de rencontrer l’erreur suivante :

"Warning: array_merge() [<a href='function.array-merge'>function.array-merge</a>]: Argument #2 is not an array in /path/to/my/project/vendor/bundles/WhiteOctober/PagerfantaBundle/Twig/PagerfantaExtension.php line 73") in MaBoite\/MonBundle\/Resources\/views\/MonControlleur\/index.html.twig at line XXX.

Ce message apparait lorsque l’on a installé une mauvaise version de PagerFanta. Les sources disponibles par défaut sur GitHub suivent la branche master de Symfony, qui avance actuellement sur la version 2.1 du Framework.

Il est très probable que la version Symfony sur laquelle vous travaillez soit une 2.0.XX (dernière version stable au moment où j’écris ces lignes). Il faut donc utiliser la branche symfony2.0 de PagerFanta. Si vous installez les sources directement par copier/coller (déconseillé), remplacez simplement les sources master par celles de la branche symfony2.0. Si vous installez le bundle via le fichier deps (recommandé), il vous faut utiliser les lignes suivantes.

[PagerFanta]
 git=http://github.com/whiteoctober/Pagerfanta.git
 target=pagerfanta
[PagerFantaBundle]
 git=http://github.com/whiteoctober/WhiteOctoberPagerfantaBundle.git
 target=bundles/WhiteOctober/PagerfantaBundle
 version=origin/symfony2.0

Lancez ensuite simplement la commande pour installer les vendors.

bin/vendors install

Attention ! Si vous aviez déjà installé la version master de PagerFanta via le fichier deps, celle ci est probablement verrouillée. Il vous faudra alors supprimer la ligne PagerFantaBundle dans le fichier deps.lock avant de lancer l’installation.

Utiliser un Validator dans un Form\Type Symfony2

Vous avez sûrement déjà dû utiliser des fonctions de validation appelées en callback sur vos formulaires (CallbackValidator) pour tester un retour de formulaire et lever une erreur sur un certain champs si la valeur de ce dernier n’est pas valide.

public function buildForm(FormBuilder $builder, array $options)
{
    $builder->add('myfield', 'text');
    $builder->addValidator(new CallbackValidator(function(FormInterface $form) {
    $myfield = $form->get('myfield');
    if ($myfield->getData() != null && !is_string($myfield->getData())) {
        $myfield->addError(new FormError('Invalid format'));
    }
}

Et bien sachez que vous pouvez utiliser dans vos Form/Type des Validator (à l’instar de ceux qu’on peut invoquer en utilisant les Assert dans nos entitées) qui vont pouvoir vous apporter un nombre important d’outils qui vont vous aider à valider vos formulaires.

Tout d’abord, il vous faudra utiliser les namespace de classes suivants :

<?php
namespace Acme\Bundle\AcmeBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\CallbackValidator;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Constraints\RegexValidator;

class FooType extends AbstractType
{
    [...]
}

Les deux derniers sont ceux que nous devons rajouter pour pouvoir utiliser cette contrainte de validation. Ici nous avons choisit la validation de Regex mais vous pouvez utiliser n’importe quel type de contrainte tant qu’elle possède une classe Validator associée. (cf. liste des toutes les contraintes)

Il vous suffira désormais de juste instancier de nouveaux objets dans votre CallbackValidator afin de pouvoir analyser et valider une valeur.

[...]
public function buildForm(FormBuilder $builder, array $options)
{
    $builder->add('myfield', 'text');
    $builder->addValidator(new CallbackValidator(function(FormInterface $form) {
    $myfield = $form->get('myfield');
    if ( ! is_null($myfield->getData()) ) {
        $validator      = new RegexValidator();
        $constraint     = new Regex(array(
            'pattern' => "/^[a-z0-9-]+$/"
        ));
        $isValid = $validator->validate( $myfield->getData(), $constraint );
        if ( ! $isValid ) {
            $myfield->addError( new FormError( "This field is not valid (only alphanumeric characters separated by hyphens)" ) );
        }
    }
}
[...]

Voilà une astuce bien sympathique qui peut vous être utile à valider un champs qui, par exemple, ne figure pas dans les attributs de votre entitée. Exactement comme vous pourriez le faire en utilisant les validations de type Assert.