Symfony2:将表单类型指定为另一个表单的字段类型

时间:2017-10-25 09:16:32

标签: php forms symfony-2.8

所以我有一个基本形式:

checklist

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

class ChecklistModel { /** @var string **/ protected $clientSatisfied; // ... getters and setters }

ChecklistFormType.php

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

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

ChecklistModel

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

我的问题是如何告诉Symfony使用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

但我得到了以下错误

{{1}}

2 个答案:

答案 0 :(得分:1)

您必须在错误消息中指定数据类,并与de / serializer一起定义数据转换器。 Symfony documentation

答案 1 :(得分:1)

根据我上面的评论,我建议不要在表单类型中进行数据转换(虽然它当然可以),而是使用json_array type进行封装模型。 / p>

这样只有该模型才能真正了解数据的持久性。

两个相关模型:

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可能是更好的选择。