无法删除CakePHP3树

无法删除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 一个客户可以有许多“合同”。所有协会都运作良好 现在

我有一个TreeBehavior的“客户”模型,运行良好:

e、 g:

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;
});
另见


我已经配置了我的客户模型:$this->hasMany('Contracts',['dependent'=>true,'cascadeCallbacks'=>true]);因此我也可以删除客户的合同。但这不适用于分配给关联子元素的合同。请提及要删除的程序。。