无法删除CakePHP3树
我有一个TreeBehavior的“客户”模型,运行良好: e、 g:无法删除CakePHP3树,php,cakephp,cakephp-3.0,Php,Cakephp,Cakephp 3.0,我有一个TreeBehavior的“客户”模型,运行良好: e、 g: id | parent_id | type | name ----|-----------|---------|-------------- 1 | NULL | husband | Jon Doe 2 | 1 | wife | Jane Doe 3 | 1 | child | Jim Doe 一个客户可以有许多“合同”。所有协会都运作良好 现在
id | parent_id | type | name
----|-----------|---------|--------------
1 | NULL | husband | Jon Doe
2 | 1 | wife | Jane Doe
3 | 1 | child | Jim Doe
一个客户可以有许多“合同”。所有协会都运作良好
现在,我尝试删除客户。这仅适用于子元素(妻子、孩子):如果妻子被删除,所有关联的合同也将被删除。但是如果我试图删除丈夫(parent_id=NULL),我会得到错误:
错误:SQLSTATE[23000]:完整性约束冲突:1451无法
删除或更新父行:外键约束失败
(`contracts`,CONSTRAINT`contracts\u fk0`外键(`customer\u id`)
引用'customers'('id'))
SQL查询:
DELETE FROM customers WHERE ((lft) >= :c0 AND (lft) <= :c1)
DELETE FROM customers,其中((lft)>=:c0和(lft)我认为没有办法让ORM自动处理这个问题,因为树行为使用Table::deleteAll()
删除子树中的节点,这将创建一个类似于所显示内容的DELETE
查询,即立即删除记录,而不是逐个删除,这是从属/级联删除所必需的
你可能想建议一个增强,我想这会很好,如果这会工作,但不知道它可能有多复杂
您这边可能的解决方法是检索父节点的子节点,然后从最外层到最内层逐个手动删除它们。但是,这可能不会很好地执行,因为每次删除时树都会同步,这反过来需要在删除实体之前重新加载实体,如lft
和<代码>rgt
否则,它们持有的值将过时
下面是一个未经测试的示例来说明我所说的内容:
$result = $this->Customers->connection()->transactional(function () {
// retrieve the parent to delete
$customer = $this->Customers->get(1);
// retrieve the parents children
$descendants = $this->Customers->find('children', ['for' => $customer->id]);
// Reverse the childrens order, by default they are ordered
// from the inner most to the outer most. This could probably
// also be done on query level by sorting on the `lft` field.
$entities = array_reverse($descendants->toArray());
// append the parent so that it is being deleted last
$entities[] = $customer;
foreach ($entities as $entity) {
$entity = $this->Customers->get($entity->id);
if (!$this->Customers->delete($entity, ['atomic' => false])) {
return false;
}
}
return true;
});
理论上,这应该一个接一个地删除节点,导致首先删除它们的从属契约
或者,您可以收集树节点及其关联的契约,首先手动删除契约,然后删除父树节点。这样可能会执行得更好。大致如下:
$result = $this->Customers->connection()->transactional(function () {
// retrieve the parent to delete
$customer = $this->Customers->get(1, [
'contain' => ['Contracts']
]);
// retrieve the parents children
$descendants = $this->Customers
->find('children', ['for' => $customer->id])
->contain(['Contracts']);
// collect all contracts
$contracts = $customer->contracts;
foreach ($descendants as $entity) {
$contracts = array_merge($contracts, $entity->contracts);
}
// delete contracts first
// in case no callbacks are required for deleting contracts,
// you could also collect the customer or contract ids instead
// and use `deleteAll()`
foreach ($contracts as $entity) {
if (!$this->Customers->Contracts->delete($entity, ['atomic' => false])) {
return false;
}
}
// then delete the customers
if (!$this->Customers->delete($customer, ['atomic' => false])) {
return false;
}
return true;
});
另见