Я рендеринг формы с полем лица в Symfony.
Это хорошо работает, когда я выбираю обычное поле сущности.
$builder
->add('parent','entity',array(
'class' => 'AppBundle:FoodAnalytics\Recipe',
'attr' => array(
'class' => 'hidden'
)
))
Он выбрасывает следующую ошибку, когда я выбираю -> добавить («родитель», «скрытый»):
Ожидается, что данные вида формы будут скалярными, массивными или
экземпляр \ ArrayAccess, но является экземпляром класса
AppBundle \ Entity \ FoodAnalytics \ Рецепт. Вы можете избежать этой ошибки,
установка опции «data_class»
«AppBundle \ Entity \ FoodAnalytics \ Recipe» или добавив представление
трансформатор, который преобразует экземпляр класса
AppBundle \ Entity \ FoodAnalytics \ Рецепт в скаляр, массив или экземпляр
ArrayAccess. 500 Внутренняя ошибка сервера — LogicException
Разве мы не можем иметь скрытые поля сущностей? Почему бы и нет? Обязан ли я добавить другое скрытое поле для получения идентификатора объекта?
РЕДАКТИРОВАТЬ :
По сути, я пытаюсь увлажнить форму перед ее отображением, но не дать пользователю изменить одно из ее полей (родительский элемент здесь).
Это потому, что мне нужно передать Id в качестве параметра, и я не могу сделать это в URL действия формы.
Я думаю, что вы просто запутались в типах полей и в том, что они представляют.
entity
поле является типом choice
поле. Предполагается, что поля выбора содержат значения, выбираемые пользователем в форме. Когда эта форма отображается, Symfony сгенерирует список возможных вариантов на основе базового класса поля сущности, а значением каждого варианта в списке является идентификатор соответствующей сущности. После отправки формы Symfony будет гидрировать объект, представляющий выбранную вами сущность. entity
поле обычно используется для рендеринга ассоциаций объектов (например, список roles
Вы можете выбрать, чтобы назначить user
).
Если вы просто пытаетесь создать заполнитель для поля идентификатора объекта, вы должны использовать hidden
вход. Но это работает только в том случае, если создаваемый вами класс формы представляет сущность (т.е. data_class
относится к сущности, которую вы определили). Поле идентификатора будет затем правильно отображаться на идентификатор объекта типа, определенного формой data_class
,
РЕДАКТИРОВАТЬ: Одним из решений вашей конкретной ситуации, описанной ниже, было бы создание нового типа поля (назовем его EntityHidden), который расширяет hidden
тип поля, но обрабатывает преобразование данных для преобразования в / из объекта / идентификатора. Таким образом, ваша форма будет содержать идентификатор объекта в качестве скрытого поля, но приложение будет иметь доступ к самому объекту после отправки формы. Преобразование, конечно, выполняется преобразователем данных.
Вот пример такой реализации для потомков:
namespace My\Bundle\Form\Extension\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\DataTransformerInterface;
/**
* Entity hidden custom type class definition
*/
class EntityHiddenType extends AbstractType
{
/**
* @var DataTransformerInterface $transformer
*/
private $transformer;
/**
* Constructor
*
* @param DataTransformerInterface $transformer
*/
public function __construct(DataTransformerInterface $transformer)
{
$this->transformer = $transformer;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// attach the specified model transformer for this entity list field
// this will convert data between object and string formats
$builder->addModelTransformer($this->transformer);
}
/**
* @inheritDoc
*/
public function getParent()
{
return 'hidden';
}
/**
* @inheritDoc
*/
public function getName()
{
return 'entityhidden';
}
}
Просто отметьте, что в классе типов формы все, что вам нужно сделать, это назначить скрытую сущность соответствующему свойству поля формы (в рамках модели формы / класса данных), и Symfony сгенерирует скрытый входной HTML-код должным образом с идентификатором сущности как его ценность. Надеюсь, это поможет.
Просто сделал это на Symfony 3 и понял, что это немного отличается от того, что уже было размещено здесь, поэтому я решил, что стоит поделиться.
Я только что сделал общий преобразователь данных, который можно легко использовать во всех типах форм. Вы просто должны передать свой тип формы и все. Нет необходимости создавать пользовательский тип формы.
Прежде всего, давайте посмотрим на преобразователь данных:
<?php
namespace AppBundle\Form;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* Class EntityHiddenTransformer
*
* @package AppBundle\Form
* @author Francesco Casula <[email protected]>
*/
class EntityHiddenTransformer implements DataTransformerInterface
{
/**
* @var ObjectManager
*/
private $objectManager;
/**
* @var string
*/
private $className;
/**
* @var string
*/
private $primaryKey;
/**
* EntityHiddenType constructor.
*
* @param ObjectManager $objectManager
* @param string $className
* @param string $primaryKey
*/
public function __construct(ObjectManager $objectManager, $className, $primaryKey)
{
$this->objectManager = $objectManager;
$this->className = $className;
$this->primaryKey = $primaryKey;
}
/**
* @return ObjectManager
*/
public function getObjectManager()
{
return $this->objectManager;
}
/**
* @return string
*/
public function getClassName()
{
return $this->className;
}
/**
* @return string
*/
public function getPrimaryKey()
{
return $this->primaryKey;
}
/**
* Transforms an object (entity) to a string (number).
*
* @param object|null $entity
*
* @return string
*/
public function transform($entity)
{
if (null === $entity) {
return '';
}
$method = 'get' . ucfirst($this->getPrimaryKey());
// Probably worth throwing an exception if the method doesn't exist
// Note: you can always use reflection to get the PK even though there's no public getter for it
return $entity->$method();
}
/**
* Transforms a string (number) to an object (entity).
*
* @param string $identifier
*
* @return object|null
* @throws TransformationFailedException if object (entity) is not found.
*/
public function reverseTransform($identifier)
{
if (!$identifier) {
return null;
}
$entity = $this->getObjectManager()
->getRepository($this->getClassName())
->find($identifier);
if (null === $entity) {
// causes a validation error
// this message is not shown to the user
// see the invalid_message option
throw new TransformationFailedException(sprintf(
'An entity with ID "%s" does not exist!',
$identifier
));
}
return $entity;
}
}
Таким образом, идея заключается в том, что вы вызываете его, передавая туда менеджер объектов, сущность, которую вы хотите использовать, а затем имя поля, чтобы получить идентификатор сущности.
В основном так:
new EntityHiddenTransformer(
$this->getObjectManager(),
Article::class, // in your case this would be FoodAnalytics\Recipe::class
'articleId' // I guess this for you would be recipeId?
)
Давайте соединим все это сейчас. Нам просто нужен тип формы и немного конфигурации YAML, и тогда все готово.
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Article;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class JustAFormType
*
* @package AppBundle\CmsBundle\Form
* @author Francesco Casula <[email protected]>
*/
class JustAFormType extends AbstractType
{
/**
* @var ObjectManager
*/
private $objectManager;
/**
* JustAFormType constructor.
*
* @param ObjectManager $objectManager
*/
public function __construct(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @return ObjectManager
*/
public function getObjectManager()
{
return $this->objectManager;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('article', HiddenType::class)
->add('save', SubmitType::class);
$builder
->get('article')
->addModelTransformer(new EntityHiddenTransformer(
$this->getObjectManager(),
Article::class,
'articleId'
));
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\MyEntity',
]);
}
}
И тогда в вашем services.yml
файл:
app.form.type.article:
class: AppBundle\Form\JustAFormType
arguments: ["@doctrine.orm.entity_manager"]
tags:
- { name: form.type }
И в вашем контроллере:
$form = $this->createForm(JustAFormType::class, new MyEntity());
$form->handleRequest($request);
Это оно 🙂
Это может быть достигнуто довольно чисто с тематикой формы, используя стандартные hidden
полевая тема вместо той для сущности. Я думаю, что использование трансформаторов, вероятно, излишне, учитывая, что скрытые и выбранные поля будут иметь одинаковый формат.
{% block _recipe_parent_widget %}
{%- set type = 'hidden' -%}
{{ block('form_widget_simple') }}
{% endblock %}
Быстрое решение без создания новых классов-преобразователей и типов. Когда вы хотите предварительно заполнить связанный объект из БД.
// Hidden selected single group
$builder->add('idGroup', 'entity', array(
'label' => false,
'class' => 'MyVendorCoreBundle:Group',
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('c');
return $qb->where($qb->expr()->eq('c.groupid', $this->groupId()));
},
'attr' => array(
'class' => 'hidden'
)
));
Это приводит к одному скрытому выбору, как:
<select id="mytool_idGroup" name="mytool[idGroup]" class="hidden">
<option value="1">MyGroup</option>
</select>
Но да, я согласен, что немного больше усилий с помощью DataTransformer
Вы можете достичь чего-то вроде:
<input type="hidden" value="1" id="mytool_idGroup" name="mytool[idGroup]"/>