【问题标题】:Symfony2: Specify form type as a field type of another formSymfony2:将表单类型指定为另一个表单的字段类型
【发布时间】:2017-10-25 09:16:32
【问题描述】:

所以我有一个基本形式:

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

其中有一个特定的字段checklist。我创建了一个模型类,它描述了清单中所有可能的选项

ChecklistModel.php

class ChecklistModel {
    /** @var string **/
    protected $clientSatisfied;

    // ... getters and setters

}

然后,我专门为 Checklist 创建了一个表单类型。

ChecklistFormType.php

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('clientSatisfied', ChoiceType::class, array(
            'choices' => array(
                'yes' => 'yes',
                'no' => 'no'
            ),
            'choices_as_values' => true,
    ))
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => ChecklistModel::class
    ));
}

我想将清单作为简单的 JSON 字符串存储在数据库中,但我想使用 ChecklistModel 来确保清单中的所有字段都正确提交。

我的问题是如何告诉 Symfony 使用 ChecklistFormType 作为基本形式 checklist 属性的字段类型?

我尝试过类似的东西

->add('checklist', ChecklistFormType::class);

但我收到以下错误

The form's view data is expected to be an instance of class PT\MyBundle\Models\Invoice\ChecklistModel, but is a(n) string. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) string to an instance of PT\MyBundle\Models\Invoice\ChecklistModel

【问题讨论】:

  • 持久化实体上的属性checklist 会是字符串类型吗?你考虑过内置类型json吗?然后,您可以在封装模型的 setter/getter 中进行转换,使表单类型(以及就此而言,将访问清单的所有其他代码)从了解有关数据如何持久化的任何信息中解放出来。

标签: php forms symfony-2.8


【解决方案1】:

根据我上面的评论,我建议不要在表单类型中进行数据转换(尽管这当然是可能的),而是在使用json_array type 的封装模型中进行。

这样只有模型真正知道数据将如何被持久化。

两个相关模型:

src/AppBundle/Entity/FooModel.php

<?php
declare(strict_types=1);

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 */
class FooModel
{
    // other properties (firstName, lastName, ...)

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

    /**
     * @param ChecklistModel $checklist
     */
    public function setChecklist(ChecklistModel $checklist)
    {
        $this->checklist = $checklist->toArray();
    }

    /**
     * @return ChecklistModel
     */
    public function getChecklist(): ChecklistModel
    {
        return ChecklistModel::fromArray($this->checklist);
    }
}

ChecklistModel实现上述方法:

src/AppBundle/Entity/ChecklistModel.php

<?php
declare(strict_types=1);

namespace AppBundle\Entity;

class ChecklistModel
{
    // properties and getters/setters

    /**
     * @param array $data
     *
     * @return ChecklistModel
     */
    public static function fromArray(array $data): ChecklistModel
    {
        $result = new self;

        foreach (get_class_vars(self::class) as $k => $v) {
            if (isset($data[$k])) {
                $result->$k = $data[$k];
            }
        }

        return $result;
    }

    /**
     * @return array
     */
    public function toArray()
    {
        return get_object_vars($this);
    }
}

表单类型:

src/AppBundle/Form/FooFormType.php

<?php
declare(strict_types=1);

namespace AppBundle\Form;

use AppBundle\Entity\FooModel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type as FormType;

class FooFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('firstName', FormType\TextType::class)
            ->add('lastName', FormType\TextType::class)
            ->add('checklist', ChecklistFormType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => FooModel::class,
            'empty_data' => new FooModel(),
        ]);
    }
}

src/AppBundle/Form/ChecklistFormType.php

<?php
declare(strict_types=1);

namespace AppBundle\Form;

use AppBundle\Entity\ChecklistModel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type as FormType;

class ChecklistFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('clientSatisfied', FormType\ChoiceType::class, [
                'choices' => [
                    'yes' => 'yes',
                    'no' => 'no'
                ],
                'choices_as_values' => true,
            ])
            ->add('clientNewCustomer', FormType\ChoiceType::class, [
                'choices' => [
                    'yes' => 'yes',
                    'no' => 'no'
                ],
                'choices_as_values' => true,
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ChecklistModel::class,
            'empty_data' => new ChecklistModel(),
        ]);
    }
}

示例用法

public function indexAction(Http\Request $request)
{
    $em = $this->getDoctrine()->getManager();

    $data = new Entity\FooModel();

    $form = $this
        ->createForm(FooFormType::class, $data)
        ->handleRequest($request)
    ;

    if ($form->isSubmitted() && $form->isValid()) {
        $em->persist($data);
        $em->flush();
    }

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

这样ChecklistFormType 不需要知道任何关于数据是 json 或其他内容的信息。 ChecklistModel 进进出出,不出意外。


也就是说,embeddables 在这里可能是一个更好的选择。

【讨论】:

  • 对不起,我的问题是如何提交数据类ChecklistModel。目标是验证清单中的不同字段,可以是选择、字符串、浮点类型。能否请您扩展您的示例并向我展示如何构建表单,以便表单将处理清单字段验证?谢谢!
  • @LuninRoman 我明天再来找你,目前无法从这里回答。 ;) 在那之前,您能否用“验证”来扩展您的问题?目前所有正常的表单验证都可以工作。如果这就是你所需要的,我明天会添加一个示例。
  • 嗨,这里是官方指南symfony.com/doc/current/validation.html,看看表单构建器。例如,我想指定所有可能的选择,可以提交给 ChecklistModel clientSatisfied 变量symfony.com/doc/current/reference/forms/types/…
  • @LuninRoman 这或多或少已经是这样了,因为表单不会接受不在ChecklistFormType 中配置的choices 数组中的值。
  • @LuninRoman 我添加了我使用的表单类型,如果您有其他问题,请告诉我。
【解决方案2】:

您必须按照错误消息中的说明指定数据类,并结合反序列化器定义数据转换器。 Symfony documentation

【讨论】:

  • 链接失效
  • 抱歉刚刚改了
  • 您的回答也很有帮助!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多