Reputation: 73
I have 2 entities : Doctor & Timetable where one doctor has many timetables(OneToMany) and many timetables have one doctor(ManyToOne).I want to create many timetables from Doctor Create form Screen. So, i followed the article embedded form collection
Everything is working fine except that I am getting the doctors dropdown on timetable create session which means I can only create timetable for existing doctors. I know that the doctors dropdown came from ManyToOne relationships.
But for my case, I need to create timetable for current doctor(the one that gonna create on the screen) without choosing the dropdown of existing doctors. To tell the logic what I want with pseudo codes:
//Doctor Create Action
public function createAction(Request $request)
{
$entity = new Doctor();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
// Save(Persist) Doctor
// Grab Newly Created Doctor ID
// Create Timetables with that Doctor ID
// done
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
I have read all similar questions but no solution for me. Please anyone help me with some example that symfony beginner can understand.Thank you.
Below is the table structures & related codes :
Doctor Entity Table
id INT PRIMARY KEY AUTO_INCREMENT
name VARCHAR(55) NOT NULL
Timetable Entity Table
id INT PRIMARY KEY AUTO_INCREMENT
doctor_id INT (foreign key to doctor)
clinic_id INT (foreign key to clinic)
_time VARCHAR(255) NOT NULL
Doctor Entity Class
<?php
namespace Nay\MMClinicsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Doctor
*
* @ORM\Table(name="doctors")
* @ORM\Entity(repositoryClass="Nay\MMClinicsBundle\Entity\DoctorRepository")
*/
class Doctor
{
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="Timetable", mappedBy="doctor", cascade={"persist"})
*/
public $timetables;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="doctor_name", type="string", length=255)
*/
private $doctorName;
public function __construct(){
$this->timetables = new ArrayCollection();
}
/**
*get timetable of this doctor
* @return arraycollection
*/
public function getTimetables(){
return $this->timetables;
}
public function addTimetable(Timetable $t)
{
$this->timetables->add($t);
}
public function removeTimetable(Timetable $t)
{
$this->timetables->removeElement($t);
}
// **setters & getters for each property here**
}
Timetable Entity Class
<?php
namespace Nay\MMClinicsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Timetable
*
* @ORM\Table(name="timetables")
* @ORM\Entity(repositoryClass="Nay\MMClinicsBundle\Entity\TimetableRepository")
*/
class Timetable
{
/**
* @var Clinic
*
* @ORM\ManyToOne(targetEntity="Clinic", inversedBy="timetables")
* @ORM\JoinColumn(name="clinic_id", referencedColumnName="id")
*/
public $clinic;
/**
* @var Doctor
*
* @ORM\ManyToOne(targetEntity="Doctor", inversedBy="timetables")
* @ORM\JoinColumn(name="doctor_id", referencedColumnName="id")
*/
public $doctor;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var integer
*
* @ORM\Column(name="clinic_id", type="integer")
*/
private $clinicId;
/**
* @var integer
*
* @ORM\Column(name="doctor_id", type="integer")
*/
private $doctorId;
/**
* @var string
*
* @ORM\Column(name="_time", type="string", length=1024)
*/
private $time;
//**setters & getters for each property here**
}
Doctor Form Type
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('doctorName')
->add('timetables', 'collection', array('type' => new TimetableType(),'allow_add' => true ,'by_reference' => false, 'allow_delete' => true ));
;
}
Timetable Form Type
<?php
namespace Nay\MMClinicsBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TimetableType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('clinic')
->add('doctor')
->add('time','text',array('attr'=>array("data-role"=>"tagsinput")))
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Nay\MMClinicsBundle\Entity\Timetable'
));
}
/**
* @return string
*/
public function getName()
{
return 'nay_mmclinicsbundle_timetable';
}
}
Upvotes: 1
Views: 3476
Reputation: 2859
http://www.doctrine-project.org/jira/browse/DDC-3480
http://www.doctrine-project.org/jira/browse/DDC-3556
Mention that only basic mapping is supported by embeddables
So, ManyToOne with Embedded still not supported by Doctrine 2, it's a pity.
Upvotes: 2
Reputation: 847
Even I am not sure this way is recommended. but I had solved it in following way. First remove the following line from timetable form type:
->add('doctor')
In your controller createAction() add this code after your doctor entity save/flush.
$doctor = $form->getData();
if($doctor->getTimetables())
{
foreach ($doctor->getTimetables() as $timetable) {
$timetable->setDoctor($doctor);
$em->persist($timetable);
$em->flush();
}
}
In your controller updateAction() add this code befor your form handleRequest.
$entity = $em->getRepository('YourBundle:Doctor')->find($id);
$originalTimetables = new ArrayCollection();
if($entity->getTimetables())
{
foreach ($entity->getTimetables() as $timetable) {
$originalTimetables->add($timetable);
}
}
In your controller updateAction() add this code after your form handleRequest and before doctor entity flush.
$editForm->handleRequest($request);
if ($editForm->isValid()) {
foreach ($originalTimetables as $timetable)
{
if (false == $entity->getTimetables()->contains($timetable))
{
$timetable->getDoctor()->removeTimetable($timetable);
$em->remove($timetable);
$em->flush();
}
}
$em->flush();
In your controller updateAction() add this code after your doctor entity flush.
$doctor = $editForm->getData();
if($doctor->getTimetables())
{
foreach ($doctor->getTimetables() as $timetable)
{
$timetable->setDoctor($doctor);
$em->flush();
}
}
}
I am not sure and even not tried $em->flush() will required in each loop or will work out side of loop only once it will work. :)
Upvotes: 0