【问题标题】:Symfony3 : choice type field filled with array of objectsSymfony3:填充对象数组的选择类型字段
【发布时间】:2016-08-28 15:36:13
【问题描述】:

我有一个实体Product。我的产品可以有多个不同语言的名称。一个法语名字,一个英文名字等等。我不想使用自动翻译。

用户必须在产品表单中输入名称并选择相应的语言。借助“添加”按钮,他可以添加任意数量的名称。

所有语言均由管理员用户创建(以另一种形式)。所以,Language 也是一个拥有名称(例如:英文)和代码(例如:EN)的实体。

我创建了实体ProductName,它具有名称和语言(符合用户在产品表单中所写的内容)。

在这种情况下,我不需要将实体ProductName 与实体Language 关联。我只想要语言代码。所以,在我的ProductName 实体中,我有这个属性:

/**
 * @ORM\Column(name="Language_Code", type="string", length=2)
 */
private $language;

我的产品表单 (ProductType) 有一个 CollectionType 字段,用于添加多个名称。

// Form/ProductType.php

    ->add('infos',      CollectionType::class, array(
        'entry_type'    => ProductInfosType::class,
        'allow_add'     => true,
        'allow_delete'  => true,
        'prototype'     => true,
        'label'         => false,
        'mapped'        => false
    ))

ProductInfosType 表单有 2 个字段:

// Form/ProductInfosType.php

        ->add('name',           TextType::class, array(
            'attr'              => array('size' => 40)
        ))
        ->add('language',       EntityType::class, array(
            'placeholder'       => '',
            'class'             => 'AppBundle:Language',
            'choice_label'      => 'code',
            'attr'              => array('class' => 'lang'),
            'query_builder'     => function (EntityRepository $er) {
                return $er->createQueryBuilder('l')->orderBy('l.code', 'ASC');
            }
        ))

所以,当我进入表单页面时,我有一个块,其中包含一个输入文本字段(名称)和一个选择字段(语言)。选择字段是这样的:

<select id="product_infos_0_language" required="required" name="product[infos][0][language]">
    <option value=""></option>
    <option value="DE">DE</option>
    <option value="EN">EN</option>
    <option value="ES">ES</option>
    <option selected="selected" value="FR">FR</option>
</select> 

此时,一切正常。我创建了一个添加按钮,以便用户可以添加其他名称等...

但是,当我提交表单时,当我在我的 ProductController 中检查表单数据时,我注意到它与我想要存储在数据库中的内容不对应。

print_r($form->get('infos')->getData());

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object
                (
                    [code:AppBundle\Entity\Language:private] => FR
                    [name:AppBundle\Entity\Language:private] => Français
                )

            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
        )
)

我想要的是:

Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [language:AppBundle\Entity\ProductName:private] => FR    
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
        )
)

我不想要语言对象,而是直接想要语言代码

这就是为什么我认为我不应该在 ProductNameType 表单中使用 EntityField 而应该在 ChoiceType 字段中使用。

如何在选择字段中加载存储在 db 中的所有语言? 我希望这个解释更容易理解;-)

【问题讨论】:

  • 如果我理解得很好:您是否尝试过创建一个 __toString() 方法来返回您的 code 属性 Language 实体?
  • 没有。我不认为这对我有帮助。我的语言代码已经是一个字符串。例如:名称 = 英文和代码 = EN。当我提交表单时,我只需要我在列表中选择的代码,而不是获取对象语言。
  • 我不太明白你提交后的“get”是什么意思
  • 我认为EntityType 正是您所需要的。为什么你想要 Language 的属性,而不是 Language 本身?提交后获取$entity-&gt;getLanguage()-&gt;getCode()即可。
  • 我修改了我的帖子以便更好地解释我的问题;-)

标签: symfony object entity choice choicefield


【解决方案1】:

感谢这篇文章,我找到了解决方案:Passing data to buildForm() in Symfony 2.8/3.0

ProductController.php :将自定义数据作为createForm() 方法中的选项传递。

// ...

// build the form
$em = $this->getDoctrine()->getManager();
$product = new Product();
$languages = $em->getRepository('AppBundle:Language')->findAllOrderedByCode();

$form = $this->createForm(ProductType::class, $product, array(
    'languages' => $languages
));

ProductType 表单:在选项解析器中传递自定义数据

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Product',
        'languages'  => null
    ));
}

然后,在 buildForm() 函数中,在 CollectionType 字段中添加 entry_options 选项:

$builder->add('infos',  CollectionType::class, array(
    'entry_type'    => ProductInfosType::class,
    'entry_options' => array('languages' => $options['languages']),
    'allow_add'     => true,
    'allow_delete'  => true,
    'prototype'     => true,
    'label'         => false,
    'by_reference'  => false
));

ProductInfosType 表单:在选项解析器中传递自定义数据(与 ProductForm 中完全相同)

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\ProductName',
        'languages'  => null
    ));
}

现在,您有两种选择:您希望表单返回实体或简单字符串。

在我的示例中,我只需要语言代码(如 FR、EN 等)。

案例一:表单发布时只返回语言代码:

// Form/ProductInfosType.php

// ...

// Convert array of objects in an array of strings
$choices = array();
foreach ($options['languages'] as $lang) {
    $code = $lang->getCode();
    $choices[$code] = $code;
}

$builder->add('language', ChoiceType::class, array(
    'placeholder'       => '',
    'choices'           => $choices
));

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
            [language:AppBundle\Entity\ProductName:private] => FR    
        )
)

案例2:表单发布时返回语言实体:

// Form/ProductInfosType.php

// ...

$builder->add('language', ChoiceType::class, array(
    'placeholder'       => '',
    'choices'           => $options['languages'],
    'choice_label'      => 'code',
    'choice_value'      => 'code'
));

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
            [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object
                (
                    [code:AppBundle\Entity\Language:private] => FR
                    [name:AppBundle\Entity\Language:private] => Français
                )    
        )
)

使用此解决方案,我们无需将表单创建为服务即可将实体管理器作为参数传递。一切都在控制器和表单选项中进行管理。

【讨论】:

    【解决方案2】:

    你应该使用choice_value的EntityType。

    'choice_value' => function ($language) {
        return $language->getCode();
    },
    

    编辑:阅读您的编辑后,您确实是对的,不要使用EntityType,而是使用ChoiceType。要填充choices,我认为您应该使用DependencyInjection 在您的表单中注入LanguageRepository,然后在您的存储库中创建一个查询以获取所有语言代码。

    【讨论】:

    • 不,我已经将代码作为我的选择选项的值。它不会改变我表单的数据。
    • 我修改了我的帖子以便更好地解释我的问题 ;-)
    • 好的,谢谢!那正是我所想。我知道如何在 LanguageRepository 中创建查询,但你能写下“使用DependencyInjection 在表单中注入LanguageRepository”的解决方案吗?
    • 在表单的构造函数中添加 LanguageRepository,然后在 services.xml 中将表单定义为服务,并将存储库作为参数(参见 symfony.com/doc/current/components/dependency_injection/…)。您还需要将存储库定义为服务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多