数组类型属性和带有数据转换器的Form CollectionType

时间:2017-06-02 21:37:52

标签: forms symfony doctrine-orm

我有一个实体,其属性设置为数组

/**
 * @ORM\Column(type="array")
 */
private $labels = [];

此数据数据存储标签的翻译,如

[
   'en' => 'foo-in-English',
   'de' => 'foo-in-German',
   'ru' => 'foo-in-Russian'
]

我有一个Form,其类型设置为:

$builder
    ->add('labels', CollectionType::class);

请注意entry_type默认(正确)到TextType。保持原样,模板将显示文本字段,如:

Labels:       en: _____FOO IN ENGLISH____
              de: _____FOO IN GERMAN_____
              ru: _____FOO IN RUSSIAN____

但是,我希望字段显示的是实际的语言 name ,而不是两个字母的代码作为标签,所以类似于:

Labels:       English: _____FOO IN ENGLISH____
              German:  _____FOO IN GERMAN_____
              Russian: _____FOO IN RUSSIAN____

我还想确保显示所有选定/支持的语言 - 即使它们目前没有价值。

所以,这似乎是DataTransformer的正确位置,但尝试我可能无法让这个概念在 Form类中工作。似乎尝试转换集合类型的数据比像文本这样的简单类型更难(或不可能?)。

我已经通过在将控制器中的数据提交到表单之前以及在持久性之前处理表单之后转换控制器中的数据来解决此问题。 e.g。

    $this->transformTranslations($fooEntity);
    $form = $this->createForm(FooType::class, $fooEntity);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        $fooEntity = $form->getData();
        $this->reverseTransformTranslations($fooEntity);
        $this->getDoctrine()->getManager()->persist($fooEntity);
        $this->getDoctrine()->getManager()->flush();
    ...

我想知道是否有人有更好的方法(比如如何使用普通数据或模型变换器)。我似乎无法在网上找到关于使用带有集合类型的数据转换器的信息。 TIA!

2 个答案:

答案 0 :(得分:0)

但是,我之前没有亲自使用过教条数组值 您可以为每个翻译选项定义“默认”form class,如下所示:

<强>的appbundle \表格\ LanguageStringEditorType.php

class LanguageStringEditorType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('en', TextareaType::class, ['label' => 'English'])
            ->add('de', TextareaType::class, ['label' => 'German'])
            ->add('ru', TextareaType::class, ['label' => 'Russian'])
        ;
    }
}

如果你保持命名('en','de'和'ru')和你的数据数组键名相同,例如有这样的(doctrine)实体:

<强>的appbundle \实体\ LanguageString.php

class LanguageString {
    private $identifier;
    private $translations; // this is the doctrine array type
                           // however I didn't feel like setting up a database for this
                           // test so I'm manually filling it see the next bit

... Getter and setter things ...

并为此创建一个类型:

<强>的appbundle \表格\ LanguageStringType.php

class LanguageStringType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('identifier')
            ->add('translations', LanguageStringEditorType::class, ['label' => 'Translations'])
        ;
    }
}

我们可以在控制器中使用它

$data = new LanguageString();
// fill some dummy content (not using database)..
$data->setIdentifier('hello_world');
$data->setTranslations([
    'en' => 'Hello world!',
    'de' => 'Hallo Welt!',
    'ru' => 'Привет мир'
]);

$form = $this->createForm(LanguageStringType::class, $data);

return $this->render('default/index.html.twig', [
    'form' => $form->createView()
]);

其余部分由魔法完成,不需要变形金刚。数据放在表单字段中。并在使用handleRequest时设置为实体。请记住,数据键值与表单构建器名称相同。

作为奖励,您已经在LanguageStringEditorType类中定义了所有默认语言字段,填写或不填写。

答案 1 :(得分:0)

所以,我了解到我需要将我的两个需求分成不同的解决方案。首先,我创建了一个新的表单类型,而不是我默认使用的文本类型:

    $builder
        ])
        ->add('labels', CollectionType::class, [
            'entry_type' => TranslationType::class
        ])

这个类非常简单,只是常规TextType的扩展:

class TranslationType extends AbstractType
{
    /**
     * @var LocaleApiInterface
     */
    private $localeApi;

    /**
     * TranslationType constructor.
     * @param LocaleApiInterface $localeApi
     */
    public function __construct(LocaleApiInterface $localeApi)
    {
        $this->localeApi = $localeApi;
    }

    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['label'] = array_search($view->vars['name'], $this->localeApi->getSupportedLocaleNames());
    }

    public function getParent()
    {
        return TextType::class;
    }
}

这满足了标签问题。其次,为了确保我的数据中包含所有受支持的语言环境,我使用了FormEventListener:

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $supportedLocales = $this->localeApi->getSupportedLocales();
        $data = $event->getData();
        $labels = $data['labels'];
        foreach ($supportedLocales as $locale) {
            if (!array_key_exists($locale, $labels)) {
                $labels[$locale] = $labels['en'];
            }
        }
        $data['labels'] = $labels;
        $event->setData($data);
    });

如果数据尚未存在,则会为数据添加所需的键。