private function moveNode(ModelInterface $target, $key, $levelUp)
{
$owner = $this->getOwner();
if ($this->getIsNewRecord()) {
throw new Exception('The node should not be new record.');
}
if ($this->getIsDeletedRecord()) {
throw new Exception('The node should not be deleted.');
}
if ($target->getIsDeletedRecord()) {
throw new Exception('The target node should not be deleted.');
}
if ($owner == $target) {
throw new Exception('The target node should not be self.');
}
if ($target->isDescendantOf($owner)) {
throw new Exception('The target node should not be descendant.');
}
if (!$levelUp && $target->isRoot()) {
throw new Exception('The target node should not be root.');
}
$this->db->begin();
$left = $owner->{$this->leftAttribute};
$right = $owner->{$this->rightAttribute};
$levelDelta = $target->{$this->levelAttribute} - $owner->{$this->levelAttribute} + $levelUp;
if ($this->hasManyRoots && $owner->{$this->rootAttribute} !== $target->{$this->rootAttribute}) {
$this->ignoreEvent = true;
// 1. Rebuild the target tree
foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) {
$condition = join(' AND ', [$attribute . '>=' . $key, $this->rootAttribute . '=' . $target->{$this->rootAttribute}]);
foreach ($target::find($condition) as $i) {
$delta = $right - $left + 1;
/** @var ModelInterface $i */
if (!$i->update([$attribute => $i->{$attribute} + $delta])) {
$this->db->rollback();
$this->ignoreEvent = false;
return false;
}
}
}
$delta = $key - $left;
// 2. Rebuild the owner's tree of children (owner sub-tree)
$condition = $this->leftAttribute . '>=' . $left . ' AND ';
$condition .= $this->rightAttribute . '<=' . $right . ' AND ';
$condition .= $this->rootAttribute . '=' . $owner->{$this->rootAttribute};
foreach ($owner::find($condition) as $i) {
$arr = [$this->leftAttribute => $i->{$this->leftAttribute} + $delta, $this->rightAttribute => $i->{$this->rightAttribute} + $delta, $this->levelAttribute => $i->{$this->levelAttribute} + $levelDelta, $this->rootAttribute => $target->{$this->rootAttribute}];
if ($i->update($arr) == false) {
$this->db->rollback();
$this->ignoreEvent = false;
return false;
}
}
// 3. Rebuild the owner tree
$this->shiftLeftRight($right + 1, $left - $right - 1, $owner);
$this->ignoreEvent = false;
$this->db->commit();
} else {
$delta = $right - $left + 1;
$this->ignoreEvent = true;
$this->shiftLeftRight($key, $delta);
if ($left >= $key) {
$left += $delta;
$right += $delta;
}
$condition = $this->leftAttribute . '>=' . $left . ' AND ' . $this->rightAttribute . '<=' . $right;
if ($this->hasManyRoots) {
$condition .= ' AND ' . $this->rootAttribute . '=' . $owner->{$this->rootAttribute};
}
foreach ($owner::find($condition) as $i) {
if ($i->update([$this->levelAttribute => $i->{$this->levelAttribute} + $levelDelta]) == false) {
$this->db->rollback();
$this->ignoreEvent = false;
return false;
}
}
foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) {
$condition = $attribute . '>=' . $left . ' AND ' . $attribute . '<=' . $right;
if ($this->hasManyRoots) {
$condition .= ' AND ' . $this->rootAttribute . '=' . $owner->{$this->rootAttribute};
}
foreach ($owner::find($condition) as $i) {
if ($i->update([$attribute => $i->{$attribute} + $key - $left]) == false) {
$this->db->rollback();
$this->ignoreEvent = false;
return false;
}
}
}
$this->shiftLeftRight($right + 1, -$delta);
$this->ignoreEvent = false;
$this->ignoreEvent = false;
$this->db->commit();
}
return true;
}