简体   繁体   English

隐藏字段上的Symfony2数据变换器

[英]Symfony2 Data Transformer on hidden field

I've searched and not found anyone with this problem. 我搜索过,没有找到任何有这个问题的人。

I've created my own Data Transformer as set out in the Cookbook and it all seems right but i get the error: 我已经创建了我自己的数据变换器,如Cookbook中所述,这一切似乎都正确但我收到错误:

The form's view data is expected to be an instance of class Niche\\SecurityBundle\\Entity\\BusinessUser, but is a(n) integer. 表单的视图数据应该是Niche \\ SecurityBundle \\ Entity \\ BusinessUser类的实例,但是是(n)整数。 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 Niche\\SecurityBundle\\Entity\\BusinessUser. 您可以通过将“data_class”选项设置为null或添加将(n)整数转换为Niche \\ SecurityBundle \\ Entity \\ BusinessUser实例的视图转换器来避免此错误。

The transformer is below: 变压器如下:

<?php
namespace Niche\SecurityBundle\Form\DataTransformer;

use JMS\SecurityExtraBundle\Security\Util\String;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Niche\SecurityBundle\Entity\BusinessUser;

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

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

/**
 * Transforms an object (BusinessUser) to a string (number)
 * 
 * @param BusinessUser|null $businessUser
 * @return String
 */
public function transform($businessUser)
{
    if (null === $businessUser) {
        return "";
    }

    return $businessUser->getId();
}

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

    $businessUser = $this->om
        ->getRepository('NicheSecurityBundle:BusinessUser')
        ->findOneById($id);

    if (null === $businessUser) {
        throw new TransformationFailedException(sprintf(
                'An issue with number "%s" does not exist!',
                $number
                ));

    }
            return $businessUser
}
}

and my form code is 我的表格代码是

<?php

namespace Niche\JobBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Niche\SecurityBundle\Form\DataTransformer\BusinessUserToIdTransformer;

class JobType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{

    $entityManager = $options['em'];
    $transformer = new BusinessUserToIdTransformer($entityManager);

    //Get the BusinessUser object passed in.
    $businessUser = $options['businessUser'];


    $builder->add('title', 'text');
    $builder->add('jobDescription', 'textarea', array(
            "label" => "Job Description", )
        );
    $builder->add('companyDescription', 'textarea', array(
            "label" => "Company Description", )
        );
    $builder->add('salary', 'text');
    $builder->add('category', 'entity', array(
        'class' => 'NicheJobBundle:Category',
        'property' => 'name',   
    ));
    $builder->add('responsibilities', 'textarea');
    $builder->add('closingDate', 'datetime');
    //Add Business User to the form - Need a way for a job to be added by site admin in addition, could just be site admin users logged in with a Business user account
    $builder->add(
            $builder->create('company', 'hidden', array(
                'data' => $businessUser,

            ))->addViewTransformer($transformer)

    );

}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Niche\JobBundle\Entity\Job'
    ));

    $resolver->setRequired(array(
            'em', 'businessUser'
    ));

    $resolver->setAllowedTypes(array(
        'em' => 'Doctrine\Common\Persistence\ObjectManager',
    ));
}

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

The error message is confusing me as it appears it has converted the BusinessUser class to an integer. 该错误消息让我感到困惑,因为它似乎已将BusinessUser类转换为整数。 I also tried by putting in the data_class => null and the form then loaded but on submission I got an error that the field was empty though the ID in the hidden field appeared correctly hen the form was generated. 我也尝试通过放入data_class => null然后加载表单,但是在提交时我得到一个错误,即字段为空,尽管隐藏字段中的ID在表单生成时正确显示。

Can someone point me in the right direction as have a feeling its something very simple that im not seeing. 有人可以指出我正确的方向,因为感觉它很简单,我没有看到。

Thanks 谢谢

UPDATE: I made some changes as realised I should be setting the company in the controller and passing it into the form, and that all seems to work but when the form is submitted I still get null in the database or if i set the field to @Assert\\NotBlank() the form wont submit as field cannot be blank even though when I check the source I can see the Id of the BusinessUser in the hidden field. 更新:我做了一些更改,因为我意识到我应该在控制器中设置公司并将其传递到表单中,并且一切似乎都有效但是当提交表单时,我仍然在数据库中获取null或者如果我将字段设置为@Assert \\ NotBlank()表单不会提交,因为字段不能为空,即使我检查源我可以在隐藏字段中看到BusinessUser的ID。

* UPDATE - While implemeting Coma's suggestion below I finally realised where I had gone wrong - it was the transformer it was not returning the object * - If anyone else comes onto this question I would recommend Coma's solution below is much better than having to create a hidden each time. * 更新 - 在下面实施Coma的建议时,我终于意识到我出错的地方 - 变压器它没有返回对象* - 如果有人提出这个问题我会建议下面的Coma解决方案比创建一个更好每次隐藏。 If it is a one off the solution above has been updated to return the object and should work fine. 如果它是一个关闭上面的解决方案已更新以返回对象,应该可以正常工作。

This is the way I handle entities with hidden inputs: 这是我处理隐藏输入的实体的方式:

DataTransformer DataTransformer

<?php

namespace Comakai\CQZBundle\Form\DataTransformer;

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

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

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

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

        return $entity->getId();
    }

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

        $entity = $this->om->getRepository($this->entityClass)->findOneById($id);

        if (null === $entity) {

            throw new TransformationFailedException(sprintf(
                    'An entity of class ' . $this->entityClass . ' with id "%s" does not exist!', $id
            ));
        }

        return $entity;
    }

}

FormType FormType

<?php

namespace Comakai\CQZBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Comakai\CQZBundle\Form\DataTransformer\EntityToIdTransformer;

class EntityHiddenType extends AbstractType
{
    /**
     * @var ManagerRegistry
     */
    private $registry;

    /**
     * @var ObjectManager
     */
    private $om;
    private $cache;

    /**
     * @param ObjectManager $om
     */
    public function __construct(ManagerRegistry $registry)
    {
        $this->registry = $registry;
        $this->om = $registry->getManager();
        $this->cache = [];
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $class = (empty($options['data_class'])) ? $this->getClassFromMetadata($builder->getName(), $builder->getParent()->getDataClass()) : $options['data_class'];

        $transformer = new EntityToIdTransformer($this->om, $class);
        $builder->addViewTransformer($transformer);

        $builder->setAttribute('data_class', $class);
    }

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

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

    protected function getClassFromMetadata($name, $parentClass)
    {
        /* @var $md \Doctrine\ORM\Mapping\ClassMetadata */
        $md = $this->getMetadata($parentClass)[0];
        $a = $md->getAssociationMapping($name);
        $class = $a['targetEntity'];

        return $class;
    }

    protected function getMetadata($class)
    {
        if (array_key_exists($class, $this->cache)) {
            return $this->cache[$class];
        }

        $this->cache[$class] = null;
        foreach ($this->registry->getManagers() as $name => $em) {
            try {
                return $this->cache[$class] = array($em->getClassMetadata($class), $name);
            } catch (MappingException $e) {
                // not an entity or mapped super class
            } catch (LegacyMappingException $e) {
                // not an entity or mapped super class, using Doctrine ORM 2.2
            }
        }
    }

}

Config (services.yml) 配置(services.yml)

services:
    cqz.form.type.entity_hidden:
        class: Comakai\CQZBundle\Form\Type\EntityHiddenType
        arguments: ["@doctrine"]
        tags:
                -  { name: form.type, alias: entity_hidden }

JobType 工作类型

$builder->add('company', 'entity_hidden');

Then in your controller 然后在你的控制器中

$job = new \Niche\JobBundle\Entity\Job();
$type = new \Niche\JobBundle\Form\JobType();

$job->setCompany($businessUser);

$form = $this->createForm($type, $job);

This way you'll have a reusable entity_hidden type. 这样,您将拥有可重用的entity_hidden类型。

UPDATE FOR 2.3 更新2.3

Since there is no $builder->getParent() anymore ( https://github.com/symfony/symfony/blob/master/UPGRADE-2.2.md ) and because I don't want to set the class of the field's data, I came up with this (btw, now I'm using the form.type_guesser.doctrine service to get the class): 由于不再有$ builder-> getParent()( https://github.com/symfony/symfony/blob/master/UPGRADE-2.2.md ),因为我不想设置字段数据的类,我想出了这个(顺便说一句,现在我正在使用form.type_guesser.doctrine服务来获取类):

Config 配置

cqz.form.type.suggest:
        class: Comakai\CQZBundle\Form\Type\SuggestType
        arguments: ["@doctrine.orm.entity_manager", "@form.type_guesser.doctrine"]

DataTransformer DataTransformer

<?php

namespace Comakai\CQZBundle\Form\DataTransformer;

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

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

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

    /**
     * Transforms an object to an id.
     *
     * @param  Object|null $object
     * @return mixed
     */
    public function transform($object)
    {
        if (null === $object) {

            return '';
        }

        return $object->getId();
    }

    /**
     * Transforms an id to an object.
     *
     * @param  mixed $id
     *
     * @return Object|null
     *
     * @throws TransformationFailedException if object is not found.
     */
    public function reverseTransform($id)
    {
        if (!$id) {

            return null;
        }

        $object = $this->om
            ->getRepository($this->objectClass)
            ->find($id)
        ;

        if (null === $object) {

            throw new TransformationFailedException(sprintf(
                'An instance of "%s" with id "%s" does not exist!',
                $this->objectClass,
                $id
            ));
        }

        return $object;
    }

    public function getObjectClass()
    {
        return $this->objectClass;
    }

    public function setObjectClass($class)
    {
        $this->objectClass = $class;
    }
}

Type 类型

<?php

namespace Comakai\CQZBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Comakai\CQZBundle\Form\DataTransformer\ObjectToIdTransformer;

use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;

class SuggestType extends AbstractType
{
    /**
     * @var ObjectManager
     */
    private $om;
    private $guesser;

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

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

        if($options['class'] === null) {

            $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($transformer, $builder) {

                /* @var $form \Symfony\Component\Form\Form */
                $form = $event->getForm();
                $class = $form->getParent()->getConfig()->getDataClass();
                $property = $form->getName();
                $guessedType = $this->guesser->guessType($class, $property);
                $options = $guessedType->getOptions();

                $transformer->setObjectClass($options['class']);

            });

        } else {

            $transformer->setObjectClass($options['class']);

        }
    }
    ...

I feel that using a PRE_SET_DATA to set the data class on the transformer is nasty, what do you think? 我觉得使用PRE_SET_DATA来设置变压器上的数据类很讨厌,你怎么看?

I just created an input type hidden: 我刚刚创建了一个隐藏的输入类型:

$builder->add('child_id', 'hidden', array('mapped' => false))

In the newAction I did fill with the parent id: 在newAction中,我确实填写了父ID:

$childForm->get('parent_id')->setData($parentEntity->getId());

And finally in createAction I did put: 最后在createAction中我做了:

$child->setParent($em->getReference('MyBundle:Parent', $form["child_id"]->getData()))

PS: I understand that you wanted to create your Data Transformer, but if your problem is persist object with parent id, it will help you. PS:我知道你想创建你的数据转换器,但如果你的问题是带有父ID的持久对象,它会对你有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM