php symfony : 我们不能有一个隐藏的实体字段吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27094901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 23:10:32  来源:igfitidea点击:

symfony : can't we have a hidden entity field?

phpsymfonyentityformbuilder

提问by Sébastien

I am rendering a form with an entity field in symfony.

我正在 symfony 中渲染一个带有实体字段的表单。

It works well when i choose a regular entity field.

当我选择常规实体字段时,它运行良好。

$builder
    ->add('parent','entity',array(
            'class' => 'AppBundle:FoodAnalytics\Recipe',
            'attr' => array(
                'class' => 'hidden'
            )
        ))

It throws the following error when I choose ->add('parent','hidden') :

当我选择 ->add('parent','hidden') 时,它会引发以下错误:

The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class AppBundle\Entity\FoodAnalytics\Recipe. You can avoid this error by setting the "data_class" option to "AppBundle\Entity\FoodAnalytics\Recipe" or by adding a view transformer that transforms an instance of class AppBundle\Entity\FoodAnalytics\Recipe to scalar, array or an instance of \ArrayAccess. 500 Internal Server Error - LogicException

表单的视图数据应该是标量、数组或 \ArrayAccess 的实例类型,但它是类 AppBundle\Entity\FoodAnalytics\Recipe 的实例。您可以通过将“data_class”选项设置为“AppBundle\Entity\FoodAnalytics\Recipe”或添加一个视图转换器将类 AppBundle\Entity\FoodAnalytics\Recipe 的实例转换为标量、数组或\数组访问。500 内部服务器错误 - LogicException

Can't we have hidden entity fields ?? Why not? Am I obliged to put another hidden field to retrieve the entity id?

我们不能有隐藏的实体字段吗??为什么不?我是否必须放置另一个隐藏字段来检索实体 ID?

EDIT :

编辑 :

Basically, what I'm trying to do is to hydrate the form before displaying it but prevent the user to change one of its fields (the parent here). This is because I need to pass the Id as a parameter and I can't do it in the form action url.

基本上,我想要做的是在显示表单之前对其进行水合,但防止用户更改其字段之一(此处为父项)。这是因为我需要将 Id 作为参数传递,而我无法在表单操作 url 中执行此操作。

回答by Leo Bedrosian

I think you are simply confused about the field types and what they each represent.

我认为您只是对字段类型及其各自代表的内容感到困惑。

An entityfield is a type of choicefield. Choice fields are meant to contain values selectable by a user in a form. When this form is rendered, Symfony will generate a list of possible choices based on the underlying class of the entity field, and the value of each choice in the list is the id of the respective entity. Once the form is submitted, Symfony will hydrate an object for you representing the selected entity. The entityfield is typically used for rendering entity associations (like for example a list of rolesyou can select to assign to a user).

一个entity字段是一个类型的choice字段。选择字段旨在包含用户可以在表单中选择的值。当这个表单被渲染时,Symfony 会根据实体字段的底层类生成一个可能的选择列表,列表中每个选择的值是各自实体的 id。提交表单后,Symfony 将为您生成一个代表所选实体的对象。该entity字段通常用于呈现实体关联(例如,roles您可以选择分配给 a的列表user)。

If you are simply trying to create a placeholder for an ID field of an entity, then you would use the hiddeninput. But this only works if the form class you are creating represents an entity (ie the form's data_classrefers to an entity you have defined). The ID field will then properly map to the ID of an entity of the type defined by the form's data_class.

如果您只是尝试为实体的 ID 字段创建占位符,那么您将使用hidden输入。但这只有在您创建的表单类代表一个实体时才有效(即表单data_class引用您定义的实体)。然后,ID 字段将正确映射到由表单的data_class.

EDIT: One solution to your particular situation described below would be to create a new field type (let's call it EntityHidden) that extends the hiddenfield type but handles data transformation for converting to/from an entity/id. In this way, your form will contain the entity ID as a hidden field, but the application will have access to the entity itself once the form is submitted. The conversion, of course, is performed by the data transformer.

编辑:下面描述的针对您的特定情况的一种解决方案是创建一个新的字段类型(我们称之为 EntityHidden),它扩展hidden字段类型但处理数据转换以转换为实体/id 或从实体/id 转换。这样,您的表单将包含实体 ID 作为隐藏字段,但一旦提交表单,应用程序将有权访问实体本身。当然,转换是由数据转换器执行的。

Here is an example of such an implementation, for posterity:

以下是此类实现的示例,供后代使用:

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

Just note that in the form type class, all you have to do is assign your hidden entity to its corresponding form field property (within the form model/data class) and Symfony will generate the hidden input HTML properly with the ID of the entity as its value. Hope that helps.

请注意,在表单类型类中,您所要做的就是将隐藏实体分配给其相应的表单字段属性(在表单模型/数据类中),Symfony 将使用实体的 ID 正确生成隐藏的输入 HTML 为它的价值。希望有帮助。

回答by Francesco Casula

Just made this on Symfony 3and realised it's a bit different from what has already been posted here so I figured it was worth sharing.

刚刚在Symfony 3上做了这个,并意识到它与这里已经发布的有点不同,所以我认为它值得分享。

I just made a generic data transformer that could be easily reusable across all your form types. You just have to pass in your form type and that's it. No need to create a custom form type.

我刚刚制作了一个通用数据转换器,可以轻松地在所有表单类型中重用。你只需要传入你的表单类型,就是这样。无需创建自定义表单类型。

First of all let's take a look at the data transformer:

首先让我们来看看数据转换器:

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

So the idea is that you call it by passing the object manager there, the entity that you want to use and then the field name to get the entity ID.

所以这个想法是你通过在那里传递对象管理器来调用它,你想要使用的实体,然后是字段名称来获取实体 ID。

Basically like this:

基本上是这样的:

new EntityHiddenTransformer(
    $this->getObjectManager(),
    Article::class, // in your case this would be FoodAnalytics\Recipe::class
    'articleId' // I guess this for you would be recipeId?
)

Let's put it all together now. We just need the form type and a bit of YAML configuration and then we're good to go.

现在让我们把它放在一起。我们只需要表单类型和一些 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',
        ]);
    }
}

And then in your services.ymlfile:

然后在您的services.yml文件中:

app.form.type.article:
    class: AppBundle\Form\JustAFormType
    arguments: ["@doctrine.orm.entity_manager"]
    tags:
        - { name: form.type }

And in your controller:

在您的控制器中:

$form = $this->createForm(JustAFormType::class, new MyEntity());
$form->handleRequest($request);

That's it :-)

就是这样 :-)

回答by Ryan

This can be achieved fairly cleanly with form theming, using the standard hiddenfield theme in place of that for the entity. I think using transformers is probably overkill, given that hidden and select fields will give the same format.

这可以通过表单主题相当干净地实现,使用标准hidden字段主题代替实体的主题。我认为使用转换器可能是矫枉过正,因为隐藏和选择字段将提供相同的格式。

{% block _recipe_parent_widget %}
    {%- set type = 'hidden' -%}
    {{ block('form_widget_simple') }}
{% endblock %}

回答by Fabian Picone

A quick solution whitout creating new transformer and type classes. When you want to prepopulate an related entity from the db.

无需创建新转换器和类型类的快速解决方案。当您想从数据库中预填充相关实体时。

// 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'
    )
));

This results a single hidden selection like:

这会导致一个隐藏的选择,如:

<select id="mytool_idGroup" name="mytool[idGroup]" class="hidden">
    <option value="1">MyGroup</option>
</select>

But yes, i agree that with a little more effort by using a DataTransformeryou can achieve something like:

但是,是的,我同意通过使用 a 多一点努力,DataTransformer您可以实现以下目标:

<input type="hidden" value="1" id="mytool_idGroup" name="mytool[idGroup]"/>