Symfony2 + Doctrine2:使用集合删除实体

时间:2016-06-07 23:07:30

标签: entity-framework symfony doctrine-orm

背景资讯

我有一个名为AnnualReport的实体,有多个集合(为了简洁起见,我们说2)。在FormType中删除其中一个集合

        //AnnualReportStaffing entity collection
        ->add('staffingTenured', 'collection', array(
            'type' => new AnnualReportStaffingType(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
        ))

其他集合是文件集合,删除不会自动处理

        //AnnualReportDetail entity collection
        ->add('documents', 'collection', array(
            'type' => new AnnualReportDocumentType(),
            'allow_add' => true,
            'allow_delete' => false, // Do NOT automatically remove documents not in the collection (i.e. edit form where Documents are not passed again)
            'by_reference' => false,
        ))

这是我的AnnualReport实体类中每个集合的属性/方法声明:

/**
 * @ORM\ManyToMany(targetEntity="AnnualReportStaffing", cascade={"persist", "detach", "remove"}, orphanRemoval=true, fetch="LAZY")
 * @ORM\JoinTable(name="annualreports_staffingtenure",
 *      joinColumns={@ORM\JoinColumn(name="annualreport_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="staffing_id", referencedColumnName="id", onDelete="CASCADE")},
 *      )
 */
private $staffingTenured;

/**
 * @ORM\ManyToMany(targetEntity="Document", cascade={"persist", "detach", "remove"}, orphanRemoval=true, fetch="LAZY")
 * @ORM\JoinTable(name="annualreports_documents",
 *      joinColumns={@ORM\JoinColumn(name="annualreport_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="document_id", referencedColumnName="id", onDelete="CASCADE")},
 *      )
 */
private $documents;

public function __construct(AnnualReportUnit $unit, $year) {
    $this->staffingTenured = new ArrayCollection();
    $this->documents = new ArrayCollection();
}

/**
 * Add staffingTenured
 *
 * @param AppBundle\Entity\AnnualReportStaffing  $staffing
 * @return AnnualReport
 */
public function addStaffingTenured(AnnualReportStaffing $staffing)
{
    $this->staffingTenured->add($staffing);

    return $this;
}

/**
 * Remove staffingTenured
 *
 * @param AppBundle\Entity\AnnualReportStaffing  $staffing
 * @return AnnualReport
 */
public function removeStaffingTenured(AnnualReportStaffing $staffing)
{
    $this->staffingTenured->removeElement($staffing);

    return $this;
}

/**
 * Get staffingTenured
 *
 * @return ArrayCollection 
 */
public function getStaffingTenured()
{
    return $this->staffingTenured;
}

/**
 * Add document
 *
 * @param AppBundle\Entity\AnnualReportDocument  $document
 * @return AnnualReport
 */
public function addDocument(AnnualReportDocument $document)
{
    $this->documents->add($document);

    return $this;
}

/**
 * Remove document
 *
 * @param AppBundle\Entity\AnnualReportDocument  $document
 * @return AnnualReport
 */
public function removeDocument(AnnualReportDocument $document)
{
    $this->documents->removeElement($document);

    return $this;
}

/**
 * Get documents
 *
 * @return ArrayCollection
 */
public function getDocuments()
{
    return $this->documents;
}

问题

当删除AnnualReport实体时:

  • 如果存在文档,我可以删除文档集合,但是我收到有关staffingTenured项目的外键约束错误。
An exception occurred while executing 'DELETE FROM annual_report WHERE id = ?' with params [57]:

SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails
     

libcommandannualreports_staffingtenure,CONSTRAINT   FK_DB56517AD4F67A27 FOREIGN KEY(annualreport_id)参考   annual_reportid))

  • 如果没有文件,则按预期删除AnnualReport实体和所有staffingTenured项目。

这是deleteAction():

public function deleteAction(Request $request, $id)
{
    $requestData = $request->request->all();
    $unit = $requestData['form']['unit'];


    $form = $this->createDeleteForm($id);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $entity = $em->getRepository('AppBundle:AnnualReport')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find AnnualReport entity.');
        }

        //Remove any documents (allow_delete set to FALSE in form so have to do manually)
        $documents = $entity->getDocuments();
        foreach($documents as $document){
            $entity->removeDocument($document);
            //$em->remove($document);
        }
        $em->persist($entity);
        $em->flush();

        $em->remove($entity);
        $em->flush(); //flush again to remove the annual report
    }

    return $this->redirect($this->generateUrl('annualreportunit_edit', array('id' => $unit)));
}

2 个答案:

答案 0 :(得分:1)

您在这里混合表格和ORM。 allow_delete是表单的参数,这意味着它用于处理表单。

正如Symfony的医生说的那样allow_delete

  

如果设置为true,那么如果提交的数据中不包含现有项目,则最终项目数组中将正确显示该项目。 这意味着您可以实施"删除"通过JavaScript按钮,它只是从DOM中删除一个表单元素。当用户提交表单时,它与提交的数据的缺失将意味着它从最终的数组中删除。

因此,它可用于实现从形式的根实体中删除集合项的可能性。但这并不意味着Doctrine也会神奇地处理它。

如果您希望在父实体保存/删除子实体时,您应该在实体映射中使用cascade属性。

答案 1 :(得分:0)

How about you catch a ForeignKeyConstraintViolationException exception or better yet catch DBALException. Then show the user that the error. Or when you catch the exception you can remove child nodes and then remove the entity again.

use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
Or
use Doctrine\DBAL\DBALException;

try {
    $em->remove($entity);
    $em->flush();

    $this->addFlash('success', 'Removed');
} catch (DBALException $e) {
    $em->remove($childEntity);
    $em->flush();

    $em->remove($entity);
    $em->flush();
--OR--
} catch (ForeignKeyConstraintViolationException $e) {
    $em->remove($childEntity);
    $em->flush();

    $em->remove($entity);
    $em->flush();
}