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 ответа
У меня была похожая проблема, я нашел решение: Symfony - динамические выпадающие списки не работают только при редактировании
Я также столкнулся с подобной проблемой, и при трассировке обнаружил, что это класс EntityType в другом раскрывающемся списке, который вызывает проблему при редактировании формы
Решение состоит в том, чтобы отправить полную форму через ajax вместо одного поля, как в случае новой формы.
Так что меняй
var data = {};
data[$service.attr('name')] = $service.val();
к
var data = $form.serializeArray()
Это должно решить проблему.