/** * @param ActiveRecord $target * @param int $key * @param int $levelUp * @throws Exception * @return boolean */ private function moveNode($target, $key, $levelUp) { if ($this->owner->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 ($this->owner->equals($target)) { throw new Exception('The target node should not be self.'); } if ($target->isDescendantOf($this->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.'); } if (!$this->beforeMoveNode($this->_previousPath)) { return false; } $db = $this->owner->getDb(); if ($db->getTransaction() === null) { $transaction = $db->beginTransaction(); } try { $left = $this->owner->getAttribute($this->leftAttribute); $right = $this->owner->getAttribute($this->rightAttribute); $levelDelta = $target->getAttribute($this->levelAttribute) - $this->owner->getAttribute($this->levelAttribute) + $levelUp; if ($this->hasManyRoots && $this->owner->getAttribute($this->rootAttribute) !== $target->getAttribute($this->rootAttribute)) { foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) { $this->owner->updateAll([$attribute => new Expression($db->quoteColumnName($attribute) . sprintf('%+d', $right - $left + 1))], $db->quoteColumnName($attribute) . '>=' . $key . ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute, [':' . $this->rootAttribute => $target->getAttribute($this->rootAttribute)]); } $delta = $key - $left; $this->owner->updateAll([$this->leftAttribute => new Expression($db->quoteColumnName($this->leftAttribute) . sprintf('%+d', $delta)), $this->rightAttribute => new Expression($db->quoteColumnName($this->rightAttribute) . sprintf('%+d', $delta)), $this->levelAttribute => new Expression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d', $levelDelta)), $this->rootAttribute => $target->getAttribute($this->rootAttribute)], $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($this->rightAttribute) . '<=' . $right . ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute, [':' . $this->rootAttribute => $this->owner->getAttribute($this->rootAttribute)]); $this->shiftLeftRight($right + 1, $left - $right - 1); if (isset($transaction)) { $transaction->commit(); } $this->correctCachedOnMoveBetweenTrees($key, $levelDelta, $target->getAttribute($this->rootAttribute)); } else { $delta = $right - $left + 1; $this->shiftLeftRight($key, $delta); if ($left >= $key) { $left += $delta; $right += $delta; } $condition = $db->quoteColumnName($this->leftAttribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($this->rightAttribute) . '<=' . $right; $params = []; if ($this->hasManyRoots) { $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute; $params[':' . $this->rootAttribute] = $this->owner->getAttribute($this->rootAttribute); } $updateColumns = []; $updateColumns[$this->levelAttribute] = new Expression($db->quoteColumnName($this->levelAttribute) . sprintf('%+d', $levelDelta)); if ($this->hasPaths && $this->owner->hasAttribute($this->pathAttribute)) { $pathLength = Tools::strlen($this->_previousPath) + 1; // SQL Server: SUBSTRING() rather than SUBSTR // SQL Server: + instead of CONCAT if ($db->getDriverName() == 'mssql') { $updateColumns[$this->pathAttribute] = new Expression($db->quoteValue($this->owner->getAttribute($this->pathAttribute)) . ' + SUBSTRING(' . $db->quoteColumnName($this->pathAttribute) . ', ' . $pathLength . '))'); } else { $updateColumns[$this->pathAttribute] = new Expression('CONCAT(' . $db->quoteValue($this->owner->getAttribute($this->pathAttribute)) . ', SUBSTR(' . $db->quoteColumnName($this->pathAttribute) . ', ' . $pathLength . '))'); } } $this->owner->updateAll($updateColumns, $condition, $params); foreach ([$this->leftAttribute, $this->rightAttribute] as $attribute) { $condition = $db->quoteColumnName($attribute) . '>=' . $left . ' AND ' . $db->quoteColumnName($attribute) . '<=' . $right; $params = []; if ($this->hasManyRoots) { $condition .= ' AND ' . $db->quoteColumnName($this->rootAttribute) . '=:' . $this->rootAttribute; $params[':' . $this->rootAttribute] = $this->owner->getAttribute($this->rootAttribute); } $this->owner->updateAll([$attribute => new Expression($db->quoteColumnName($attribute) . sprintf('%+d', $key - $left))], $condition, $params); } $this->shiftLeftRight($right + 1, -$delta); $result = $this->afterMoveNode($this->_previousPath); if (isset($transaction)) { if ($result) { $transaction->commit(); } else { $transaction->rollback(); $this->_previousPath = ''; return false; } } $this->correctCachedOnMoveNode($key, $levelDelta); } } catch (\Exception $e) { if (isset($transaction)) { $transaction->rollback(); } throw $e; } $this->_previousPath = ''; return true; }
/** * Check changed attributes and compare the table schema, truncating any fields as required * * @param array $attributes Array of attributes to limit checking to those only */ public function checkAndFixLongStrings($attributes = false) { $hasChanges = $this->getDirtyAttributes(); if ($hasChanges) { $columns = self::getTableSchema()->columns; foreach ($hasChanges as $name => $value) { if (array_key_exists($name, $columns)) { if (!$attributes || in_array($name, $attributes) !== false) { if ($columns[$name]->type == 'string' && $columns[$name]->size) { if (Tools::strlen($value) > $columns[$name]->size) { $this->setAttribute($name, Tools::substr($value, 0, $columns[$name]->size)); } } } } } } }