/** * Saves the model to the database. Models are only saved if they have been * modified and related models are saved before this model. If a related model * is modified and needs saving the deems the model to be modified and need * saving, which ensures that keys are updated. * Cyclic relationships are prevented from causing problems by the * save only proceeding to non-saved models. */ public function save($runValidation = true, array $attributeNames = null) { if ($attributeNames !== null) { throw new NotSupportedException(); } if ($this->isSaving) { return true; } $this->isSaving = true; try { if (!$runValidation || $this->validate()) { if ($this->beforeSave()) { $beans = array_values($this->modelClassNameToBean); $this->linkBeans(); // The breakLink/link is deferred until the save to avoid // disconnecting or creating an empty row if the model was // never actually saved. foreach ($this->unlinkedRelationNames as $key => $relationName) { $bean = $this->attributeNameToBeanAndClassName[$relationName][0]; $relationAndOwns = static::getRelationNameToRelationTypeModelClassNameAndOwnsForModel(); $relatedModelClassName = $relationAndOwns[$relationName][1]; $tempRelatedModelClassName = $relatedModelClassName; self::resolveModelClassNameForClassesWithoutBeans($tempRelatedModelClassName); $relatedTableName = self::getTableName($tempRelatedModelClassName); $linkName = strtolower($relationName); if ($linkName == strtolower($relatedModelClassName)) { $linkName = null; } elseif (static::getRelationType($relationName) == self::HAS_ONE && static::getRelationLinkType($relationName) == self::LINK_TYPE_SPECIFIC) { $linkName = strtolower(static::getRelationLinkName($relationName)); } ZurmoRedBeanLinkManager::breakLink($bean, $relatedTableName, $linkName); //Check the $this->{$relationName} second in the if clause to avoid accidentially getting //a relation to now save. //todo: this needs to be properly handled. if (isset($this->unlinkedOwnedRelatedModelsToRemove[$relationName]) && $this->{$relationName} !== null) { //Remove hasOne owned related models that are no longer needed because they have //been replaced with another hasOne owned model. if ($this->unlinkedOwnedRelatedModelsToRemove[$relationName]->id > 0) { $this->unlinkedOwnedRelatedModelsToRemove[$relationName]->unrestrictedDelete(); } unset($this->unlinkedOwnedRelatedModelsToRemove[$relationName]); } unset($this->unlinkedRelationNames[$key]); } assert('count($this->unlinkedRelationNames) == 0'); foreach ($this->relationNameToRelatedModel as $relationName => $relatedModel) { $relationAndOwns = static::getRelationNameToRelationTypeModelClassNameAndOwnsForModel(); $relationType = $relationAndOwns[$relationName][0]; if (!in_array($relationType, array(self::HAS_ONE_BELONGS_TO, self::HAS_MANY_BELONGS_TO))) { if ($relatedModel->isModified() || $this->isAttributeRequired($relationName)) { //If the attribute is required, but already exists and has not been modified we do //not have to worry about saving it. if ($this->isSavableFromRelation && !($this->isAttributeRequired($relationName) && !$relatedModel->isModified() && $relatedModel->id > 0)) { if (!$relatedModel->save(false)) { $this->isSaving = false; return false; } } elseif ($relatedModel->isModified()) { throw new NotSuportedException(); } } } if ($relatedModel instanceof RedBeanModel) { $bean = $this->attributeNameToBeanAndClassName[$relationName][0]; $relationAndOwns = static::getRelationNameToRelationTypeModelClassNameAndOwnsForModel(); $relatedModelClassName = $relationAndOwns[$relationName][1]; $linkName = strtolower($relationName); if (strtolower($linkName) == strtolower($relatedModelClassName) || static::getRelationLinkType($relationName) == self::LINK_TYPE_ASSUMPTIVE) { $linkName = null; } elseif ($relationType == self::HAS_ONE && static::getRelationLinkType($relationName) == self::LINK_TYPE_SPECIFIC) { $linkName = strtolower(static::getRelationLinkName($relationName)); } elseif ($relationType == RedBeanModel::HAS_MANY_BELONGS_TO || $relationType == RedBeanModel::HAS_ONE_BELONGS_TO) { $label = 'Relations of type HAS_MANY_BELONGS_TO OR HAS_ONE_BELONGS_TO must have the relation name ' . 'the same as the related model class name. Relation: {relationName} ' . 'Relation model class name: {relationModelClassName}'; throw new NotSupportedException(Zurmo::t('Core', $label, array('{relationName}' => $linkName, '{relationModelClassName}' => $relatedModelClassName))); } //Needed to exclude HAS_ONE_BELONGS_TO because an additional column was being created //on the wrong side. if ($relationType != RedBeanModel::HAS_ONE_BELONGS_TO && ($relatedModel->isModified() || $relatedModel->id > 0 || $this->isAttributeRequired($relationName))) { $relatedModel = $this->relationNameToRelatedModel[$relationName]; $relatedBean = $relatedModel->getClassBean($relatedModelClassName); //Exclude HAS_MANY_BELONGS_TO because if the existing relation is unlinked, then //this link should not be reactivated, because it will improperly create the bean //in the database. if (!($relationType == RedBeanModel::HAS_MANY_BELONGS_TO && $this->{$relationName}->id < 0)) { ZurmoRedBeanLinkManager::link($bean, $relatedBean, $linkName); } if (!RedBeanDatabase::isFrozen()) { $tableName = self::getTableName(static::getAttributeModelClassName($relationName)); $columnName = self::getForeignKeyName(get_class($this), $relationName); RedBeanColumnTypeOptimizer::optimize($tableName, $columnName, 'id'); } } } } $baseModelClassName = null; foreach ($this->modelClassNameToBean as $modelClassName => $bean) { R::store($bean); assert('$bean->id > 0'); if (!RedBeanDatabase::isFrozen()) { static::resolveMixinsOnSaveForEnsuringColumnsAreCorrectlyFormed($baseModelClassName, $modelClassName); $baseModelClassName = $modelClassName; } } $this->modified = false; $this->afterSave(); $calledModelClassName = get_called_class(); if ($calledModelClassName::isCacheable()) { RedBeanModelsCache::cacheModel($this); } $this->isSaving = false; return true; } } $this->isSaving = false; return false; } catch (Exception $e) { $this->isSaving = false; throw $e; } }
public function testMixedInPersonInUser() { $user = new User(); $user->username = '******'; $user->lastName = 'Dude'; $this->assertTrue($user->save()); $this->assertTrue($user->isAttribute('id')); // From RedBeanModel. $this->assertTrue($user->isAttribute('createdDateTime')); // From Item. $this->assertTrue($user->isAttribute('firstName')); // From Person. $this->assertTrue($user->isAttribute('username')); // From User. $this->assertTrue($user->isRelation('createdByUser')); // From Item. $this->assertTrue($user->isRelation('rights')); // From Permitable. $this->assertTrue($user->isRelation('title')); // From Person. $this->assertTrue($user->isRelation('manager')); // From User. unset($user); $user = User::getByUsername('dude'); $this->assertTrue($user->isAttribute('id')); // From RedBeanModel. $this->assertTrue($user->isAttribute('createdDateTime')); // From Item. $this->assertTrue($user->isAttribute('firstName')); // From Person. $this->assertTrue($user->isAttribute('username')); // From User. $this->assertTrue($user->isRelation('createdByUser')); // From Item. $this->assertTrue($user->isRelation('rights')); // From Permitable. $this->assertTrue($user->isRelation('title')); // From Person. $this->assertTrue($user->isRelation('manager')); // From User. RedBeanModelsCache::cacheModel($user); $modelIdentifier = $user->getModelIdentifier(); unset($user); RedBeanModelsCache::forgetAll(true); // Forget it at the php level. RedBeansCache::forgetAll(); if (MEMCACHE_ON) { $user = RedBeanModelsCache::getModel($modelIdentifier); $this->assertTrue($user->isAttribute('id')); // From RedBeanModel. $this->assertTrue($user->isAttribute('createdDateTime')); // From Item. $this->assertTrue($user->isAttribute('firstName')); // From Person. $this->assertTrue($user->isAttribute('username')); // From User. $this->assertTrue($user->isRelation('createdByUser')); // From Item. $this->assertTrue($user->isRelation('rights')); // From Permitable. $this->assertTrue($user->isRelation('title')); // From Person. $this->assertTrue($user->isRelation('manager')); // From User. } }
public function testFileContentModelNotBeingCached() { $fileContent = new FileContent(); $fileContent->content = str_repeat('a', 1000); $this->assertTrue($fileContent->save()); $modelIdentifier = $fileContent->getModelIdentifier(); RedBeanModelsCache::cacheModel($fileContent); try { RedBeanModelsCache::getModel($modelIdentifier); } catch (NotFoundException $e) { $this->fail('NotFoundException exception is thrown.'); } $prefix = RedBeanModelsCache::getCachePrefix($modelIdentifier, RedBeanModelsCache::$cacheType); $cachedData = false; if (isset(Yii::app()->cache)) { $cachedData = Yii::app()->cache->get($prefix . $modelIdentifier); } $this->assertFalse($cachedData); }
/** * Saves the model to the database. Models are only saved if they have been * modified and related models are saved before this model. If a related model * is modified and needs saving the deems the model to be modified and need * saving, which ensures that keys are updated. * Cyclic relationships are prevented from causing problems by the * save only proceeding to non-saved models. */ public function save($runValidation = true, array $attributeNames = null) { if ($attributeNames !== null) { throw new NotSupportedException(); } if ($this->isSaving) { return true; } $this->isSaving = true; try { if (!$runValidation || $this->validate()) { if ($this->beforeSave()) { $beans = array_values($this->modelClassNameToBean); $this->linkBeans(); // The breakLink/link is deferred until the save to avoid // disconnecting or creating an empty row if the model was // never actually saved. foreach ($this->unlinkedRelationNames as $key => $relationName) { $bean = $this->attributeNameToBeanAndClassName[$relationName][0]; $relatedModelClassName = $this->relationNameToRelationTypeModelClassNameAndOwns[$relationName][1]; $relatedTableName = self::getTableName($relatedModelClassName); $linkName = strtolower($relationName); if ($linkName == strtolower($relatedModelClassName)) { $linkName = null; } ZurmoRedBeanLinkManager::breakLink($bean, $relatedTableName, $linkName); unset($this->unlinkedRelationNames[$key]); } assert('count($this->unlinkedRelationNames) == 0'); foreach ($this->relationNameToRelatedModel as $relationName => $relatedModel) { $relationType = $this->relationNameToRelationTypeModelClassNameAndOwns[$relationName][0]; if (!in_array($relationType, array(self::HAS_ONE_BELONGS_TO, self::HAS_MANY_BELONGS_TO))) { if ($relatedModel->isModified() || $this->isAttributeRequired($relationName)) { //If the attribute is required, but already exists and has not been modified we do //not have to worry about saving it. if ($this->isSavableFromRelation && !($this->isAttributeRequired($relationName) && !$relatedModel->isModified() && $relatedModel->id > 0)) { if (!$relatedModel->save(false)) { $this->isSaving = false; return false; } } elseif ($relatedModel->isModified()) { throw new NotSuportedException(); } } } if ($relatedModel instanceof RedBeanModel) { $bean = $this->attributeNameToBeanAndClassName[$relationName][0]; $relatedModelClassName = $this->relationNameToRelationTypeModelClassNameAndOwns[$relationName][1]; $linkName = strtolower($relationName); if (strtolower($linkName) == strtolower($relatedModelClassName)) { $linkName = null; } elseif ($relationType == RedBeanModel::HAS_MANY_BELONGS_TO || $relationType == RedBeanModel::HAS_ONE_BELONGS_TO) { $label = 'Relations of type HAS_MANY_BELONGS_TO OR HAS_ONE_BELONGS_TO must have the relation name ' . 'the same as the related model class name. Relation: {relationName} ' . 'Relation model class name: {relationModelClassName}'; throw new NotSupportedException(Yii::t('Default', $label, array('{relationName}' => $linkName, '{relationModelClassName}' => $relatedModelClassName))); } //Needed to exclude HAS_ONE_BELONGS_TO because an additional column was being created //on the wrong side. if ($relationType != RedBeanModel::HAS_ONE_BELONGS_TO && ($relatedModel->isModified() || $relatedModel->id > 0 || $this->isAttributeRequired($relationName))) { $relatedModel = $this->relationNameToRelatedModel[$relationName]; $relatedBean = $relatedModel->getClassBean($relatedModelClassName); ZurmoRedBeanLinkManager::link($bean, $relatedBean, $linkName); if (!RedBeanDatabase::isFrozen()) { $tableName = self::getTableName($this->getAttributeModelClassName($relationName)); $columnName = self::getForeignKeyName(get_class($this), $relationName); RedBeanColumnTypeOptimizer::optimize($tableName, $columnName, 'id'); } } } } $baseModelClassName = null; foreach ($this->modelClassNameToBean as $modelClassName => $bean) { R::store($bean); assert('$bean->id > 0'); if (!RedBeanDatabase::isFrozen()) { static::resolveMixinsOnSaveForEnsuringColumnsAreCorrectlyFormed($baseModelClassName, $modelClassName); $baseModelClassName = $modelClassName; } } $this->modified = false; $this->afterSave(); RedBeanModelsCache::cacheModel($this); $this->isSaving = false; return true; } } $this->isSaving = false; return false; } catch (Exception $e) { $this->isSaving = false; throw $e; } }