symfony: разве мы не можем иметь скрытое поле сущности?

Я рендеринг формы с полем сущности в symfony.

Он хорошо работает, когда я выбираю регулярное поле сущности.

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

он выдает следующую ошибку, когда я выбираю - >add ('parent', 'hidden'):

ожидается, что данные представления формы будут скалярного типа, массива или экземпляр ArrayAccess, но является экземпляром класса AppBundleEntityFoodAnalyticsРецепт. Вы можете избежать этой ошибки установка параметра "data_class для формы" для "AppBundleEntityFoodAnalyticsRecipe" или путем добавления представления трансформатор, преобразующий экземпляр класса AppBundleEntityFoodAnalyticsRecipe для скаляра, массива или экземпляра из интерфейса ArrayAccess. 500 Внутренняя Ошибка Сервера-LogicException

мы не можем иметь скрытые поля ?? Почему бы и нет? Обязан ли я поместить другое скрытое поле для получения идентификатора сущности?

изменить :

в основном, то, что я пытаюсь сделать, это увлажнить форму до отображение его, но запретить пользователю изменять одно из его полей (родитель здесь). Это потому, что мне нужно передать идентификатор в качестве параметра, и я не могу сделать это в виде URL-адреса действия.

4 ответов


Я думаю, вы просто смущены типами полей и тем, что они представляют.

An


только что сделал это на 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;
    }
}

Итак, идея в том, что вы звоните это путем передачи диспетчера объектов, сущности, которую вы хотите использовать, а затем имени поля для получения идентификатора сущности.

в основном так:

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

и затем в :

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

вот именно :-)


быстрое решение whitout создание новых классов трансформатора и типа. Если вы хотите предварительно заполнить Связанный объект из БД.

// 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]"/>

этого можно достигнуть справедливо чисто с theming формы, используя стандарт hidden тема поля вместо этого для сущности. Я думаю, что использование трансформаторов, вероятно, излишне, учитывая, что скрытые и выбранные поля дадут тот же формат.

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