web-dev-qa-db-fra.com

Passer des options personnalisées à un formulaire symfony2

dans symfony 1.4, il était possible de paramétrer une définition de classe de formulaire, via les options du formulaire. Existe-t-il un moyen de passer des options personnalisées à mon type de formulaire personnalisé ??? j'ai essayé d'utiliser le paramètre options de la méthode buildForm, mais je ne suis pas très sûr de ce qu'est ce tableau, et apparemment ce n'est pas pour ce que je veux ... Merci!

26
Throoze

La solution est simple, si vous souhaitez que votre option personnalisée soit également disponible dans le modèle Twig, vous devez utiliser $builder->setAttribute() in buildForm method and $view->set() dans la méthode buildView() également.

<?php

namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

// For Symfony 2.1 and higher:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * ImagePreviewType
 *
 */
class ImagePreviewType extends FormAbstractType
{

    /**
     * {@inheritDoc}
     * For Symfony 2.0
     */
    //public function getDefaultOptions(array $options)
    //{
    //    $options = parent::getDefaultOptions($options);
    //    $options['base_path'] = 'path/to/default/dir/';
    //
    //    return $options;
    //}

    /**
     * {@inheritDoc}
     * For Symfony 2.1 and higher
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'base_path'         => '',
        ));
    }

    /**
     * {@inheritDoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // For Symfony 2.0:
        // $view->set('base_path', $form->getAttribute('base_path'));

        // For Symfony 2.1 and higher:
        $view->vars['base_path'] = $options['base_path'];
    }

    /**
     * {@inheritDoc}
     */
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->setAttribute('base_path', $options['base_path'])
        ;
    }

    /**
     * {@inheritDoc}
     */
    public function getName()
    {
        return 'image_preview';
    }

    public function getParent(array $options)
    {
        // for Symfony 2.0:
        // return 'field';

        // for Symfony 2.1 and higher:
        return 'form';
    }
}

Modèle pour le type de formulaire personnalisé (fichier ... Acme/DemoBundle/Resources/views/Form/fields.html.twig):

{% block image_preview_widget %}
{% spaceless %}
<img src="{{ base_path ~ value }}" alt=""  {{ block('widget_container_attributes') }} />
{% endspaceless %}
{% endblock %}

Enregistrez votre modèle pour les types de formulaires personnalisés dans app/config/config.yml

twig:
    debug:            %kernel.debug%
    strict_variables: %kernel.debug%
    form:
        resources:
            - 'AcmeDemoAdminBundle:Form:fields.html.twig'

Utilisation: Affichez un aperçu de l'image de l'utilisateur lors de la modification de son profil:

// src/Acme/DemoBundle/Form/Type/UserType.php
namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class UserType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('user_profile_image_file_name', new ImagePreviewType(), array(
            'base_path' => 'some/other/dir',
        ));
    }
}

2014-08-18: mis à jour pour Symfony 2.1 ou supérieur

43
pulzarraider

PDATE: Veuillez noter que cette solution ne fonctionne que dans Symfony 2.0.x, qui est obsolète, utilisez setDefaultOptions au lieu de getDefaultOptions.


À juste titre, les types de formulaires Symfony 2 acceptent des options que vous pouvez utiliser pour tout ce que vous voulez dans le type de formulaire. Vous devez remplacer la méthode getDefaultOptions pour spécifier vos options de type.

Par exemple, j'ai un type MyCustomType qui accepte my_option, cette option a une valeur par défaut de false, l'implémentation de MyCustomType peut être quelque chose comme ça.

class MyCustomType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        if($options['my_option']){
            //do something
        } else {
            //do another thing
        }
        ...
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'my_option' => false
        );
    }

    public function getName()
    {
        return 'mycustomtype';
    }
}

Plus tard, vous devrez spécifier l'option lorsque vous créez le formulaire dans le contrôleur, en utilisant le troisième paramètre de buildForm:

$form = $this->buildForm(new MyCustomType(), null, array(
    'my_option' => true
));

Si vous ne spécifiez pas le my_option option, il prend la valeur par défaut (false).

18
eagleoneraptor

En utilisant symfony 2.8, j'ai réussi à utiliser la solution proposée en étendant la méthode configureOptions().

class ElementType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'my_custom_option_parameter' => null,
        ));
    }
}

J'avais besoin d'utiliser le ElementType, comme une collection et un formulaire intégré. J'ai reconnu qu'il n'était pas possible de passer le my_custom_option_parameter Au CollectionType, car je n'ai pas personnalisé configureOptions() de CollectionType, mais de mon ElementType. Si vous devez passer le my_custom_option_parameter À travers un CollectionType, vous pouvez réussir en définissant my_custom_option_parameter Dans entry_options (Voir Documentation Champ CollectionType =) tableau de CollectionType.

Exemple en passant my_custom_option_parameter À travers un CollectionType:

class OuterFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    { 
        //...
        $builder->add('elements', CollectionType::class, array(
            'entry_type' => ElementType::class,
            // ...
            'entry_options' => array(
                'my_custom_option_parameter' => 'value is now set!'
            )
        ));
        //...
    }
}
10
Erik Theoboldt

sur la base de la réponse @pulzarraider, j'ai créé du code avec des modifications pour Symfony 3.

Vous devez changer

OptionsResolverInterface pour OptionsResolver

FormBuilder pour FormBuilderInterface

Dans mon cas:

namespace MediaBundle\Form;

use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;

class ImageType extends FormAbstractType {

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'max_images' => ''
        ));
    }

    public function buildView(FormView $view, FormInterface $form, array $options) {

        $view->vars['max_images'] = $options['max_images'];
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {

        $builder
                ->setAttribute('max_images', $options['max_images'])
        ;
    }

    public function getName() {
        return 'image_preview';
    }

    public function getParent() {
        return TextareaType::class;
    }
}
4
Piotrek Zatorski

En utilisant Symfony 3, j'ai pu passer des options personnalisées au formulaire en définissant une option par défaut dans OptionsResolver injecté dans la méthode configureOptions de ma classe de type de formulaire:

Dans le contrôleur:

//Compile whatever your options are here. Assume an array is returned
$customOptions = $this->getMyCustomOptions($args);
//Build the form:
$form = $this->createForm(MyCustomFormType::class, array('my_custom_options' => $customOptions));

MyCustomFormType.php:

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => DataModel::class,
        'my_custom_options' => []
    ]);
}
//custom options is now set in "$options" array:
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('my_custom_fields', Type\ChoiceType::class, [
       'choices' => $options['my_custom_options'],
       'mapped' => false //if not part of the data model.
       . . .

Il semble donc que vous puissiez définir un contrat avec votre formulaire et le fournisseur de données pour définir des données arbitraires sur le formulaire.

Je viens de mettre en œuvre cette procédure avec succès. Notez que lors du voyage de retour, puisque vous avez défini 'mapped' => false, Dans le formBuilder, $form->getData() ne renverra pas la sélection. Pour obtenir la valeur sélectionnée:

$mySelectedValue = $form->get('my_custom_options')->getViewData();

dans votre contrôleur. Pourquoi cela me dépasse. . .

2
eggmatters

J'ai essayé d'utiliser ces options array sans succès car il semblait qu'il ne pouvait transporter que de petits sous-ensembles de clés prédéfinis. C'était, par tous les moyens, inacceptable pour moi ...

Cependant, vous pouvez passer toutes les options via les formulaires __construct méthode et le stocker dans les propriétés de classe pour une utilisation ultérieure. Ensuite, à partir de buildForm, vous pouvez y accéder en utilisant $this->"propertyName"...

C'est à vous de décider si vous voulez passer une seule array ou juste quelques variables à __construct...

Ceci est juste un exemple approximatif:

class Foobar{
    private $update = false;

    public function __construct($update = false){
        $this->update = $update;
    }


    public function buildForm(FormBuilder builder, array options){
        if ( $update ){
            // something
        }else{
            // well, this is not an update - do something else
        }
    }
}
2
Jovan Perovic