Example #1
0
 /**
  * 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;
     }));
 }
Example #2
0
 /**
  * @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;
 }
Example #4
0
 /**
  * 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;
 }
Example #5
0
 /**
  * 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;
 }
Example #7
0
 /**
  * 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;
 }
Example #8
0
 /**
  * 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);
 }