Symfony 2: динамическое событие формы возвращает InvalidArgumentException только при редактировании

Я получил объект под названием "Деятельность", который определяет связь между еще двумя объектами: "Сервис" и "Местоположение".

И "Сервис", и "Местоположение" используют другую сущность, называемую "Распределение", чтобы определить, какие сервисы можно использовать в конкретном месте.

Когда я создаю новую активность, после выбора службы я хочу обновить поле выбора местоположения со значениями, определенными распределением.

Я следовал документации Symfony, чтобы создать это поле выбора в зависимости от местоположения.

Динамическая модификация формы

Все отлично работает при создании / создании новой формы, но когда я пытаюсь отредактировать значение поля службы в уже созданной Деятельности, поле местоположения не обновляется, и профилировщик Symfony показывает мне следующее сообщение:

Uncaught PHP Exception Symfony \ Component \ PropertyAccess \ Exception \ InvalidArgumentException: "Ожидаемый аргумент типа"AppBundle\Entity\Location", "NULL"задан" в F:\xampp\htdocs\gcd\vendor\symfony\symfony\src\Symfony\Component\PropertyAccess\PropertyAccessor.php строка 253 Контекст: { "исключение": "Объект (Symfony\Component\PropertyAccess\Exception\InvalidArgumentException)" }

Это раздел моей деятельности

    /**
 * Activity
 *
 * @ORM\Table(name="activity")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ActivityRepository")
 */
class Activity
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var Service
     *
     * @ORM\ManyToOne(targetEntity="Service", fetch="EAGER")
     * @ORM\JoinColumn(name="service_id", referencedColumnName="id", nullable=false)
     */
    private $service;

    /**
     * @var Location
     *
     * @ORM\ManyToOne(targetEntity="Location", fetch="EAGER")
     * @ORM\JoinColumn(name="location_id", referencedColumnName="id", nullable=false)
     */
    private $location;

Мой контроллер.

/**
 * Creates a new Activity entity.
 *
 * @Route("/new", name="core_admin_activity_new")
 * @Method({"GET", "POST"})
 */
public function newAction(Request $request)
{
    $activity = new Activity();
    $form = $this->createForm('AppBundle\Form\ActivityType', $activity);
    $form->handleRequest($request);

    if($form->isSubmitted() && $form->isValid()){

        $locationAvailable = $this->isLocationAvailable($activity);
        $activityOverlap = $this->hasOverlap($activity);

        if($locationAvailable && !$activityOverlap){
            $em = $this->getDoctrine()->getManager();
            $em->persist($activity);
            $em->flush();

            return $this->redirectToRoute('core_admin_activity_show', array('id' => $activity->getId()));
        }
    }

    return $this->render('activity/new.html.twig', array(
        'activity' => $activity,
        'form' => $form->createView(),
    ));
}


/**
 * Displays a form to edit an existing Activity entity.
 *
 * @Route("/{id}/edit", name="core_admin_activity_edit")
 * @Method({"GET", "POST"})
 */
public function editAction(Request $request, Activity $activity)
{
    $deleteForm = $this->createDeleteForm($activity);
    $editForm = $this->createForm('AppBundle\Form\ActivityType', $activity);
    $editForm->handleRequest($request);

    if ($editForm->isSubmitted() && $editForm->isValid()) {

        $locationAvailable = $this->isLocationAvailable($activity);
        $activityOverlap = $this->hasOverlap($activity);

        if($locationAvailable && !$activityOverlap){
            $em = $this->getDoctrine()->getManager();
            $em->persist($activity);
            $em->flush();

            return $this->redirectToRoute('core_admin_activity_show', array('id' => $activity->getId()));
        }
    }

    return $this->render('activity/edit.html.twig', array(
        'activity' => $activity,
        'edit_form' => $editForm->createView(),
        'delete_form' => $deleteForm->createView(),
    ));
}

Мой FormType

class ActivityType extends AbstractType

{

private $em;

public function __construct(EntityManager $entityManager)
{
    $this->em = $entityManager;
}

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('service', EntityType::class, array(
            'class' => 'AppBundle:Service',
            'placeholder' => 'elige servicio',
        ))
        ->add('location', EntityType::class, array(
            'class' => 'AppBundle:Location',
            'choices' => array(),
                    ))
        ->add('name')
        ->add('virtual')
        ->add('customerSeats')
        ->add('customerVacants')
        ->add('employeeSeats')
        ->add('firstDate', 'date')
        ->add('lastDate', 'date')
        ->add('weekday')
        ->add('beginTime', 'time')
        ->add('endTime', 'time')
        ->add('admissionType')
        ->add('status');



    $formModifier = function (FormInterface $form, Service $service = null) {
        $locations = null === $service ? array() : $this->em->getRepository('AppBundle:Allocation')->findLocationsByService($service);

        $form->add('location', EntityType::class, array(
            'class' => 'AppBundle:Location',
            'choices' => $locations,
        ));
    };


    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function (FormEvent $event) use ($formModifier) {
            $data = $event->getData();
            $formModifier($event->getForm(), $data->getService());
        }
    );

    $builder->get('service')->addEventListener(
        FormEvents::POST_SUBMIT,
        function (FormEvent $event) use ($formModifier) {
            // It's important here to fetch $event->getForm()->getData(), as
            // $event->getData() will get you the client data (that is, the ID)
            $service = $event->getForm()->getData();

            // since we've added the listener to the child, we'll have to pass on
            // the parent to the callback functions!
            $formModifier($event->getForm()->getParent(), $service);
        }
    );
}

/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Activity'
    ));
}

}

функция javaScript

<script>
    var $service = $('#activity_service');
    // When sport gets selected ...
    $service.change(function() {
        // ... retrieve the corresponding form.
        var $form = $(this).closest('form');
        // Simulate form data, but only include the selected service value.
        var data = {};
        data[$service.attr('name')] = $service.val();
        // Submit data via AJAX to the form's action path.
        $.ajax({
            url : $form.attr('action'),
            type: $form.attr('method'),
            data : data,
            success: function(html) {
                // Replace current position field ...
                $('#activity_location').replaceWith(
                        // ... with the returned one from the AJAX response.
                        $(html).find('#activity_location')
                );
            }
        });
    });
</script>

Любая помощь будет отличной, спасибо.

2 ответа

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

Решение состоит в том, чтобы отправить полную форму через ajax вместо одного поля, как в случае новой формы.

Так что меняй

var data = {};
data[$service.attr('name')] = $service.val();

к

var data = $form.serializeArray()

Это должно решить проблему.

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