从具有多个对象的表单保存数据

时间:2019-08-13 14:54:46

标签: php symfony doctrine symfony4

按照本指南https://symfony.com/doc/current/form/embedded.html,我创建了一个包含三个实体的表单。患者,PatientSample和PatientOrder。当我提交表格时,我收到一个错误,Patient_id不能为null。

我一直在尝试各种方法在PatientOrderController中创建一个新的Patient对象,并在那里获取数据,然后调用$ entityManager-> persist($ Patient),但是在使用表单数据填充新的Patient对象时遇到了问题。 / p>

PatientOrderType

public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('accession_number')
            ->add('patient', PatientType::class)
            ->add('patientSample', PatientSampleType::class);
    }

PatientOrderController

    {
        $patientOrder = new PatientOrder();

        $form = $this->createForm(PatientOrderType::class, $patientOrder);

        $form->handleRequest($request);

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

            $patientOrder = $form->getData();

            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($patientOrder);
            $entityManager->flush();

            return $this->redirectToRoute('patient_order_index');
   }

患者分类

class Patient
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    public $id;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\PatientOrder", 
     *   mappedBy="patient")
     */
    public $patientOrders;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\PatientSample", 
     * mappedBy="patient")
     */
    public $patientSamples;

PatientOrder类

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Patient", 
     *  inversedBy="patientOrders", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     * @Assert\Valid()
     */
    protected  $patient;


/**
     * @ORM\ManyToOne(targetEntity="App\Entity\PatientSample", 
     *inversedBy="patientOrders", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     * @Assert\Valid()
     */
    private $patientSample;

PatientSample类

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Patient", 
     * inversedBy="patientSamples")
     * @ORM\JoinColumn(nullable=false)
     */
    private $patient;

   /**
     * @ORM\OneToMany(targetEntity="App\Entity\PatientOrder", 
     * mappedBy="patientSample")
     */
    private $patientOrders;

我的期望是,当用户单击“保存”按钮时,它将在数据库的3个单独的表中插入3个新行。相反,我得到了SQLSTATE [23000]:违反完整性约束:1048列'Patient_id'不能为空

1 个答案:

答案 0 :(得分:4)

好的。因此问题如下:

Patient是一个独立的实体,这很好。它有两个关联,每个关联PatientSamplePatientOrderPatientSamplePatientOrder也有关联

您的表单会创建一个新的PatientOrder,它会自动与Patient关联,因为PatientOrder是该关联的所有者,并且由于{{ 1}}。

cascade="PERSIST"也会自动与PatientOrder相关联,您可能也将其标记为级联持续存在。 但是,据我所知,PatientSample的{​​{1}}属性/关联并没有自动设置。至少不是 the 问题,那至少是 a 问题。 -幸运的是,这是一个小问题,因为它仅在依赖关系图中创建一个三角形,但是-由于Patient是独立的,因此不会创建周期(那将是一个大问题)。但是,您的表单不容易能够设置这些类型的依赖项,尤其是因为它们并不明显。从技术上讲,样本可能属于另一位患者...(请参阅答案末尾的验证评论)

简单的解决方案:明确设置

可能有几种解决方案,最简单但不是最好的:

patient

更干净的方法是在表单事件中的某个地方执行此操作...

添加form event处理程序(代替)

您应将PatientSample代码修改为如下形式:

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

        $patientOrder = $form->getData();
        // NEW! However, I don't know if this violates the Law of Demeter:
        $patientOrder->getSample()->setPatient($patientOrder->getPatient());

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($patientOrder);
        $entityManager->flush();

        return $this->redirectToRoute('patient_order_index');
    }

这种方法有些驯服。如果PatientOrderType已经分配了不同患者,则可能还会引发异常(可以使用实体预先填写/设置表单)。但是,此方法将设置一名失踪患者,从而避免了该表单将// be aware you need to set the following use clauses: // use Symfony\Component\Form\FormEvent; // use Symfony\Component\Form\FormEvents; $builder // ... ->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $patientOrder = $event->getData(); // view data, i.e. your objects $patientSample = $patientOrder->getPatientSample(); if(!$patientSample->getPatient()) { $patientSample->setPatient($patientOrder->getPatient()); } }) ; 保留为$patientSample的情况。

为什么在表单事件中而不是直接在控制器中设置它?因为这样一来,表单类型就可以重用,而无需记住“哦,我必须使用此表单类型在PersonSample上显式设置该人”。

奖金:添加验证

对于validating患者样本属于相同患者的患者订单,您可以在PatientSample上添加以下Expression约束(实体,而不是表单类型):

Patient

或回调约束,并且本质上是一样的...实际上,在注解中编写代码感觉很奇怪(平均而言,IDE不会有太大帮助)

更新:在验证部分混合了患者订单和患者样本