模型转换器和预期的表单视图数据不匹配

Model transformer and expected form view data mismatch

我有一个 Symfony 2 应用程序,其表单需要在隐藏字段中存储对另一个实体(项目)的引用。项目实体是通过表单选项传入的,我的计划是有一个 'hidden' 类型的字段,它只包含实体 ID,然后应该在提交表单时将其转换为项目实体。

我将通过使用模型转换器在实体与字符串(它的 ID)之间进行转换来解决这个问题。但是,当我尝试查看表单时,出现以下错误:

The form's view data is expected to be an instance of class Foo\BarBundle\Entity\Project, 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 Foo\BarBundle\Entity\Project.

这是我的表格class:

<?php

namespace Foo\BarBundle\Form\SED\Waste;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Foo\BarBundle\Form\DataTransformer\EntityToEntityIdTransformer;

/**
 * Class WasteContractorEntryType
 * @package Foo\BarBundle\Form\CommunityInvestment\Base
 */
class WasteContractorEntryType extends AbstractType
{

    protected $name;

    protected $type;

    protected $phase;

    protected $wasteComponent;

    public function __construct($formName, $type, $phase, $wasteComponent)
    {
        $this->name = $formName;
        $this->type = $type;
        $this->phase = $phase;
        $this->wasteComponent = $wasteComponent;
    }

    /**
     * @return mixed
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @return mixed
     */
    public function getPhase()
    {
        return $this->phase;
    }

    /**
     * @return mixed
     */
    public function getProject()
    {
        return $this->project;
    }

    /**
     * @return mixed
     */
    public function getWasteComponent()
    {
        return $this->wasteComponent;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $em = $options['em'];

        $wasteComponentTransformer = new EntityToEntityIdTransformer($em,
            'Foo\BarBundle\Entity\SED\Waste\WasteComponent');

        $projectTransformer = new EntityToEntityIdTransformer($em, 'Foo\BarBundle\Entity\Project');

        $builder->add('id', 'hidden');

        $builder->add(
            $builder->create('project', 'hidden', array(
                'data' => $options['project'],
                'by_reference' => false
            ))
                ->addModelTransformer($projectTransformer)
        );

        $builder->add(
            $builder->create('wasteComponent', 'hidden', array(
                'data' => $this->getWasteComponent()
            ))
                ->addModelTransformer($wasteComponentTransformer)
        );

        $builder->add('phase', 'hidden', array(
            'data' => $this->getPhase()
        ));

        $builder->add('type', 'hidden', array(
            'data' => $this->getType()
        ));

        $builder->add('percentDivertedFromLandfill', 'text', array());

        $builder->add('wasteContractor', 'entity', array(
            'class' => 'Foo\BazBundle\Entity\Contractor',
            'property' => 'name',
            'attr' => array(
                'class' => 'js-select2'
            )
        ));
    }


    public function getName()
    {
        return $this->name;
    }

    /**
     * {@inheritDoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'csrf_protection' => true,
            'data_class' => 'Foo\BarBundle\Entity\SED\Waste\WasteContractorEntry'
        ))
            ->setRequired(array(
                'em',
                'project'
            ))
            ->setAllowedTypes(array(
                'em' => 'Doctrine\Common\Persistence\ObjectManager',
                'project' => 'Foo\BarBundle\Entity\Project'
            ));

    }

} 

还有我的模型变压器class:

<?php

namespace Foo\BarBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;

class EntityToEntityIdTransformer implements DataTransformerInterface
{
    /**
     * @var ObjectManager
     */
    private $om;

    /**
     * @var Entity class
     */
    protected $entityClass;

    /**
     * @param ObjectManager $om
     */
    public function __construct(ObjectManager $om, $className)
    {
        $this->om = $om;
        $this->entityClass = $className;
    }

    protected function getEntityClass()
    {
        return $this->entityClass;
    }

    /**
     * Transforms an object (project) to a string (id).
     *
     * @param  Project|null $issue
     * @return string
     */
    public function transform($entity)
    {
        if (null === $entity) {
            return "";
        }

        return $entity->getId();
    }

    /**
     * Transforms a string (id) to an object (project).
     *
     * @param  string $id
     *
     * @return Issue|null
     *
     * @throws TransformationFailedException if object (project) is not found.
     */
    public function reverseTransform($id)
    {
        if (!$id) {
            return null;
        }

        $entity = $this->om
            ->getRepository($this->getEntityClass())
            ->find($id);

        if (null === $entity) {
            throw new TransformationFailedException(sprintf(
                'An entity of class %s with id "%s" does not exist!',
                $this->getEntityClass(),
                $id
            ));
        }

        return $entity;
    }
}

我尝试使用添加转换器作为视图转换器而不是模型转换器,但是我只是得到一个稍微不同的错误:

The form's view data is expected to be an instance of class Foo\BarBundle\Entity\Project, but is a(n) integer. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) integer to an instance of Foo\BarBundle\Entity\Project.

似乎按照上面异常消息的建议将 'data_class' 设置为 null 是解决方案。我之前拒绝了这一点,因为当我们知道该字段的目的是引用项目实体时,这似乎违反直觉。

将 'data_class' 选项设置为 null,隐藏的项目字段包含项目 ID,提交后,在创建的实体上调用 getProject() returns 正确的项目对象。