/** * Writes all changes to this object to the database. * - It will insert a record whenever ID isn't set, otherwise update. * - All relevant tables will be updated. * - $this->onBeforeWrite() gets called beforehand. * - Extensions such as Versioned will ammend the database-write to ensure that a version is saved. * - Calls to {@link DataObjectLog} can be used to see everything that's been changed. * * @uses DataObjectDecorator->augmentWrite() * * @param boolean $showDebug Show debugging information * @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists * @param boolean $forceWrite Write to database even if there are no changes * @param boolean $writeComponents Call write() on all associated component instances which were previously * retrieved through {@link getComponent()}, {@link getComponents()} or {@link getManyManyComponents()} * (Default: false) * * @return int The ID of the record * @throws ValidationException Exception that can be caught and handled by the calling function */ public function write($showDebug = false, $forceInsert = false, $forceWrite = false, $writeComponents = false) { $firstWrite = false; $this->brokenOnWrite = true; $isNewRecord = false; if(self::get_validation_enabled()) { $valid = $this->validate(); if(!$valid->valid()) { throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ". Object not written.", E_USER_WARNING); return false; } } $this->onBeforeWrite(); if($this->brokenOnWrite) { user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR); } // New record = everything has changed if(($this->ID && is_numeric($this->ID)) && !$forceInsert) { $dbCommand = 'update'; // Update the changed array with references to changed obj-fields foreach($this->record as $k => $v) { if(is_object($v) && method_exists($v, 'isChanged') && $v->isChanged()) { $this->changed[$k] = true; } } } else{ $dbCommand = 'insert'; $this->changed = array(); foreach($this->record as $k => $v) { $this->changed[$k] = 2; } $firstWrite = true; } // No changes made if($this->changed) { foreach($this->getClassAncestry() as $ancestor) { if(self::has_own_table($ancestor)) $ancestry[] = $ancestor; } // Look for some changes to make if(!$forceInsert) unset($this->changed['ID']); $hasChanges = false; foreach($this->changed as $fieldName => $changed) { if($changed) { $hasChanges = true; break; } } if($hasChanges || $forceWrite || !$this->record['ID']) { // New records have their insert into the base data table done first, so that they can pass the // generated primary key on to the rest of the manipulation if((!isset($this->record['ID']) || !$this->record['ID']) && isset($ancestry[0])) { $baseTable = $ancestry[0]; DB::query("INSERT INTO `{$baseTable}` SET Created = NOW()"); $this->record['ID'] = DB::getGeneratedID($baseTable); $this->changed['ID'] = 2; $isNewRecord = true; } // Divvy up field saving into a number of database manipulations $manipulation = array(); if(isset($ancestry) && is_array($ancestry)) { foreach($ancestry as $idx => $class) { $classSingleton = singleton($class); foreach($this->record as $fieldName => $fieldValue) { if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) { $fieldObj = $this->dbObject($fieldName); if(!isset($manipulation[$class])) $manipulation[$class] = array(); // if database column doesn't correlate to a DBField instance... if(!$fieldObj) { $fieldObj = DBField::create('Varchar', $this->record[$fieldName], $fieldName); } // CompositeDBFields handle their own value storage; regular fields need to be // re-populated from the database if(!$fieldObj instanceof CompositeDBField) { $fieldObj->setValue($this->record[$fieldName], $this->record); } $fieldObj->writeToManipulation($manipulation[$class]); } } // Add the class name to the base object if($idx == 0) { $manipulation[$class]['fields']["LastEdited"] = "now()"; if($dbCommand == 'insert') { $manipulation[$class]['fields']["Created"] = "now()"; //echo "<li>$this->class - " .get_class($this); $manipulation[$class]['fields']["ClassName"] = "'$this->class'"; } } // In cases where there are no fields, this 'stub' will get picked up on if(self::has_own_table($class)) { $manipulation[$class]['command'] = $dbCommand; $manipulation[$class]['id'] = $this->record['ID']; } else { unset($manipulation[$class]); } } } $this->extend('augmentWrite', $manipulation); // New records have their insert into the base data table done first, so that they can pass the // generated ID on to the rest of the manipulation if(isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) { $manipulation[$baseTable]['command'] = 'update'; } DB::manipulate($manipulation); if(isset($isNewRecord) && $isNewRecord) { DataObjectLog::addedObject($this); } else { DataObjectLog::changedObject($this); } $this->onAfterWrite(); $this->changed = null; } elseif ( $showDebug ) { echo "<b>Debug:</b> no changes for DataObject<br />"; } // Clears the cache for this object so get_one returns the correct object. $this->flushCache(); if(!isset($this->record['Created'])) { $this->record['Created'] = date('Y-m-d H:i:s'); } $this->record['LastEdited'] = date('Y-m-d H:i:s'); } // Write ComponentSets as necessary if($writeComponents) { $this->writeComponents(true); } return $this->record['ID']; }
/** * Writes all changes to this object to the database. * - It will insert a record whenever ID isn't set, otherwise update. * - All relevant tables will be updated. * - $this->onBeforeWrite() gets called beforehand. * - Extensions such as Versioned will ammend the database-write to ensure that a version is saved. * - Calls to {@link DataObjectLog} can be used to see everything that's been changed. * * @param boolean $showDebug Show debugging information * @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists * @param boolean $forceWrite Write to database even if there are no changes * * @return int The ID of the record */ public function write($showDebug = false, $forceInsert = false, $forceWrite = false) { $firstWrite = false; $this->brokenOnWrite = true; $isNewRecord = false; $this->onBeforeWrite(); if ($this->brokenOnWrite) { user_error("{$this->class} has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR); } // New record = everything has changed if ($this->ID && is_numeric($this->ID) && !$forceInsert) { $dbCommand = 'update'; } else { $dbCommand = 'insert'; $this->changed = array(); foreach ($this->record as $k => $v) { $this->changed[$k] = 2; } $firstWrite = true; } // No changes made if ($this->changed) { foreach ($this->getClassAncestry() as $ancestor) { if (ClassInfo::hasTable($ancestor)) { $ancestry[] = $ancestor; } } // Look for some changes to make unset($this->changed['ID']); $hasChanges = false; foreach ($this->changed as $fieldName => $changed) { if ($changed) { $hasChanges = true; break; } } if ($hasChanges || $forceWrite || !$this->record['ID']) { // New records have their insert into the base data table done first, so that they can pass the // generated primary key on to the rest of the manipulation if (!$this->record['ID'] && isset($ancestry[0])) { $baseTable = $ancestry[0]; DB::query("INSERT INTO `{$baseTable}` SET Created = NOW()"); $this->record['ID'] = DB::getGeneratedID($baseTable); $this->changed['ID'] = 2; $isNewRecord = true; } // Divvy up field saving into a number of database manipulations if (isset($ancestry) && is_array($ancestry)) { foreach ($ancestry as $idx => $class) { $classSingleton = singleton($class); foreach ($this->record as $fieldName => $value) { if (isset($this->changed[$fieldName]) && $this->changed[$fieldName] && ($fieldType = $classSingleton->fieldExists($fieldName))) { $manipulation[$class]['fields'][$fieldName] = $value ? "'" . addslashes($value) . "'" : singleton($fieldType)->nullValue(); } } // Add the class name to the base object if ($idx == 0) { $manipulation[$class]['fields']["LastEdited"] = "now()"; if ($dbCommand == 'insert') { $manipulation[$class]['fields']["Created"] = "now()"; //echo "<li>$this->class - " .get_class($this); $manipulation[$class]['fields']["ClassName"] = "'{$this->class}'"; } } // In cases where there are no fields, this 'stub' will get picked up on if (ClassInfo::hasTable($class)) { $manipulation[$class]['command'] = $dbCommand; $manipulation[$class]['id'] = $this->record['ID']; } else { unset($manipulation[$class]); } } } $this->extend('augmentWrite', $manipulation); // New records have their insert into the base data table done first, so that they can pass the // generated ID on to the rest of the manipulation if (isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) { $manipulation[$baseTable]['command'] = 'update'; } DB::manipulate($manipulation); if (isset($isNewRecord) && $isNewRecord) { DataObjectLog::addedObject($this); } else { DataObjectLog::changedObject($this); } $this->changed = null; } elseif ($showDebug) { echo "<b>Debug:</b> no changes for DataObject<br />"; } // Clears the cache for this object so get_one returns the correct object. $this->flushCache(); if (!isset($this->record['Created'])) { $this->record['Created'] = date('Y-m-d H:i:s'); } $this->record['LastEdited'] = date('Y-m-d H:i:s'); } // Write ComponentSets as necessary if ($this->components) { foreach ($this->components as $component) { $component->write($firstWrite); } } return $this->record['ID']; }