1
0
mirror of synced 2024-12-15 15:46:02 +03:00
doctrine2/lib/Doctrine/Connection/UnitOfWork.php

369 lines
13 KiB
PHP
Raw Normal View History

<?php
2006-12-29 17:01:31 +03:00
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
*/
Doctrine::autoload('Doctrine_Connection_Module');
/**
2006-09-20 15:29:35 +04:00
* Doctrine_Connection_UnitOfWork
*
* @package Doctrine
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @category Object Relational Mapping
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
2007-05-17 01:28:33 +04:00
class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module
2006-12-29 17:40:47 +03:00
{
/**
* buildFlushTree
* builds a flush tree that is used in transactions
2006-12-29 17:01:31 +03:00
*
* The returned array has all the initialized components in
2006-12-29 17:01:31 +03:00
* 'correct' order. Basically this means that the records of those
* components can be saved safely in the order specified by the returned array.
*
* @param array $tables
* @return array
*/
2006-12-29 17:40:47 +03:00
public function buildFlushTree(array $tables)
{
$tree = array();
2006-12-29 17:01:31 +03:00
foreach ($tables as $k => $table) {
2007-02-17 15:38:02 +03:00
2006-12-29 17:01:31 +03:00
if ( ! ($table instanceof Doctrine_Table)) {
$table = $this->conn->getTable($table, false);
2006-12-29 17:01:31 +03:00
}
$nm = $table->getComponentName();
2007-02-17 15:38:02 +03:00
$index = array_search($nm, $tree);
2006-12-29 17:01:31 +03:00
if ($index === false) {
$tree[] = $nm;
$index = max(array_keys($tree));
}
$rels = $table->getRelations();
2006-12-29 17:01:31 +03:00
// group relations
2006-12-29 17:01:31 +03:00
foreach ($rels as $key => $rel) {
if ($rel instanceof Doctrine_Relation_ForeignKey) {
unset($rels[$key]);
array_unshift($rels, $rel);
}
}
2006-12-29 17:01:31 +03:00
foreach ($rels as $rel) {
$name = $rel->getTable()->getComponentName();
$index2 = array_search($name,$tree);
$type = $rel->getType();
// skip self-referenced relations
2007-05-17 02:22:54 +04:00
if ($name === $nm) {
continue;
}
2006-12-29 17:01:31 +03:00
if ($rel instanceof Doctrine_Relation_ForeignKey) {
if ($index2 !== false) {
if ($index2 >= $index)
continue;
unset($tree[$index]);
array_splice($tree,$index2,0,$nm);
$index = $index2;
} else {
$tree[] = $name;
}
2006-12-29 17:01:31 +03:00
} elseif ($rel instanceof Doctrine_Relation_LocalKey) {
if ($index2 !== false) {
if ($index2 <= $index)
continue;
unset($tree[$index2]);
array_splice($tree,$index,0,$name);
} else {
array_unshift($tree,$name);
$index++;
}
2006-12-29 17:01:31 +03:00
} elseif ($rel instanceof Doctrine_Relation_Association) {
$t = $rel->getAssociationFactory();
$n = $t->getComponentName();
2006-12-29 17:01:31 +03:00
if ($index2 !== false)
unset($tree[$index2]);
2006-12-29 17:01:31 +03:00
2007-02-17 15:38:02 +03:00
array_splice($tree, $index, 0, $name);
$index++;
2007-02-17 15:38:02 +03:00
$index3 = array_search($n, $tree);
2006-12-29 17:01:31 +03:00
if ($index3 !== false) {
if ($index3 >= $index)
continue;
unset($tree[$index]);
2007-02-17 15:38:02 +03:00
array_splice($tree, $index3, 0, $n);
$index = $index2;
} else {
$tree[] = $n;
}
}
}
}
return array_values($tree);
}
/**
* saveRelated
* saves all related records to $record
*
2006-10-31 13:42:46 +03:00
* @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record
*/
2006-12-29 17:40:47 +03:00
public function saveRelated(Doctrine_Record $record)
{
$saveLater = array();
2007-05-17 00:27:03 +04:00
foreach ($record->getReferences() as $k => $v) {
$fk = $record->getTable()->getRelation($k);
2006-12-29 17:01:31 +03:00
if ($fk instanceof Doctrine_Relation_ForeignKey ||
2007-05-17 00:27:03 +04:00
$fk instanceof Doctrine_Relation_LocalKey) {
$local = $fk->getLocal();
$foreign = $fk->getForeign();
if ($record->getTable()->hasPrimaryKey($fk->getLocal())) {
if ( ! $record->exists()) {
$saveLater[$k] = $fk;
} else {
$v->save($this->conn);
}
} else {
// ONE-TO-ONE relationship
$obj = $record->get($fk->getAlias());
2007-05-17 00:27:03 +04:00
if ($obj->exists()) {
$obj->save($this->conn);
}
}
}
}
return $saveLater;
}
/**
* saveAssociations
2006-12-29 17:01:31 +03:00
*
* this method takes a diff of one-to-many / many-to-many original and
* current collections and applies the changes
*
* 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
* 3, 4 and 5, this method would first destroy the associations to 1 and 2 and then
* save new associations to 4 and 5
*
2006-10-31 13:42:46 +03:00
* @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record
* @return void
*/
2006-12-29 17:40:47 +03:00
public function saveAssociations(Doctrine_Record $record)
{
2007-05-17 01:47:23 +04:00
foreach ($record->getReferences() as $k => $v) {
$rel = $record->getTable()->getRelation($k);
2007-05-17 01:28:33 +04:00
2007-05-17 01:47:23 +04:00
if ($rel instanceof Doctrine_Relation_Association) {
$v->save($this->conn);
2007-05-17 01:28:33 +04:00
2007-05-17 01:47:23 +04:00
$assocTable = $rel->getAssociationTable();
foreach ($v->getDeleteDiff() as $r) {
$query = 'DELETE FROM ' . $assocTable->getTableName()
. ' WHERE ' . $rel->getForeign() . ' = ?'
. ' AND ' . $rel->getLocal() . ' = ?';
$this->query($r->getIncremented(), $record->getIncremented());
}
foreach ($v->getInsertDiff() as $r) {
$assocRecord = $assocTable->create();
$assocRecord->set($rel->getForeign(), $r);
$assocRecord->set($rel->getLocal(), $record);
$assocRecord->save($this->conn);
}
}
}
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
2006-10-31 13:42:46 +03:00
* @throws PDOException if something went wrong at database level
* @return void
*/
2006-12-29 17:40:47 +03:00
public function deleteComposites(Doctrine_Record $record)
{
2006-12-29 17:01:31 +03:00
foreach ($record->getTable()->getRelations() as $fk) {
switch ($fk->getType()) {
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$obj = $record->get($fk->getAlias());
$obj->delete($this->conn);
break;
2007-05-17 01:28:33 +04:00
}
}
}
2006-10-31 13:42:46 +03:00
/**
2006-12-29 17:01:31 +03:00
* saveAll
2006-10-31 13:42:46 +03:00
* persists all the pending records from all tables
*
* @throws PDOException if something went wrong at database level
* @return void
*/
2006-12-29 17:40:47 +03:00
public function saveAll()
{
2006-10-31 13:42:46 +03:00
// get the flush tree
$tree = $this->buildFlushTree($this->conn->getTables());
// save all records
2006-12-29 17:01:31 +03:00
foreach ($tree as $name) {
2006-10-31 13:42:46 +03:00
$table = $this->conn->getTable($name);
2006-12-29 17:01:31 +03:00
foreach ($table->getRepository() as $record) {
2006-10-31 13:42:46 +03:00
$this->conn->save($record);
}
}
2006-12-29 17:01:31 +03:00
2006-10-31 13:42:46 +03:00
// save all associations
2006-12-29 17:01:31 +03:00
foreach ($tree as $name) {
2006-10-31 13:42:46 +03:00
$table = $this->conn->getTable($name);
2006-12-29 17:01:31 +03:00
foreach ($table->getRepository() as $record) {
2006-10-31 13:42:46 +03:00
$this->saveAssociations($record);
}
}
}
/**
* update
* updates the given record
*
* @param Doctrine_Record $record record to be updated
* @return boolean whether or not the update was successful
*/
2006-12-29 17:40:47 +03:00
public function update(Doctrine_Record $record)
{
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
$array = $record->getPrepared();
2006-12-29 17:01:31 +03:00
if (empty($array)) {
return false;
2006-12-29 17:01:31 +03:00
}
$set = array();
2006-12-29 17:01:31 +03:00
foreach ($array as $name => $value) {
2007-04-18 13:49:54 +04:00
$set[] = $name . ' = ?';
2007-04-18 13:49:54 +04:00
if ($value instanceof Doctrine_Record) {
switch ($value->state()) {
case Doctrine_Record::STATE_TCLEAN:
case Doctrine_Record::STATE_TDIRTY:
$record->save($this->conn);
default:
$array[$name] = $value->getIncremented();
$record->set($name, $value->getIncremented());
}
2007-04-18 13:49:54 +04:00
}
}
$params = array_values($array);
$id = $record->obtainIdentifier();
2006-12-29 17:01:31 +03:00
if ( ! is_array($id)) {
$id = array($id);
2006-12-29 17:01:31 +03:00
}
$id = array_values($id);
$params = array_merge($params, $id);
2007-04-18 13:49:54 +04:00
$sql = 'UPDATE ' . $this->conn->quoteIdentifier($record->getTable()->getTableName())
2006-12-29 17:01:31 +03:00
. ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', $record->getTable()->getPrimaryKeys())
2006-12-14 16:26:16 +03:00
. ' = ?';
$stmt = $this->conn->getDbh()->prepare($sql);
$stmt->execute($params);
$record->assignIdentifier(true);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
return true;
}
/**
* inserts a record into database
*
* @param Doctrine_Record $record record to be inserted
* @return boolean
*/
2006-12-29 17:40:47 +03:00
public function insert(Doctrine_Record $record)
{
// listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
2006-12-29 17:01:31 +03:00
$array = $record->getPrepared();
2006-12-29 17:01:31 +03:00
if (empty($array)) {
return false;
2006-12-29 17:01:31 +03:00
}
$table = $record->getTable();
$keys = $table->getPrimaryKeys();
2007-02-08 16:56:23 +03:00
$seq = $record->getTable()->sequenceName;
2006-12-29 17:01:31 +03:00
if ( ! empty($seq)) {
$id = $this->conn->sequence->nextId($seq);
$name = $record->getTable()->getIdentifier();
$array[$name] = $id;
2007-01-29 02:55:30 +03:00
$record->assignIdentifier($id);
}
$this->conn->insert($table->getTableName(), $array);
2007-02-09 01:07:11 +03:00
if (empty($seq) && count($keys) == 1 && $keys[0] == $table->getIdentifier() &&
$table->getIdentifierType() != Doctrine_Identifier::NORMAL) {
2007-01-29 22:58:00 +03:00
if (strtolower($this->conn->getName()) == 'pgsql') {
2007-02-08 16:56:23 +03:00
$seq = $table->getTableName() . '_' . $keys[0];
2007-01-29 22:58:00 +03:00
}
$id = $this->conn->sequence->lastInsertId($seq);
2007-01-29 02:55:30 +03:00
if ( ! $id) {
$id = $table->getMaxIdentifier();
2007-01-29 02:55:30 +03:00
}
2006-12-29 17:01:31 +03:00
$record->assignIdentifier($id);
2006-12-29 17:01:31 +03:00
} else {
$record->assignIdentifier(true);
2006-12-29 17:01:31 +03:00
}
// listen the onInsert event
$table->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
return true;
}
}