The steps to move a subtree around in a category tree using the nested set model (with left and right columns) are:
1. transform the lft and rgt columns in their negative counterparts for the category you wish to move and its subcategories (this will "remove" the subtree from the tree, for the time being)
2. if you are moving the subtree up (or "left" in the nested set representation), move all the categories between the new parent of the subtree and its old left(or right, in the second case) limit to the right, otherwise (when moving the subtree down) to the right. This involves setting the left and right columns of these categories to their values plus (or minus, in the second case) the distance between the left and right column of the subtree (or the category to be moved)
3. after this, all you have to do is to revert the left and right columns to the positive values and in the same time substract (or add, in the second case) the difference between its left limit and the new parent left column (or between the parent left and the right limit in the second case)
This all seems very complicated, expressed in one case, so I broke it down to the two cases:
$step = 1+ $this->_categoriesTable->rgt
- $this->_categoriesTable->lft;
$lft = $this->_categoriesTable->lft;
$rgt = $this->_categoriesTable->rgt;
$id = $this->_categoriesTable->id;
$distance = $lft - $parentLeft - 1;
$query = '
UPDATE %s SET lft=-lft, rgt=-rgt
WHERE lft>=%d AND lft<=%d;
UPDATE %s SET lft=lft+%d WHERE lft>%d AND lft<%d;
UPDATE %s SET rgt=rgt+%d WHERE rgt>%d AND rgt<%d;
UPDATE %s SET lft=-lft-%d, rgt=-rgt-%d WHERE lft<=-%d
AND lft>=-%d;
UPDATE %s SET parent_id=%d, title=%s, description=%s,
metadescription=%s WHERE id=%s';
$query = sprintf($query,
$this->_db->nameQuote('#__categories'),
$lft, $rgt,
$this->_db->nameQuote('#__categories'), $step,
$parentLeft, $lft,
$this->_db->nameQuote('#__categories'), $step,
$parentLeft, $lft,
$this->_db->nameQuote('#__categories'), $distance,
$distance, $lft, $rgt,
$this->_db->nameQuote('#__categories'),
$data['parent_id'],
$this->_db->Quote($this->_categoriesTable->title),
$this->_db->Quote($this->_categoriesTable->description),
$this->_db->Quote(
$this->_categoriesTable->metadescription),
$this->_db->Quote($id));
// and for the moving to the "right" case
$step = 1+ $this->_categoriesTable->rgt
- $this->_categoriesTable->lft;
$distance = $parentLeft - $this->_categoriesTable->rgt;
// Memorize this because we bind and we need the old values
$lft = $this->_categoriesTable->lft;
$rgt = $this->_categoriesTable->rgt;
$id = $this->_categoriesTable->id;
$query = sprintf($query,
$this->_db->nameQuote('#__categories'),
$lft, $rgt,
$this->_db->nameQuote('#__categories'), $step,
$rgt, $parentLeft,
$this->_db->nameQuote('#__categories'), $step,
$rgt, $parentLeft,
$this->_db->nameQuote('#__categories'), $distance,
$distance, $lft, $rgt,
$this->_db->nameQuote('#__categories'),
$data['parent_id'],
$this->_db->Quote($this->_categoriesTable->title),
$this->_db->Quote($this->_categoriesTable->description),
$this->_db->Quote(
$this->_categoriesTable->metadescription),
$this->_db->Quote($id));