带有自定义FormType的Symfony2表单调用DataTransformer,在两个方向上都有相同的数据

时间:2015-11-21 21:44:47

标签: symfony symfony-forms

我制作了一个新的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作为int调用reverseTransform而不是$val ->getId()在非对象上失败。

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

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

尝试设置'data_class' => null,但没有快乐。检查网络选项卡显示发布表单时正确发送的值。

更新1

所以我重新阅读DataTransformer页面并且该图让我思考,特别是在上面的橡皮鸭编程之后,我问实体的形式但是期望它接收整数...所以我实际上需要一个单向变压器,ViewTransformer - >获取要显示的实体,从窗口小部件发布一个int,不要转换它直接通过。哪个有效,我只是得到了#34;无效数据"更新错误。

现在我的变形金刚了:

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

更新2

现在似乎已经修复了它,虽然出于某种原因,如果我在我的表单中发布int 2字符串" 2 /"发送到我的变压器。有什么想法吗?现在我正在清理变压器中的琴弦,但似乎不应该发生。

2 个答案:

答案 0 :(得分:1)

通过我在变换器类中看到的,你没有正确实现代码。这应该是正确的实现:

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,其中包含一个工作示例并从那里进行构建。

答案 1 :(得分:1)

根据我的上一次更新,我意识到,因为我只使用我的表单数据来显示当前保存的实体关系(其余部分由ajax提供)而不是采用相同的格式,表单将在其中接收它导致有些混乱。

要关注tutorials wording

模型数据

这一切都保持原样(无需型号数据变换器)

规范数据

无变化

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

  • 变换()
    • ID to Entity,因此窗口小部件可以访问其他属性
  • ReverseTransform()
    • 发布的ID格式正确,所以我们只返回

代码

非常简化:

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

希望这可以帮助那些让他们的要求混淆的人!