Symfony主义与嵌入式表单集合有多对一的关系

时间:2015-04-13 18:18:56

标签: php symfony doctrine-orm

我有2个实体:医生& 时间表其中一位医生有很多时间表(OneToMany),许多时间表都有一位医生(ManyToOne)。我想从Doctor Create form Screen创建许多时间表。所以,我按照文章embedded form collection 一切都工作得很好,除了我正在让医生下载时间表创建会话,这意味着我只能为现有医生创建时间表。我知道医生下拉来自ManyToOne关系。 但就我而言,我需要为当前的医生(即将在屏幕上创建的医生)制定时间表,而不选择现有医生的下拉菜单。用伪代码告诉逻辑我想要什么:

//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(),
    );
}

我已经阅读了所有类似的问题但没有解决方案。请大家帮我一些symfony初学者可以理解的例子。谢谢。

下面是表格结构&相关代码:

医生实体表

id INT PRIMARY KEY AUTO_INCREMENT
  name VARCHAR(55)NOT NULL

时间表实体表
  id INT PRIMARY KEY AUTO_INCREMENT
  doctor_id INT (医生的外键)
  clinic_id INT (诊所的外键)
  _time VARCHAR(255)NOT NULL

医生实体班

<?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** 


}

时间表实体类

<?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** 

}

医生表格类型

    /**
 * @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 ));
    ;
}

时间表表单类型

<?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';
    }
}

2 个答案:

答案 0 :(得分:2)

http://www.doctrine-project.org/jira/browse/DDC-3480

http://www.doctrine-project.org/jira/browse/DDC-3556

  

提到嵌入式广告只支持基本映射

因此,带有Embedded的ManyToOne仍未得到Doctrine 2的支持,很遗憾。

答案 1 :(得分:0)

即使我不确定这种推荐方式。但我已经按照以下方式解决了它。 首先从时间表表单类型中删除以下行:

->add('doctor')

在您的控制器中,createAction()会在您的医生实体保存/刷新后添加此代码。

$doctor = $form->getData();
if($doctor->getTimetables())
{
     foreach ($doctor->getTimetables() as $timetable) {
           $timetable->setDoctor($doctor);
           $em->persist($timetable);
           $em->flush();
     }
}

在您的控制器updateAction()中,为表单handleRequest添加此代码。

$entity = $em->getRepository('YourBundle:Doctor')->find($id);
$originalTimetables = new ArrayCollection();
if($entity->getTimetables())
{
     foreach ($entity->getTimetables() as $timetable) {
          $originalTimetables->add($timetable);
     }
}

在您的控制器中,updateAction()在表单handleRequest之后和医生实体刷新之前添加此代码。

$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();

在您的控制器中updateAction()在您的医生实体刷新后添加此代码。

   $doctor = $editForm->getData();
   if($doctor->getTimetables())
   {
        foreach ($doctor->getTimetables() as $timetable) 
        {
           $timetable->setDoctor($doctor);
           $em->flush();
        }
   }
}

我不确定,甚至没有尝试过$ em-&gt; flush()将在每个循环中使用,或者只有在它工作时才能解决循环的一面。 :)