Séb*_*ien 16 php entity formbuilder symfony
我正在使用symfony中的实体字段渲染表单.
当我选择常规实体字段时,它运行良好.
$builder
->add('parent','entity',array(
'class' => 'AppBundle:FoodAnalytics\Recipe',
'attr' => array(
'class' => 'hidden'
)
))
Run Code Online (Sandbox Code Playgroud)
当我选择 - > add('parent','hidden')时会抛出以下错误:
表单的视图数据应该是标量,数组或\ ArrayAccess的实例,但是类AppBundle\Entity\FoodAnalytics\Recipe的实例.您可以通过将"data_class"选项设置为"AppBundle\Entity\FoodAnalytics\Recipe"或添加视图转换器来将类AppBundle\Entity\FoodAnalytics\Recipe的实例转换为标量,数组或实例来避免此错误ArrayAccess接口.500内部服务器错误 - LogicException
我们不能有隐藏的实体字段吗?为什么不?我是否有义务将另一个隐藏字段用于检索实体ID?
编辑:
基本上,我正在尝试做的是在显示它之前水合表单,但阻止用户更改其中一个字段(这里的父级).这是因为我需要将Id作为参数传递,我不能在表单操作URL中执行此操作.
Hai*_*ian 17
我认为你只是对字段类型以及它们各自代表的内容感到困惑.
一个entity字段是一个类型的choice字段.选择字段旨在包含用户在表单中可选择的值.呈现此表单时,Symfony将根据实体字段的基础类生成可能选择的列表,列表中每个选项的值是相应实体的ID.提交表单后,Symfony将为您呈现代表所选实体的对象.该entity字段通常用于呈现实体关联(例如,roles您可以选择分配给a 的列表user).
如果您只是尝试为实体的ID字段创建占位符,那么您将使用hidden输入.但这只适用于您正在创建的表单类表示实体(即表单是data_class指您已定义的实体).然后,ID字段将正确映射到由表单定义的类型的实体的ID data_class.
编辑:下面描述的特定情况的一种解决方案是创建一个新的字段类型(让我们称之为EntityHidden)扩展hidden字段类型,但处理数据转换以转换为/从实体/ id.这样,您的表单将包含实体ID作为隐藏字段,但是一旦提交表单,应用程序就可以访问实体本身.当然,转换由数据转换器执行.
以下是后代的这种实现示例:
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';
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在表单类型类中,您所要做的就是将隐藏的实体分配给其对应的表单字段属性(在表单模型/数据类中),Symfony将使用实体的ID正确生成隐藏的输入HTML它的价值.希望有所帮助.
Fra*_*ula 13
刚刚在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 <fra.casula@gmail.com>
*/
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;
}
}
Run Code Online (Sandbox Code Playgroud)
所以我们的想法是你通过在那里传递对象管理器,你想要使用的实体然后是字段名来获取实体ID来调用它.
基本上是这样的:
new EntityHiddenTransformer(
$this->getObjectManager(),
Article::class, // in your case this would be FoodAnalytics\Recipe::class
'articleId' // I guess this for you would be recipeId?
)
Run Code Online (Sandbox Code Playgroud)
我们现在就把它们放在一起吧.我们只需要表单类型和一些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 <fra.casula@gmail.com>
*/
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',
]);
}
}
Run Code Online (Sandbox Code Playgroud)
然后在你的services.yml文件中:
app.form.type.article:
class: AppBundle\Form\JustAFormType
arguments: ["@doctrine.orm.entity_manager"]
tags:
- { name: form.type }
Run Code Online (Sandbox Code Playgroud)
在你的控制器中:
$form = $this->createForm(JustAFormType::class, new MyEntity());
$form->handleRequest($request);
Run Code Online (Sandbox Code Playgroud)
而已 :-)
小智 6
在 Symfony 5 中,我使用实现 DataTransformerInterface 接口的隐藏类型的解决方案。
<?php
namespace App\Form\Type;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Defines the custom form field type used to add a hidden entity
*
* See https://symfony.com/doc/current/form/create_custom_field_type.html
*/
class EntityHiddenType extends HiddenType implements DataTransformerInterface
{
/** @var ManagerRegistry $dm */
private $dm;
/** @var string $entityClass */
private $entityClass;
/**
*
* @param ManagerRegistry $doctrine
*/
public function __construct(ManagerRegistry $doctrine)
{
$this->dm = $doctrine;
}
/**
*
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Set class, eg: App\Entity\RuleSet
$this->entityClass = sprintf('App\Entity\%s', ucfirst($builder->getName()));
$builder->addModelTransformer($this);
}
public function transform($data): string
{
// Modified from comments to use instanceof so that base classes or interfaces can be specified
if (null === $data || !$data instanceof $this->entityClass) {
return '';
}
$res = $data->getId();
return $res;
}
public function reverseTransform($data)
{
if (!$data) {
return null;
}
$res = null;
try {
$rep = $this->dm->getRepository($this->entityClass);
$res = $rep->findOneBy(array(
"id" => $data
));
}
catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
if ($res === null) {
throw new TransformationFailedException(sprintf('A %s with id "%s" does not exist!', $this->entityClass, $data));
}
return $res;
}
}
Run Code Online (Sandbox Code Playgroud)
并在表单中使用该字段:
use App\Form\Type\EntityHiddenType;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Field name must match entity class, eg 'ruleSet' for App\Entity\RuleSet
$builder->add('ruleSet', EntityHiddenType::class);
}
Run Code Online (Sandbox Code Playgroud)
这可以通过表单主题相当干净地实现,使用标准hidden字段主题代替实体的主题。我认为使用转换器可能是矫枉过正,因为隐藏和选择字段将提供相同的格式。
{% block _recipe_parent_widget %}
{%- set type = 'hidden' -%}
{{ block('form_widget_simple') }}
{% endblock %}
Run Code Online (Sandbox Code Playgroud)