I am having trouble inserting/editing an entity with MM relationships between tables. I have the bidirectional pregnant-risk and pregnant-risk_factor relationships. This is the action code to display and register a pregnant:
public function captarAction(Request $request)
{
if ($request->isXmlHttpRequest())
{
$idEstructura = $request->get('cmfId', 0);
$em = $this->getDoctrine()->getManager();
$estructuraOrganizativa = $em->getRepository('AppBundle:EstructuraOrganizativa')->findOneJoinTipoEstructuraOrganizativa($idEstructura);
if (!is_object($estructuraOrganizativa) || $estructuraOrganizativa->getTipoEstructuraOrganizativa()->getId() !== 6)
{
return new Response("Seleccione el CMF al que pertenece la embarazada.", 404);
}
$nuevaEmbarazada = new Embarazada();
$nuevaEmbarazada->setEstructuraOrganizativa($estructuraOrganizativa);
$form = $this->createForm(EmbarazadaType::class, $nuevaEmbarazada, array(
'action' => $this->generateUrl('embarazadas_captar', array('cmfId' => $estructuraOrganizativa->getId())),
'method' => 'POST',
));
if ($request->getMethod() === 'POST')
{
$form->handleRequest($request);
if ($form->isValid())
{
$em->persist($nuevaEmbarazada);
$em->flush();
return $this->redirect($this->generateUrl('embarazadas_captar', array('cmfId' => $estructuraOrganizativa->getId())));
} else
{
return new Response($this->renderView('AppBundle:Embarazadas:frmCaptarEmbarazada.html.twig', array('form' => $form->createView())), 406);
}
}
return $this->render('AppBundle:Embarazadas:frmCaptarEmbarazada.html.twig', array('form' => $form->createView(), 'estructura' => $estructuraOrganizativa));
} else
{
throw $this->createNotFoundException("Recurso no encontrado");
}
}
Exactly what is happening to me is that the form always gives invalid but no error associated with its fields is displayed, and no error is recorded in the dev.log, only Debug type messages. When giving always invalid because the$em->persist($nuevaEmbarazada);
Anh, the Pregnant entity has the remove
, add
and methods get
of the risk and risk_factor collections set.
If I show the form ready to edit a pregnant record, the selects (expanded in the form of a checkbox) with the associated risks and risk factors checked are correctly populated, but when trying to update the browser's debugging bar it returns a 406 error with invalid form.
What could be misfocusing when managing this type of relationship?
It turns out that checking the Entity Embarazada
I think one of the setter methods is failing:
public function setFechaUltimaMenstruacion($fechaUltimaMenstruacion)
{
if (($fechaUltimaMenstruacion instanceof \DateTime) == false)
{
$fum = new \DateTime($fechaUltimaMenstruacion);
//$fum = \DateTime::createFromFormat('d-m-Y', $fechaUltimaMenstruacion);
$this->fechaUltimaMenstruacion = $fum;
} else
{
$this->fechaUltimaMenstruacion = $fechaUltimaMenstruacion;
}
$fecha = clone $this->fechaUltimaMenstruacion;
$fpp = new \DateTime($fecha->format('Y-m-d'));
$fpp->modify('+280 days');
$this->setFpp($fpp);
return $this;
}
and in the case of setFpp($ffp)
:
/**
* Establece la posible fecha de parto de la embarazada basado en la fecha de ultima menstruacion +280 días.
*/
public function setFpp($fpp)
{
if (($fpp instanceof \DateTime) == false)
{
$fecha = \DateTime::createFromFormat("Y-m-d", $fpp);
$this->fpp = $fecha;
} else
{
$this->fpp = $fpp;
}
return $this;
}
Now then, why all this entanglement in these two methods? It turns out that the form control that sets these dates is a Text type intput to which I associate a bootstrap-datepicker, so I introduce these two fields in the form within a type event FormEvents::PRE_SET_DATA
so that if the form is edit, display the dates correctly within the input. And because of this I realized that it is getting setFechaUltminaMenstruacion
a date value in format string
and not \DateTime
, and that is where the change I made in the set methods of the entity is failing. anyway I expose the code of EmbarazadaType as requested by user @Muriano
class EmbarazadaType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nombre', TextType::class, array('label' => 'Nombre y Apellidos', 'attr' => array('class' => 'col-sm-8'), 'label_attr' => array('class' => 'col-sm-4',)))
//->add('fechaNacimiento', TextType::class, array('label' => 'Fecha de Nacimiento'))
->add('numeroIdentidad', TextType::class, array('label' => 'Número de Identidad'))
->add('direccionParticular', TextareaType::class, array('label' => 'Dirección Particular'))
->add('telefono1', TextType::class, array('label' => 'Teléfono 1'))
->add('telefono2', TextType::class, array('label' => 'Teléfono 2'))
->add('centroDeTrabajo', TextType::class, array('label' => 'Centro de Trabajo'))
//->add('fechaUltimaMenstruacion', TextType::class, array('label' => 'F.U.M'))
->add('fumConfiable')
//->add('estructuraOrganizativa', HiddenType::class)
->add('factoresDeRiesgos', EntityType::class, array(
'label' => false,
'class' => 'AppBundle:FactorDeRiesgo',
'multiple' => true,
'expanded' => true
))
->add('riesgos', EntityType::class, array(
'label' => false,
'class' => 'AppBundle:Riesgo',
'multiple' => true,
'expanded' => true,
))
;
$builder->addEventListener(
FormEvents::PRE_SET_DATA, function (FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
if ($data->getId() === null)
{
$form->add('fechaNacimiento', TextType::class, array('label' => 'Fecha de Nacimiento'));
$form->add('fechaUltimaMenstruacion', TextType::class, array('label' => 'F.U.M'));
} else
{
$form->add('fechaNacimiento', TextType::class, array(
'label' => 'Fecha de Nacimiento',
'data' => $data->getFechaNacimiento()->format('d/m/Y')
));
$form->add('fechaUltimaMenstruacion', TextType::class, array(
'label' => 'F.U.M',
'data' => $data->getFechaUltimaMenstruacion()->format('d/m/Y')
));
}
});
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Embarazada'
));
}
}
In the way that the setter of the Pregnant entity has been left to me, the transaction for the insert is already started but the statement is not executed because there is an error when mapping the Date data type. This is what appears in the dev.log:
doctrine.DEBUG: "START TRANSACTION" [] []
request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalErrorException: "Error: Call to a member function format() on string" at C:\xampp\htdocs\pami2.local\vendor\doctrine\dbal\lib\Doctrine\DBAL\Types\DateType.php line 53 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalErrorException(code: 0): Error: Call to a member function format() on string at C:\\xampp\\htdocs\\pami2.local\\vendor\\doctrine\\dbal\\lib\\Doctrine\\DBAL\\Types\\DateType.php:53)"} []
Expanding the information:
It turns out that the problem is in the way I try to handle the fields of the form referring to DateLastMenstruation and DateBirth, being properties of type \DateTime
I declared those fields as DateTimeType, outside the event and left the normal Pregnant entity setter, that is, a simple variable assignment.
in it, captarAction
before persisting the new entity, I calculated the LastMenstruation Date, and that way the action works perfectly.
Now, in this way, the form is not very usable, and everything starts at the beginning, that is, how can I handle the dates in the form as a string of characters when the associated entity is understood to be \DateTime. All this to be able to use a bootstrap-datepicker control.
Well, in the end it was very simple, I was simply wrong in the approach that I had given to the implementation of the form: I put the widgets to capture the dates of type DateType, add options to it so that they behave like a text, and in this way associate it the bootstrap-datepicker correctly. and in the action then simply make a call to
$em->persist($nuevaEmbarazada).
the setters of the Pregnant class should not change them. And no event was needed in the form class, in the end, buildForm was left like this: