3

我已经搜索并没有找到有这个问题的人。

我已经按照 Cookbook 中的说明创建了自己的数据转换器,看起来一切正常,但出现错误:

表单的视图数据应该是 Niche\SecurityBundle\Entity\BusinessUser 类的实例,但它是一个 (n) 整数。您可以通过将“data_class”选项设置为 null 或添加将 a(n) 整数转换为 Niche\SecurityBundle\Entity\BusinessUser 实例的视图转换器来避免此错误。

变压器如下:

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

我的表单代码是

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

该错误消息使我感到困惑,因为它似乎已将 BusinessUser 类转换为整数。我还尝试输入 data_class => null 然后加载表单,但是在提交时我收到一个错误,即该字段为空,尽管在生成表单后隐藏字段中的 ID 正确显示。

有人能指出我正确的方向吗,因为有一种我没有看到的非常简单的感觉。

谢谢

更新:我做了一些更改,因为意识到我应该在控制器中设置公司并将其传递到表单中,这一切似乎都有效,但是当提交表单时,我仍然在数据库中得到 null 或者如果我将字段设置为@Assert\NotBlank() 表单不会提交,因为字段不能为空,即使当我检查源时我可以在隐藏字段中看到 BusinessUser 的 Id。

*更新 - 在下面实施 Coma 的建议时,我终于意识到我哪里出错了 - 它是变压器,它没有返回对象 * - 如果其他人遇到这个问题,我会推荐以下 Coma 的解决方案比必须创建一个要好得多每次都隐藏。如果它是一个单独的解决方案,上面的解决方案已经更新为返回对象并且应该可以正常工作。

4

2 回答 2

7

这是我处理具有隐藏输入的实体的方式:

数据转换器

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

}

表单类型

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

}

配置(services.yml)

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

工作类型

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

然后在你的控制器中

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

$job->setCompany($businessUser);

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

这样,您将拥有一个可重用的 entity_hidden 类型。

2.3 更新

由于不再有 $builder->getParent() (https://github.com/symfony/symfony/blob/master/UPGRADE-2.2.md)并且因为我不想设置字段数据的类,我想出了这个(顺便说一句,现在我正在使用 form.type_guesser.doctrine 服务来获取课程):

配置

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

数据转换器

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

类型

<?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']);

        }
    }
    ...

我觉得在transformer上使用一个PRE_SET_DATA来设置数据类很讨厌,你怎么看?

于 2013-06-03T21:14:35.897 回答
1

我刚刚创建了一个隐藏的输入类型:

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

在 newAction 中,我确实填写了父 ID:

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

最后在 createAction 中我确实放了:

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

PS:我知道你想创建你的数据转换器,但如果你的问题是带有父 ID 的持久对象,它会帮助你。

于 2013-09-17T14:42:33.450 回答