具有自定义 FormType 的 Symfony2 Form 在两个方向上调用具有相同数据的 DataTransformer

Symfony2 Form with custom FormType calls DataTransformer with same data in both directions

我创建了一个新的 FormType,它通过

扩展了 entity 类型
//...
public function getParent()
{
    return 'entity';
}

这导致我的编辑表单抱怨整数不是 My/Entity/Type,我需要一个数据转换器。所以我创造了一个。这是精简版(只是基础教程版)

//...
public function reverseTransform($val)
{
    // Entity to int
    return $val->getId();
}
public function transform($val)
{
    // Int to Entity
    return $repo->findOneBy($val);
}
//...

然后将其添加到我的表单类型中

//...
public function buildForm(FormBuilderInterface $builder, array $options)                                                          
{                                                                                                                                 
    $builder->addViewTransformer(new IdToMyModelTransformer($this->em));                                                            
}    

这修复了我查看我的表单的问题,但现在当我使用从我的自定义小部件中选择的实体提交表单时,它会尝试调用 transform 而不是 reverseTransform 并使用 $val 作为->getId() 的 int 在非对象上失败。

我想不出正确的方法。如果我使用 'choice' 作为我的小部件父级,我会遇到一组不同的问题(触发选择默认约束说它是无效数据?)

我需要一个实体传递给我的小部件,以便它可以提取元数据进行显示,但我当然不能 post 一个实体返回。我该如何告诉表格?

尝试设置 'data_class' => null 但没有成功。检查网络选项卡显示 post 填写表单时值已正确发送。

更新 1

所以我重新阅读了 DataTransformer 页面,那个图表让我开始思考,尤其是在上面的橡皮鸭编程之后,我询问实体的表单,但希望它接收整数..所以我实际上需要一个单向转换器,ViewTransformer -> 获取显示实体,从小部件中获取 posted 一个 int,不转换它 直接通过。哪个有效,我只是在更新时收到 "invalid data" 错误。

现在我的变形金刚中有:

public function transform($val)
{
    // Int to Entity
    return $repo->findOneBy($val);
}
public function reverseTransform($val)
{
    // Do nothing
    return $val;
}

更新 2

现在似乎已经修复了它,尽管出于某种原因,如果我在我的表单中 post int 2 字符串“2/”被发送到我的转换器。对此有什么想法吗?现在我正在清理变压器中的字符串,但似乎它不应该发生。

根据我在你的转换器中看到的情况 class 你没有正确实现代码。这应该是正确的实现:

namespace App\YourBundle\Form\DataTransformer;

use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class IdToMyModelTransformer implements DataTransformerInterface
{

    /**
     * @var EntityManager
     */
    private $em;

    /**
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    /**
     * Transforms a value from the original representation to a transformed representation.
     *
     * This method is called on two occasions inside a form field:
     *
     * 1. When the form field is initialized with the data attached from the datasource (object or array).
     * 2. When data from a request is submitted using {@link Form::submit()} to transform the new input data
     *    back into the renderable format. For example if you have a date field and submit '2009-10-10'
     *    you might accept this value because its easily parsed, but the transformer still writes back
     *    "2009/10/10" onto the form field (for further displaying or other purposes).
     *
     * This method must be able to deal with empty values. Usually this will
     * be NULL, but depending on your implementation other empty values are
     * possible as well (such as empty strings). The reasoning behind this is
     * that value transformers must be chainable. If the transform() method
     * of the first value transformer outputs NULL, the second value transformer
     * must be able to process that value.
     *
     * By convention, transform() should return an empty string if NULL is
     * passed.
     *
     * @param mixed $object The value in the original representation
     *
     * @return mixed The value in the transformed representation
     *
     * @throws TransformationFailedException When the transformation fails.
     */
    public function transform($object) {
        if (null === $object) {
            return null;
        }

        return $object->getId();
    }

    /**
     * Transforms a value from the transformed representation to its original
     * representation.
     *
     * This method is called when {@link Form::submit()} is called to transform the requests tainted data
     * into an acceptable format for your data processing/model layer.
     *
     * This method must be able to deal with empty values. Usually this will
     * be an empty string, but depending on your implementation other empty
     * values are possible as well (such as empty strings). The reasoning behind
     * this is that value transformers must be chainable. If the
     * reverseTransform() method of the first value transformer outputs an
     * empty string, the second value transformer must be able to process that
     * value.
     *
     * By convention, reverseTransform() should return NULL if an empty string
     * is passed.
     *
     * @param mixed $categoryId The value in the transformed representation
     *
     * @return mixed The value in the original representation
     *
     * @throws TransformationFailedException When the transformation fails.
     */
    public function reverseTransform($id) {

        if (!$id || $id <= 0) {
            return null;
        }

        if(!ctype_digit($id)){
            throw new TransformationFailedException();
        }

        $repo = $this->em->getRepository('...');
        $result = $repo->findOneBy(array('id' => $id));

        if (null === $result) {
            throw new TransformationFailedException(
                sprintf(
                    'Entity with id does not exist!',
                    $id
                )
            );
        }

        return $result;
    }
}

IdToMyIntType 中你会得到这样的东西:

namespace App\YourBundle\Form\Type;

use App\YourBundle\Form\DataTransformer\IdToMyModelTransformer ;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;


class IdToMyModelType extends AbstractType {

    /**
     * @var EntityManager
     */
    private $em;

    /**
     * @param EntityManager $em
     */
    public function __construct( EntityManager $em ) {
        $this->em = $em;
    }

    public function buildForm( FormBuilderInterface $builder, array $options ) {
        $transformer = new IdToMyModelTransformer ( $this->em );
        $builder->addModelTransformer( $transformer );
    }

    public function setDefaultOptions( OptionsResolverInterface $resolver ) {
        $resolver->setDefaults(array('invalid_message' => 'Something went wrong message.'));
    }

    public function getParent() {
        return 'entity';
    }

    public function getName() {
        return 'id_to_model_type';
    }
}

我建议您查看 DataTransformerInterface 并阅读有关方法的文档。它将简要说明该方法的预期用途。此外,如果您在实施它时遇到问题,您可以随时查看 official documentation,其中包含一个工作示例并从那里构建。

根据我最近的更新,我意识到,因为我只使用我的表单数据来显示当前保存的实体关系(其余部分由 ajax 提供)并且与表单的格式不同收到它会导致一些混乱。

关注tutorials wording

模型数据

这一切都保持原样(不需要模型数据转换器)

规范数据

无变化

查看数据(需要单向转换)

  • 变换()
    • 实体的 ID,以便小部件可以访问其他属性
  • 反向变换()
    • 发布的 ID 格式正确,所以我们 return 它

代码

非常简单:

private $om;
public function __construct (ObjectManager om)
{
    $this->om = $om;
}

public function transform($val)
{
    // Int to Entity
    return $om->getRepository('MyBundle:EntityName')->findOneBy($val);
}

public function reverseTransform($val)
{
    // Do nothing
    return $val;
}

希望这能帮助那些让自己的要求感到困惑的其他人!