Symfony2:用同名的自定义字段类型覆盖内置字段类型


Symfony2: Overriding a built-in field type with a custom field type having the same name

根据这篇关于Symfony文档的文章,我已经创建了一个自定义字段类型,并在services.yml中进行了设置,并且我能够成功地使用它。

例如,我创建了一个名为customdate的自定义字段,如下所示,效果很好:
# src/Acme/DemoBundle/Resources/config/services.yml
services:
    acme_demo.form.type.date:
        class: Acme'DemoBundle'Form'Type'DateType
        tags:
            - { name: form.type, alias: customdate }

然而,如果我尝试将我的自定义字段命名为date(这与现有的Symfony字段类型相同,因为这是我试图覆盖的),如下所示,那么Symfony完全忽略我的自定义字段,并默认为内置的Symfony date字段类型:

# src/Acme/DemoBundle/Resources/config/services.yml
services:
    acme_demo.form.type.date:
        class: Acme'DemoBundle'Form'Type'DateType
        tags:
            - { name: form.type, alias: date }

我已经检查了我的getName()函数返回正确的名称,与我在services.yml中提供的别名匹配。

我使用上述服务的代码如下:

如此:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('date', 'customdate')));
}

这不起作用:(或者更确切地说,Symfony使用内置字段类型而不是我的)

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('date', 'date')));
}

我应该注意,如果我用手动创建的对象(如new Date())替换'customdate'或'date',那么它就可以正常工作。问题似乎特别在于Symfony更喜欢其内置字段类型,而不是在services.yml中指定的字段类型。

我的问题:是否有任何方法可以用具有相同名称的自定义字段类型覆盖内置的Symfony字段类型?显然,从我上面描述的情况来看,Symfony似乎忽略了与内置Symfony字段类型别名相同的任何自定义字段。还有别的办法吗?

据我所知,没有办法真正覆盖基本字段类型,您可以继承它们并使用自己的名称。

但是,如果您想要覆盖的字段类型没有提供您认为应该提供的功能,则该类型可能存在问题,应该报告。

对于您的例子,date类型不采用典型的php date()格式字符串。查看这里的文档,我们看到日期格式是由IntlDateFormatter类解析的。对于有效的格式,请查看

要完成您想要的date('d M Y')格式,您可以使用:

$builder->add('my_date_field', 'date', array(
    'format'=>'d MMM Y'
));

要回答第一个问题,有一种方法可以覆盖内置的symfony表单类型。上面的代码几乎是正确的。它只需要使用symfony中使用的相同的服务id。查看Symfony服务配置并使用相同的服务id:

# src/Acme/DemoBundle/Resources/config/services.yml
services:
    form.type.date:
        class: Acme'DemoBundle'Form'Type'DateType
        tags:
            - { name: form.type, alias: date }

我已经测试了这个,似乎工作得很好。Acme'DemoBundle'Form'Type'DateType应该扩展symfony类Symfony'Component'Form'Extension'Core'Type'DateType,进行任何需要的更改。这可以用任何Symfony表单类型完成。

另一种方法稍微复杂一些,但更适合将来使用,它使用编译器传递来更改服务定义的类,但其余部分保持不变。它看起来像:

//src/Acme/DemoBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php
namespace Acme'DemoBundle'DependencyInjection'Compiler;
use Symfony'Component'DependencyInjection'Compiler'CompilerPassInterface;
use Symfony'Component'DependencyInjection'ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $definition = $container->getDefinition('form.type.date');
        $definition->setClass('Acme'DemoBundle'Form'Type'DateType');
    }
}

则编译器传递被注册到AcmeDemoBundle类中,如;

// src/Acme/DemoBundle/AcmeDemoBundle.php
namespace Acme'DemoBundle;
use Symfony'Component'HttpKernel'Bundle'Bundle;
use Symfony'Component'DependencyInjection'ContainerBuilder;
use Acme'DemoBundle'DependencyInjection'Compiler'OverrideServiceCompilerPass;
class AcmeDemoBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new OverrideServiceCompilerPass());
    }
}

参考重写服务文档和编译器传递文档获取更多信息