/** * Generate and write the database manipulation for all changed fields * * @param string $baseTable Base table * @param string $now Timestamp to use for the current time * @param bool $isNewRecord If this is a new record */ protected function writeManipulation($baseTable, $now, $isNewRecord) { // Generate database manipulations for each class $manipulation = array(); foreach ($this->getClassAncestry() as $class) { if (self::has_own_table($class)) { $this->prepareManipulationTable($baseTable, $now, $isNewRecord, $manipulation, $class); } } // Allow extensions to extend this manipulation $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 ($isNewRecord) { $manipulation[$baseTable]['command'] = 'update'; } // Perform the manipulation DB::manipulate($manipulation); }
/** * 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. * * @uses DataExtension->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()) { // Used by DODs to clean up after themselves, eg, Versioned $this->extend('onAfterSkippedWrite'); 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 $baseTable = $ancestry[0]; if ((!isset($this->record['ID']) || !$this->record['ID']) && isset($ancestry[0])) { DB::query("INSERT INTO \"{$baseTable}\" (\"Created\") VALUES (" . DB::getConn()->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_field('Varchar', $this->record[$fieldName], $fieldName); } // Both CompositeDBFields and regular fields need to be repopulated $fieldObj->setValue($this->record[$fieldName], $this->record); if ($class != $baseTable || $fieldName != 'ID') { $fieldObj->writeToManipulation($manipulation[$class]); } } } // Add the class name to the base object if ($idx == 0) { $manipulation[$class]['fields']["LastEdited"] = "'" . SS_Datetime::now()->Rfc2822() . "'"; if ($dbCommand == 'insert') { $manipulation[$class]['fields']["Created"] = "'" . SS_Datetime::now()->Rfc2822() . "'"; //echo "<li>$this->class - " .get_class($this); $manipulation[$class]['fields']["ClassName"] = DB::getConn()->prepStringForDB($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); $this->onAfterWrite(); $this->changed = null; } elseif ($showDebug) { echo "<b>Debug:</b> no changes for DataObject<br />"; // Used by DODs to clean up after themselves, eg, Versioned $this->extend('onAfterSkippedWrite'); } // Clears the cache for this object so get_one returns the correct object. $this->flushCache(); if (!isset($this->record['Created'])) { $this->record['Created'] = SS_Datetime::now()->Rfc2822(); } $this->record['LastEdited'] = SS_Datetime::now()->Rfc2822(); } else { // Used by DODs to clean up after themselves, eg, Versioned $this->extend('onAfterSkippedWrite'); } // Write relations as necessary if ($writeComponents) { $this->writeComponents(true); } return $this->record['ID']; }
/** * @param String $identifier Unique identifier for this fixture type * @param Array $data Map of property names to their values. * @param Array $fixtures Map of fixture names to an associative array of their in-memory * identifiers mapped to their database IDs. Used to look up * existing fixtures which might be referenced in the $data attribute * via the => notation. * @return DataObject */ public function createObject($identifier, $data = null, $fixtures = null) { // We have to disable validation while we import the fixtures, as the order in // which they are imported doesnt guarantee valid relations until after the import is complete. $validationenabled = Config::inst()->get('DataObject', 'validation_enabled'); Config::inst()->update('DataObject', 'validation_enabled', false); $this->invokeCallbacks('beforeCreate', array($identifier, &$data, &$fixtures)); try { $class = $this->class; $obj = DataModel::inst()->{$class}->newObject(); // If an ID is explicitly passed, then we'll sort out the initial write straight away // This is just in case field setters triggered by the population code in the next block // Call $this->write(). (For example, in FileTest) if (isset($data['ID'])) { $obj->ID = $data['ID']; // The database needs to allow inserting values into the foreign key column (ID in our case) $conn = DB::getConn(); if (method_exists($conn, 'allowPrimaryKeyEditing')) { $conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($class), true); } $obj->write(false, true); if (method_exists($conn, 'allowPrimaryKeyEditing')) { $conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($class), false); } } // Populate defaults if ($this->defaults) { foreach ($this->defaults as $fieldName => $fieldVal) { if (isset($data[$fieldName]) && $data[$fieldName] !== false) { continue; } if (is_callable($fieldVal)) { $obj->{$fieldName} = $fieldVal($obj, $data, $fixtures); } else { $obj->{$fieldName} = $fieldVal; } } } // Populate overrides if ($data) { foreach ($data as $fieldName => $fieldVal) { // Defer relationship processing if ($obj->many_many($fieldName) || $obj->has_many($fieldName) || $obj->has_one($fieldName)) { continue; } $this->setValue($obj, $fieldName, $fieldVal, $fixtures); } } $obj->write(); // Save to fixture before relationship processing in case of reflexive relationships if (!isset($fixtures[$class])) { $fixtures[$class] = array(); } $fixtures[$class][$identifier] = $obj->ID; // Populate all relations if ($data) { foreach ($data as $fieldName => $fieldVal) { if ($obj->many_many($fieldName) || $obj->has_many($fieldName)) { $parsedItems = array(); $items = preg_split('/ *, */', trim($fieldVal)); foreach ($items as $item) { // Check for correct format: =><relationname>.<identifier>. // Ignore if the item has already been replaced with a numeric DB identifier if (!is_numeric($item) && !preg_match('/^=>[^\\.]+\\.[^\\.]+/', $item)) { throw new InvalidArgumentException(sprintf('Invalid format for relation "%s" on class "%s" ("%s")', $fieldName, $class, $item)); } $parsedItems[] = $this->parseValue($item, $fixtures); } $obj->write(); if ($obj->has_many($fieldName)) { $obj->getComponents($fieldName)->setByIDList($parsedItems); } elseif ($obj->many_many($fieldName)) { $obj->getManyManyComponents($fieldName)->setByIDList($parsedItems); } } elseif ($obj->has_one($fieldName)) { // Sets has_one with relation name $obj->{$fieldName . 'ID'} = $this->parseValue($fieldVal, $fixtures); } elseif ($obj->has_one(preg_replace('/ID$/', '', $fieldName))) { // Sets has_one with database field $obj->{$fieldName} = $this->parseValue($fieldVal, $fixtures); } } } $obj->write(); // If LastEdited was set in the fixture, set it here if ($data && array_key_exists('LastEdited', $data)) { $edited = $this->parseValue($data['LastEdited'], $fixtures); DB::manipulate(array($class => array("command" => "update", "id" => $obj->id, "fields" => array("LastEdited" => "'" . $edited . "'")))); } } catch (Exception $e) { Config::inst()->update('DataObject', 'validation_enabled', $validationenabled); throw $e; } Config::inst()->update('DataObject', 'validation_enabled', $validationenabled); $this->invokeCallbacks('afterCreate', array($obj, $identifier, &$data, &$fixtures)); return $obj; }
protected function overrideField($obj, $fieldName, $value, $fixtures = null) { $table = ClassInfo::table_for_object_field(get_class($obj), $fieldName); $value = $this->parseValue($value, $fixtures); DB::manipulate(array($table => array("command" => "update", "id" => $obj->ID, "fields" => array($fieldName => $value)))); $obj->{$fieldName} = $value; }
/** * Writes the fixture into the database directly using a database manipulation * * @param string $table * @param array $items */ protected function writeSQL($table, $items) { foreach ($items as $identifier => $fields) { $manipulation = array($table => array("fields" => array(), "command" => "insert")); foreach ($fields as $fieldName => $fieldVal) { $manipulation[$table]["fields"][$fieldName] = "'" . $this->parseFixtureVal($fieldVal) . "'"; } DB::manipulate($manipulation); $this->fixtureDictionary[$table][$identifier] = DB::getGeneratedID($table); } }
/** * Add an item to this many_many relationship * Does so by adding an entry to the joinTable. * * @param mixed $item * @param array $extraFields A map of additional columns to insert into the joinTable. * Column names should be ANSI quoted. */ public function add($item, $extraFields = array()) { // Ensure nulls or empty strings are correctly treated as empty arrays if (empty($extraFields)) { $extraFields = array(); } // Determine ID of new record if (is_numeric($item)) { $itemID = $item; } elseif ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } // Validate foreignID $foreignIDs = $this->getForeignID(); if (empty($foreignIDs)) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } // Apply this item to each given foreign ID record if (!is_array($foreignIDs)) { $foreignIDs = array($foreignIDs); } foreach ($foreignIDs as $foreignID) { // Check for existing records for this item if ($foreignFilter = $this->foreignIDWriteFilter($foreignID)) { // With the current query, simply add the foreign and local conditions // The query can be a bit odd, especially if custom relation classes // don't join expected tables (@see Member_GroupSet for example). $query = new SQLQuery("*", "\"{$this->joinTable}\""); $query->addWhere($foreignFilter); $query->addWhere(array("\"{$this->joinTable}\".\"{$this->localKey}\"" => $itemID)); $hasExisting = $query->count() > 0; } else { $hasExisting = false; } // Blank manipulation $manipulation = array($this->joinTable => array('command' => $hasExisting ? 'update' : 'insert', 'fields' => array())); if ($hasExisting) { $manipulation[$this->joinTable]['where'] = array("\"{$this->joinTable}\".\"{$this->foreignKey}\"" => $foreignID, "\"{$this->joinTable}\".\"{$this->localKey}\"" => $itemID); } if ($extraFields && $this->extraFields) { // Write extra field to manipluation in the same way // that DataObject::prepareManipulationTable writes fields foreach ($this->extraFields as $fieldName => $fieldSpec) { // Skip fields without an assignment if (array_key_exists($fieldName, $extraFields)) { $fieldObject = Object::create_from_string($fieldSpec, $fieldName); $fieldObject->setValue($extraFields[$fieldName]); $fieldObject->writeToManipulation($manipulation[$this->joinTable]); } } } $manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID; $manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID; DB::manipulate($manipulation); } }
/** * Add an item to this many_many relationship * Does so by adding an entry to the joinTable. * @param $extraFields A map of additional columns to insert into the joinTable */ function add($item, $extraFields = null) { if (is_numeric($item)) { $itemID = $item; } else { if ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } } // Validate foreignID if (!$this->foreignID) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } // Delete old entries, to prevent duplication $this->removeById($itemID); // Insert new entry/entries foreach ((array) $this->foreignID as $foreignID) { $manipulation = array(); $manipulation[$this->joinTable]['command'] = 'insert'; if ($extraFields) { foreach ($extraFields as $k => $v) { $manipulation[$this->joinTable]['fields'][$k] = "'" . Convert::raw2sql($v) . "'"; } } $manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID; $manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID; DB::manipulate($manipulation); } }
/** * Add an item to this many_many relationship * Does so by adding an entry to the joinTable. * @param $extraFields A map of additional columns to insert into the joinTable */ public function add($item, $extraFields = null) { if (is_numeric($item)) { $itemID = $item; } else { if ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } } // Validate foreignID if (!$this->foreignID) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } if ($filter = $this->foreignIDFilter()) { $query = new SQLQuery("*", array("\"{$this->joinTable}\"")); $query->setWhere($filter); $hasExisting = $query->count() > 0; } else { $hasExisting = false; } // Insert or update foreach ((array) $this->foreignID as $foreignID) { $manipulation = array(); if ($hasExisting) { $manipulation[$this->joinTable]['command'] = 'update'; $manipulation[$this->joinTable]['where'] = "\"{$this->joinTable}\".\"{$this->foreignKey}\" = " . "'" . Convert::raw2sql($foreignID) . "'" . " AND \"{$this->localKey}\" = {$itemID}"; } else { $manipulation[$this->joinTable]['command'] = 'insert'; } if ($extraFields) { foreach ($extraFields as $k => $v) { if (is_null($v)) { $manipulation[$this->joinTable]['fields'][$k] = 'NULL'; } else { $manipulation[$this->joinTable]['fields'][$k] = "'" . Convert::raw2sql($v) . "'"; } } } $manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID; $manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID; DB::manipulate($manipulation); } }
/** * Add an item to this many_many relationship. Does so by adding an entry * to the joinTable. * * @param mixed $item * @param array $extraFields A map of additional columns to insert into the * joinTable */ public function add($item, $extraFields = null) { if (is_numeric($item)) { $itemID = $item; } else { if ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } } $foreignIDs = $this->getForeignID(); $foreignFilter = $this->foreignIDWriteFilter(); // Validate foreignID if (!$foreignIDs) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } if ($foreignFilter) { $query = new SQLQuery("*", array("\"{$this->joinTable}\"")); $query->setWhere($foreignFilter); $hasExisting = $query->count() > 0; } else { $hasExisting = false; } // Insert or update foreach ((array) $foreignIDs as $foreignID) { $manipulation = array(); if ($hasExisting) { $manipulation[$this->joinTable]['command'] = 'update'; $manipulation[$this->joinTable]['where'] = "\"{$this->joinTable}\".\"{$this->foreignKey}\" = " . "'" . Convert::raw2sql($foreignID) . "'" . " AND \"{$this->localKey}\" = {$itemID}"; } else { $manipulation[$this->joinTable]['command'] = 'insert'; } if ($extraFields) { foreach ($extraFields as $k => $v) { if (is_null($v)) { $manipulation[$this->joinTable]['fields'][$k] = 'NULL'; } else { if (is_object($v) && $v instanceof DBField) { // rely on writeToManipulation to manage the changes // required for this field. $working = array('fields' => array()); // create a new instance of the field so we can // modify the field name to the correct version. $field = DBField::create_field(get_class($v), $v); $field->setName($k); $field->writeToManipulation($working); foreach ($working['fields'] as $extraK => $extraV) { $manipulation[$this->joinTable]['fields'][$extraK] = $extraV; } } else { $manipulation[$this->joinTable]['fields'][$k] = "'" . Convert::raw2sql($v) . "'"; } } } } $manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID; $manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID; DB::manipulate($manipulation); } }
/** * 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']; }
/** * Add an item to this many_many relationship * Does so by adding an entry to the joinTable. * * @param mixed $item * @param array $extraFields A map of additional columns to insert into the joinTable. * Column names should be ANSI quoted. */ public function add($item, $extraFields = array()) { // Ensure nulls or empty strings are correctly treated as empty arrays if (empty($extraFields)) { $extraFields = array(); } // Determine ID of new record if (is_numeric($item)) { $itemID = $item; } elseif ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } // Validate foreignID $foreignIDs = $this->getForeignID(); if (empty($foreignIDs)) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } // Apply this item to each given foreign ID record if (!is_array($foreignIDs)) { $foreignIDs = array($foreignIDs); } foreach ($foreignIDs as $foreignID) { // Check for existing records for this item if ($foreignFilter = $this->foreignIDWriteFilter($foreignID)) { // With the current query, simply add the foreign and local conditions // The query can be a bit odd, especially if custom relation classes // don't join expected tables (@see Member_GroupSet for example). $query = new SQLSelect("*", "\"{$this->joinTable}\""); $query->addWhere($foreignFilter); $query->addWhere(array("\"{$this->joinTable}\".\"{$this->localKey}\"" => $itemID)); $hasExisting = $query->count() > 0; } else { $hasExisting = false; } $manipulation = array(); if ($hasExisting) { $manipulation[$this->joinTable]['command'] = 'update'; $manipulation[$this->joinTable]['where'] = array("\"{$this->joinTable}\".\"{$this->foreignKey}\"" => $foreignID, "\"{$this->joinTable}\".\"{$this->localKey}\"" => $itemID); } else { $manipulation[$this->joinTable]['command'] = 'insert'; } if ($extraFields) { foreach ($extraFields as $fieldName => $fieldValue) { if (is_null($fieldValue)) { $manipulation[$this->joinTable]['fields'][$fieldName] = null; } elseif ($fieldValue instanceof DBField) { // rely on writeToManipulation to manage the changes // required for this field. $working = array('fields' => array()); // create a new instance of the field so we can // modify the field name to the correct version. $field = DBField::create_field(get_class($fieldValue), $fieldValue); $field->setName($fieldName); $field->writeToManipulation($working); foreach ($working['fields'] as $extraName => $extraValue) { $manipulation[$this->joinTable]['fields'][$extraName] = $extraValue; } } else { $manipulation[$this->joinTable]['fields'][$fieldName] = $fieldValue; } } } $manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID; $manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID; DB::manipulate($manipulation); } }
/** * Remove all fixtures previously defined through {@link createObject()} * or {@link createRaw()}, both from the internal fixture mapping and the database. * If the $class argument is set, limit clearing to items of this class. * * @param String $class */ public function clear($limitToClass = null) { $classes = $limitToClass ? array($limitToClass) : array_keys($this->fixtures); foreach ($classes as $class) { $ids = $this->fixtures[$class]; foreach ($ids as $id => $dbId) { if (class_exists($class)) { $class::get()->byId($dbId)->delete(); } else { $table = $class; DB::manipulate(array($table => array("fields" => array('ID' => $dbId), "command" => "delete"))); } unset($this->fixtures[$class][$id]); } } }
/** * Add an item to this many_many relationship. * Does so by adding an entry to the LinkSets on both parent objects. * * @param $extraFields NOTE: Does not support extra fields */ public function add($item, $extraFields = null) { if (is_numeric($item)) { $itemID = $item; } else { if ($item instanceof $this->dataClass) { $itemID = $item->ID; } else { throw new InvalidArgumentException("ManyManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } } // Validate foreignID, ID of the record that owns the LinkSet $foreignID = $this->getForeignID(); if (!$foreignID) { throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); } //@todo do we need to wrap these two dependent DB manipulations in a transaction? if ($itemID && $foreignID) { $manipulation = array(); $tableName = $this->getParentClass(); $componentName = $this->getComponentName(); //Unfortunately need to use "set" command the first time $hasExisting = false; $parentItem = $tableName::get()->filter(array('ID' => $foreignID))->first(); $hasExisting = $parentItem->{$componentName}()->exists(); //We are updating a record's LinkSet $manipulation[$tableName]['command'] = 'update_container'; $manipulation[$tableName]['id'] = $foreignID; //Use "set" command the first time we add entries to this list if ($hasExisting) { $manipulation[$tableName]['container_command'] = "add {$componentName}"; $manipulation[$tableName]['container_values'] = '#' . $itemID; } else { $manipulation[$tableName]['container_command'] = "set {$componentName}"; $manipulation[$tableName]['container_values'] = '[#' . $itemID . ']'; } DB::manipulate($manipulation); //Get corresponding relation on the other object to update $belongsTableName = $this->dataClass; $belongsComponentName = null; //@todo probably cleaner way to do this $belongsManyMany = Config::inst()->get($this->dataClass, 'belongs_many_many', Config::UNINHERITED); if (is_array($belongsManyMany)) { foreach ($belongsManyMany as $componentName => $class) { if ($class == $tableName) { $belongsComponentName = $componentName; break; } } } $hasExisting = false; $parentItem = $belongsTableName::get()->filter(array('ID' => $itemID))->first(); $hasExisting = $parentItem->{$belongsComponentName}()->exists(); //We are updating a record's LinkSet $manipulation[$belongsTableName]['command'] = 'update_container'; $manipulation[$belongsTableName]['id'] = $itemID; //Use "set" command the first time we add entries to this list if ($hasExisting) { $manipulation[$belongsTableName]['container_command'] = "add {$belongsComponentName}"; $manipulation[$belongsTableName]['container_values'] = '#' . $foreignID; } else { $manipulation[$belongsTableName]['container_command'] = "set {$belongsComponentName}"; $manipulation[$belongsTableName]['container_values'] = '[#' . $foreignID . ']'; } DB::manipulate($manipulation); } }