EntityType choice_value с составленным первичным ключом и событием setData

Мой пользовательский тип формы основан на EntityType,
Моя сущность имеет составной первичный ключ, поэтому я установил choice_value отразить значения обоих ключевых полей в опциях "value" атрибут, который работает как ожидалось.

Я подписался на formType PRE_SUBMIT событие, чтобы я мог разобрать это значение и преобразовать его обратно в экземпляр сущности спасибо Doctrine\ORM\EntityManager::getReference метод.

Моя проблема возникает при подаче. Я ожидал событие setData($my_retrieved_entity) метод (внутри прослушивателя событий), чтобы успешно заменить представленное значение формы соответствующей сущностью (удовлетворяющей конвейеру проверки формы), но вместо этого я получаю то, что кажется сообщением об ошибке Symfony по умолчанию:

"Это значение недействительно."

    // …
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(['entityManager','country']);
        $resolver->setAllowedTypes('country', Country::class);
        $resolver->setAllowedTypes('entityManager', EntityManager::class);

        $resolver->setDefaults([
            'class' => NetworkTypeModel::class,
            'placeholder' => 'Choose a network type',
            'choice_value' => function ($networkType) {
                // Set specific format for for the value attribute
                // so that it reflects both primary key fiels values
                return $networkType
                    ? "{$networkType->getId()}¤{$networkType->getCountry()->getId()}"
                    : ''
                ;
            }
            ,'query_builder' => function (Options $options) {
                return function (EntityRepository $er) use ($options) {
                    $qb = $er->createQueryBuilder('nt')
                        ->leftJoin('nt.country', 'c')
                        ->addSelect('c');
                    return $qb->where($qb->expr()->eq('nt.country', ':country'))
                        ->setParameter('country', $options['country']->getId())
                        ->orderBy('nt.label', 'ASC');
                };
            },
        ]);
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $entityManager = $options['entityManager'];

        // Listen to post DATA in order to transform Option's value 
        // back to a networkType instance
        $builder->addEventListener(
            FormEvents::PRE_SUBMIT,
            function (FormEvent $event) use ($entityManager) {
                if ($data = $event->getData()) {
                    // Special value has been sent through POST field
                    // It needs to be parsed and transformed back to a networkType entity
                    // According to the format set from inside choice_value options.
                    $ids = explode('¤', $data);
                    $networkType = $entityManager->getReference(
                        NetworkTypeModel::class,
                        ['id' => $ids[0], 'country' => $ids[1]]
                    );
                    // I expect $event->setData to populate the form submitted value
                    // to be the selected value
                    $event->setData($networkType);
                    // But I get the following common error message on that field
                    // «This value is not valid.»
                }
            }
        );
    }

    public function getParent()
    {
        return EntityType::class;
    }

Тем не менее, выполнение dump($networkType) прямо перед $event->setData($networkType); выводит совершенно правильный экземпляр сущности и фактически правильный, связанный с отправленным элементом.

Такое чувство, что я почти все понял правильно, но я понятия не имею, что здесь не так. Любая идея о том, как правильно преобразовать представленные данные в объект, внедрить их в форму и удовлетворить цепочку валидатора?

РЕДАКТИРОВАТЬ

Я выполнил свою работу, но, вероятно, не совсем в стиле Symfony Это может помочь вам понять мою потребность.

Я использовал общую переменную:

  • Населенный изнутри PRE_SUBMIT прослушиватель событий с ожидаемым экземпляром networkType, восстановленный с использованием $event->getData() разобранная строка.
  • Затем потребляется этот экземпляр ссылки внутри POST_SUBMIT событие как FormEvent::setData аргумент.

Изменен / добавлен код:

    $entityManager = $options['entityManager'];
    $selectedNetworkType = null;

    // Transform Option's value back to a networkType instance
    $builder->addEventListener(
        FormEvents::PRE_SUBMIT,
        function (FormEvent $event) use ($entityManager, &$selectedNetworkType) {
            // Transform the custom dropdown "value" attribute coming from POST
            // into a my networkType model instance
            if ($data = $event->getData()) {
                // PRE_SUBMIT event data holds the view data as a string
                // which needs to be parsed accordingly to what have been
                // done when encoding the entity's ids in choice_value callable
                $ids = explode('¤', $data);
                // We don't need to retrieve the entire record from DB anyway
                // so we use getReference
                $networkType = $entityManager->getReference(
                    NetworkTypeModel::class,
                    ['id' => $ids[0], 'country' => $ids[1]]
                );
                // Now store the newly created networkType instance
                // for later
                $selectedNetworkType = $networkType;
            }
        }
    )->addEventListener(
        FormEvents::POST_SUBMIT,
        function (FormEvent $event) use (&$selectedNetworkType) {
            // Use the fresh stored instance to feed the model data
            $selectedNetworkType && $event->setData($selectedNetworkType);
        }
    );

Я надеюсь, что кто-то придумает правильные указания для решения этого варианта использования, для лучшего понимания компонента Symfony Form.
Спасибо.

1 ответ

Насколько я понял, ваша задача состоит в том, чтобы изменить сущность, которую ваша форма собирает из данных формы, непосредственно перед тем, как она будет отправлена ​​обратно клиенту. Вам на самом деле нужно использовать FormEvents::SUBMIT, чтобы сделать это ($event->setData($data) и $event->getData(), которые здесь используются для управления пересобранной сущностью здесь), и что вы делаете, это изменяете массив необработанных данных формы (который вы получите с помощью вызова $event->getData() внутри FormEvents::PRE_SUBMIT, вы можете проверить это, вызвав var_dump() для него) для прокси-объекта, который при отправке формы пытается воссоздать вашу сущность из прокси-сервера (пока он действителен) данные для отправки должны быть предоставлены в виде массива). Также обратите внимание, что вы используете другой экземпляр диспетчера сущностей, кроме самой формы, поэтому ваша сущность, которую вы получите из формы, не будет управляться. Таким образом, лучшим подходом будет изменение измененной сущности, возвращаемое $event->getData() внутри события submit, но я не чувствую, что вы получаете его из-за составного ключа. Также обратите внимание, что вы можете получить данные формы в событии submit, вызвав его таким образом

$event->getForm()->get('id')->getData();

Это было не ясно для меня, когда я впервые столкнулся с такой проблемой, поэтому может быть полезным для вас.

Другие вопросы по тегам