Example #1
0
	/**
	 * 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'];
	}
Example #2
0
 /**
  * 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'];
 }