Symfony вложенные данные карты формы заказа назад - PullRequest
1 голос
/ 23 марта 2020

Я создаю форму заказа со встроенными данными, такими как orderItems (collection) в Symfony 4.5. Мой OrderType выглядит следующим образом:

<?php
class OrderType extends BaseOrderType
{
    /**
     * {@inheritDoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('note', TextareaType::class, array(
            'label' => 'order.note_company',
            'required' => false,
        ));

        $builder->add('orderItems', CollectionType::class, array(
            'entry_type' => OrderItemType::class,
            'entry_options' => array(
                'label' => false
            ),
            'allow_add' => true,
            'prototype_data' => new OrderItem()
        ));
    }

    /**
     * {@inheritDoc}
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(array(
            'data_class' => Order::class,
            'translation_domain' => 'forms',
        ));
    }
}

И для моей встроенной формы OrderItemType

<?php
class OrderItemType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('amount', IntegerType::class, array(
            'label' => 'order.amount',
            'attr' => array(
                'min' => 0,
            ),
            'data' => 1,
            'required' => false,
        ));

        $builder->add('configureProductType', ConfigureProductType::class, array(
            'label' => false,
            'show_submit_btn' => false,
            'required' => false,
        ));
    }

    /**
     * {@inheritDoc}
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(array(
            'data_class' => OrderItem::class,
            'translation_domain' => 'forms',
            'allow_extra_fields' => true
        ));
    }
}

OrderItemType содержит поле configureProductType, которое также является формой с повторно используемыми полями, такими как product, price, и др c. Но как это поле configureProductType может быть сопоставлено с исходным OrderItemType.

Ответы [ 2 ]

0 голосов
/ 24 марта 2020

Проблема в том, что ConfigureProductType использует * _SET_DATA

Позвольте мне поделиться этим кодом с

<?php
class ConfigureProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $product = $options['product'];        
        $orderItem = $options['data'];        

        // Type.
        $builder->add('type', ChoiceType::class, array(
            'label' => 'configure_product.type.label',
            'choices' => ProductHelper::getOrderTypes($product),
            'choice_label' => function($choice, $key, $value) {
                return sprintf('configure_product.type.choices.%s', $choice);
            },
            'constraints' => array(
                new NotBlank(array('message' => 'configure_product.type.not_blank')),
            ),
            'attr' => array(
                'class' => 'configure-product-type'
            ),
            'empty_data' => $orderItem->getType() ?? ProductHelper::TYPE_ORDER_DEFAULT,
            'expanded' => true,
        ));

        $builder->addEventListener(FormEvents::POST_SET_DATA, function(FormEvent $event) use ($options) {
            if (null === $item = $event->getData()) {
                return;
            }

            $form = $event->getForm();

            $form->add('length', IntegerType::class, array(
                'label' => 'configure_product.length',
                'attr' => array(
                    'min' => static::LENGTH_MIN,
                ),
                'empty_data' => '100',
            ));

            $form->add('width', IntegerType::class, array(
                'label' => 'configure_product.width',
                'attr' => array(
                    'min' => static::WIDTH_MIN,
                    'step' => static::WIDTH_STEP,
                ),
                'empty_data' => '100',
            ));
        });
    }

    /**
     * {@inheritDoc}
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(array(
            'data_class' => OrderItem::class,
            'translation_domain' => 'forms',
            'product' => null,
        ));
    }
}

Как вы могли заметить, для ConfigureProduct Type нужны продукт и данные опции элемента заказа, а также * _SET_DATE , Позвольте мне объяснить это еще немного. ConfigureProductType - это форма, используемая в веб-интерфейсе приложения для настройки продукта. На данный момент данные продукта известны в форме.

В бэкэнде мы хотим использовать ту же форму ConfigureProductType, но теперь вам нужно сначала выбрать продукт, затем запрос ajax отправляет форму, чтобы форма знала продукт, но затем, как мы можем использовать повторно ConfigureProductType. Попытка вызвать parent :: buildform в приемнике событий, но он не работает, см. Пример ниже (второй подход с расширением ConfigureProductType)

<?php
class OrderItemType extends ConfigureProductType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('amount', IntegerType::class, array(
            'label' => 'order.amount',
            'attr' => array(
                'min' => 0,
            ),
            'data' => 1,
            'required' => false,
        ));

        $builder->add('configureProductType', ConfigureProductType::class, array(
            'label' => false,
            'show_submit_btn' => false,
            'required' => false,
        ));

        $orderItemModifier = function(FormInterface $form, OrderItem $orderItem, $eventType = null) use ($builder, $options) {
            if(null === $product = $orderItem->getProduct()) {
                return;
            }

            // how to rebuild options with selected product and then call parent
            // parent::buildForm($builder, $options); ??
        };

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($orderItemModifier) {
            $form = $event->getForm();

            if(null === $orderItem = $event->getData()) {
                return;
            }

            $orderItemModifier($form, $orderItem, FormEvents::PRE_SET_DATA);
        });

        $builder->addEventListener(FormEvents::SUBMIT, function(FormEvent $event) use ($orderItemModifier) {
            $form = $event->getForm();

            if(null === $orderItem = $event->getData()) {
                return;
            }

            $orderItemModifier($form, $orderItem, FormEvents::SUBMIT);
        });
    }

    /**
     * {@inheritDoc}
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(array(
            'data_class' => OrderItem::class,
            'translation_domain' => 'forms',
            'allow_extra_fields' => true
        ));
    }
}
0 голосов
/ 24 марта 2020

Вы ищете inherit_data вариант . Документы symfony объясняют это в деталях, но все, что вам нужно, это установить для параметра inherit_data значение true для типа вложенной формы:

// in ConfigureProductType
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'inherit_data' => true,
        'data_class' => OrderItem::class
    ]);
}

Для использования inherit_data есть одно предупреждение. что вам нужно знать. Из symfony документов:

Формы с установленным параметром наследия_данных не могут иметь прослушивателей событий * _SET_DATA.

...