Forms 带有数据转换器的数组类型属性和窗体CollectionType

Forms 带有数据转换器的数组类型属性和窗体CollectionType,forms,symfony,doctrine-orm,Forms,Symfony,Doctrine Orm,我有一个属性设置为数组的实体 /** * @ORM\Column(type="array") */ private $labels = []; 此数据数组存储标签的翻译,如 [ 'en' => 'foo-in-English', 'de' => 'foo-in-German', 'ru' => 'foo-in-Russian' ] 我有一个为标签设置了类型的表单,如: $builder ->add('labels', Collection

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

/**
 * @ORM\Column(type="array")
 */
private $labels = [];
此数据数组存储标签的翻译,如

[
   'en' => 'foo-in-English',
   'de' => 'foo-in-German',
   'ru' => 'foo-in-Russian'
]
我有一个为标签设置了类型的表单,如:

$builder
    ->add('labels', CollectionType::class);
请注意,
entry\u type
在此默认(正确)为
TextType
。如左图所示,模板将显示文本字段,如:

Labels:       en: _____FOO IN ENGLISH____
              de: _____FOO IN GERMAN_____
              ru: _____FOO IN RUSSIAN____
但是,我希望字段以实际语言名称显示,而不是以两个字母的代码作为标签,因此类似于:

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();
    ...

我想知道是否有人有更好的方法(比如如何使用普通数据或模型转换器)。我在网上似乎找不到太多关于使用收集类型的数据转换器的信息。蒂亚

但是,我个人以前没有使用过原则数组值 您可以为每个翻译选项定义“默认值”,如下所示:

AppBundle\Form\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'])
        ;
    }
}
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 ...
class LanguageStringType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('identifier')
            ->add('translations', LanguageStringEditorType::class, ['label' => 'Translations'])
        ;
    }
}
如果您保持命名('en'、'de'和'ru')与数据数组键名称相同,例如具有如下(条令)实体:

AppBundle\Entity\LanguageString.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'])
        ;
    }
}
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 ...
class LanguageStringType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('identifier')
            ->add('translations', LanguageStringEditorType::class, ['label' => 'Translations'])
        ;
    }
}
并为此创建一个类型:

AppBundle\Form\LanguageStringType.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'])
        ;
    }
}
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 ...
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()
]);
其余的都是用魔法完成的,不需要变压器。数据放置在表单字段中。并在使用HandlerRequest时设置为实体。请记住,数据键值与表单生成器名称相同


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

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

AppBundle\Form\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'])
        ;
    }
}
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 ...
class LanguageStringType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('identifier')
            ->add('translations', LanguageStringEditorType::class, ['label' => 'Translations'])
        ;
    }
}
如果您保持命名('en'、'de'和'ru')与数据数组键名称相同,例如具有如下(条令)实体:

AppBundle\Entity\LanguageString.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'])
        ;
    }
}
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 ...
class LanguageStringType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('identifier')
            ->add('translations', LanguageStringEditorType::class, ['label' => 'Translations'])
        ;
    }
}
并为此创建一个类型:

AppBundle\Form\LanguageStringType.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'])
        ;
    }
}
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 ...
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()
]);
其余的都是用魔法完成的,不需要变压器。数据放置在表单字段中。并在使用HandlerRequest时设置为实体。请记住,数据键值与表单生成器名称相同


作为奖励,您已经在LanguageStringEditorType类中定义了所有默认的语言字段,无论是否填写。

因此,我学到了我需要将我的两个需求分成不同的解决方案。首先,我创建了一个新的表单类型来代替默认使用的文本类型:

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

这会将所需的键添加到数据中,如果它们还不存在。

因此,我了解到我需要将我的两个需求分离到不同的解决方案中。首先,我创建了一个新的表单类型来代替默认使用的文本类型:

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

这会将所需的键添加到数据中(如果它们尚未出现)。

感谢您抽出时间回答!我真的很感激!我想我需要更灵活一点的。我找到了一个答案,我将在下面分享:谢谢你花时间回答!我真的很感激!我想我需要更灵活一点的。我找到了一个答案,我将在下面分享: