/** * Test is_closure function */ public function testIsClosure() { $this->assertFalse(Tools::is_closure(array())); $this->assertFalse(Tools::is_closure(5)); $this->assertFalse(Tools::is_closure('string')); $this->assertFalse(Tools::is_closure(new Item())); $this->assertTrue(Tools::is_closure(function () { $a = 1; })); }
/** * @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; }
/** * This method is called at the beginning of a deleteFull() request on a record array * * @param boolean $hasParentModel * whether this method was called from the top level or by a parent * If false, it means the method was called at the top level * @return boolean whether the deleteFull() method call should continue * If false, deleteFull() will be cancelled. */ public function beforeDeleteFullInternal($hasParentModel = false) { $this->clearActionErrors(); $this->resetChildHasChanges(); $canDeleteFull = true; if (!$hasParentModel) { //$event = new ModelEvent; //$this->trigger(self::EVENT_BEFORE_SAVE_ALL, $event); //$canSaveAll = $event->isValid; } if ($this->getReadOnly()) { // will be ignored during deleteFull() } elseif (!$this->getCanDelete()) { // will be ignored during deleteFull() } else { if ($canDeleteFull) { if ($this->count()) { $iterator = $this->getIterator(); while ($iterator->valid()) { $isReadOnly = false; $canDelete = true; if ($iterator->current() instanceof ActiveRecordReadOnlyInterface) { $isReadOnly = $iterator->current()->getReadOnly(); $canDelete = $iterator->current()->getCanDelete(); } if (!$isReadOnly && $canDelete) { $this->setChildHasChanges($iterator->key()); if ($iterator->current() instanceof ActiveRecordSaveAllInterface) { $this->setChildOldValues($iterator->key(), $iterator->current()->getResetDataForFailedSave()); } else { $this->setChildOldValues($iterator->key(), array('new' => $iterator->current()->getIsNewRecord(), 'oldValues' => $iterator->current()->getOldAttributes(), 'current' => $iterator->current()->getAttributes())); } $canDeleteThis = true; if ($iterator->current() instanceof ActiveRecordSaveAllInterface) { $canDeleteThis = $iterator->current()->beforeDeleteFullInternal(true); if (!$canDeleteThis) { if (method_exists($iterator->current(), 'hasActionErrors')) { if ($iterator->current()->hasActionErrors()) { $this->mergeActionErrors($iterator->current()->getActionErrors()); } } } } elseif (method_exists($iterator->current(), 'beforeDeleteFull')) { $canDeleteThis = $iterator->current()->beforeDeleteFull(); if (!$canDeleteThis) { $errors = $iterator->current()->getErrors(); foreach ($errors as $errorField => $errorDescription) { $this->addActionError($errorDescription, 0, $errorField, Tools::getClassName($iterator->current())); } } } if (!$canDeleteThis) { $canDeleteFull = false; } } $iterator->next(); } } } } if ($this->hasActionErrors()) { $canDeleteFull = false; } elseif (!$canDeleteFull) { $this->addActionError('beforeDeleteFullInternal checks failed'); } if (!$canDeleteFull) { $this->resetChildHasChanges(); } return $canDeleteFull; }
/** * Return default input field type based on column schema for attribute * @param string $attributeName * @param \yii\db\ColumnSchema $columnSchema * @param array|null $config attributeConfig (active element) * @return string */ public static function getDefaultInputFieldType($attributeName, $columnSchema, $config = null) { if (is_array($config) && $config) { $type = ArrayHelper::getValue($config, 'type', ''); if ($type != '') { return $type; } } switch ($attributeName) { case 'id': $type = self::INPUT_STATIC; break; case 'created_at': case 'createdAt': case 'modified_at': case 'modifiedAt': $type = self::INPUT_STATIC; break; default: switch ($columnSchema->type) { case 'string': // case 'char': // case 'varchar': if (is_array($columnSchema->enumValues) && $columnSchema->enumValues) { $type = self::INPUT_DROPDOWN_LIST; } else { $type = self::INPUT_TEXT; } break; case 'text': //case 'tinytext': //case 'mediumtext': //case 'longtext': //case 'tinytext': //case 'mediumtext': //case 'longtext': case 'binary': //case 'varbinary': //case 'blob': //case 'tinyblob': //case 'mediumblob': //case 'longblob': $type = self::INPUT_TEXTAREA; break; case 'int': case 'integer': case 'tinyint': case 'smallint': case 'mediumint': case 'bigint': if ($columnSchema->size == 1) { //$type = self::INPUT_CHECKBOX_SWITCH; $type = self::INPUT_CHECKBOX_ICHECK; //$type = self::INPUT_CHECKBOX; } else { $type = self::INPUT_INTEGER; } break; case 'float': case 'real': case 'double': case 'decimal': case 'numeric': $type = self::INPUT_DECIMAL; break; case 'date': if ($columnSchema->size == 4) { $type = self::INPUT_YEAR; } else { $type = self::INPUT_DATE; } break; case 'datetime': $type = self::INPUT_DATETIME; break; case 'time': $type = self::INPUT_TIME; break; default: \fangface\Tools::debug($columnSchema, __CLASS__); $type = self::INPUT_TEXT; break; } break; } return $type; }
/** * Adds a new action warning * @param string $message new warning message * @param integer $code new warning code * @param string $attribute attribute to which the error applies * @param string $modelName model to which the error applies */ public function addActionWarning($message, $code = 0, $attribute = '', $modelName = null) { $message = is_array($message) ? $message : array($message); $this->actionWarnings[] = array('message' => $message, 'code' => $code, 'attribute' => $attribute, 'model' => $modelName !== null ? $modelName : (true ? Tools::getClassName($this) : get_called_class())); }
/** * Save the current objects attributes * * @param boolean $runValidation * should validations be executed on all models before allowing saveAll() * @param boolean $hasParentModel * whether this method was called from the top level or by a parent * If false, it means the method was called at the top level * @param boolean $fromSaveAll * has the save() call come from saveAll() or not * @return boolean * did save() successfully process */ public function save($runValidation = true, $hasParentModel = false, $push = false, $fromSaveAll = false) { if ($this->getReadOnly() && !$hasParentModel) { // return failure if we are at the top of the tree and should not be asking to saveAll // not allowed to amend or delete $message = 'Attempting to save on ' . Tools::getClassName($this) . ' readOnly model'; //$this->addActionError($message); throw new Exception($message); } elseif ($this->getReadOnly() && $hasParentModel) { $message = 'Skipping save on ' . Tools::getClassName($this) . ' readOnly model'; $this->addActionWarning($message); return true; } $allOk = true; if (($this->loaded || $this->isNewRecord && $this->isNewPrepared) && $this->changedData) { if ($this->entityId === false) { throw new Exception('No entity id available for ' . __METHOD__ . '()'); } if (!$this->objectId) { throw new Exception('No object id available for ' . __METHOD__ . '()'); } $thisTime = time(); $attributeDefs = $this->getEntityAttributeList(); // we do not record modified, modifiedBy, created or createdBy against individual attributes but we will support // automatically updating them if these attributeNames have been setup as their own attributes for this entity if (\Yii::$app->has('user')) { try { if (\Yii::$app->user->isGuest) { $userId = 0; } else { $userId = \Yii::$app->user->getId(); } } catch (InvalidConfigException $e) { if ($e->getMessage() == 'User::identityClass must be set.') { $userId = 0; } else { throw $e; } } } $extraChangeFields = array(); if (array_key_exists('modifiedAt', $attributeDefs)) { if (!array_key_exists('modifiedAt', $this->changedData)) { $exists = array_key_exists('modifiedAt', $this->data); $this->changedData['modifiedAt'] = array_key_exists('modifiedAt', $this->data) ? $this->data['modifiedAt'] : Tools::DATE_TIME_DB_EMPTY; $this->data['modifiedAt'] = date(Tools::DATETIME_DATABASE, $thisTime); if ($this->lazyAttributes && array_key_exists('modifiedAt', $this->lazyAttributes)) { unset($this->lazyAttributes['modifiedAt']); } } } if (array_key_exists('modifiedBy', $attributeDefs)) { if (!array_key_exists('modifiedBy', $this->changedData)) { if (!isset($this->data['modifiedBy']) || $this->data['modifiedBy'] != $userId) { $this->changedData['modifiedBy'] = array_key_exists('modifiedBy', $this->data) ? $this->data['modifiedBy'] : 0; $this->data['modifiedBy'] = $userId; if ($this->lazyAttributes && array_key_exists('modifiedBy', $this->lazyAttributes)) { unset($this->lazyAttributes['modifiedBy']); } } } } if (array_key_exists('createdAt', $attributeDefs)) { if (!array_key_exists('createdAt', $this->changedData)) { $exists = array_key_exists('createdAt', $this->data); if (!$exists || $exists && $this->data['createdAt'] == Tools::DATE_TIME_DB_EMPTY) { $this->changedData['createdAt'] = array_key_exists('createdAt', $this->data) ? $this->data['createdAt'] : Tools::DATE_TIME_DB_EMPTY; $this->data['createdAt'] = date(Tools::DATETIME_DATABASE, $thisTime); if ($this->lazyAttributes && array_key_exists('created', $this->lazyAttributes)) { unset($this->lazyAttributes['createdAt']); } } } } if (array_key_exists('createdBy', $attributeDefs)) { if (!array_key_exists('createdBy', $this->changedData)) { $exists = array_key_exists('createdBy', $this->data); if (!$exists || $exists && $this->data['createdBy'] != $userId) { $this->changedData['createdBy'] = array_key_exists('createdBy', $this->data) ? $this->data['createdBy'] : 0; $this->data['createdBy'] = $userId; if ($this->lazyAttributes && array_key_exists('createdBy', $this->lazyAttributes)) { unset($this->lazyAttributes['createdBy']); } } } } if (!$this->changedData) { $updateColumns = $this->data; } else { $updateColumns = array(); foreach ($this->changedData as $field => $value) { $updateColumns[$field] = $this->data[$field]; } } foreach ($updateColumns as $attributeName => $attributeValue) { $attributeId = 0; $attributeDef = isset($attributeDefs[$attributeName]) ? $attributeDefs[$attributeName] : false; if ($attributeDef) { $attributeId = $attributeDef['id']; } $ok = false; if ($attributeId) { $attributeValue = Tools::formatAttributeValue($attributeValue, $attributeDef); if ($attributeDef['deleteOnDefault'] && $attributeValue === Tools::formatAttributeValue($attributeDef['defaultValue'], $attributeDef)) { // value is default so we will remove it from the attribtue table as not required $ok = true; if (array_key_exists($attributeName, $this->attributeValues)) { $ok = $this->attributeValues[$attributeName]->deleteFull(true); if ($ok) { $this->setChildOldValues($attributeName, true, 'deleted'); } else { if ($this->attributeValues[$attributeName]->hasActionErrors()) { $this->mergeActionErrors($this->attributeValues[$attributeName]->getActionErrors()); } else { $this->addActionError('Failed to delete attribute', 0, $attributeName); } } } } else { switch (strtolower($attributeDef['dataType'])) { case 'boolean': $attributeValue = $attributeValue ? '1' : '0'; break; default: break; } if (is_null($attributeValue)) { // typically where null is permitted it will be the default value with deleteOnDefault set, so should have been caught in the deleteOnDefault if ($attributeDef['isNullable']) { $attributeValue = '__NULL__'; // we do not want to allow null in the attribute database so use this string to denote null when it is permitted } else { $attributeValue = '__NULL__'; // needs to be caught elsewhere } } if (!array_key_exists($attributeName, $this->attributeValues)) { $this->attributeValues[$attributeName] = new $this->attributeValuesClass(); $this->attributeValues[$attributeName]->entityId = $this->entityId; $this->attributeValues[$attributeName]->attributeId = $attributeId; // this is a new entry that has not been included in the childHasChanges array yet $this->setChildHasChanges($attributeName); $this->setChildOldValues($attributeName, $this->attributeValues[$attributeName]->getResetDataForFailedSave()); } if ($this->newObjectId) { $this->attributeValues[$attributeName]->objectId = $this->newObjectId; } else { $this->attributeValues[$attributeName]->objectId = $this->objectId; } $this->attributeValues[$attributeName]->value = $attributeValue; $ok = $this->attributeValues[$attributeName]->save(false, null, true, true); if (!$ok) { if ($this->attributeValues[$attributeName]->hasActionErrors()) { $this->mergeActionErrors($this->attributeValues[$attributeName]->getActionErrors()); } else { $this->addActionError('Failed to save attribute', 0, $attributeName); } } } } if (!$ok) { $allOk = false; } } if ($allOk) { $this->changedData = array(); $this->loaded = true; $this->isNewRecord = false; } } if ($allOk && $this->loaded && $this->newObjectId) { // we need to update the objectId for all attributes belonging to // the current object to a new value taking into account that not // all attributes might have been loaded yet, if any. foreach ($this->attributeValues as $attributeName => $attributeValue) { $this->attributeValues[$attributeName]->objectId = $this->newObjectId; $this->attributeValues[$attributeName]->setOldAttribute('objectId', $this->newObjectId); } $attributeValuesClass = $this->attributeValuesClass; $ok = $attributeValuesClass::updateAll(array('objectId' => $this->newObjectId), array('objectId' => $this->objectId)); $this->objectId = $this->newObjectId; $this->newObjectId = false; } return $allOk; }
/** * Convert a string to an array of lines word wrapped at a fixed number of characters using * a multi-byte safe word wrap function * * @param string $inputText * @param integer $lines * @param integer $width * @param boolean $cut * @param string $break * @param string $charSet * @return string */ public static function convertLongTextToLines($inputText, $lines = 5, $width = 35, $cut = true, $break = "\n", $charSet = 'utf-8') { $newText = Tools::iconv_wordwrap($inputText, $width, $break, $cut, $charSet); $array = mb_split($break, $newText); $arrayCount = count($array); if ($arrayCount < $lines) { for ($x = $arrayCount; $x < $lines; $x++) { $array[$x] = ''; } } return $array; }
/** * PHP setter magic method. * This method is overridden so that AR attributes can be accessed like properties, * but only if the current model is not read only * @param string $name property name * @param mixed $value property value * @throws Exception if the current record is read only */ public function __set($name, $value) { if ($this->getReadOnly()) { throw new Exception('Attempting to set attribute `' . $name . '` on a read only ' . Tools::getClassName($this) . ' model'); } parent::__set($name, $value); }