Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
a3c39fed
Commit
a3c39fed
authored
Oct 22, 2007
by
phuson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Applied patch for ticket #499. Tested and it seems to be working correctly with patch.
parent
a4ae926a
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
635 additions
and
635 deletions
+635
-635
UnitOfWork.php
lib/Doctrine/Connection/UnitOfWork.php
+635
-635
No files found.
lib/Doctrine/Connection/UnitOfWork.php
View file @
a3c39fed
<?php
/*
* $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'
);
/**
* Doctrine_Connection_UnitOfWork
*
* @package Doctrine
* @subpackage Connection
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
class
Doctrine_Connection_UnitOfWork
extends
Doctrine_Connection_Module
{
/**
* buildFlushTree
* builds a flush tree that is used in transactions
*
* The returned array has all the initialized components in
* '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 an array of Doctrine_Table objects or component names
* @return array an array of component names in flushing order
*/
public
function
buildFlushTree
(
array
$tables
)
{
$tree
=
array
();
foreach
(
$tables
as
$k
=>
$table
)
{
if
(
!
(
$table
instanceof
Doctrine_Table
))
{
$table
=
$this
->
conn
->
getTable
(
$table
,
false
);
}
$nm
=
$table
->
getComponentName
();
$index
=
array_search
(
$nm
,
$tree
);
if
(
$index
===
false
)
{
$tree
[]
=
$nm
;
$index
=
max
(
array_keys
(
$tree
));
}
$rels
=
$table
->
getRelations
();
// group relations
foreach
(
$rels
as
$key
=>
$rel
)
{
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
unset
(
$rels
[
$key
]);
array_unshift
(
$rels
,
$rel
);
}
}
foreach
(
$rels
as
$rel
)
{
$name
=
$rel
->
getTable
()
->
getComponentName
();
$index2
=
array_search
(
$name
,
$tree
);
$type
=
$rel
->
getType
();
// skip self-referenced relations
if
(
$name
===
$nm
)
{
continue
;
}
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
;
}
}
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
++
;
}
}
elseif
(
$rel
instanceof
Doctrine_Relation_Association
)
{
$t
=
$rel
->
getAssociationFactory
();
$n
=
$t
->
getComponentName
();
if
(
$index2
!==
false
)
unset
(
$tree
[
$index2
]);
array_splice
(
$tree
,
$index
,
0
,
$name
);
$index
++
;
$index3
=
array_search
(
$n
,
$tree
);
if
(
$index3
!==
false
)
{
if
(
$index3
>=
$index
)
continue
;
unset
(
$tree
[
$index
]);
array_splice
(
$tree
,
$index3
,
0
,
$n
);
$index
=
$index2
;
}
else
{
$tree
[]
=
$n
;
}
}
}
}
return
array_values
(
$tree
);
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public
function
saveGraph
(
Doctrine_Record
$record
)
{
$conn
=
$this
->
getConnection
();
$state
=
$record
->
state
();
if
(
$state
===
Doctrine_Record
::
STATE_LOCKED
)
{
return
false
;
}
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
$conn
->
beginTransaction
();
$saveLater
=
$this
->
saveRelated
(
$record
);
$record
->
state
(
$state
);
if
(
$record
->
isValid
())
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_SAVE
);
$record
->
preSave
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preSave
(
$event
);
if
(
!
$event
->
skipOperation
)
{
switch
(
$state
)
{
case
Doctrine_Record
::
STATE_TDIRTY
:
$this
->
insert
(
$record
);
break
;
case
Doctrine_Record
::
STATE_DIRTY
:
case
Doctrine_Record
::
STATE_PROXY
:
$this
->
update
(
$record
);
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_TCLEAN
:
break
;
}
}
$record
->
getTable
()
->
getRecordListener
()
->
postSave
(
$event
);
$record
->
postSave
(
$event
);
}
else
{
$conn
->
transaction
->
addInvalid
(
$record
);
}
$state
=
$record
->
state
();
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
foreach
(
$saveLater
as
$fk
)
{
$alias
=
$fk
->
getAlias
();
if
(
$record
->
hasReference
(
$alias
))
{
$obj
=
$record
->
$alias
;
// check that the related object is not an instance of Doctrine_Null
if
(
!
(
$obj
instanceof
Doctrine_Null
))
{
$obj
->
save
(
$conn
);
}
}
}
// save the MANY-TO-MANY associations
$this
->
saveAssociations
(
$record
);
$record
->
state
(
$state
);
$conn
->
commit
();
return
true
;
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public
function
save
(
Doctrine_Record
$record
)
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_SAVE
);
$record
->
preSave
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preSave
(
$event
);
if
(
!
$event
->
skipOperation
)
{
switch
(
$record
->
state
())
{
case
Doctrine_Record
::
STATE_TDIRTY
:
$this
->
insert
(
$record
);
break
;
case
Doctrine_Record
::
STATE_DIRTY
:
case
Doctrine_Record
::
STATE_PROXY
:
$this
->
update
(
$record
);
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_TCLEAN
:
// do nothing
break
;
}
}
$record
->
getTable
()
->
getRecordListener
()
->
postSave
(
$event
);
$record
->
postSave
(
$event
);
}
/**
* deletes given record and all the related composites
* this operation is isolated by a transaction
*
* this event can be listened by the onPreDelete and onDelete listeners
*
* @return boolean true on success, false on failure
*/
public
function
delete
(
Doctrine_Record
$record
)
{
if
(
!
$record
->
exists
())
{
return
false
;
}
$this
->
conn
->
beginTransaction
();
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_DELETE
);
$record
->
preDelete
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preDelete
(
$event
);
$state
=
$record
->
state
();
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
$this
->
deleteComposites
(
$record
);
if
(
!
$event
->
skipOperation
)
{
$record
->
state
(
Doctrine_Record
::
STATE_TDIRTY
);
$this
->
deleteRecord
(
$record
);
$record
->
state
(
Doctrine_Record
::
STATE_TCLEAN
);
}
else
{
// return to original state
$record
->
state
(
$state
);
}
$record
->
getTable
()
->
getRecordListener
()
->
postDelete
(
$event
);
$record
->
postDelete
(
$event
);
$this
->
conn
->
commit
();
return
true
;
}
public
function
deleteRecord
(
Doctrine_Record
$record
)
{
$ids
=
$record
->
identifier
();
$tmp
=
array
();
foreach
(
array_keys
(
$ids
)
as
$id
)
{
$tmp
[]
=
$id
.
' = ? '
;
}
$params
=
array_values
(
$ids
);
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' WHERE '
.
implode
(
' AND '
,
$tmp
);
return
$this
->
conn
->
exec
(
$query
,
$params
);
}
/**
* deleteMultiple
* deletes all records from the pending delete list
*
* @return void
*/
public
function
deleteMultiple
(
array
$records
)
{
foreach
(
$this
->
delete
as
$name
=>
$deletes
)
{
$record
=
false
;
$ids
=
array
();
if
(
is_array
(
$deletes
[
count
(
$deletes
)
-
1
]
->
getTable
()
->
getIdentifier
()))
{
if
(
count
(
$deletes
)
>
0
)
{
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$deletes
[
0
]
->
getTable
()
->
getTableName
())
.
' WHERE '
;
$params
=
array
();
$cond
=
array
();
foreach
(
$deletes
as
$k
=>
$record
)
{
$ids
=
$record
->
identifier
();
$tmp
=
array
();
foreach
(
array_keys
(
$ids
)
as
$id
)
{
$tmp
[]
=
$id
.
' = ? '
;
}
$params
=
array_merge
(
$params
,
array_values
(
$ids
));
$cond
[]
=
'('
.
implode
(
' AND '
,
$tmp
)
.
')'
;
}
$query
.=
implode
(
' OR '
,
$cond
);
$this
->
conn
->
execute
(
$query
,
$params
);
}
}
else
{
foreach
(
$deletes
as
$k
=>
$record
)
{
$ids
[]
=
$record
->
getIncremented
();
}
if
(
$record
instanceof
Doctrine_Record
)
{
$params
=
substr
(
str_repeat
(
'?, '
,
count
(
$ids
)),
0
,
-
2
);
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' WHERE '
.
$record
->
getTable
()
->
getIdentifier
()
.
' IN('
.
$params
.
')'
;
$this
->
conn
->
execute
(
$query
,
$ids
);
}
}
}
}
/**
* saveRelated
* saves all related records to $record
*
* @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record
*/
public
function
saveRelated
(
Doctrine_Record
$record
)
{
$saveLater
=
array
();
foreach
(
$record
->
getReferences
()
as
$k
=>
$v
)
{
$rel
=
$record
->
getTable
()
->
getRelation
(
$k
);
$local
=
$rel
->
getLocal
();
$foreign
=
$rel
->
getForeign
();
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
$saveLater
[
$k
]
=
$rel
;
}
elseif
(
$rel
instanceof
Doctrine_Relation_LocalKey
)
{
// ONE-TO-ONE relationship
$obj
=
$record
->
get
(
$rel
->
getAlias
());
// Protection against infinite function recursion before attempting to save
if
(
$obj
instanceof
Doctrine_Record
&&
$obj
->
isModified
())
{
$obj
->
save
(
$this
->
conn
);
/**
$id = array_values($obj->identifier());
foreach ((array) $rel->getLocal() as $k => $field) {
$record->set($field, $id[$k]);
}
*/
}
}
}
return
$saveLater
;
}
/**
* saveAssociations
*
* 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
*
* @throws Doctrine_Connection_Exception if something went wrong at database level
* @param Doctrine_Record $record
* @return void
*/
public
function
saveAssociations
(
Doctrine_Record
$record
)
{
foreach
(
$record
->
getReferences
()
as
$k
=>
$v
)
{
$rel
=
$record
->
getTable
()
->
getRelation
(
$k
);
if
(
$rel
instanceof
Doctrine_Relation_Association
)
{
$v
->
save
(
$this
->
conn
);
$assocTable
=
$rel
->
getAssociationTable
();
foreach
(
$v
->
getDeleteDiff
()
as
$r
)
{
$query
=
'DELETE FROM '
.
$assocTable
->
getTableName
()
.
' WHERE '
.
$rel
->
getForeign
()
.
' = ?'
.
' AND '
.
$rel
->
getLocal
()
.
' = ?'
;
$this
->
conn
->
execute
(
$query
,
array
(
$r
->
getIncremented
(),
$record
->
getIncremented
()));
}
foreach
(
$v
->
getInsertDiff
()
as
$r
)
{
$assocRecord
=
$assocTable
->
create
();
$assocRecord
->
set
(
$rel
->
getForeign
(),
$r
);
$assocRecord
->
set
(
$rel
->
getLocal
(),
$record
);
$this
->
saveGraph
(
$assocRecord
);
}
}
}
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
* @throws PDOException if something went wrong at database level
* @return void
*/
public
function
deleteComposites
(
Doctrine_Record
$record
)
{
foreach
(
$record
->
getTable
()
->
getRelations
()
as
$fk
)
{
if
(
$fk
->
isComposite
())
{
$obj
=
$record
->
get
(
$fk
->
getAlias
());
if
(
$obj
instanceof
Doctrine_Record
&&
$obj
->
state
()
!=
Doctrine_Record
::
STATE_LOCKED
)
{
$obj
->
delete
(
$this
->
conn
);
}
}
}
}
/**
* saveAll
* persists all the pending records from all tables
*
* @throws PDOException if something went wrong at database level
* @return void
*/
public
function
saveAll
()
{
// get the flush tree
$tree
=
$this
->
buildFlushTree
(
$this
->
conn
->
getTables
());
// save all records
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
conn
->
getTable
(
$name
);
foreach
(
$table
->
getRepository
()
as
$record
)
{
$this
->
save
(
$record
);
}
}
// save all associations
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
conn
->
getTable
(
$name
);
foreach
(
$table
->
getRepository
()
as
$record
)
{
$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
*/
public
function
update
(
Doctrine_Record
$record
)
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_UPDATE
);
$record
->
preUpdate
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preUpdate
(
$event
);
if
(
!
$event
->
skipOperation
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
{
return
false
;
}
$set
=
array
();
foreach
(
$array
as
$name
=>
$value
)
{
if
(
$value
instanceof
Doctrine_Expression
)
{
$set
[]
=
$
value
->
getSql
();
unset
(
$array
[
$name
]);
}
else
{
$set
[]
=
$name
.
' = ?'
;
if
(
$value
instanceof
Doctrine_Record
)
{
if
(
!
$value
->
exists
())
{
$record
->
save
(
$this
->
conn
);
}
$array
[
$name
]
=
$value
->
getIncremented
();
$record
->
set
(
$name
,
$value
->
getIncremented
());
}
}
}
$params
=
array_values
(
$array
);
$id
=
$record
->
identifier
();
if
(
!
is_array
(
$id
))
{
$id
=
array
(
$id
);
}
$id
=
array_values
(
$id
);
$params
=
array_merge
(
$params
,
$id
);
$sql
=
'UPDATE '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' SET '
.
implode
(
', '
,
$set
)
.
' WHERE '
.
implode
(
' = ? AND '
,
(
array
)
$record
->
getTable
()
->
getIdentifier
())
.
' = ?'
;
$stmt
=
$this
->
conn
->
prepare
(
$sql
);
$stmt
->
execute
(
$params
);
$record
->
assignIdentifier
(
true
);
}
$record
->
getTable
()
->
getRecordListener
()
->
postUpdate
(
$event
);
$record
->
postUpdate
(
$event
);
return
true
;
}
/**
* inserts a record into database
*
* @param Doctrine_Record $record record to be inserted
* @return boolean
*/
public
function
insert
(
Doctrine_Record
$record
)
{
// listen the onPreInsert event
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_INSERT
);
$record
->
preInsert
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preInsert
(
$event
);
if
(
!
$event
->
skipOperation
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
{
return
false
;
}
$table
=
$record
->
getTable
();
$keys
=
(
array
)
$table
->
getIdentifier
();
$seq
=
$record
->
getTable
()
->
sequenceName
;
if
(
!
empty
(
$seq
))
{
$id
=
$this
->
conn
->
sequence
->
nextId
(
$seq
);
$name
=
$record
->
getTable
()
->
getIdentifier
();
$array
[
$name
]
=
$id
;
$record
->
assignIdentifier
(
$id
);
}
$this
->
conn
->
insert
(
$table
->
getTableName
(),
$array
);
if
(
empty
(
$seq
)
&&
count
(
$keys
)
==
1
&&
$keys
[
0
]
==
$table
->
getIdentifier
()
&&
$table
->
getIdentifierType
()
!=
Doctrine
::
IDENTIFIER_NATURAL
)
{
if
(
strtolower
(
$this
->
conn
->
getName
())
==
'pgsql'
)
{
$seq
=
$table
->
getTableName
()
.
'_'
.
$keys
[
0
];
}
$id
=
$this
->
conn
->
sequence
->
lastInsertId
(
$seq
);
if
(
!
$id
)
{
throw
new
Doctrine_Connection_Exception
(
"Couldn't get last insert identifier."
);
}
$record
->
assignIdentifier
(
$id
);
}
else
{
$record
->
assignIdentifier
(
true
);
}
}
$record
->
getTable
()
->
addRecord
(
$record
);
$record
->
getTable
()
->
getRecordListener
()
->
postInsert
(
$event
);
$record
->
postInsert
(
$event
);
return
true
;
}
}
<?php
/*
* $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'
);
/**
* Doctrine_Connection_UnitOfWork
*
* @package Doctrine
* @subpackage Connection
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
class
Doctrine_Connection_UnitOfWork
extends
Doctrine_Connection_Module
{
/**
* buildFlushTree
* builds a flush tree that is used in transactions
*
* The returned array has all the initialized components in
* '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 an array of Doctrine_Table objects or component names
* @return array an array of component names in flushing order
*/
public
function
buildFlushTree
(
array
$tables
)
{
$tree
=
array
();
foreach
(
$tables
as
$k
=>
$table
)
{
if
(
!
(
$table
instanceof
Doctrine_Table
))
{
$table
=
$this
->
conn
->
getTable
(
$table
,
false
);
}
$nm
=
$table
->
getComponentName
();
$index
=
array_search
(
$nm
,
$tree
);
if
(
$index
===
false
)
{
$tree
[]
=
$nm
;
$index
=
max
(
array_keys
(
$tree
));
}
$rels
=
$table
->
getRelations
();
// group relations
foreach
(
$rels
as
$key
=>
$rel
)
{
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
unset
(
$rels
[
$key
]);
array_unshift
(
$rels
,
$rel
);
}
}
foreach
(
$rels
as
$rel
)
{
$name
=
$rel
->
getTable
()
->
getComponentName
();
$index2
=
array_search
(
$name
,
$tree
);
$type
=
$rel
->
getType
();
// skip self-referenced relations
if
(
$name
===
$nm
)
{
continue
;
}
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
;
}
}
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
++
;
}
}
elseif
(
$rel
instanceof
Doctrine_Relation_Association
)
{
$t
=
$rel
->
getAssociationFactory
();
$n
=
$t
->
getComponentName
();
if
(
$index2
!==
false
)
unset
(
$tree
[
$index2
]);
array_splice
(
$tree
,
$index
,
0
,
$name
);
$index
++
;
$index3
=
array_search
(
$n
,
$tree
);
if
(
$index3
!==
false
)
{
if
(
$index3
>=
$index
)
continue
;
unset
(
$tree
[
$index
]);
array_splice
(
$tree
,
$index3
,
0
,
$n
);
$index
=
$index2
;
}
else
{
$tree
[]
=
$n
;
}
}
}
}
return
array_values
(
$tree
);
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public
function
saveGraph
(
Doctrine_Record
$record
)
{
$conn
=
$this
->
getConnection
();
$state
=
$record
->
state
();
if
(
$state
===
Doctrine_Record
::
STATE_LOCKED
)
{
return
false
;
}
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
$conn
->
beginTransaction
();
$saveLater
=
$this
->
saveRelated
(
$record
);
$record
->
state
(
$state
);
if
(
$record
->
isValid
())
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_SAVE
);
$record
->
preSave
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preSave
(
$event
);
if
(
!
$event
->
skipOperation
)
{
switch
(
$state
)
{
case
Doctrine_Record
::
STATE_TDIRTY
:
$this
->
insert
(
$record
);
break
;
case
Doctrine_Record
::
STATE_DIRTY
:
case
Doctrine_Record
::
STATE_PROXY
:
$this
->
update
(
$record
);
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_TCLEAN
:
break
;
}
}
$record
->
getTable
()
->
getRecordListener
()
->
postSave
(
$event
);
$record
->
postSave
(
$event
);
}
else
{
$conn
->
transaction
->
addInvalid
(
$record
);
}
$state
=
$record
->
state
();
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
foreach
(
$saveLater
as
$fk
)
{
$alias
=
$fk
->
getAlias
();
if
(
$record
->
hasReference
(
$alias
))
{
$obj
=
$record
->
$alias
;
// check that the related object is not an instance of Doctrine_Null
if
(
!
(
$obj
instanceof
Doctrine_Null
))
{
$obj
->
save
(
$conn
);
}
}
}
// save the MANY-TO-MANY associations
$this
->
saveAssociations
(
$record
);
$record
->
state
(
$state
);
$conn
->
commit
();
return
true
;
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public
function
save
(
Doctrine_Record
$record
)
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_SAVE
);
$record
->
preSave
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preSave
(
$event
);
if
(
!
$event
->
skipOperation
)
{
switch
(
$record
->
state
())
{
case
Doctrine_Record
::
STATE_TDIRTY
:
$this
->
insert
(
$record
);
break
;
case
Doctrine_Record
::
STATE_DIRTY
:
case
Doctrine_Record
::
STATE_PROXY
:
$this
->
update
(
$record
);
break
;
case
Doctrine_Record
::
STATE_CLEAN
:
case
Doctrine_Record
::
STATE_TCLEAN
:
// do nothing
break
;
}
}
$record
->
getTable
()
->
getRecordListener
()
->
postSave
(
$event
);
$record
->
postSave
(
$event
);
}
/**
* deletes given record and all the related composites
* this operation is isolated by a transaction
*
* this event can be listened by the onPreDelete and onDelete listeners
*
* @return boolean true on success, false on failure
*/
public
function
delete
(
Doctrine_Record
$record
)
{
if
(
!
$record
->
exists
())
{
return
false
;
}
$this
->
conn
->
beginTransaction
();
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_DELETE
);
$record
->
preDelete
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preDelete
(
$event
);
$state
=
$record
->
state
();
$record
->
state
(
Doctrine_Record
::
STATE_LOCKED
);
$this
->
deleteComposites
(
$record
);
if
(
!
$event
->
skipOperation
)
{
$record
->
state
(
Doctrine_Record
::
STATE_TDIRTY
);
$this
->
deleteRecord
(
$record
);
$record
->
state
(
Doctrine_Record
::
STATE_TCLEAN
);
}
else
{
// return to original state
$record
->
state
(
$state
);
}
$record
->
getTable
()
->
getRecordListener
()
->
postDelete
(
$event
);
$record
->
postDelete
(
$event
);
$this
->
conn
->
commit
();
return
true
;
}
public
function
deleteRecord
(
Doctrine_Record
$record
)
{
$ids
=
$record
->
identifier
();
$tmp
=
array
();
foreach
(
array_keys
(
$ids
)
as
$id
)
{
$tmp
[]
=
$id
.
' = ? '
;
}
$params
=
array_values
(
$ids
);
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' WHERE '
.
implode
(
' AND '
,
$tmp
);
return
$this
->
conn
->
exec
(
$query
,
$params
);
}
/**
* deleteMultiple
* deletes all records from the pending delete list
*
* @return void
*/
public
function
deleteMultiple
(
array
$records
)
{
foreach
(
$this
->
delete
as
$name
=>
$deletes
)
{
$record
=
false
;
$ids
=
array
();
if
(
is_array
(
$deletes
[
count
(
$deletes
)
-
1
]
->
getTable
()
->
getIdentifier
()))
{
if
(
count
(
$deletes
)
>
0
)
{
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$deletes
[
0
]
->
getTable
()
->
getTableName
())
.
' WHERE '
;
$params
=
array
();
$cond
=
array
();
foreach
(
$deletes
as
$k
=>
$record
)
{
$ids
=
$record
->
identifier
();
$tmp
=
array
();
foreach
(
array_keys
(
$ids
)
as
$id
)
{
$tmp
[]
=
$id
.
' = ? '
;
}
$params
=
array_merge
(
$params
,
array_values
(
$ids
));
$cond
[]
=
'('
.
implode
(
' AND '
,
$tmp
)
.
')'
;
}
$query
.=
implode
(
' OR '
,
$cond
);
$this
->
conn
->
execute
(
$query
,
$params
);
}
}
else
{
foreach
(
$deletes
as
$k
=>
$record
)
{
$ids
[]
=
$record
->
getIncremented
();
}
if
(
$record
instanceof
Doctrine_Record
)
{
$params
=
substr
(
str_repeat
(
'?, '
,
count
(
$ids
)),
0
,
-
2
);
$query
=
'DELETE FROM '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' WHERE '
.
$record
->
getTable
()
->
getIdentifier
()
.
' IN('
.
$params
.
')'
;
$this
->
conn
->
execute
(
$query
,
$ids
);
}
}
}
}
/**
* saveRelated
* saves all related records to $record
*
* @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record
*/
public
function
saveRelated
(
Doctrine_Record
$record
)
{
$saveLater
=
array
();
foreach
(
$record
->
getReferences
()
as
$k
=>
$v
)
{
$rel
=
$record
->
getTable
()
->
getRelation
(
$k
);
$local
=
$rel
->
getLocal
();
$foreign
=
$rel
->
getForeign
();
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
$saveLater
[
$k
]
=
$rel
;
}
elseif
(
$rel
instanceof
Doctrine_Relation_LocalKey
)
{
// ONE-TO-ONE relationship
$obj
=
$record
->
get
(
$rel
->
getAlias
());
// Protection against infinite function recursion before attempting to save
if
(
$obj
instanceof
Doctrine_Record
&&
$obj
->
isModified
())
{
$obj
->
save
(
$this
->
conn
);
/**
$id = array_values($obj->identifier());
foreach ((array) $rel->getLocal() as $k => $field) {
$record->set($field, $id[$k]);
}
*/
}
}
}
return
$saveLater
;
}
/**
* saveAssociations
*
* 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
*
* @throws Doctrine_Connection_Exception if something went wrong at database level
* @param Doctrine_Record $record
* @return void
*/
public
function
saveAssociations
(
Doctrine_Record
$record
)
{
foreach
(
$record
->
getReferences
()
as
$k
=>
$v
)
{
$rel
=
$record
->
getTable
()
->
getRelation
(
$k
);
if
(
$rel
instanceof
Doctrine_Relation_Association
)
{
$v
->
save
(
$this
->
conn
);
$assocTable
=
$rel
->
getAssociationTable
();
foreach
(
$v
->
getDeleteDiff
()
as
$r
)
{
$query
=
'DELETE FROM '
.
$assocTable
->
getTableName
()
.
' WHERE '
.
$rel
->
getForeign
()
.
' = ?'
.
' AND '
.
$rel
->
getLocal
()
.
' = ?'
;
$this
->
conn
->
execute
(
$query
,
array
(
$r
->
getIncremented
(),
$record
->
getIncremented
()));
}
foreach
(
$v
->
getInsertDiff
()
as
$r
)
{
$assocRecord
=
$assocTable
->
create
();
$assocRecord
->
set
(
$rel
->
getForeign
(),
$r
);
$assocRecord
->
set
(
$rel
->
getLocal
(),
$record
);
$this
->
saveGraph
(
$assocRecord
);
}
}
}
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
* @throws PDOException if something went wrong at database level
* @return void
*/
public
function
deleteComposites
(
Doctrine_Record
$record
)
{
foreach
(
$record
->
getTable
()
->
getRelations
()
as
$fk
)
{
if
(
$fk
->
isComposite
())
{
$obj
=
$record
->
get
(
$fk
->
getAlias
());
if
(
$obj
instanceof
Doctrine_Record
&&
$obj
->
state
()
!=
Doctrine_Record
::
STATE_LOCKED
)
{
$obj
->
delete
(
$this
->
conn
);
}
}
}
}
/**
* saveAll
* persists all the pending records from all tables
*
* @throws PDOException if something went wrong at database level
* @return void
*/
public
function
saveAll
()
{
// get the flush tree
$tree
=
$this
->
buildFlushTree
(
$this
->
conn
->
getTables
());
// save all records
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
conn
->
getTable
(
$name
);
foreach
(
$table
->
getRepository
()
as
$record
)
{
$this
->
save
(
$record
);
}
}
// save all associations
foreach
(
$tree
as
$name
)
{
$table
=
$this
->
conn
->
getTable
(
$name
);
foreach
(
$table
->
getRepository
()
as
$record
)
{
$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
*/
public
function
update
(
Doctrine_Record
$record
)
{
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_UPDATE
);
$record
->
preUpdate
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preUpdate
(
$event
);
if
(
!
$event
->
skipOperation
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
{
return
false
;
}
$set
=
array
();
foreach
(
$array
as
$name
=>
$value
)
{
if
(
$value
instanceof
Doctrine_Expression
)
{
$set
[]
=
$
name
.
' = '
.
$value
->
getSql
();
unset
(
$array
[
$name
]);
}
else
{
$set
[]
=
$name
.
' = ?'
;
if
(
$value
instanceof
Doctrine_Record
)
{
if
(
!
$value
->
exists
())
{
$record
->
save
(
$this
->
conn
);
}
$array
[
$name
]
=
$value
->
getIncremented
();
$record
->
set
(
$name
,
$value
->
getIncremented
());
}
}
}
$params
=
array_values
(
$array
);
$id
=
$record
->
identifier
();
if
(
!
is_array
(
$id
))
{
$id
=
array
(
$id
);
}
$id
=
array_values
(
$id
);
$params
=
array_merge
(
$params
,
$id
);
$sql
=
'UPDATE '
.
$this
->
conn
->
quoteIdentifier
(
$record
->
getTable
()
->
getTableName
())
.
' SET '
.
implode
(
', '
,
$set
)
.
' WHERE '
.
implode
(
' = ? AND '
,
(
array
)
$record
->
getTable
()
->
getIdentifier
())
.
' = ?'
;
$stmt
=
$this
->
conn
->
prepare
(
$sql
);
$stmt
->
execute
(
$params
);
$record
->
assignIdentifier
(
true
);
}
$record
->
getTable
()
->
getRecordListener
()
->
postUpdate
(
$event
);
$record
->
postUpdate
(
$event
);
return
true
;
}
/**
* inserts a record into database
*
* @param Doctrine_Record $record record to be inserted
* @return boolean
*/
public
function
insert
(
Doctrine_Record
$record
)
{
// listen the onPreInsert event
$event
=
new
Doctrine_Event
(
$record
,
Doctrine_Event
::
RECORD_INSERT
);
$record
->
preInsert
(
$event
);
$record
->
getTable
()
->
getRecordListener
()
->
preInsert
(
$event
);
if
(
!
$event
->
skipOperation
)
{
$array
=
$record
->
getPrepared
();
if
(
empty
(
$array
))
{
return
false
;
}
$table
=
$record
->
getTable
();
$keys
=
(
array
)
$table
->
getIdentifier
();
$seq
=
$record
->
getTable
()
->
sequenceName
;
if
(
!
empty
(
$seq
))
{
$id
=
$this
->
conn
->
sequence
->
nextId
(
$seq
);
$name
=
$record
->
getTable
()
->
getIdentifier
();
$array
[
$name
]
=
$id
;
$record
->
assignIdentifier
(
$id
);
}
$this
->
conn
->
insert
(
$table
->
getTableName
(),
$array
);
if
(
empty
(
$seq
)
&&
count
(
$keys
)
==
1
&&
$keys
[
0
]
==
$table
->
getIdentifier
()
&&
$table
->
getIdentifierType
()
!=
Doctrine
::
IDENTIFIER_NATURAL
)
{
if
(
strtolower
(
$this
->
conn
->
getName
())
==
'pgsql'
)
{
$seq
=
$table
->
getTableName
()
.
'_'
.
$keys
[
0
];
}
$id
=
$this
->
conn
->
sequence
->
lastInsertId
(
$seq
);
if
(
!
$id
)
{
throw
new
Doctrine_Connection_Exception
(
"Couldn't get last insert identifier."
);
}
$record
->
assignIdentifier
(
$id
);
}
else
{
$record
->
assignIdentifier
(
true
);
}
}
$record
->
getTable
()
->
addRecord
(
$record
);
$record
->
getTable
()
->
getRecordListener
()
->
postInsert
(
$event
);
$record
->
postInsert
(
$event
);
return
true
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment