/** * @throws Exception */ public function beforeUpdate() { if ($this->node !== null && !$this->node->getIsNewRecord()) { $this->node->refresh(); } switch ($this->operation) { case self::OPERATION_MAKE_ROOT: if ($this->treeAttribute === false) { throw new Exception('Can not move a node as the root when "treeAttribute" is false.'); } if ($this->owner->isRoot()) { throw new Exception('Can not move the root node as the root.'); } break; case self::OPERATION_INSERT_BEFORE: case self::OPERATION_INSERT_AFTER: if ($this->node->isRoot()) { throw new Exception('Can not move a node when the target node is root.'); } case self::OPERATION_PREPEND_TO: case self::OPERATION_APPEND_TO: if ($this->node->getIsNewRecord()) { throw new Exception('Can not move a node when the target node is new record.'); } if ($this->owner->equals($this->node)) { throw new Exception('Can not move a node when the target node is same.'); } if ($this->node->isChildOf($this->owner)) { throw new Exception('Can not move a node when the target node is child.'); } } }
/** * @inheritdoc * @param ActiveRecord $owner */ public function attach($owner) { $this->attribute = (array) $this->attribute; $primaryKey = $owner->primaryKey(); $primaryKey = is_array($primaryKey) ? array_shift($primaryKey) : $primaryKey; if (in_array($primaryKey, $this->attribute, true) && $owner->getIsNewRecord()) { $this->attributes[ActiveRecord::EVENT_AFTER_INSERT] = $this->slugAttribute; } parent::attach($owner); }
/** * @param bool $forInsertNear * @throws Exception */ protected function checkNode($forInsertNear = false) { if ($forInsertNear && $this->node->isRoot()) { throw new Exception('Can not move a node before/after root.'); } if ($this->node->getIsNewRecord()) { throw new Exception('Can not move a node when the target node is new record.'); } if ($this->owner->equals($this->node)) { throw new Exception('Can not move a node when the target node is same.'); } if ($this->checkLoop && $this->node->isChildOf($this->owner)) { throw new Exception('Can not move a node when the target node is child.'); } }
/** * @param int $to * @param int $depth * @throws Exception */ protected function insertNode($to, $depth = 0) { if ($this->node->getIsNewRecord()) { throw new Exception('Can not create a node when the target node is new record.'); } if ($depth === 0 && $this->node->isRoot()) { throw new Exception('Can not insert a node before/after root.'); } $this->owner->setAttribute($this->leftAttribute, $to); $this->owner->setAttribute($this->rightAttribute, $to + 1); $this->owner->setAttribute($this->depthAttribute, $this->node->getAttribute($this->depthAttribute) + $depth); if ($this->treeAttribute !== null) { $this->owner->setAttribute($this->treeAttribute, $this->node->getAttribute($this->treeAttribute)); } $this->shift($to, null, 2); }
/** * @throws Exception */ public function beforeUpdate() { if ($this->node !== null && !$this->node->getIsNewRecord()) { $this->node->refresh(); } switch ($this->operation) { case self::OPERATION_MAKE_ROOT: if ($this->treeAttribute === false) { throw new Exception('Can not move a node as the root when "treeAttribute" is false.'); } if ($this->owner->isRoot()) { throw new Exception('Can not move the root node as the root.'); } if (!$this->treeAttributeById) { if ($this->owner->getOldAttribute($this->treeAttribute) === $this->owner->getAttribute($this->treeAttribute)) { throw new Exception('Can not move a node as the root when its tree attribute "' . $this->treeAttribute . '" is not changed.'); } if ($this->owner->find()->andWhere([$this->treeAttribute => $this->owner->getAttribute($this->treeAttribute), $this->leftAttribute => 1])->one()) { throw new Exception('Can not move a node as the root when another root with this "' . $this->treeAttribute . '" already exists.'); } } break; case self::OPERATION_INSERT_BEFORE: case self::OPERATION_INSERT_AFTER: if ($this->node->isRoot()) { throw new Exception('Can not move a node when the target node is root.'); } case self::OPERATION_PREPEND_TO: case self::OPERATION_APPEND_TO: if ($this->node->getIsNewRecord()) { throw new Exception('Can not move a node when the target node is new record.'); } if ($this->owner->equals($this->node)) { throw new Exception('Can not move a node when the target node is same.'); } if ($this->node->isChildOf($this->owner)) { throw new Exception('Can not move a node when the target node is child.'); } } }
/** * Find all SEO data. * Result will be in format ['condition1'=>Seo(), 'condition2'=>Seo(), ...] * @param ActiveRecord $owner the model for which to find data * @return self[] */ public static function findAll(ActiveRecord $owner) { /** @var self[] $seoList */ $seoList = []; if ($owner->getIsNewRecord()) { return $seoList; } $query = (new Query())->from(Seo::tableName($owner))->andWhere(['model_id' => $owner->getPrimaryKey()]); foreach ($query->all($owner->getDb()) as $seo) { $seo = new Seo($seo); $seo->_isNewRecord = false; $seo->_owner = $owner; $seoList[$seo['condition']] = $seo; } return $seoList; }
/** * Establishes the relationship between two models. * * The relationship is established by setting the foreign key value(s) in one model * to be the corresponding primary key value(s) in the other model. * The model with the foreign key will be saved into database without performing validation. * * If the relationship involves a pivot table, a new row will be inserted into the * pivot table which contains the primary key values from both models. * * Note that this method requires that the primary key value is not null. * * @param string $name the case sensitive name of the relationship * @param ActiveRecord $model the model to be linked with the current one. * @param array $extraColumns additional column values to be saved into the pivot table. * This parameter is only meaningful for a relationship involving a pivot table * (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.) * @throws InvalidCallException if the method is unable to link two models. */ public function link($name, $model, $extraColumns = []) { $relation = $this->getRelation($name); if ($relation->via !== null) { if ($this->getIsNewRecord() || $model->getIsNewRecord()) { throw new InvalidCallException('Unable to link models: both models must NOT be newly created.'); } if (is_array($relation->via)) { /** @var ActiveRelation $viaRelation */ list($viaName, $viaRelation) = $relation->via; $viaClass = $viaRelation->modelClass; // unset $viaName so that it can be reloaded to reflect the change unset($this->_related[$viaName]); } else { $viaRelation = $relation->via; $viaTable = reset($relation->via->from); } $columns = []; foreach ($viaRelation->link as $a => $b) { $columns[$a] = $this->{$b}; } foreach ($relation->link as $a => $b) { $columns[$b] = $model->{$a}; } foreach ($extraColumns as $k => $v) { $columns[$k] = $v; } if (is_array($relation->via)) { /** @var $viaClass ActiveRecord */ /** @var $record ActiveRecord */ $record = new $viaClass(); foreach ($columns as $column => $value) { $record->{$column} = $value; } $record->insert(false); } else { /** @var $viaTable string */ static::getDb()->createCommand()->insert($viaTable, $columns)->execute(); } } else { $p1 = $model->isPrimaryKey(array_keys($relation->link)); $p2 = $this->isPrimaryKey(array_values($relation->link)); if ($p1 && $p2) { if ($this->getIsNewRecord() && $model->getIsNewRecord()) { throw new InvalidCallException('Unable to link models: both models are newly created.'); } elseif ($this->getIsNewRecord()) { $this->bindModels(array_flip($relation->link), $this, $model); } else { $this->bindModels($relation->link, $model, $this); } } elseif ($p1) { $this->bindModels(array_flip($relation->link), $this, $model); } elseif ($p2) { $this->bindModels($relation->link, $model, $this); } else { throw new InvalidCallException('Unable to link models: the link does not involve any primary key.'); } } // update lazily loaded related objects if (!$relation->multiple) { $this->_related[$name] = $model; } elseif (isset($this->_related[$name])) { if ($relation->indexBy !== null) { $indexBy = $relation->indexBy; $this->_related[$name][$model->{$indexBy}] = $model; } else { $this->_related[$name][] = $model; } } }
/** * @param int $left * @param int $right * @param int $depth * @param bool $forward * @param array|null $parent * @throws Exception */ protected function insertNode($left, $right, $depth = 0, $forward = true, $parent = null) { if ($this->node->getIsNewRecord()) { throw new Exception('Can not create a node when the target node is new record.'); } if ($depth === 0 && $this->getNodeBehavior()->isRoot()) { throw new Exception('Can not insert a node before/after root.'); } $this->owner->setAttribute($this->depthAttribute, $this->node->getAttribute($this->depthAttribute) + $depth); if ($this->treeAttribute !== null) { $this->owner->setAttribute($this->treeAttribute, $this->node->getAttribute($this->treeAttribute)); } if ($right - $left < 3) { for ($i = $right - $left; $i < 3; $i++) { $unallocated = $this->findUnallocatedAll($left, $right); if ($unallocated < $left) { $this->shift($unallocated, $left, -1); $left--; } else { $this->shift($right, $unallocated, 1); $right++; } } $this->owner->setAttribute($this->leftAttribute, $left + 1); $this->owner->setAttribute($this->rightAttribute, $right - 1); } else { $left++; $right--; $isPadding = false; if ($depth === 1 || $parent !== null) { // prepending/appending if (is_array($this->amountOptimize)) { if (isset($this->amountOptimize[$depth - 1])) { $amountOptimize = $this->amountOptimize[$depth - 1]; } else { $amountOptimize = $this->amountOptimize[count($this->amountOptimize) - 1]; } } else { $amountOptimize = $this->amountOptimize; } $pLeft = $parent !== null ? (int) $parent[$this->leftAttribute] : $this->node->getAttribute($this->leftAttribute); $pRight = $parent !== null ? (int) $parent[$this->rightAttribute] : $this->node->getAttribute($this->rightAttribute); $isCenter = !$this->noAppend && !$this->noPrepend; $isFirst = $left === $pLeft + 1 && $right === $pRight - 1; $isPadding = !$this->noInsert || $isFirst && ($forward ? !$this->noPrepend : !$this->noAppend); $step = $amountOptimize + $this->reserveFactor * ($this->noInsert ? $isCenter ? 2 : 1 : $amountOptimize + 1); $step = ($pRight - $pLeft - 1) / $step; $stepGap = $step * $this->reserveFactor; $padding = $isPadding ? $stepGap : 0; if ($forward) { $pLeft = $left + (int) floor($padding); $pRight = $left + (int) floor($padding + $step) - 1; } else { $pLeft = $right - (int) floor($padding + $step) + 1; $pRight = $right - (int) floor($padding); } if ($isFirst && $isCenter) { $initPosition = (int) floor(($amountOptimize - 1) / 2) * (int) floor($step + ($this->noInsert ? 0 : $stepGap)) + ($this->noInsert ? 1 : 0); $pLeft += $forward ? $initPosition : -$initPosition; $pRight += $forward ? $initPosition : -$initPosition; } if ($pLeft < $pRight && $pRight <= $right && $left <= $pLeft && ($forward || $left < $pLeft) && (!$forward || $pRight < $right)) { $this->owner->setAttribute($this->leftAttribute, $pLeft); $this->owner->setAttribute($this->rightAttribute, $pRight); return; } } $isPadding = $isPadding || !$this->noInsert; $step = (int) floor(($right - $left) / ($isPadding ? 3 : 2)); $this->owner->setAttribute($this->leftAttribute, $left + ($forward && !$isPadding ? 0 : $step)); $this->owner->setAttribute($this->rightAttribute, $right - (!$forward && !$isPadding ? 0 : $step)); } }