提问者:小点点

在Symfony表单AbstractType中合并自定义和默认选项setOptionDefaults()


我有一个基本控制器(ContentEditControlllerBase),它可以完成编辑页面的所有标准功能。这个基本控制器由许多不同编辑页的控制器扩展,通常只是简单地向基本控制器传递一些基本选项。基本控制器加载一个表单,该表单通过基于加载的页面类型的自定义formType编辑View对象(每个编辑页面表单看起来不同)。

编辑文章本身(用于唯一内容类型的唯一捆绑包)

Route: /Admin/Article/Edit
Controller: \Gutensite\ArticleBundle\Controller\ArticleEditController (extends ContentEditControllerBase)
FormType: \Gutensite\ArticleBundle\Form\Type\ArticleEditType (extends ViewType)

编辑此页面的SEO字段(所有页面的标准CMS编辑字段)

Route: /Admin/Content/SEO/Edit
Controller: \Gutensite\CmsBundle\Controller\ContentSeoEditController (extends ContentEditControllerBase)
FormType: \Gutensite\CmsBundle\Form\Type\ContentSeoEditType (extends ViewType)   

扩展的ViewType表单加载一个自定义的ViewVersionType表单(一些选项需要传递给该表单中的关联实体)。

ContentEditControllerBase(由所有编辑页面扩展)

// the View Object is created
$view = new View;

// We determine the correct custom formType to load based on settings for the 
// particular editing page being loaded (e.g. some pages will edit the main 
// content, others the SEO settings. This will create a path like:
// \Gutensite\CmsBundle\Form\Type\ContentSeoEditType
$formTypePath = $currentPage->getBundleInfo()['bundleNamespace']
                    .'\Form\Type\\'.$$currentPage->getBundleInfo()['controller']
                    .'Type';

// We then load the correct formType and pass in any options, e.g. access level to different fields (i.e. will this form show the publish button or just the save button)
$form = $this->createForm(new $formTypePath, $view, $options);

在ViewType中,我们使用setDefaultOptions(),以及一些合理的默认值(请参见下面的代码)。这允许我的控制器将选项传递给自定义表单类型。我的ViewType表单将其中一些值传递给ViewVersionType

但是,我需要能够设置默认值,然后只覆盖特定值,因此实际上这些选项需要合并。当前,如果传入自定义选项,则会覆盖默认值的整个数组。

ContentSeoEditType(使用额外字段扩展ViewType)

namespace Gutensite\CmsBundle\Form\Type;

use Symfony\Component\Form\FormBuilderInterface;

class ContentSeoEditType extends ViewType
{

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

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

        // Enable existing options on ViewVersion object
        $options['options']['viewVersion']['enableGroup']['seo'] = TRUE;

        /**
         * Add New Inputs to View object
         */
        $builder->add('routing', 'collection', array(
            'label' => false,
            'type' => new RoutingType(),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype'     => true,
            'by_reference' => false
        ));

        parent::buildForm($builder, $options);
    }


}

ViewType(包含关联表单的表单类型)

namespace Gutensite\CmsBundle\Form\Type;

use Gutensite\CmsBundle\Entity\View;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ViewType extends AbstractType
{

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


    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Gutensite\CmsBundle\Entity\View\View',
            'cascade_validation' => true,
            // Custom Options
            'options' => array(
                // Options for viewVersion
                'viewVersion'       => array(),
                // Enabling Options
                'enableAction' => array(
                    'create'        => TRUE,
                    'save'          => TRUE,
                    'publish'       => FALSE,
                    'version'       => FALSE,
                    'duplicate'     => FALSE,
                    'delete'        => FALSE
                )
            )
        ));
    }

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

        // Pass Specific Variables to ViewVersion Options (when loaded below)
        $options['options']['viewVersion']['enableAction']['publish'] = $options['options']['enableAction']['publish'];


        $builder
            ->add('version', new ViewVersionType(), array(
                'label' => false,
                'required' => false,
                // Pass options to viewVersion form type
                'options' => $options['options']['viewVersion']
            ))
        ;
    }
}

ViewVersionType(自定义表单类型基)

namespace Gutensite\CmsBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ViewVersionType extends AbstractType
{

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

    public function setDefaultOptions(OptionsResolverInterface $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'Gutensite\CmsBundle\Entity\View\ViewVersion',
            // Add a custom option bucket
            'options' => array(
                    'enableField'       => array(
                        'title'         => FALSE,
                        'versionNotes'  => FALSE,
                        'timeCustom'    => FALSE
                ),
                'enableAction'      => array(
                    'publish'       => FALSE
                ),
                'enableEntity'      => array(
                        'viewSettings'  => FALSE,
                        'content'       => FALSE
                ),
                'enableGroup'       => array(
                        'layout'        => FALSE,
                        'seo'           => FALSE
                )
            )
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // **********************************
        // ERROR: $options['options'] does NOT have all the default options listed in setDefaultOptions()
        // Only has $options['options']['enableGroup']['seo'] = 1
        // Only has $options['options']['enableAction']['publish'] = 1
        // **********************************


    }
}

如何在formType上设置默认选项,并允许控制器传入特定的自定义选项以仅覆盖该选项,即将自定义选项与默认选项合并。我需要能够将选项从ViewType传递到从ViewType加载的子表单ViewTypeVersion(相同的合并)。


共2个答案

匿名用户

默认选项不用于合并。这是预期的行为。它们被称为默认值,因为它们只有在您未定义它们时才存在。您可以在此处阅读更多有关界面的信息

以下是覆盖OptionResolver中选项的代码部分:

// Make sure this method can be called multiple times
$combinedOptions = clone $this->defaultOptions;

// Override options set by the user
foreach ($options as $option => $value) {
    $combinedOptions->set($option, $value);
}

没有必要在“选项”下定义你的数组,使用你自己独特的索引。

// These options will be available for you
$resolver->setDefaults(array(
    'enableAction' => array('publish' => FALSE),
    'enableEntity' => array(
        'viewSettings'  => FALSE,
        'content'       => FALSE
     ),
));

创建表单并使其共享选项的另一种灵活方式是将它们创建为服务。这样,您可以根据需要共享配置中的选项。

最后,扩展FormType与扩展类不同。当您像对ContentSeo表单类型那样扩展类时,Symfony无法解析选项和依赖项。FormType通过在类中定义getParent()方法相互继承。在一个类中使用getParent()方法,让symfony知道此表单继承自另一个,选项将正确解析。

你的主要问题有两个:

>

  • 首先,您尝试使用选项,这已经使用了我的Symfony,如果您深入代码,您将看到Symfony在尝试合并任何内容之前检查它是否定义。您应该使用自己的索引键,而不是symfony默认使用的索引键。

    其次,在扩展表单类型时,需要使用getParent,而不是扩展类。

    尝试这两个更改,看看它对您的效果如何。我认为这应该是一个良好的开端。

    希望这有助于澄清一些问题。

  • 匿名用户

    ?为什么不在自定义表单类型中覆盖buildView函数?

    $view变量有一个名为vars的属性,其中包含选项(一旦解析),您可能需要使用实际合并的值更新这些选项。

    看看这个例子:

    class MyFormType extends AbstractType
    {
        //some code ...
    
        public function buildView(FormView $view, FormInterface $form, array $options)
        {
            $view->vars['options'] = array_merge($this->fixedOptions,$view->vars['options']);
        }
    
    }
    

    "'