How to delete an MPTT Node

By Andy, filed under CakePHP, Tools, ACL, Tips, MPTT, Hacks, SQL

If you use MPTT tables anywhere (The cake acl system uses it) you may have found that when you delete an entry, the lft and rght fields for the neighouring nodes don't get updated. This may have rather dire concequences ;)

There is an open ticket for how to delete from the cake acl tables, but as it is potentially quite generic, I thought I'd include the (potential) fix here. Below are two versions of a delete method, one takes care of associations, and the other doesn't:

// If there are associations to take care of
function 

delete($Alias) {
    $class = $this->name;
    $result = $this->findByAlias($Alias);
    $object = 

$result[$class];
    if ($object == null || count($object) == 0) {
        return false;
    }
    

$idList[] = $result[$class]['id'];

    // won't somebody please think of the children.
    $Children= 

$this->findAll(array("rght"=>"< {$result[$class]['rght']}","lft"=>"> {$result[$class]['lft']}"),"id",null,null,null,-1);
    

foreach($Children as $Child) {
        $idList[] = $Child[$class]['id'];
    }
    // Delete associated 

permissions.
    $this->ArosAco->query("DELETE FROM ".$this->ArosAco->tablePrefix.$this->ArosAco->table." WHERE 

{$class}_id in (".implode(",",$idList).")");

    // Perform the delete
    $table = 

$this->tablePrefix.$this->table;
    $this->query("DELETE FROM $table WHERE $table.lft >= {$result[$class]['lft']} AND 

$table.rght <= {$result[$class]['rght']}");

    // Reindex.
    $shift = 1+ $result[$class]['rght'] - 

$result[$class]['lft'];
    $this->query('UPDATE ' . $table . ' SET `rght` = `rght` - ' . $shift . ' WHERE `rght` > ' 

. $result[$class]['rght']);
    $this->query('UPDATE ' . $table . ' SET `lft` = `lft` - ' . $shift . ' WHERE `lft` 

> ' . $result[$class]['lft']);
    return true;
}

// No associations to take care or
function delete($id) 

{
    $result = $this-read();
    // Perform the delete
    $table = $this->tablePrefix.$this->table;
    

$this->query("DELETE FROM $table WHERE $table.lft >= {$result[$class]['lft']} AND $table.rght <= 

{$result[$class]['rght']}");

    // Reindex.
    $shift = 1+ $result[$class]['rght'] - $result[$class]['lft'];
    

$this->query('UPDATE ' . $table . ' SET `rght` = `rght` - ' . $shift . ' WHERE `rght` > ' . 

$result[$class]['rght']);
    $this->query('UPDATE ' . $table . ' SET `lft` = `lft` - ' . $shift . ' WHERE `lft` > ' . 

$result[$class]['lft']);
    return true;
}
  1. // If there are associations to take care of
  2. function
  3. delete($Alias) {
  4. $class = $this->name;
  5. $result = $this->findByAlias($Alias);
  6. $object =
  7. $result[$class];
  8. if ($object == null || count($object) == 0) {
  9. return false;
  10. }
  11. $idList[] = $result[$class]['id'];
  12. // won't somebody please think of the children.
  13. $Children=
  14. $this->findAll(array("rght"=>"< {$result[$class]['rght']}","lft"=>"> {$result[$class]['lft']}"),"id",null,null,null,-1);
  15. foreach($Children as $Child) {
  16. $idList[] = $Child[$class]['id'];
  17. }
  18. // Delete associated
  19. permissions.
  20. $this->ArosAco->query("DELETE FROM ".$this->ArosAco->tablePrefix.$this->ArosAco->table." WHERE
  21. {$class}_id in (".implode(",",$idList).")");
  22. // Perform the delete
  23. $table =
  24. $this->tablePrefix.$this->table;
  25. $this->query("DELETE FROM $table WHERE $table.lft >= {$result[$class]['lft']} AND
  26. $table.rght <= {$result[$class]['rght']}");
  27. // Reindex.
  28. $shift = 1+ $result[$class]['rght'] -
  29. $result[$class]['lft'];
  30. $this->query('UPDATE ' . $table . ' SET `rght` = `rght` - ' . $shift . ' WHERE `rght` > '
  31. . $result[$class]['rght']);
  32. $this->query('UPDATE ' . $table . ' SET `lft` = `lft` - ' . $shift . ' WHERE `lft`
  33. > ' . $result[$class]['lft']);
  34. return true;
  35. }
  36. // No associations to take care or
  37. function delete($id)
  38. {
  39. $result = $this-read();
  40. // Perform the delete
  41. $table = $this->tablePrefix.$this->table;
  42. $this->query("DELETE FROM $table WHERE $table.lft >= {$result[$class]['lft']} AND $table.rght <=
  43. {$result[$class]['rght']}");
  44. // Reindex.
  45. $shift = 1+ $result[$class]['rght'] - $result[$class]['lft'];
  46. $this->query('UPDATE ' . $table . ' SET `rght` = `rght` - ' . $shift . ' WHERE `rght` > ' .
  47. $result[$class]['rght']);
  48. $this->query('UPDATE ' . $table . ' SET `lft` = `lft` - ' . $shift . ' WHERE `lft` > ' .
  49. $result[$class]['lft']);
  50. return true;
  51. }
  52.  

Should anybody use this and find it causes a problem please let me know.

«  »
  • Andy, on 1/1/70

    The delete method in the Cake ACL system was recently updated - so if you came here because deleting a node has corrupted your data: Grab the latest trunk ;)

Comments are now closed, however feel free to send an email with your thoughts