Commit a3c39fed authored by phuson's avatar phuson

Applied patch for ticket #499. Tested and it seems to be working correctly with patch.

parent a4ae926a
<?php <?php
/* /*
* $Id$ * $Id$
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>. * <http://www.phpdoctrine.com>.
*/ */
Doctrine::autoload('Doctrine_Connection_Module'); Doctrine::autoload('Doctrine_Connection_Module');
/** /**
* Doctrine_Connection_UnitOfWork * Doctrine_Connection_UnitOfWork
* *
* @package Doctrine * @package Doctrine
* @subpackage Connection * @subpackage Connection
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com * @link www.phpdoctrine.com
* @since 1.0 * @since 1.0
* @version $Revision$ * @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/ */
class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module
{ {
/** /**
* buildFlushTree * buildFlushTree
* builds a flush tree that is used in transactions * builds a flush tree that is used in transactions
* *
* The returned array has all the initialized components in * The returned array has all the initialized components in
* 'correct' order. Basically this means that the records of those * 'correct' order. Basically this means that the records of those
* components can be saved safely in the order specified by the returned array. * components can be saved safely in the order specified by the returned array.
* *
* @param array $tables an array of Doctrine_Table objects or component names * @param array $tables an array of Doctrine_Table objects or component names
* @return array an array of component names in flushing order * @return array an array of component names in flushing order
*/ */
public function buildFlushTree(array $tables) public function buildFlushTree(array $tables)
{ {
$tree = array(); $tree = array();
foreach ($tables as $k => $table) { foreach ($tables as $k => $table) {
if ( ! ($table instanceof Doctrine_Table)) { if ( ! ($table instanceof Doctrine_Table)) {
$table = $this->conn->getTable($table, false); $table = $this->conn->getTable($table, false);
} }
$nm = $table->getComponentName(); $nm = $table->getComponentName();
$index = array_search($nm, $tree); $index = array_search($nm, $tree);
if ($index === false) { if ($index === false) {
$tree[] = $nm; $tree[] = $nm;
$index = max(array_keys($tree)); $index = max(array_keys($tree));
} }
$rels = $table->getRelations(); $rels = $table->getRelations();
// group relations // group relations
foreach ($rels as $key => $rel) { foreach ($rels as $key => $rel) {
if ($rel instanceof Doctrine_Relation_ForeignKey) { if ($rel instanceof Doctrine_Relation_ForeignKey) {
unset($rels[$key]); unset($rels[$key]);
array_unshift($rels, $rel); array_unshift($rels, $rel);
} }
} }
foreach ($rels as $rel) { foreach ($rels as $rel) {
$name = $rel->getTable()->getComponentName(); $name = $rel->getTable()->getComponentName();
$index2 = array_search($name,$tree); $index2 = array_search($name,$tree);
$type = $rel->getType(); $type = $rel->getType();
// skip self-referenced relations // skip self-referenced relations
if ($name === $nm) { if ($name === $nm) {
continue; continue;
} }
if ($rel instanceof Doctrine_Relation_ForeignKey) { if ($rel instanceof Doctrine_Relation_ForeignKey) {
if ($index2 !== false) { if ($index2 !== false) {
if ($index2 >= $index) if ($index2 >= $index)
continue; continue;
unset($tree[$index]); unset($tree[$index]);
array_splice($tree,$index2,0,$nm); array_splice($tree,$index2,0,$nm);
$index = $index2; $index = $index2;
} else { } else {
$tree[] = $name; $tree[] = $name;
} }
} elseif ($rel instanceof Doctrine_Relation_LocalKey) { } elseif ($rel instanceof Doctrine_Relation_LocalKey) {
if ($index2 !== false) { if ($index2 !== false) {
if ($index2 <= $index) if ($index2 <= $index)
continue; continue;
unset($tree[$index2]); unset($tree[$index2]);
array_splice($tree,$index,0,$name); array_splice($tree,$index,0,$name);
} else { } else {
array_unshift($tree,$name); array_unshift($tree,$name);
$index++; $index++;
} }
} elseif ($rel instanceof Doctrine_Relation_Association) { } elseif ($rel instanceof Doctrine_Relation_Association) {
$t = $rel->getAssociationFactory(); $t = $rel->getAssociationFactory();
$n = $t->getComponentName(); $n = $t->getComponentName();
if ($index2 !== false) if ($index2 !== false)
unset($tree[$index2]); unset($tree[$index2]);
array_splice($tree, $index, 0, $name); array_splice($tree, $index, 0, $name);
$index++; $index++;
$index3 = array_search($n, $tree); $index3 = array_search($n, $tree);
if ($index3 !== false) { if ($index3 !== false) {
if ($index3 >= $index) if ($index3 >= $index)
continue; continue;
unset($tree[$index]); unset($tree[$index]);
array_splice($tree, $index3, 0, $n); array_splice($tree, $index3, 0, $n);
$index = $index2; $index = $index2;
} else { } else {
$tree[] = $n; $tree[] = $n;
} }
} }
} }
} }
return array_values($tree); return array_values($tree);
} }
/** /**
* saves the given record * saves the given record
* *
* @param Doctrine_Record $record * @param Doctrine_Record $record
* @return void * @return void
*/ */
public function saveGraph(Doctrine_Record $record) public function saveGraph(Doctrine_Record $record)
{ {
$conn = $this->getConnection(); $conn = $this->getConnection();
$state = $record->state(); $state = $record->state();
if ($state === Doctrine_Record::STATE_LOCKED) { if ($state === Doctrine_Record::STATE_LOCKED) {
return false; return false;
} }
$record->state(Doctrine_Record::STATE_LOCKED); $record->state(Doctrine_Record::STATE_LOCKED);
$conn->beginTransaction(); $conn->beginTransaction();
$saveLater = $this->saveRelated($record); $saveLater = $this->saveRelated($record);
$record->state($state); $record->state($state);
if ($record->isValid()) { if ($record->isValid()) {
$event = new Doctrine_Event($record, Doctrine_Event::RECORD_SAVE); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_SAVE);
$record->preSave($event); $record->preSave($event);
$record->getTable()->getRecordListener()->preSave($event); $record->getTable()->getRecordListener()->preSave($event);
if ( ! $event->skipOperation) { if ( ! $event->skipOperation) {
switch ($state) { switch ($state) {
case Doctrine_Record::STATE_TDIRTY: case Doctrine_Record::STATE_TDIRTY:
$this->insert($record); $this->insert($record);
break; break;
case Doctrine_Record::STATE_DIRTY: case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_PROXY: case Doctrine_Record::STATE_PROXY:
$this->update($record); $this->update($record);
break; break;
case Doctrine_Record::STATE_CLEAN: case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_TCLEAN: case Doctrine_Record::STATE_TCLEAN:
break; break;
} }
} }
$record->getTable()->getRecordListener()->postSave($event); $record->getTable()->getRecordListener()->postSave($event);
$record->postSave($event); $record->postSave($event);
} else { } else {
$conn->transaction->addInvalid($record); $conn->transaction->addInvalid($record);
} }
$state = $record->state(); $state = $record->state();
$record->state(Doctrine_Record::STATE_LOCKED); $record->state(Doctrine_Record::STATE_LOCKED);
foreach ($saveLater as $fk) { foreach ($saveLater as $fk) {
$alias = $fk->getAlias(); $alias = $fk->getAlias();
if ($record->hasReference($alias)) { if ($record->hasReference($alias)) {
$obj = $record->$alias; $obj = $record->$alias;
// check that the related object is not an instance of Doctrine_Null // check that the related object is not an instance of Doctrine_Null
if ( ! ($obj instanceof Doctrine_Null)) { if ( ! ($obj instanceof Doctrine_Null)) {
$obj->save($conn); $obj->save($conn);
} }
} }
} }
// save the MANY-TO-MANY associations // save the MANY-TO-MANY associations
$this->saveAssociations($record); $this->saveAssociations($record);
$record->state($state); $record->state($state);
$conn->commit(); $conn->commit();
return true; return true;
} }
/** /**
* saves the given record * saves the given record
* *
* @param Doctrine_Record $record * @param Doctrine_Record $record
* @return void * @return void
*/ */
public function save(Doctrine_Record $record) public function save(Doctrine_Record $record)
{ {
$event = new Doctrine_Event($record, Doctrine_Event::RECORD_SAVE); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_SAVE);
$record->preSave($event); $record->preSave($event);
$record->getTable()->getRecordListener()->preSave($event); $record->getTable()->getRecordListener()->preSave($event);
if ( ! $event->skipOperation) { if ( ! $event->skipOperation) {
switch ($record->state()) { switch ($record->state()) {
case Doctrine_Record::STATE_TDIRTY: case Doctrine_Record::STATE_TDIRTY:
$this->insert($record); $this->insert($record);
break; break;
case Doctrine_Record::STATE_DIRTY: case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_PROXY: case Doctrine_Record::STATE_PROXY:
$this->update($record); $this->update($record);
break; break;
case Doctrine_Record::STATE_CLEAN: case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_TCLEAN: case Doctrine_Record::STATE_TCLEAN:
// do nothing // do nothing
break; break;
} }
} }
$record->getTable()->getRecordListener()->postSave($event); $record->getTable()->getRecordListener()->postSave($event);
$record->postSave($event); $record->postSave($event);
} }
/** /**
* deletes given record and all the related composites * deletes given record and all the related composites
* this operation is isolated by a transaction * this operation is isolated by a transaction
* *
* this event can be listened by the onPreDelete and onDelete listeners * this event can be listened by the onPreDelete and onDelete listeners
* *
* @return boolean true on success, false on failure * @return boolean true on success, false on failure
*/ */
public function delete(Doctrine_Record $record) public function delete(Doctrine_Record $record)
{ {
if ( ! $record->exists()) { if ( ! $record->exists()) {
return false; return false;
} }
$this->conn->beginTransaction(); $this->conn->beginTransaction();
$event = new Doctrine_Event($record, Doctrine_Event::RECORD_DELETE); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_DELETE);
$record->preDelete($event); $record->preDelete($event);
$record->getTable()->getRecordListener()->preDelete($event); $record->getTable()->getRecordListener()->preDelete($event);
$state = $record->state(); $state = $record->state();
$record->state(Doctrine_Record::STATE_LOCKED); $record->state(Doctrine_Record::STATE_LOCKED);
$this->deleteComposites($record); $this->deleteComposites($record);
if ( ! $event->skipOperation) { if ( ! $event->skipOperation) {
$record->state(Doctrine_Record::STATE_TDIRTY); $record->state(Doctrine_Record::STATE_TDIRTY);
$this->deleteRecord($record); $this->deleteRecord($record);
$record->state(Doctrine_Record::STATE_TCLEAN); $record->state(Doctrine_Record::STATE_TCLEAN);
} else { } else {
// return to original state // return to original state
$record->state($state); $record->state($state);
} }
$record->getTable()->getRecordListener()->postDelete($event); $record->getTable()->getRecordListener()->postDelete($event);
$record->postDelete($event); $record->postDelete($event);
$this->conn->commit(); $this->conn->commit();
return true; return true;
} }
public function deleteRecord(Doctrine_Record $record) public function deleteRecord(Doctrine_Record $record)
{ {
$ids = $record->identifier(); $ids = $record->identifier();
$tmp = array(); $tmp = array();
foreach (array_keys($ids) as $id) { foreach (array_keys($ids) as $id) {
$tmp[] = $id . ' = ? '; $tmp[] = $id . ' = ? ';
} }
$params = array_values($ids); $params = array_values($ids);
$query = 'DELETE FROM ' $query = 'DELETE FROM '
. $this->conn->quoteIdentifier($record->getTable()->getTableName()) . $this->conn->quoteIdentifier($record->getTable()->getTableName())
. ' WHERE ' . implode(' AND ', $tmp); . ' WHERE ' . implode(' AND ', $tmp);
return $this->conn->exec($query, $params); return $this->conn->exec($query, $params);
} }
/** /**
* deleteMultiple * deleteMultiple
* deletes all records from the pending delete list * deletes all records from the pending delete list
* *
* @return void * @return void
*/ */
public function deleteMultiple(array $records) public function deleteMultiple(array $records)
{ {
foreach ($this->delete as $name => $deletes) { foreach ($this->delete as $name => $deletes) {
$record = false; $record = false;
$ids = array(); $ids = array();
if (is_array($deletes[count($deletes)-1]->getTable()->getIdentifier())) { if (is_array($deletes[count($deletes)-1]->getTable()->getIdentifier())) {
if (count($deletes) > 0) { if (count($deletes) > 0) {
$query = 'DELETE FROM ' $query = 'DELETE FROM '
. $this->conn->quoteIdentifier($deletes[0]->getTable()->getTableName()) . $this->conn->quoteIdentifier($deletes[0]->getTable()->getTableName())
. ' WHERE '; . ' WHERE ';
$params = array(); $params = array();
$cond = array(); $cond = array();
foreach ($deletes as $k => $record) { foreach ($deletes as $k => $record) {
$ids = $record->identifier(); $ids = $record->identifier();
$tmp = array(); $tmp = array();
foreach (array_keys($ids) as $id) { foreach (array_keys($ids) as $id) {
$tmp[] = $id . ' = ? '; $tmp[] = $id . ' = ? ';
} }
$params = array_merge($params, array_values($ids)); $params = array_merge($params, array_values($ids));
$cond[] = '(' . implode(' AND ', $tmp) . ')'; $cond[] = '(' . implode(' AND ', $tmp) . ')';
} }
$query .= implode(' OR ', $cond); $query .= implode(' OR ', $cond);
$this->conn->execute($query, $params); $this->conn->execute($query, $params);
} }
} else { } else {
foreach ($deletes as $k => $record) { foreach ($deletes as $k => $record) {
$ids[] = $record->getIncremented(); $ids[] = $record->getIncremented();
} }
if ($record instanceof Doctrine_Record) { if ($record instanceof Doctrine_Record) {
$params = substr(str_repeat('?, ', count($ids)), 0, -2); $params = substr(str_repeat('?, ', count($ids)), 0, -2);
$query = 'DELETE FROM ' $query = 'DELETE FROM '
. $this->conn->quoteIdentifier($record->getTable()->getTableName()) . $this->conn->quoteIdentifier($record->getTable()->getTableName())
. ' WHERE ' . ' WHERE '
. $record->getTable()->getIdentifier() . $record->getTable()->getIdentifier()
. ' IN(' . $params . ')'; . ' IN(' . $params . ')';
$this->conn->execute($query, $ids); $this->conn->execute($query, $ids);
} }
} }
} }
} }
/** /**
* saveRelated * saveRelated
* saves all related records to $record * saves all related records to $record
* *
* @throws PDOException if something went wrong at database level * @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record * @param Doctrine_Record $record
*/ */
public function saveRelated(Doctrine_Record $record) public function saveRelated(Doctrine_Record $record)
{ {
$saveLater = array(); $saveLater = array();
foreach ($record->getReferences() as $k => $v) { foreach ($record->getReferences() as $k => $v) {
$rel = $record->getTable()->getRelation($k); $rel = $record->getTable()->getRelation($k);
$local = $rel->getLocal(); $local = $rel->getLocal();
$foreign = $rel->getForeign(); $foreign = $rel->getForeign();
if ($rel instanceof Doctrine_Relation_ForeignKey) { if ($rel instanceof Doctrine_Relation_ForeignKey) {
$saveLater[$k] = $rel; $saveLater[$k] = $rel;
} elseif ($rel instanceof Doctrine_Relation_LocalKey) { } elseif ($rel instanceof Doctrine_Relation_LocalKey) {
// ONE-TO-ONE relationship // ONE-TO-ONE relationship
$obj = $record->get($rel->getAlias()); $obj = $record->get($rel->getAlias());
// Protection against infinite function recursion before attempting to save // Protection against infinite function recursion before attempting to save
if ($obj instanceof Doctrine_Record && if ($obj instanceof Doctrine_Record &&
$obj->isModified()) { $obj->isModified()) {
$obj->save($this->conn); $obj->save($this->conn);
/** /**
$id = array_values($obj->identifier()); $id = array_values($obj->identifier());
foreach ((array) $rel->getLocal() as $k => $field) { foreach ((array) $rel->getLocal() as $k => $field) {
$record->set($field, $id[$k]); $record->set($field, $id[$k]);
} }
*/ */
} }
} }
} }
return $saveLater; return $saveLater;
} }
/** /**
* saveAssociations * saveAssociations
* *
* this method takes a diff of one-to-many / many-to-many original and * this method takes a diff of one-to-many / many-to-many original and
* current collections and applies the changes * current collections and applies the changes
* *
* for example if original many-to-many related collection has records with * for example if original many-to-many related collection has records with
* primary keys 1,2 and 3 and the new collection has records with primary keys * primary keys 1,2 and 3 and the new collection has records with primary keys
* 3, 4 and 5, this method would first destroy the associations to 1 and 2 and then * 3, 4 and 5, this method would first destroy the associations to 1 and 2 and then
* save new associations to 4 and 5 * save new associations to 4 and 5
* *
* @throws Doctrine_Connection_Exception if something went wrong at database level * @throws Doctrine_Connection_Exception if something went wrong at database level
* @param Doctrine_Record $record * @param Doctrine_Record $record
* @return void * @return void
*/ */
public function saveAssociations(Doctrine_Record $record) public function saveAssociations(Doctrine_Record $record)
{ {
foreach ($record->getReferences() as $k => $v) { foreach ($record->getReferences() as $k => $v) {
$rel = $record->getTable()->getRelation($k); $rel = $record->getTable()->getRelation($k);
if ($rel instanceof Doctrine_Relation_Association) { if ($rel instanceof Doctrine_Relation_Association) {
$v->save($this->conn); $v->save($this->conn);
$assocTable = $rel->getAssociationTable(); $assocTable = $rel->getAssociationTable();
foreach ($v->getDeleteDiff() as $r) { foreach ($v->getDeleteDiff() as $r) {
$query = 'DELETE FROM ' . $assocTable->getTableName() $query = 'DELETE FROM ' . $assocTable->getTableName()
. ' WHERE ' . $rel->getForeign() . ' = ?' . ' WHERE ' . $rel->getForeign() . ' = ?'
. ' AND ' . $rel->getLocal() . ' = ?'; . ' AND ' . $rel->getLocal() . ' = ?';
$this->conn->execute($query, array($r->getIncremented(), $record->getIncremented())); $this->conn->execute($query, array($r->getIncremented(), $record->getIncremented()));
} }
foreach ($v->getInsertDiff() as $r) { foreach ($v->getInsertDiff() as $r) {
$assocRecord = $assocTable->create(); $assocRecord = $assocTable->create();
$assocRecord->set($rel->getForeign(), $r); $assocRecord->set($rel->getForeign(), $r);
$assocRecord->set($rel->getLocal(), $record); $assocRecord->set($rel->getLocal(), $record);
$this->saveGraph($assocRecord); $this->saveGraph($assocRecord);
} }
} }
} }
} }
/** /**
* deletes all related composites * deletes all related composites
* this method is always called internally when a record is deleted * this method is always called internally when a record is deleted
* *
* @throws PDOException if something went wrong at database level * @throws PDOException if something went wrong at database level
* @return void * @return void
*/ */
public function deleteComposites(Doctrine_Record $record) public function deleteComposites(Doctrine_Record $record)
{ {
foreach ($record->getTable()->getRelations() as $fk) { foreach ($record->getTable()->getRelations() as $fk) {
if ($fk->isComposite()) { if ($fk->isComposite()) {
$obj = $record->get($fk->getAlias()); $obj = $record->get($fk->getAlias());
if ( $obj instanceof Doctrine_Record && if ( $obj instanceof Doctrine_Record &&
$obj->state() != Doctrine_Record::STATE_LOCKED) { $obj->state() != Doctrine_Record::STATE_LOCKED) {
$obj->delete($this->conn); $obj->delete($this->conn);
} }
} }
} }
} }
/** /**
* saveAll * saveAll
* persists all the pending records from all tables * persists all the pending records from all tables
* *
* @throws PDOException if something went wrong at database level * @throws PDOException if something went wrong at database level
* @return void * @return void
*/ */
public function saveAll() public function saveAll()
{ {
// get the flush tree // get the flush tree
$tree = $this->buildFlushTree($this->conn->getTables()); $tree = $this->buildFlushTree($this->conn->getTables());
// save all records // save all records
foreach ($tree as $name) { foreach ($tree as $name) {
$table = $this->conn->getTable($name); $table = $this->conn->getTable($name);
foreach ($table->getRepository() as $record) { foreach ($table->getRepository() as $record) {
$this->save($record); $this->save($record);
} }
} }
// save all associations // save all associations
foreach ($tree as $name) { foreach ($tree as $name) {
$table = $this->conn->getTable($name); $table = $this->conn->getTable($name);
foreach ($table->getRepository() as $record) { foreach ($table->getRepository() as $record) {
$this->saveAssociations($record); $this->saveAssociations($record);
} }
} }
} }
/** /**
* update * update
* updates the given record * updates the given record
* *
* @param Doctrine_Record $record record to be updated * @param Doctrine_Record $record record to be updated
* @return boolean whether or not the update was successful * @return boolean whether or not the update was successful
*/ */
public function update(Doctrine_Record $record) public function update(Doctrine_Record $record)
{ {
$event = new Doctrine_Event($record, Doctrine_Event::RECORD_UPDATE); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_UPDATE);
$record->preUpdate($event); $record->preUpdate($event);
$record->getTable()->getRecordListener()->preUpdate($event); $record->getTable()->getRecordListener()->preUpdate($event);
if ( ! $event->skipOperation) { if ( ! $event->skipOperation) {
$array = $record->getPrepared(); $array = $record->getPrepared();
if (empty($array)) { if (empty($array)) {
return false; return false;
} }
$set = array(); $set = array();
foreach ($array as $name => $value) { foreach ($array as $name => $value) {
if ($value instanceof Doctrine_Expression) { if ($value instanceof Doctrine_Expression) {
$set[] = $value->getSql(); $set[] = $name . ' = ' . $value->getSql();
unset($array[$name]); unset($array[$name]);
} else { } else {
$set[] = $name . ' = ?'; $set[] = $name . ' = ?';
if ($value instanceof Doctrine_Record) { if ($value instanceof Doctrine_Record) {
if ( ! $value->exists()) { if ( ! $value->exists()) {
$record->save($this->conn); $record->save($this->conn);
} }
$array[$name] = $value->getIncremented(); $array[$name] = $value->getIncremented();
$record->set($name, $value->getIncremented()); $record->set($name, $value->getIncremented());
} }
} }
} }
$params = array_values($array); $params = array_values($array);
$id = $record->identifier(); $id = $record->identifier();
if ( ! is_array($id)) { if ( ! is_array($id)) {
$id = array($id); $id = array($id);
} }
$id = array_values($id); $id = array_values($id);
$params = array_merge($params, $id); $params = array_merge($params, $id);
$sql = 'UPDATE ' . $this->conn->quoteIdentifier($record->getTable()->getTableName()) $sql = 'UPDATE ' . $this->conn->quoteIdentifier($record->getTable()->getTableName())
. ' SET ' . implode(', ', $set) . ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', (array) $record->getTable()->getIdentifier()) . ' WHERE ' . implode(' = ? AND ', (array) $record->getTable()->getIdentifier())
. ' = ?'; . ' = ?';
$stmt = $this->conn->prepare($sql); $stmt = $this->conn->prepare($sql);
$stmt->execute($params); $stmt->execute($params);
$record->assignIdentifier(true); $record->assignIdentifier(true);
} }
$record->getTable()->getRecordListener()->postUpdate($event); $record->getTable()->getRecordListener()->postUpdate($event);
$record->postUpdate($event); $record->postUpdate($event);
return true; return true;
} }
/** /**
* inserts a record into database * inserts a record into database
* *
* @param Doctrine_Record $record record to be inserted * @param Doctrine_Record $record record to be inserted
* @return boolean * @return boolean
*/ */
public function insert(Doctrine_Record $record) public function insert(Doctrine_Record $record)
{ {
// listen the onPreInsert event // listen the onPreInsert event
$event = new Doctrine_Event($record, Doctrine_Event::RECORD_INSERT); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_INSERT);
$record->preInsert($event); $record->preInsert($event);
$record->getTable()->getRecordListener()->preInsert($event); $record->getTable()->getRecordListener()->preInsert($event);
if ( ! $event->skipOperation) { if ( ! $event->skipOperation) {
$array = $record->getPrepared(); $array = $record->getPrepared();
if (empty($array)) { if (empty($array)) {
return false; return false;
} }
$table = $record->getTable(); $table = $record->getTable();
$keys = (array) $table->getIdentifier(); $keys = (array) $table->getIdentifier();
$seq = $record->getTable()->sequenceName; $seq = $record->getTable()->sequenceName;
if ( ! empty($seq)) { if ( ! empty($seq)) {
$id = $this->conn->sequence->nextId($seq); $id = $this->conn->sequence->nextId($seq);
$name = $record->getTable()->getIdentifier(); $name = $record->getTable()->getIdentifier();
$array[$name] = $id; $array[$name] = $id;
$record->assignIdentifier($id); $record->assignIdentifier($id);
} }
$this->conn->insert($table->getTableName(), $array); $this->conn->insert($table->getTableName(), $array);
if (empty($seq) && count($keys) == 1 && $keys[0] == $table->getIdentifier() && if (empty($seq) && count($keys) == 1 && $keys[0] == $table->getIdentifier() &&
$table->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) { $table->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) {
if (strtolower($this->conn->getName()) == 'pgsql') { if (strtolower($this->conn->getName()) == 'pgsql') {
$seq = $table->getTableName() . '_' . $keys[0]; $seq = $table->getTableName() . '_' . $keys[0];
} }
$id = $this->conn->sequence->lastInsertId($seq); $id = $this->conn->sequence->lastInsertId($seq);
if ( ! $id) { if ( ! $id) {
throw new Doctrine_Connection_Exception("Couldn't get last insert identifier."); throw new Doctrine_Connection_Exception("Couldn't get last insert identifier.");
} }
$record->assignIdentifier($id); $record->assignIdentifier($id);
} else { } else {
$record->assignIdentifier(true); $record->assignIdentifier(true);
} }
} }
$record->getTable()->addRecord($record); $record->getTable()->addRecord($record);
$record->getTable()->getRecordListener()->postInsert($event); $record->getTable()->getRecordListener()->postInsert($event);
$record->postInsert($event); $record->postInsert($event);
return true; return true;
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment