将自定义选项传递给Symfony2表单

27
在Symfony 1.4中,可以通过表单的选项来对表单类定义进行参数化。现在我想知道是否有方法可以向自定义的表单类型传递自定义选项?我已经尝试使用`buildForm`方法的选项参数,但是我不太确定这个数组是什么,而且显然它并不是我想要的... 谢谢!
6个回答

45

解决方案很简单,如果你希望在Twig模板中也能使用你的自定义选项,你需要在buildForm方法中使用$builder->setAttribute()方法,并且在buildView()方法中也要使用$view->set()方法。

<?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';
    }
}

自定义表单类型的模板(文件...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 %}

在 app/config/config.yml 中注册您的模板以用于自定义表单类型。

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

用法:在编辑用户资料时显示用户头像的预览。

// 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: 为Symfony 2.1或更高版本更新


我错过了使用buildView将选项传递给Twig的步骤!谢谢! - ROLO
如果我想要在所有表单类型(包括默认类型)中添加一个选项,有没有简单的方法可以做到这一点? - smarques
可以使用表单类型扩展实现。 - pulzarraider

18

更新:请注意,此解决方案仅适用于已经过时的Symfony 2.0.x版本,请使用setDefaultOptions替代getDefaultOptions


Symfony 2表单类型公正地接受您可以在表单类型内部使用的选项。 您需要重写getDefaultOptions方法来指定您的类型选项。

例如,我有一个类型MyCustomType,接受my_option,此选项的默认值为false,则MyCustomType的实现可如下所示。

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';
    }
}

稍后,您需要在控制器中创建表单时使用buildForm的第三个参数来指定选项:

$form = $this->buildForm(new MyCustomType(), null, array(
    'my_option' => true
));
如果您没有指定my_option选项,它会采用默认值(false)。

在来这里询问之前,我已经尝试过了...但是正如@jperovic所说,它只支持选项数组中一小部分预定义的键...我还没有尝试他的解决方案...但我会去试试看...无论如何还是谢谢! - Throoze
4
此解决方案支持使用任何您想要的键,您只需覆盖 getDefaultOptions 方法即可。 - eagleoneraptor
也许这个答案不适合问题的提出者,但对我来说肯定是有用的。因此,点赞。 :) - Sasa
哈哈...谢谢,但我不明白为什么我的答案不适合这个问题。传递给buildForm$options数组用于向类型传递自定义选项。然后,基于这些选项,您可以设置Form和/或FormView属性以在不同位置(例如视图)中使用选项,或者您可以设置视图属性而无需任何选项。但问题是,“如何将选项传递给自定义表单类型”,也许这个问题并不适合作者的需求。 - eagleoneraptor
9
显然现在不再使用getDefaultOptions(),而是使用setDefaultOptions()。否则会显示:“选项'no_animal_type'不存在。已知的选项包括:"action"、"attr"、"auto_initialize",…” - Bonswouar
显然@Bonswouar有正确的答案,得益于先前答案/评论的共享知识。谢谢大家(+1) - Nico

10

使用Symfony 2.8,通过使用建议的解决方案扩展configureOptions()方法,我成功了。

class ElementType extends AbstractType
{
    // ...

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

我需要将ElementType作为集合和嵌入式表单使用。我意识到无法将my_custom_option_parameter传递给CollectionType,因为我没有自定义CollectionTypeconfigureOptions(),而是自定义了ElementTypeconfigureOptions()。如果您需要通过CollectionType传递my_custom_option_parameter,可以将其定义在CollectionTypeentry_options(请参见文档CollectionType Field)数组中。

通过CollectionType传递my_custom_option_parameter的示例:

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!'
            )
        ));
        //...
    }
}

5

基于 @pulzarraider 的答案,我创建了一个有关Symfony 3的修改代码。

您需要更改:

OptionsResolverInterfaceOptionsResolver

FormBuilderFormBuilderInterface

在我的情况下:

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;
    }
}

3
使用Symfony 3,通过在表单类型类的configureOptions方法中设置默认选项,我可以将自定义选项传递到表单中。以下是示例代码:
控制器中的代码如下:
//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.
       . . .

看起来您可以为表单和数据提供程序定义合同,以在表单上设置任意数据。

我刚刚成功地实施了这个过程。请注意,在回程时,由于您在formBuilder中设置了'mapped' => false$form->getData()不会返回选择内容。要获取所选值:

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

在你的控制器中。为什么会出现这种情况我不清楚...

2

我曾尝试使用选项 array,但似乎它只能携带小而预定义的键的子集,因此没有成功。对我来说,这是完全不可接受的...

然而,你可以通过表单的 __construct 方法传递所有选项,并将其存储在类属性中以供以后使用。然后,在 buildForm 中,可以使用 $this->"propertyName" 访问它...

你可以自行决定是否要将单个 array 或仅几个变量传递给 __construct...

这只是一个简单的示例:

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
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接