php 在清空ManyToOne所有者的集合后,Doctrine cascade“remove”不删除行

vxf3dgd4  于 2023-10-15  发布在  PHP
关注(0)|答案(1)|浏览(97)

我有以下两个实体:

/** @Entity @Table(name="invoice") */
class Invoice {
    /** @Id @Column(type="integer") @GeneratedValue */
    protected $id;
    ...
    /** @OneToMany(targetEntity="InvoiceRow", mappedBy="invoice", fetch="EAGER", cascade={"persist", "remove"}, orphanRemoval=true) */
    protected $rows = null;
    ...

    public function __construct() {
        $this->rows = new ArrayCollection();
    }
    ...

    public function getRows() {
        return $this->rows;
    }

    public function addRow($row) {
        $this->rows[] = $row;
        $row->setInvoice($this);
    }

    public function emptyRows() {
        $this->rows = new ArrayCollection();
    }
}
/** @Entity @Table(name="invoice_row") */
class InvoiceRow {
    /** @Id @Column(type="integer") @GeneratedValue */
    protected $id;
    ...
    /** @ManyToOne(targetEntity="Invoice", inversedBy="rows") */
    protected $invoice;
    ...
    
    public function getInvoice() {
        return $this->invoice;
    }

    public function setInvoice($invoice) {
        $this->invoice = $invoice;
    }
}

因此,一个Invoice的示例可以关联许多InvoiceRow的示例。在DB上反映实体结构之后,我在表invoice_row中有字段invoice_id,它正确地引用了表invoice的相关记录的id
由于可以添加、删除或修改发票的每一行,因此每次保存发票时,我都希望删除InvoiceRow的所有关联记录,并在从前端接收到这些记录时重新创建这些记录。
这是代码:

public static function saveInvoice($invoiceData, $invoiceRows) {
    $em = getEntityManager();

    $dbInvoice = null;
    if ($invoiceData->id) {
        // if is an exsisting invoice, i get it from the DB, so Doctrine will execute an update instead of an insert
        $dbInvoice = $em->find(Invoice::class, $invoiceData->id);
    }

    $dbInvoice->emptyRows(); // deleting all the rows from the entity Invoice

    // adding all the rows, as they were received from the frontend
    foreach ($invoiceRows as $invoiceRow) {
        $dbInvoiceRow = self::getDbInvoiceRow($invoiceRow) // map frontend object into the Doctrine entity InvoiceRow
        $dbInvoice->addRiga($dbInvoiceRow);
    }

    $em->persist($dbInvoice);
    $em->flush();
}

问题是,最后,不是只有从前端接收的行,而是仍然有所有旧的行,因为没有删除任何行。
举例来说:invoice_row表中的原始数据:

| id | invoice_id | code | description |
| -- | ---------- | ---- | ----------- |
| 1  | 12         | 010  | product 01  |
| 2  | 12         | 008  | product 02  |

从前端接收的行:

| code | description |
| ---- | ----------- |
| 010  | product 01  |
| 012  | product 03  |

invoice_row表的预期内容:

| id | invoice_id | code | description |
| -- | ---------- | ---- | ----------- |
| 1  | 12         | 010  | product 01  |
| 3  | 12         | 012  | product 03  |

坚持后的真实的内容

| id | invoice_id | code | description |
| -- | ---------- | ---- | ----------- |
| 1  | 12         | 010  | product 01  | -> old row
| 2  | 12         | 008  | product 02  | -> old row
| 3  | 12         | 010  | product 01  |
| 4  | 12         | 012  | product 03  |

我做错了什么?
提前感谢大家。

olmpazwi

olmpazwi1#

最后我发现了重点。我很失望我不能通过cascade remove或其他注解来实现这一点,但我不得不编写更多的代码。下面是我怎么想出来的:

  • 我将以下方法添加到类Invoice
public function deleteRows() {
    foreach ($this->rows as $row) {
        $this->rows->removeElement($row);
    }
    $this->rows = new ArrayCollection();
}

我将发票保存程序更改如下:

public static function saveInvoice($invoiceData, $invoiceRows) {
    $em = getEntityManager();

    $dbInvoice = null;
    if ($invoiceData->id) {
        // if is an existing invoice, i get it from the DB, so Doctrine will execute an update instead of an insert
        $dbInvoice = $em->find(Invoice::class, $invoiceData->id);
    }

    $dbInvoice->deleteRows(); // deleting all the rows from the entity Invoice

    // adding all the rows, as they were received from the frontend
    foreach ($invoiceRows as $invoiceRow) {
        $dbInvoiceRow = self::getDbInvoiceRow($invoiceRow) // map frontend object into the Doctrine entity InvoiceRow
        $dbInvoice->addRow($dbInvoiceRow);
    }

    $em->persist($dbInvoice);
    $em->flush();
}

最后,为了使它更容易,我唯一能弄清楚的方法是显式调用removeElement,但将其作为Invoice类中的一个函数包含在内,使其与实体本身更加一致。
我希望这对未来的读者有所帮助。

相关问题