Преобразователь данных Symfony2 на скрытом поле

Я искал и не нашел никого с этой проблемой.

Я создал свой собственный Data Transformer, как указано в Cookbook, и все кажется правильным, но я получаю ошибку:

Ожидается, что данные вида формы будут экземпляром класса Niche \ SecurityBundle \ Entity \ BusinessUser, но являются (n) целым числом. Вы можете избежать этой ошибки, установив для параметра «data_class» значение null или добавив трансформатор вида, который преобразует целое число (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, и форма затем загрузилась, но при отправке я получил сообщение об ошибке, что поле было пустым, хотя идентификатор в скрытом поле появился правильно, если была сформирована форма.

Может кто-то указать мне в правильном направлении, так как у него есть что-то очень простое, что я не вижу.

благодаря

UPDATE: я внес некоторые изменения, так как понял, что должен установить компанию в контроллере и передать ее в форму, и все это работает, но когда форма отправлена, я все равно получаю null в базе данных или если я установил поле в @Assert \ NotBlank () форма не будет отправлена, так как поле не может быть пустым, даже если при проверке источника я могу увидеть идентификатор BusinessUser в скрытом поле.

* ОБНОВЛЕНИЕ – При реализации предложения Комы ниже я, наконец, понял, где я поступил не так – трансформатор не возвращал объект *. Если кто-то еще затронет этот вопрос, я бы рекомендовал, чтобы решение Комы было намного лучше, чем создавать каждый раз скрывается. Если это одно решение выше было обновлено, чтобы вернуть объект и должно работать нормально.

Solutions Collecting From Web of "Преобразователь данных Symfony2 на скрытом поле"

Так я обрабатываю объекты со скрытыми входами:

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

 <?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: 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"] 

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

Тип

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

Я считаю, что использование PRE_SET_DATA для установки класса данных на трансформаторе является неприятным, как вы думаете?

Я просто создал скрытый тип ввода:

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

В newAction я заполнил родительский идентификатор:

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

И, наконец, в createAction я поставил:

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

PS: Я понимаю, что вы хотели создать свой Data Transformer, но если ваша проблема остается в объекте с родительским идентификатором, это поможет вам.