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
d9975c36
Commit
d9975c36
authored
Jul 20, 2008
by
romanb
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Checkin of occasional work from the past weeks.
parent
c43f9588
Changes
40
Hide whitespace changes
Inline
Side-by-side
Showing
40 changed files
with
2604 additions
and
1393 deletions
+2604
-1393
Association.php
lib/Doctrine/Association.php
+228
-0
ManyToMany.php
lib/Doctrine/Association/ManyToMany.php
+80
-0
OneToMany.php
lib/Doctrine/Association/OneToMany.php
+28
-0
OneToOne.php
lib/Doctrine/Association/OneToOne.php
+139
-0
ClassMetadata.php
lib/Doctrine/ClassMetadata.php
+347
-548
Factory.php
lib/Doctrine/ClassMetadata/Factory.php
+46
-23
Collection.php
lib/Doctrine/Collection.php
+66
-110
Connection.php
lib/Doctrine/Connection.php
+56
-40
UnitOfWork.php
lib/Doctrine/Connection/UnitOfWork.php
+597
-436
Entity.php
lib/Doctrine/Entity.php
+59
-121
EntityManager.php
lib/Doctrine/EntityManager.php
+35
-7
EventSubscriber.php
lib/Doctrine/EventSubscriber.php
+3
-8
Exception.php
lib/Doctrine/Exception.php
+2
-1
RecordDriver.php
lib/Doctrine/Hydrator/RecordDriver.php
+1
-1
HydratorNew.php
lib/Doctrine/HydratorNew.php
+8
-10
CommitOrderCalculator.php
lib/Doctrine/Internal/CommitOrderCalculator.php
+110
-0
CommitOrderNode.php
lib/Doctrine/Internal/CommitOrderNode.php
+136
-0
MappingException.php
lib/Doctrine/MappingException.php
+16
-0
Overloadable.php
lib/Doctrine/Overloadable.php
+1
-1
Tree.php
lib/Doctrine/Tree.php
+1
-0
Validator.php
lib/Doctrine/Validator.php
+1
-0
CascadeTest.php
tests/Orm/Associations/CascadeTest.php
+74
-0
OneToOneMappingTest.php
tests/Orm/Associations/OneToOneMappingTest.php
+36
-0
CollectionTest.php
tests/Orm/Component/CollectionTest.php
+4
-4
AccessorTest.php
tests/Orm/Entity/AccessorTest.php
+26
-8
ConstructorTest.php
tests/Orm/Entity/ConstructorTest.php
+12
-3
UnitOfWorkTest.php
tests/Orm/UnitOfWorkTest.php
+120
-19
Doctrine_ConnectionMock.php
tests/lib/mocks/Doctrine_ConnectionMock.php
+20
-0
Doctrine_EntityManagerMock.php
tests/lib/mocks/Doctrine_EntityManagerMock.php
+25
-0
Doctrine_EntityPersisterMock.php
tests/lib/mocks/Doctrine_EntityPersisterMock.php
+56
-0
Doctrine_SequenceMock.php
tests/lib/mocks/Doctrine_SequenceMock.php
+37
-0
CmsArticle.php
tests/models/cms/CmsArticle.php
+36
-9
CmsComment.php
tests/models/cms/CmsComment.php
+34
-7
CmsPhonenumber.php
tests/models/cms/CmsPhonenumber.php
+17
-5
CmsUser.php
tests/models/cms/CmsUser.php
+38
-10
ForumAdministrator.php
tests/models/forum/ForumAdministrator.php
+7
-2
ForumBoard.php
tests/models/forum/ForumBoard.php
+20
-6
ForumCategory.php
tests/models/forum/ForumCategory.php
+22
-6
ForumEntry.php
tests/models/forum/ForumEntry.php
+32
-0
ForumUser.php
tests/models/forum/ForumUser.php
+28
-8
No files found.
lib/Doctrine/Association.php
0 → 100644
View file @
d9975c36
<?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.org>.
*/
#namespace Doctrine::ORM::Mapping;
/**
* Base class for association mappings.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to AssociationMapping.
*/
class
Doctrine_Association
implements
Serializable
{
const
FETCH_MANUAL
=
1
;
const
FETCH_LAZY
=
2
;
const
FETCH_EAGER
=
3
;
/**
* Cascade types enumeration.
*
* @var array
*/
protected
static
$_cascadeTypes
=
array
(
'all'
,
'none'
,
'save'
,
'delete'
,
'refresh'
);
protected
$_cascades
=
array
();
protected
$_isCascadeDelete
;
protected
$_isCascadeSave
;
protected
$_isCascadeRefresh
;
/**
* The fetch mode used for the association.
*
* @var integer
*/
protected
$_fetchMode
=
self
::
FETCH_MANUAL
;
/**
* Flag that indicates whether the class that defines this mapping is
* the owning side of the association.
*
* @var boolean
*/
protected
$_isOwningSide
=
true
;
/**
* The name of the source Entity (the Entity that defines this mapping).
*
* @var string
*/
protected
$_sourceEntityName
;
/**
* The name of the target Entity (the Enitity that is the target of the
* association).
*
* @var unknown_type
*/
protected
$_targetEntityName
;
/**
* Identifies the field on the source class (the class this AssociationMapping
* belongs to) that represents the association.
*
* @var string
*/
protected
$_sourceFieldName
;
/**
* Identifies the field on the owning side that has the mapping for the
* association.
*
* @var string
*/
protected
$_mappedByFieldName
;
/**
* Constructor.
* Creates a new AssociationMapping.
*
* @param array $mapping The mapping definition.
*/
public
function
__construct
(
array
$mapping
)
{
$this
->
_validateMapping
(
$mapping
);
if
(
$this
->
_isOwningSide
)
{
$this
->
_sourceEntityName
=
$mapping
[
'sourceEntity'
];
$this
->
_targetEntityName
=
$mapping
[
'targetEntity'
];
$this
->
_sourceFieldName
=
$mapping
[
'fieldName'
];
}
else
{
$this
->
_mappedByFieldName
=
$mapping
[
'mappedBy'
];
}
}
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping
* @return array The validated & completed mapping.
*/
protected
function
_validateMapping
(
array
$mapping
)
{
if
(
isset
(
$mapping
[
'mappedBy'
]))
{
$this
->
_isOwningSide
=
false
;
}
if
(
$this
->
_isOwningSide
)
{
if
(
!
isset
(
$mapping
[
'targetEntity'
]))
{
throw
Doctrine_MappingException
::
missingTargetEntity
();
}
else
if
(
!
isset
(
$mapping
[
'fieldName'
]))
{
throw
Doctrine_MappingException
::
missingFieldName
();
}
}
return
$mapping
;
}
public
function
isCascadeDelete
()
{
if
(
is_null
(
$this
->
_isCascadeDelete
))
{
$this
->
_isCascadeDelete
=
in_array
(
'delete'
,
$this
->
_cascades
);
}
return
$this
->
_isCascadeDelete
;
}
public
function
isCascadeSave
()
{
if
(
is_null
(
$this
->
_isCascadeSave
))
{
$this
->
_isCascadeSave
=
in_array
(
'save'
,
$this
->
_cascades
);
}
return
$this
->
_isCascadeSave
;
}
public
function
isCascadeRefresh
()
{
if
(
is_null
(
$this
->
_isCascadeRefresh
))
{
$this
->
_isCascadeRefresh
=
in_array
(
'refresh'
,
$this
->
_cascades
);
}
return
$this
->
_isCascadeRefresh
;
}
public
function
isEagerlyFetched
()
{
return
$this
->
_fetchMode
==
self
::
FETCH_EAGER
;
}
public
function
isLazilyFetched
()
{
return
$this
->
_fetchMode
==
self
::
FETCH_LAZY
;
}
public
function
isManuallyFetched
()
{
return
$this
->
_fetchMode
==
self
::
FETCH_MANUAL
;
}
public
function
isOwningSide
()
{
return
$this
->
_isOwningSide
;
}
public
function
isInverseSide
()
{
return
!
$this
->
_isOwningSide
;
}
public
function
getSourceEntityName
()
{
return
$this
->
_sourceEntityName
;
}
public
function
getTargetEntityName
()
{
return
$this
->
_targetEntityName
;
}
/**
* Get the name of the field the association is mapped into.
*
*/
public
function
getSourceFieldName
()
{
return
$this
->
_sourceFieldName
;
}
public
function
getMappedByFieldName
()
{
return
$this
->
_mappedByFieldName
;
}
/* Serializable implementation */
public
function
serialize
()
{
return
""
;
}
public
function
unserialize
(
$serialized
)
{
return
true
;
}
}
?>
\ No newline at end of file
lib/Doctrine/Association/ManyToMany.php
0 → 100644
View file @
d9975c36
<?php
#namespace Doctrine::ORM::Mappings;
/**
* A many-to-many mapping describes the mapping between two collections of
* entities.
*
* @since 2.0
* @todo Rename to ManyToManyMapping
*/
class
Doctrine_Association_ManyToMany
extends
Doctrine_Association
{
/**
* Whether the mapping uses an association class.
*
* @var boolean
*/
private
$_usesAssociationClass
;
/**
* The name of the association class (if an association class is used).
*
* @var string
*/
private
$_associationClassName
;
/**
* The name of the intermediate table.
*
* @var string
*/
private
$_relationTableName
;
/** The field in the source table that corresponds to the key in the relation table */
protected
$_sourceKeyFields
;
/** The field in the target table that corresponds to the key in the relation table */
protected
$_targetKeyFields
;
/** The field in the intermediate table that corresponds to the key in the source table */
protected
$_sourceRelationKeyFields
;
/** The field in the intermediate table that corresponds to the key in the target table */
protected
$_targetRelationKeyFields
;
/**
* Whether the mapping uses an association class for the intermediary
* table.
*
* @return boolean
*/
public
function
usesAssociationClass
()
{
}
/**
* Gets the name of the intermediate table.
*
* @return string
*/
public
function
getRelationTableName
()
{
return
$this
->
_relationTableName
;
}
/**
* Gets the name of the association class.
*
* @return string
*/
public
function
getAssociationClassName
()
{
return
$this
->
_associationClassName
;
}
}
?>
\ No newline at end of file
lib/Doctrine/Association/OneToMany.php
0 → 100644
View file @
d9975c36
<?php
#namespace Doctrine::ORM::Mappings;
class
Doctrine_Association_OneToMany
extends
Doctrine_Association
{
/** The target foreign key fields that reference the sourceKeyFields. */
protected
$_targetForeignKeyFields
;
/** The (typically primary) source key fields that are referenced by the targetForeignKeyFields. */
protected
$_sourceKeyFields
;
/** This maps the target foreign key fields to the corresponding (primary) source key fields. */
protected
$_targetForeignKeysToSourceKeys
;
/** This maps the (primary) source key fields to the corresponding target foreign key fields. */
protected
$_sourceKeysToTargetForeignKeys
;
/** Whether to delete orphaned elements (removed from the collection) */
protected
$_isCascadeDeleteOrphan
=
false
;
public
function
isCascadeDeleteOrphan
()
{
return
$this
->
_isCascadeDeleteOrphan
;
}
}
?>
\ No newline at end of file
lib/Doctrine/Association/OneToOne.php
0 → 100644
View file @
d9975c36
<?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.org>.
*/
#namespace Doctrine::ORM::Mappings;
#use Doctrine::ORM::Entity;
/**
* A one-to-one mapping describes a uni-directional mapping from one entity
* to another entity.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to OneToOneMapping
*/
class
Doctrine_Association_OneToOne
extends
Doctrine_Association
{
/**
* Maps the source foreign/primary key fields to the target primary/foreign key fields.
* i.e. source.id (pk) => target.user_id (fk).
* Reverse mapping of _targetToSourceKeyFields.
*/
protected
$_sourceToTargetKeyColumns
=
array
();
/**
* Maps the target primary/foreign key fields to the source foreign/primary key fields.
* i.e. target.user_id (fk) => source.id (pk).
* Reverse mapping of _sourceToTargetKeyFields.
*/
protected
$_targetToSourceKeyColumns
=
array
();
/**
* Constructor.
* Creates a new OneToOneMapping.
*
* @param array $mapping The mapping info.
*/
public
function
__construct
(
array
$mapping
)
{
parent
::
__construct
(
$mapping
);
if
(
$this
->
isOwningSide
())
{
$this
->
_sourceToTargetKeyColumns
=
$mapping
[
'joinColumns'
];
$this
->
_targetToSourceKeyColumns
=
array_flip
(
$this
->
_sourceToTargetKeyColumns
);
}
}
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping The mapping to validate & complete.
* @return array The validated & completed mapping.
* @override
*/
protected
function
_validateMapping
(
array
$mapping
)
{
$mapping
=
parent
::
_validateMapping
(
$mapping
);
if
(
$this
->
isOwningSide
())
{
if
(
!
isset
(
$mapping
[
'joinColumns'
]))
{
throw
Doctrine_MappingException
::
missingJoinColumns
();
}
}
return
$mapping
;
}
/**
* Gets the source-to-target key column mapping.
*
* @return unknown
*/
public
function
getSourceToTargetKeyColumns
()
{
return
$this
->
_sourceToTargetKeyColumns
;
}
/**
* Gets the target-to-source key column mapping.
*
* @return unknown
*/
public
function
getTargetToSourceKeyColumns
()
{
return
$this
->
_targetToSourceKeyColumns
;
}
/**
* Lazy-loads the associated entity for a given entity.
*
* @param Doctrine::ORM::Entity $entity
* @return void
*/
public
function
lazyLoadFor
(
Doctrine_Entity
$entity
)
{
if
(
$entity
->
getClassName
()
!=
$this
->
_sourceClass
->
getClassName
())
{
//error?
}
$dql
=
'SELECT t.* FROM '
.
$this
->
_targetClass
->
getClassName
()
.
' t WHERE '
;
$params
=
array
();
foreach
(
$this
->
_sourceToTargetKeyFields
as
$sourceKeyField
=>
$targetKeyField
)
{
if
(
$params
)
{
$dql
.=
" AND "
;
}
$dql
.=
"t.
$targetKeyField
= ?"
;
$params
[]
=
$entity
->
_rawGetField
(
$sourceKeyField
);
}
$otherEntity
=
$this
->
_targetClass
->
getEntityManager
()
->
query
(
$dql
,
$params
)
->
getFirst
();
if
(
!
$otherEntity
)
{
$otherEntity
=
Doctrine_Null
::
$INSTANCE
;
}
$entity
->
_rawSetReference
(
$this
->
_sourceFieldName
,
$otherEntity
);
}
}
?>
\ No newline at end of file
lib/Doctrine/ClassMetadata.php
View file @
d9975c36
...
...
@@ -21,6 +21,8 @@
#namespace Doctrine::ORM::Mapping;
#use Doctrine::ORM::EntityManager;
/**
* A <tt>ClassMetadata</tt> instance holds all the information (metadata) of an entity and
* it's associations and how they're mapped to the relational database.
...
...
@@ -28,11 +30,50 @@
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Rename to ClassDescriptor?
*/
class
Doctrine_ClassMetadata
implements
Doctrine_Configurable
,
Serializable
{
/* The inheritance mapping types */
const
INHERITANCE_TYPE_NONE
=
'none'
;
const
INHERITANCE_TYPE_JOINED
=
'joined'
;
const
INHERITANCE_TYPE_SINGLE_TABLE
=
'singleTable'
;
const
INHERITANCE_TYPE_TABLE_PER_CLASS
=
'tablePerClass'
;
/**
* Inheritance types enumeration.
*
* @var array
*/
protected
static
$_inheritanceTypes
=
array
(
self
::
INHERITANCE_TYPE_NONE
,
self
::
INHERITANCE_TYPE_JOINED
,
self
::
INHERITANCE_TYPE_SINGLE_TABLE
,
self
::
INHERITANCE_TYPE_TABLE_PER_CLASS
);
/* The Id generator types. TODO: belongs more in a DBAL class */
const
GENERATOR_TYPE_AUTO
=
'auto'
;
const
GENERATOR_TYPE_SEQUENCE
=
'sequence'
;
const
GENERATOR_TYPE_TABLE
=
'table'
;
const
GENERATOR_TYPE_IDENTITY
=
'identity'
;
const
GENERATOR_TYPE_NONE
=
'none'
;
/**
* Id Generator types enumeration.
*
* @var array
*/
protected
static
$_generatorTypes
=
array
(
self
::
GENERATOR_TYPE_AUTO
,
self
::
GENERATOR_TYPE_SEQUENCE
,
self
::
GENERATOR_TYPE_TABLE
,
self
::
GENERATOR_TYPE_IDENTITY
,
self
::
GENERATOR_TYPE_NONE
);
/**
* The name of the entity class
that is mapped to the database with this metadata
.
* The name of the entity class.
*
* @var string
*/
...
...
@@ -41,7 +82,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* The name of the entity class that is at the root of the entity inheritance
* hierarchy. If the entity is not part of an inheritance hierarchy this is the same
* as
the
$_entityName.
* as $_entityName.
*
* @var string
*/
...
...
@@ -56,18 +97,23 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
protected
$_customRepositoryClassName
;
/**
*
* @var Doctrine_Connection
* The EntityManager.
*
* @var Doctrine::ORM::EntityManager
*/
protected
$_em
;
/**
* The names of the parent classes (ancestors).
*
* @var array
*/
protected
$_parentClasses
=
array
();
/**
* The names of all subclasses
* The names of all subclasses.
*
* @var array
*/
protected
$_subClasses
=
array
();
...
...
@@ -78,22 +124,20 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @var array
*/
protected
$_identifier
=
array
();
/**
* The i
dentifier type of
the class.
* The i
nheritance mapping type used by
the class.
*
* @see Doctrine::IDENTIFIER_* constants
* @var integer
*/
protected
$_i
dentifierType
;
protected
$_i
nheritanceType
=
self
::
INHERITANCE_TYPE_NONE
;
/**
* The inheritance mapping type used by the class.
*
* The Id generator type used by the class.
*
* @var
integer
* @var
string
*/
protected
$_
inheritanceType
=
Doctrine
::
INHERITANCE
_TYPE_NONE
;
protected
$_
generatorType
=
self
::
GENERATOR
_TYPE_NONE
;
/**
* An array containing all behaviors attached to the class.
...
...
@@ -102,30 +146,51 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @var array $_templates
* @todo Unify under 'Behaviors'.
*/
protected
$_behaviors
=
array
();
/**
* An array containing all behavior generators attached to the class.
*
* @see Doctrine_Record_Generator
* @var array $_generators
* @todo Unify under 'Behaviors'.
*/
protected
$_generators
=
array
();
//protected $_behaviors = array();
/**
* The field mappings of the class.
* Keys are field names and values are mapping definitions.
*
* The mapping definition array has at least the following values:
*
* -- type the column type, eg. 'integer'
* -- length the column length, eg. 11
*
* additional keys:
* -- notnull whether or not the column is marked as notnull
* -- values enum values
* ... many more
* The mapping definition array has the following values:
*
* - <b>fieldName</b> (string)
* The name of the field in the Entity.
*
* - <b>type</b> (string)
* The database type of the column. Can be one of Doctrine's portable types.
*
* - <b>columnName</b> (string, optional)
* The column name. Optional. Defaults to the field name.
*
* - <b>length</b> (integer, optional)
* The database length of the column. Optional. Defaults to Doctrine's
* default values for the portable types.
*
* - <b>id</b> (boolean, optional)
* Marks the field as the primary key of the Entity. Multiple fields of an
* entity can have the id attribute, forming a composite key.
*
* - <b>generatorType</b> (string, optional, requires: id)
* The generation type used for the field. Optional. Can only be applied on
* fields that are marked with the 'id' attribute. The possible values are:
* auto, identity, sequence, table.
*
* - <b>generator</b> (array, optional, requires: generationType=table|sequence)
* The generator options for a table or sequence generator. Can only be applied
* on fields that have a generationType of 'table' or 'sequence'.
*
* - <b>nullable</b> (boolean, optional)
* Whether the column is nullable. Defaults to TRUE.
*
* - <b>columnDefinition</b> (string, optional)
* The SQL fragment that is used when generating the DDL for the column.
*
* - <b>precision</b> (integer, optional)
* The precision of a decimal column. Only valid if the column type is decimal.
*
* - <b>scale</b> (integer, optional)
* The scale of a decimal column. Only valid if the column type is decimal.
*
* @var array
*/
...
...
@@ -137,7 +202,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @var array
* @TODO Implementation (Value Object support)
*/
protected
$_mappedEmbeddedValue
s
=
array
();
//protected $_embeddedValueMapping
s = array();
/**
* Enter description here...
...
...
@@ -180,62 +245,14 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
//protected $_subclassFieldNames = array();
/**
* Caches enum value mappings. Keys are field names and values arrays with the
* mapping.
*/
protected
$_enumValues
=
array
();
/**
* Tree object associated with the class.
*
* @var Doctrine_Tree
* @todo Belongs to the NestedSet Behavior plugin.
*/
protected
$_tree
;
/**
* Cached column count, Doctrine_Entity uses this column count when
* determining its state.
*
* @var integer
*/
//protected $_columnCount;
/**
* Whether or not this class has default values.
*
* @var boolean
*/
protected
$_hasDefaultValues
;
/**
* Relation parser object. Manages the relations for the class.
*
* @var Doctrine_Relation_Parser $_parser
* @todo Remove.
*/
protected
$_parser
;
/**
* Enum value arrays.
*/
protected
$_enumMap
=
array
();
/**
* @var array $options an array containing all options
*
* -- treeImpl the tree implementation of this table (if any)
*
* -- treeOptions the tree options
*
* -- queryParts the bound query parts
*/
protected
$_options
=
array
(
'treeImpl'
=>
null
,
'treeOptions'
=>
null
,
'queryParts'
=>
array
()
);
/**
* Inheritance options.
*/
...
...
@@ -276,11 +293,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
'indexes'
=>
array
(),
'checks'
=>
array
()
);
/**
* @var array $_invokedMethods method invoker cache
*/
protected
$_invokedMethods
=
array
();
/**
* The cached lifecycle listeners. There is only one instance of each
...
...
@@ -304,6 +316,19 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
protected
$_lifecycleListeners
=
array
();
/**
* The association mappings.
*
* @var array
*/
protected
$_associationMappings
=
array
();
/**
* Flag indicating whether the identifier/primary key of the class is composite.
*
* @var boolean
*/
protected
$_isIdentifierComposite
=
false
;
/**
* Constructs a new ClassMetadata instance.
...
...
@@ -315,11 +340,12 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$this
->
_entityName
=
$entityName
;
$this
->
_rootEntityName
=
$entityName
;
$this
->
_em
=
$em
;
$this
->
_parser
=
new
Doctrine_Relation_Parser
(
$this
);
}
/**
*
*
@deprecated
*/
public
function
getConnection
()
{
...
...
@@ -370,21 +396,21 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public
function
isIdentifier
(
$fieldName
)
{
if
(
$this
->
_identifierType
!=
Doctrine
::
IDENTIFIER_COMPOSITE
)
{
if
(
!
$this
->
_isIdentifierComposite
)
{
return
$fieldName
===
$this
->
_identifier
[
0
];
}
return
in_array
(
$fieldName
,
$this
->
_identifier
);
}
/**
* Check if the
field is unique
* Check if the
class has a composite identifier.
*
* @param string $fieldName The field name
* @return boolean TRUE if the
field is uniqu
e, FALSE otherwise.
* @return boolean TRUE if the
identifier is composit
e, FALSE otherwise.
*/
public
function
isIdentifierComposite
()
{
return
$this
->
_i
dentifierType
==
Doctrine
::
IDENTIFIER_COMPOSITE
;
return
$this
->
_i
sIdentifierComposite
;
}
/**
...
...
@@ -490,7 +516,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return
$this
->
_tableOptions
[
$name
];
}
public
function
getBehaviorForMethod
(
$method
)
/*
public function getBehaviorForMethod($method)
{
return (isset($this->_invokedMethods[$method])) ?
$this->_invokedMethods[$method] : false;
...
...
@@ -498,10 +524,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
public function addBehaviorMethod($method, $behavior)
{
$this->_invokedMethods[$method] = $class;
}
}
*/
/**
* getOption
* returns the value of given option
*
* @param string $name the name of the option
...
...
@@ -562,8 +587,11 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public
function
getFieldMapping
(
$fieldName
)
{
return
isset
(
$this
->
_fieldMappings
[
$fieldName
])
?
$this
->
_fieldMappings
[
$fieldName
]
:
false
;
if
(
!
isset
(
$this
->
_fieldMappings
[
$fieldName
]))
{
throw
Doctrine_MappingException
::
mappingNotFound
(
$fieldName
);
}
return
$this
->
_fieldMappings
[
$fieldName
];
}
/**
...
...
@@ -575,7 +603,21 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*/
public
function
getAssociationMapping
(
$fieldName
)
{
//...
if
(
!
isset
(
$this
->
_associationMappings
[
$fieldName
]))
{
throw
Doctrine_MappingException
::
mappingNotFound
(
$fieldName
);
}
return
$this
->
_associationMappings
[
$fieldName
];
}
/**
* Gets all association mappings of the class.
*
* @return array
*/
public
function
getAssociationMappings
()
{
return
$this
->
_associationMappings
;
}
/**
...
...
@@ -626,7 +668,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* during hydration because the hydrator caches effectively.
*
* @return string The field name.
* @throws Doctrine::ORM::Exceptions::ClassMetadataException
i
f the field name could
* @throws Doctrine::ORM::Exceptions::ClassMetadataException
I
f the field name could
* not be found.
*/
public
function
lookupFieldName
(
$lcColumnName
)
...
...
@@ -649,90 +691,95 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
throw
new
Doctrine_ClassMetadata_Exception
(
"No field name found for column name '
$lcColumnName
' during lookup."
);
}
/**
*
Maps a field of the class to a database column
.
*
Adds a field mapping
.
*
* @param string $name The name of the column to map. Syntax: columnName [as propertyName].
* The property name is optional. If not used the column will be
* mapped to a property with the same name.
* @param string $type The type of the column.
* @param integer $length The length of the column.
* @param mixed $options
* @param boolean $prepend Whether to prepend or append the new column to the column list.
* By default the column gets appended.
*
* @throws Doctrine_ClassMetadata_Exception If trying use wrongly typed parameter.
* @todo Rename to mapField()/addFieldMapping().
* @param array $mapping
*/
public
function
map
Column
(
$name
,
$type
,
$length
=
null
,
$options
=
array
()
)
public
function
map
Field
(
array
$mapping
)
{
// converts 0 => 'primary' to 'primary' => true etc.
foreach
(
$options
as
$k
=>
$option
)
{
if
(
is_numeric
(
$k
))
{
if
(
!
empty
(
$option
)
&&
$option
!==
false
)
{
$options
[
$option
]
=
true
;
}
unset
(
$options
[
$k
]);
}
$mapping
=
$this
->
_validateAndCompleteFieldMapping
(
$mapping
);
if
(
isset
(
$this
->
_fieldMappings
[
$mapping
[
'fieldName'
]]))
{
throw
Doctrine_MappingException
::
duplicateFieldMapping
();
}
// extract column name & field name & lowercased column name
$parts
=
explode
(
' as '
,
$name
);
if
(
count
(
$parts
)
>
1
)
{
$fieldName
=
$parts
[
1
];
}
else
{
$fieldName
=
$parts
[
0
];
$this
->
_fieldMappings
[
$mapping
[
'fieldName'
]]
=
$mapping
;
}
/**
* Overrides an existant field mapping.
* Used i.e. by Entity classes deriving from another Entity class that acts
* as a mapped superclass to refine the basic mapping.
*
* @param array $newMapping
* @todo Implementation.
*/
public
function
overrideFieldMapping
(
array
$newMapping
)
{
//...
}
/**
* Validates & completes the field mapping. Default values are applied here.
*
* @param array $mapping
* @return array
*/
private
function
_validateAndCompleteFieldMapping
(
array
$mapping
)
{
// Check mandatory fields
if
(
!
isset
(
$mapping
[
'fieldName'
]))
{
throw
Doctrine_MappingException
::
missingFieldName
();
}
$columnName
=
$parts
[
0
];
$lcColumnName
=
strtolower
(
$parts
[
0
]);
if
(
isset
(
$this
->
_fieldMappings
[
$fieldName
]))
{
return
;
if
(
!
isset
(
$mapping
[
'type'
]))
{
throw
Doctrine_MappingException
::
missingType
();
}
// Fill column name <-> field name lookup maps
$this
->
_columnNames
[
$fieldName
]
=
$columnName
;
$this
->
_fieldNames
[
$columnName
]
=
$fieldName
;
$this
->
_lcColumnToFieldNames
[
$lcColumnName
]
=
$fieldName
;
$this
->
_lcColumnToFieldNames
[
$lcColumnName
]
=
$fieldName
;
// Inspect & fill $options
if
(
$length
==
null
)
{
$length
=
$this
->
_getDefaultLength
(
$type
);
// Complete fieldName and columnName mapping
if
(
!
isset
(
$mapping
[
'columnName'
]))
{
$mapping
[
'columnName'
]
=
$mapping
[
'fieldName'
];
}
$lcColumnName
=
strtolower
(
$mapping
[
'columnName'
]);
$options
[
'type'
]
=
$type
;
$options
[
'length'
]
=
$length
;
$this
->
_columnNames
[
$mapping
[
'fieldName'
]]
=
$mapping
[
'columnName'
];
$this
->
_fieldNames
[
$mapping
[
'columnName'
]]
=
$mapping
[
'fieldName'
];
$this
->
_lcColumnToFieldNames
[
$lcColumnName
]
=
$mapping
[
'fieldName'
];
if
(
!
$this
->
_hasDefaultValues
&&
isset
(
$options
[
'default'
]))
{
$this
->
_hasDefaultValues
=
true
;
// Complete length mapping
if
(
!
isset
(
$mapping
[
'length'
]))
{
$mapping
[
'length'
]
=
$this
->
_getDefaultLength
(
$mapping
[
'type'
]);
}
if
(
!
empty
(
$options
[
'primary'
]))
{
if
(
!
in_array
(
$fieldName
,
$this
->
_identifier
))
{
$this
->
_identifier
[]
=
$fieldName
;
// Complete id mapping
if
(
isset
(
$mapping
[
'id'
])
&&
$mapping
[
'id'
]
===
true
)
{
if
(
!
in_array
(
$mapping
[
'fieldName'
],
$this
->
_identifier
))
{
$this
->
_identifier
[]
=
$mapping
[
'fieldName'
];
}
/*if (isset($options['autoincrement']) && $options['autoincrement'] === true) {
}*/
if
(
isset
(
$mapping
[
'generatorType'
]))
{
if
(
!
in_array
(
$mapping
[
'generatorType'
],
self
::
$_generatorTypes
))
{
throw
Doctrine_MappingException
::
invalidGeneratorType
(
$mapping
[
'generatorType'
]);
}
else
if
(
count
(
$this
->
_identifier
)
>
1
)
{
throw
Doctrine_MappingException
::
generatorNotAllowedWithCompositeId
();
}
$this
->
_generatorType
=
$mapping
[
'generatorType'
];
}
// TODO: validate/complete 'generator' mapping
// Check for composite key
if
(
!
$this
->
_isIdentifierComposite
&&
count
(
$this
->
_identifier
)
>
1
)
{
$this
->
_isIdentifierComposite
=
true
;
}
}
/*
if ( ! isset($options['immutable'])) {
$options['immutable'] = false;
}*/
$this
->
_fieldMappings
[
$fieldName
]
=
$options
;
$this
->
_columnCount
++
;
return
$mapping
;
}
/**
* Gets the default length for a
field
type.
* Gets the default length for a
column
type.
*
* @param
unknown_type
$type
* @return
unknown
* @param
string
$type
* @return
mixed
*/
private
function
_getDefaultLength
(
$type
)
{
...
...
@@ -777,39 +824,10 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @param string The field name.
* @return array The names of all validators that are applied on the specified field.
*/
public
function
getFieldValidators
(
$fieldName
)
/*
public function getFieldValidators($fieldName)
{
return isset($this->_fieldMappings[$fieldName]['validators']) ?
$this->_fieldMappings[$fieldName]['validators'] : array();
}
/**
* Checks whether the mapped class has a default value on any field.
*
* @return boolean TRUE if the entity has a default value on any field, otherwise false.
*/
/*public function hasDefaultValues()
{
return $this->_hasDefaultValues;
}*/
/**
* getDefaultValueOf
* returns the default value(if any) for given field
*
* @param string $fieldName
* @return mixed
*/
/*public function getDefaultValueOf($fieldName)
{
if ( ! isset($this->_fieldMappings[$fieldName])) {
throw new Doctrine_Table_Exception("Couldn't get default value. Column ".$fieldName." doesn't exist.");
}
if (isset($this->_fieldMappings[$fieldName]['default'])) {
return $this->_fieldMappings[$fieldName]['default'];
} else {
return null;
}
}*/
/**
...
...
@@ -832,42 +850,30 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
return
$this
->
_identifier
;
}
public
function
setIdentifier
(
array
$identifier
)
{
$this
->
_identifier
=
$identifier
;
}
/**
* Gets the type of the identifier (primary key) used by the mapped class. The type
* can be either
* <tt>Doctrine::IDENTIFIER_NATURAL</tt>,
* <tt>Doctrine::IDENTIFIER_AUTOINCREMENT</tt>,
* <tt>Doctrine::IDENTIFIER_SEQUENCE</tt> or
* <tt>Doctrine::IDENTIFIER_COMPOSITE</tt>.
* Gets the name of the single id field. Note that this only works on
* entity classes that have a single-field pk.
*
* @return integer
*/
public
function
getIdentifierType
()
{
return
$this
->
_identifierType
;
}
/**
* Sets the identifier type used by the mapped class.
* @return string
*/
public
function
setIdentifierType
(
$type
)
public
function
getSingleIdentifierFieldName
(
)
{
$this
->
_identifierType
=
$type
;
if
(
$this
->
_isIdentifierComposite
)
{
throw
new
Doctrine_Exception
(
"Calling getSingleIdentifierFieldName "
.
"on a class that uses a composite identifier is not allowed."
);
}
return
$this
->
_identifier
[
0
];
}
public
function
hasMappedColumn
(
$columnName
)
public
function
setIdentifier
(
array
$identifier
)
{
return
isset
(
$this
->
_fieldNames
[
$columnName
])
;
$this
->
_identifier
=
$identifier
;
}
/**
* hasField
* Checks whether the class has a (mapped) field with a certain name.
*
* @return boolean
*/
public
function
hasField
(
$fieldName
)
...
...
@@ -875,87 +881,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return
isset
(
$this
->
_columnNames
[
$fieldName
]);
}
/**
* @param string $fieldName
* @return array
*/
/*public function getEnumValues($fieldName)
{
if (isset($this->_fieldMappings[$fieldName]['values'])) {
return $this->_fieldMappings[$fieldName]['values'];
} else {
return array();
}
}*/
/**
* enumValue
*
* @param string $field
* @param integer $index
* @return mixed
*/
/*public function enumValue($fieldName, $index)
{
if ($index instanceof Doctrine_Null) {
return $index;
}
if (isset($this->_enumValues[$fieldName][$index])) {
return $this->_enumValues[$fieldName][$index];
}
$columnName = $this->getColumnName($fieldName);
if ( ! $this->_em->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM) &&
isset($this->_fieldMappings[$fieldName]['values'][$index])) {
$enumValue = $this->_fieldMappings[$fieldName]['values'][$index];
} else {
$enumValue = $index;
}
$this->_enumValues[$fieldName][$index] = $enumValue;
return $enumValue;
}*/
/**
* enumIndex
*
* @param string $field
* @param mixed $value
* @return mixed
*/
/*public function enumIndex($fieldName, $value)
{
$values = $this->getEnumValues($fieldName);
$index = array_search($value, $values);
if ($index === false || ! $this->_em->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) {
return $index;
}
return $value;
}*/
/**
* getColumnCount
*
* @return integer the number of columns in this table
* @deprecated
*/
/*public function getColumnCount()
{
return $this->_columnCount;
}*/
/**
* getMappedColumnCount
*
* @return integer the number of mapped columns in the class.
*/
public
function
getMappedFieldCount
()
{
return
$this
->
_columnCount
;
}
/**
* Gets the custom accessor of a field.
*
...
...
@@ -983,7 +908,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* Gets all field mappings.
*
* @return
unknown
* @return
array
*/
public
function
getFieldMappings
()
{
...
...
@@ -991,28 +916,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* removeColumn
* removes given column
*
* @return boolean
*/
/*public function removeColumn($fieldName)
{
$columnName = array_search($fieldName, $this->_fieldNames);
unset($this->_fieldNames[$columnName]);
if (isset($this->_fieldMappings[$fieldName])) {
unset($this->_fieldMappings[$fieldName]);
return true;
}
$this->_columnCount--;
return false;
}*/
/**
* returns an array containing all the column names.
* Gets an array containing all the column names.
*
* @return array
*/
...
...
@@ -1024,14 +928,13 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$columnNames
=
array
();
foreach
(
$fieldNames
as
$fieldName
)
{
$columnNames
[]
=
$this
->
getColumnName
(
$fieldName
);
}
}
return
$columnNames
;
}
}
/**
*
r
eturns an array with all the identifier column names.
*
R
eturns an array with all the identifier column names.
*
* @return array
*/
...
...
@@ -1041,7 +944,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
*
r
eturns an array containing all the field names.
*
R
eturns an array containing all the field names.
*
* @return array
*/
...
...
@@ -1049,19 +952,67 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
return
array_values
(
$this
->
_fieldNames
);
}
/**
*
getDefinitionOf
*
Gets the Id generator type used by the class.
*
* @return mixed array on success, false on failure
* @deprecated
* @return string
*/
/*public function getDefinitionOf($fieldName
)
public
function
getIdGeneratorType
(
)
{
$columnName = $this->getColumnName($fieldName);
return $this->getColumnDefinition($columnName);
}*/
return
$this
->
_generatorType
;
}
/**
* Checks whether the class uses an Id generator.
*
* @return boolean TRUE if the class uses an Id generator, FALSE otherwise.
*/
public
function
usesIdGenerator
()
{
return
$this
->
_generatorType
!=
self
::
GENERATOR_TYPE_NONE
;
}
/**
* Checks whether the class uses an identity column for the Id generation.
*
* @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
*/
public
function
isIdGeneratorIdentity
()
{
return
$this
->
_generatorType
==
self
::
GENERATOR_TYPE_IDENTITY
;
}
/**
* Checks whether the class uses a sequence for id generation.
*
* @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
*/
public
function
isIdGeneratorSequence
()
{
return
$this
->
_generatorType
==
self
::
GENERATOR_TYPE_SEQUENCE
;
}
/**
* Checks whether the class uses a table for id generation.
*
* @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
*/
public
function
isIdGeneratorTable
()
{
$this
->
_generatorType
==
self
::
GENERATOR_TYPE_TABLE
;
}
/**
* Checks whether the class has a natural identifier/pk (which means it does
* not use any Id generator.
*
* @return boolean
*/
public
function
isIdentifierNatural
()
{
return
$this
->
_generatorType
==
self
::
GENERATOR_TYPE_NONE
;
}
/**
* Gets the type of a field.
...
...
@@ -1127,10 +1078,10 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @return array an array containing all templates
* @todo Unify under 'Behaviors'
*/
public
function
getBehaviors
()
/*
public function getBehaviors()
{
return $this->_behaviors;
}
}
*/
/**
* Gets the inheritance type used by the class.
...
...
@@ -1186,6 +1137,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* Sets the parent class names.
* Assumes that the class names in the passed array are in the order:
* directParent -> directParentParent -> directParentParentParent ... -> root.
*/
public
function
setParentClasses
(
array
$classNames
)
{
...
...
@@ -1213,20 +1166,17 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
public
function
setInheritanceType
(
$type
,
array
$options
=
array
())
{
if
(
$parentClassNames
=
$this
->
getParentClasses
())
{
if
(
$this
->
_em
->
getClassMetadata
(
$parentClassNames
[
0
])
->
getInheritanceType
()
!=
$type
)
{
throw
new
Doctrine_ClassMetadata_Exception
(
"All classes in an inheritance hierarchy"
.
" must share the same inheritance mapping type. Mixing is not allowed."
);
}
throw
new
Doctrine_MappingException
(
"All classes in an inheritance hierarchy"
.
" must share the same inheritance mapping type and this type must be set"
.
" in the root class of the hierarchy."
);
}
if
(
$type
==
Doctrine
::
INHERITANCE_TYPE_SINGLE_TABLE
)
{
$this
->
_checkRequiredDiscriminatorOptions
(
$options
);
}
else
if
(
$type
==
Doctrine
::
INHERITANCE_TYPE_JOINED
)
{
if
(
!
in_array
(
$type
,
self
::
$_inheritanceTypes
))
{
throw
Doctrine_MappingException
::
invalidInheritanceType
(
$type
);
}
if
(
$type
==
Doctrine
::
INHERITANCE_TYPE_SINGLE_TABLE
||
$type
==
Doctrine
::
INHERITANCE_TYPE_JOINED
)
{
$this
->
_checkRequiredDiscriminatorOptions
(
$options
);
}
else
if
(
$type
==
Doctrine
::
INHERITANCE_TYPE_TABLE_PER_CLASS
)
{
;
}
else
{
throw
new
Doctrine_ClassMetadata_Exception
(
"Invalid inheritance type '
$type
'."
);
}
$this
->
_inheritanceType
=
$type
;
...
...
@@ -1284,19 +1234,24 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
throw
new
Doctrine_ClassMetadata_Exception
(
"Unknown inheritance option: '
$name
'."
);
}
switch
(
$name
)
{
case
'discriminatorColumn'
:
if
(
$value
!==
null
&&
!
is_string
(
$value
))
{
throw
new
Doctrine_ClassMetadata_Exception
(
"Invalid value '
$value
' for option"
.
" 'discriminatorColumn'."
);
}
break
;
case
'discriminatorMap'
:
if
(
!
is_array
(
$value
))
{
throw
new
Doctrine_ClassMetadata_Exception
(
"Value for option 'discriminatorMap'"
.
" must be an array."
);
}
break
;
if
(
$this
->
_inheritanceType
==
'joined'
||
$this
->
_inheritanceType
==
'singleTable'
)
{
switch
(
$name
)
{
case
'discriminatorColumn'
:
if
(
$value
!==
null
&&
!
is_string
(
$value
))
{
throw
new
Doctrine_ClassMetadata_Exception
(
"Invalid value '
$value
' for option"
.
" 'discriminatorColumn'."
);
}
break
;
case
'discriminatorMap'
:
if
(
!
is_array
(
$value
))
{
throw
new
Doctrine_ClassMetadata_Exception
(
"Value for option 'discriminatorMap'"
.
" must be an array."
);
}
break
;
// ... further validation checks as needed
default
:
throw
Doctrine_MappingException
::
invalidInheritanceOption
(
$name
);
}
}
$this
->
_inheritanceOptions
[
$name
]
=
$value
;
...
...
@@ -1438,135 +1393,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
'options'
=>
array_merge
(
$options
,
$this
->
getTableOptions
()));
}
/**
* getTemplate
*
* @param string $template
* @return void
* @todo Unify under 'Behaviors'.
*/
public
function
getBehavior
(
$behaviorName
)
{
if
(
!
isset
(
$this
->
_behaviors
[
$behaviorName
]))
{
throw
new
Doctrine_Table_Exception
(
'Template '
.
$behaviorName
.
' not loaded'
);
}
return
$this
->
_behaviors
[
$behaviorName
];
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
hasBehavior
(
$behaviorName
)
{
return
isset
(
$this
->
_behaviors
[
$behaviorName
]);
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
addBehavior
(
$behaviorName
,
Doctrine_Template
$impl
)
{
$this
->
_behaviors
[
$behaviorName
]
=
$impl
;
return
$this
;
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
getGenerators
()
{
return
$this
->
_generators
;
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
getGenerator
(
$generator
)
{
if
(
!
isset
(
$this
->
_generators
[
$generator
]))
{
throw
new
Doctrine_Table_Exception
(
'Generator '
.
$generator
.
' not loaded'
);
}
return
$this
->
_generators
[
$plugin
];
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
hasGenerator
(
$generator
)
{
return
isset
(
$this
->
_generators
[
$generator
]);
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
addGenerator
(
Doctrine_Record_Generator
$generator
,
$name
=
null
)
{
if
(
$name
===
null
)
{
$this
->
_generators
[]
=
$generator
;
}
else
{
$this
->
_generators
[
$name
]
=
$generator
;
}
return
$this
;
}
/**
* loadBehavior
*
* @param string $template
* @todo Unify under 'Behaviors'.
*/
public
function
loadBehavior
(
$behavior
,
array
$options
=
array
())
{
$this
->
actAs
(
$behavior
,
$options
);
}
/**
* @todo Unify under 'Behaviors'.
*/
public
function
loadGenerator
(
Doctrine_Record_Generator
$generator
)
{
$generator
->
initialize
(
$this
->
_table
);
$this
->
addGenerator
(
$generator
,
get_class
(
$generator
));
}
/**
* getTree
*
* getter for associated tree
*
* @return mixed if tree return instance of Doctrine_Tree, otherwise returns false
* @todo Belongs to the NestedSet Behavior.
*/
public
function
getTree
()
{
if
(
$this
->
getOption
(
'treeImpl'
))
{
if
(
!
$this
->
_tree
)
{
$options
=
$this
->
getOption
(
'treeOptions'
)
?
$this
->
getOption
(
'treeOptions'
)
:
array
();
$this
->
_tree
=
Doctrine_Tree
::
factory
(
$this
,
$this
->
getOption
(
'treeImpl'
),
$options
);
}
return
$this
->
_tree
;
}
return
false
;
}
/**
* isTree
*
* determine if table acts as tree
*
* @return mixed if tree return true, otherwise returns false
* @todo Belongs to the NestedSet Behavior.
*/
public
function
isTree
()
{
return
(
!
is_null
(
$this
->
getOption
(
'treeImpl'
)))
?
true
:
false
;
}
/**
* Checks whether a persistent field is inherited from a superclass.
*
...
...
@@ -1577,47 +1403,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return
isset
(
$this
->
_fieldMappings
[
$fieldName
][
'inherited'
]);
}
/**
* bindQueryParts
* binds query parts to given component
*
* @param array $queryParts an array of pre-bound query parts
* @return Doctrine_Entity this object
*/
public
function
bindQueryParts
(
array
$queryParts
)
{
$this
->
_options
[
'queryParts'
]
=
$queryParts
;
return
$this
;
}
/**
* bindQueryPart
* binds given value to given query part
*
* @param string $queryPart
* @param mixed $value
* @return Doctrine_Entity this object
*/
public
function
bindQueryPart
(
$queryPart
,
$value
)
{
$this
->
_options
[
'queryParts'
][
$queryPart
]
=
$value
;
return
$this
;
}
/**
* getBoundQueryPart
*
* @param string $queryPart
* @return string $queryPart
*/
public
function
getBoundQueryPart
(
$queryPart
)
{
if
(
!
isset
(
$this
->
_options
[
'queryParts'
][
$queryPart
]))
{
return
null
;
}
return
$this
->
_options
[
'queryParts'
][
$queryPart
];
}
/**
* Sets the name of the primary table the class is mapped to.
*
...
...
@@ -1657,17 +1442,23 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
* Adds a one-to-one association mapping.
*
* @todo Implementation.
*/
public
function
oneToOne
(
$targetEntity
,
$definition
)
public
function
mapOneToOne
(
array
$mapping
)
{
$oneToOneMapping
=
new
Doctrine_Association_OneToOne
(
$mapping
);
if
(
isset
(
$this
->
_associationMappings
[
$oneToOneMapping
->
getSourceFieldName
()]))
{
throw
Doctrine_MappingException
::
duplicateFieldMapping
();
}
$this
->
_associationMappings
[
$oneToOneMapping
->
getSourceFieldName
()]
=
$oneToOneMapping
;
}
/**
* @todo Implementation.
*/
public
function
oneToMany
(
$targetEntity
,
$definition
)
public
function
mapOneToMany
(
array
$mapping
)
{
}
...
...
@@ -1675,15 +1466,15 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* @todo Implementation.
*/
public
function
ma
nyToOne
(
$targetEntity
,
$definition
)
public
function
ma
pManyToOne
(
array
$mapping
)
{
}
/**
* @todo Implementation.
*/
public
function
ma
nyToMany
(
$targetEntity
,
$definition
)
public
function
ma
pManyToMany
(
array
$mapping
)
{
}
...
...
@@ -1696,7 +1487,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @param array $options
* @todo Unify under 'Behaviors'.
*/
public
function
actAs
(
$tpl
,
array
$options
=
array
())
/*
public function actAs($tpl, array $options = array())
{
if ( ! is_object($tpl)) {
if (class_exists($tpl, true)) {
...
...
@@ -1723,7 +1514,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
$tpl->setTableDefinition();
return $this;
}
}
*/
/**
* check
...
...
@@ -1735,7 +1526,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* @todo Should be done through $_tableOptions
* @deprecated
*/
public
function
check
(
$constraint
,
$name
=
null
)
/*
public function check($constraint, $name = null)
{
if (is_array($constraint)) {
foreach ($constraint as $name => $def) {
...
...
@@ -1754,7 +1545,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
} else {
$this->_tableOptions['checks'][] = $definition;
}
}
}
*/
/**
* Registers a custom mapper for the entity class.
...
...
@@ -1868,7 +1659,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
/**
* Adds a lifecycle callback for Entities of this class.
*
* Note: If
a
the same callback is registered more than once, the old one
* Note: If the same callback is registered more than once, the old one
* will be overridden.
*
* @param string $callback
...
...
@@ -2015,6 +1806,14 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
return
$this
->
_parser
->
getRelations
();
}
/**
* @todo Set by the factory if type is AUTO. Not pretty. Find sth. better.
*/
public
function
setIdGeneratorType
(
$type
)
{
$this
->
_generatorType
=
$type
;
}
/**
*
...
...
lib/Doctrine/ClassMetadata/Factory.php
View file @
d9975c36
...
...
@@ -19,6 +19,8 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal;
/**
* The metadata factory is used to create ClassMetadata objects that contain all the
* metadata of a class.
...
...
@@ -31,10 +33,11 @@
* @version $Revision$
* @link www.phpdoctrine.org
* @since 2.0
* @todo Rename to ClassMetadataFactory.
*/
class
Doctrine_ClassMetadata_Factory
{
protected
$_
conn
;
protected
$_
em
;
protected
$_driver
;
/**
...
...
@@ -52,7 +55,7 @@ class Doctrine_ClassMetadata_Factory
*/
public
function
__construct
(
Doctrine_EntityManager
$em
,
$driver
)
{
$this
->
_
conn
=
$em
;
$this
->
_
em
=
$em
;
$this
->
_driver
=
$driver
;
}
...
...
@@ -101,7 +104,7 @@ class Doctrine_ClassMetadata_Factory
$class
=
$classes
[
$loadedParentClass
];
}
else
{
$rootClassOfHierarchy
=
count
(
$parentClasses
)
>
0
?
array_shift
(
$parentClasses
)
:
$name
;
$class
=
new
Doctrine_ClassMetadata
(
$rootClassOfHierarchy
,
$this
->
_
conn
);
$class
=
new
Doctrine_ClassMetadata
(
$rootClassOfHierarchy
,
$this
->
_
em
);
$this
->
_loadMetadata
(
$class
,
$rootClassOfHierarchy
);
$classes
[
$rootClassOfHierarchy
]
=
$class
;
}
...
...
@@ -115,7 +118,7 @@ class Doctrine_ClassMetadata_Factory
$parent
=
$class
;
foreach
(
$parentClasses
as
$subclassName
)
{
$subClass
=
new
Doctrine_ClassMetadata
(
$subclassName
,
$this
->
_
conn
);
$subClass
=
new
Doctrine_ClassMetadata
(
$subclassName
,
$this
->
_
em
);
$subClass
->
setInheritanceType
(
$parent
->
getInheritanceType
(),
$parent
->
getInheritanceOptions
());
$this
->
_addInheritedFields
(
$subClass
,
$parent
);
$this
->
_addInheritedRelations
(
$subClass
,
$parent
);
...
...
@@ -130,14 +133,10 @@ class Doctrine_ClassMetadata_Factory
protected
function
_addInheritedFields
(
$subClass
,
$parentClass
)
{
foreach
(
$parentClass
->
getFieldMappings
()
as
$
name
=>
$definition
)
{
foreach
(
$parentClass
->
getFieldMappings
()
as
$
fieldName
=>
$mapping
)
{
$fullName
=
"
$name
as "
.
$parentClass
->
getFieldName
(
$name
);
$definition
[
'inherited'
]
=
true
;
$subClass
->
mapColumn
(
$fullName
,
$definition
[
'type'
],
$definition
[
'length'
],
$definition
);
$mapping
[
'inherited'
]
=
true
;
$subClass
->
mapField
(
$mapping
);
}
}
...
...
@@ -163,6 +162,7 @@ class Doctrine_ClassMetadata_Factory
$names
=
array
();
$className
=
$name
;
// get parent classes
//TODO: Skip Entity types MappedSuperclass/Transient
do
{
if
(
$className
===
'Doctrine_Entity'
)
{
break
;
...
...
@@ -182,11 +182,13 @@ class Doctrine_ClassMetadata_Factory
// load further metadata
$this
->
_driver
->
loadMetadataForClass
(
$name
,
$class
);
// set default table name, if necessary
$tableName
=
$class
->
getTableName
();
if
(
!
isset
(
$tableName
))
{
$class
->
setTableName
(
Doctrine
::
tableize
(
$class
->
getClassName
()));
}
// complete identifier mapping
$this
->
_initIdentifier
(
$class
);
return
$class
;
...
...
@@ -199,7 +201,7 @@ class Doctrine_ClassMetadata_Factory
*/
protected
function
_initIdentifier
(
Doctrine_ClassMetadata
$class
)
{
switch
(
count
((
array
)
$class
->
getIdentifier
()))
{
/*switch (count(
$class->getIdentifier())) {
case 0: // No identifier in the class mapping yet
// If its a subclass, inherit the identifier from the parent.
...
...
@@ -217,7 +219,7 @@ class Doctrine_ClassMetadata_Factory
}
// add all inherited primary keys
foreach
(
(
array
)
$class
->
getIdentifier
()
as
$id
)
{
foreach ($class->getIdentifier() as $id) {
$definition = $rootClass->getDefinitionOf($id);
// inherited primary keys shouldn't contain autoinc
...
...
@@ -231,16 +233,7 @@ class Doctrine_ClassMetadata_Factory
$definition, true);
}
} else {
throw
Doctrine_MappingException
::
identifierRequired
(
$class
->
getClassName
());
/* Legacy behavior of auto-adding an id field
$definition = array('type' => 'integer',
'length' => 20,
'autoincrement' => true,
'primary' => true);
$class->mapColumn('id', $definition['type'], $definition['length'], $definition, true);
$class->setIdentifier(array('id'));
$class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC);
*/
throw Doctrine_MappingException::identifierRequired($class->getClassName());
}
break;
case 1: // A single identifier is in the mapping
...
...
@@ -293,6 +286,36 @@ class Doctrine_ClassMetadata_Factory
break;
default: // Multiple identifiers are in the mapping so its a composite id
$class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE);
}*/
// If the chosen generator type is "auto", then pick the one appropriate for
// the database.
// FIXME: This is very ugly here. Such switch()es on the database driver
// are unnecessary as we can easily replace them with polymorphic calls on
// the connection (or another) object. We just need to decide where to put
// the id generation types.
if
(
$class
->
getIdGeneratorType
()
==
Doctrine_ClassMetadata
::
GENERATOR_TYPE_AUTO
)
{
switch
(
strtolower
(
$this
->
_em
->
getConnection
()
->
getDriverName
()))
{
case
'mysql'
:
// pick IDENTITY
$class
->
setIdGeneratorType
(
Doctrine_ClassMetadata
::
GENERATOR_TYPE_IDENTITY
);
break
;
case
'oracle'
:
//pick SEQUENCE
break
;
case
'postgres'
:
//pick SEQUENCE
break
;
case
'firebird'
:
//pick what?
break
;
case
'mssql'
:
//pick what?
default
:
throw
new
Doctrine_Exception
(
"Encountered unknown database driver: "
.
$this
->
_em
->
getConnection
()
->
getDriverName
());
}
}
}
...
...
lib/Doctrine/Collection.php
View file @
d9975c36
...
...
@@ -33,10 +33,10 @@
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Rename to EntityCollection
*/
class
Doctrine_Collection
extends
Doctrine_Access
implements
Countable
,
IteratorAggregate
,
Serializable
{
{
protected
$_entityBaseType
;
/**
...
...
@@ -44,42 +44,30 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*
* @var array
*/
protected
$data
=
array
();
/**
* The mapper object used to map the records of this collection to the database.
*
* @var Doctrine_Mapper
*/
protected
$_mapper
;
protected
$_data
=
array
();
/**
* A snapshot of the fetched data.
* A snapshot of the collection at the moment it was fetched from the database.
* This is used to create a diff of the collection at commit time.
*
* @var array
* @var array
*/
protected
$_snapshot
=
array
();
/**
* This
record this collection is attached to, if any
.
* This
entity that owns this collection
.
*
* @var Doctrine
_
Entity
* @var Doctrine
::ORM::
Entity
*/
protected
$_owner
;
/**
* The reference field of the collection.
*
* @var string $referenceField
*/
protected
$referenceField
;
/**
* The relation this collection is related to, if any.
* The association mapping the collection belongs to.
* This is currently either a OneToManyMapping or a ManyToManyMapping.
*
* @var Doctrine
_Relation
* @var Doctrine
::ORM::Mapping::AssociationMapping
*/
protected
$
relation
;
protected
$
_associationMapping
;
/**
* The name of the column that is used for collection key mapping.
...
...
@@ -101,6 +89,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @var EntityManager
*/
protected
$_em
;
protected
$_isDirty
=
false
;
/**
* Constructor.
...
...
@@ -113,18 +102,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
{
$this
->
_entityBaseType
=
$entityBaseType
;
$this
->
_em
=
Doctrine_EntityManagerFactory
::
getManager
(
$entityBaseType
);
$this
->
_mapper
=
$this
->
_em
->
getEntityPersister
(
$entityBaseType
);
if
(
$keyField
===
null
)
{
$keyField
=
$this
->
_mapper
->
getClassMetadata
()
->
getBoundQueryPart
(
'indexBy'
);
}
if
(
$keyField
===
null
)
{
//$keyField = $mapper->getClassMetadata()->getAttribute(Doctrine::ATTR_COLL_KEY);
}
if
(
$keyField
!==
null
)
{
if
(
!
$this
->
_
mapper
->
getClassMetadata
(
)
->
hasField
(
$keyField
))
{
if
(
!
$this
->
_
em
->
getClassMetadata
(
$entityBaseType
)
->
hasField
(
$keyField
))
{
throw
new
Doctrine_Collection_Exception
(
"Invalid field '
$keyField
' can't be uses as key."
);
}
$this
->
_keyField
=
$keyField
;
...
...
@@ -139,7 +119,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
setData
(
array
$data
)
{
$this
->
data
=
$data
;
$this
->
_
data
=
$data
;
}
/**
...
...
@@ -155,14 +135,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$vars
=
get_object_vars
(
$this
);
unset
(
$vars
[
'reference'
]);
unset
(
$vars
[
'reference_field'
]);
unset
(
$vars
[
'relation'
]);
unset
(
$vars
[
'expandable'
]);
unset
(
$vars
[
'expanded'
]);
unset
(
$vars
[
'generator'
]);
$vars
[
'_mapper'
]
=
$vars
[
'_mapper'
]
->
getComponentName
();
return
serialize
(
$vars
);
}
...
...
@@ -187,12 +164,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$this
->
$name
=
$values
;
}
$this
->
_mapper
=
$manager
->
getEntityPersister
(
$this
->
_entityBaseType
);
$keyColumn
=
isset
(
$array
[
'keyField'
])
?
$array
[
'keyField'
]
:
null
;
if
(
$keyColumn
===
null
)
{
$keyColumn
=
$this
->
_mapper
->
getClassMetadata
()
->
getBoundQueryPart
(
'indexBy'
);
}
if
(
$keyColumn
!==
null
)
{
$this
->
_keyField
=
$keyColumn
;
...
...
@@ -231,7 +203,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getData
()
{
return
$this
->
data
;
return
$this
->
_
data
;
}
/**
...
...
@@ -242,7 +214,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getFirst
()
{
return
reset
(
$this
->
data
);
return
reset
(
$this
->
_
data
);
}
/**
...
...
@@ -253,7 +225,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getLast
()
{
return
end
(
$this
->
data
);
return
end
(
$this
->
_
data
);
}
/**
...
...
@@ -263,7 +235,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
end
()
{
return
end
(
$this
->
data
);
return
end
(
$this
->
_
data
);
}
/**
...
...
@@ -273,7 +245,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
key
()
{
return
key
(
$this
->
data
);
return
key
(
$this
->
_
data
);
}
/**
...
...
@@ -285,13 +257,13 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
public
function
setReference
(
Doctrine_Entity
$entity
,
Doctrine_Relation
$relation
)
{
$this
->
_owner
=
$entity
;
$this
->
relation
=
$relation
;
//
$this->relation = $relation;
if
(
$relation
instanceof
Doctrine_Relation_ForeignKey
||
/*
if ($relation instanceof Doctrine_Relation_ForeignKey ||
$relation instanceof Doctrine_Relation_LocalKey) {
$this->referenceField = $relation->getForeignFieldName();
$value = $entity->get($relation->getLocalFieldName());
foreach
(
$this
->
data
as
$entity
)
{
foreach ($this->
_
data as $entity) {
if ($value !== null) {
$entity->set($this->referenceField, $value, false);
} else {
...
...
@@ -300,7 +272,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
} else if ($relation instanceof Doctrine_Relation_Association) {
}
}
*/
}
/**
...
...
@@ -322,8 +294,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
remove
(
$key
)
{
$removed
=
$this
->
data
[
$key
];
unset
(
$this
->
data
[
$key
]);
$removed
=
$this
->
_
data
[
$key
];
unset
(
$this
->
_
data
[
$key
]);
return
$removed
;
}
...
...
@@ -336,7 +308,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
contains
(
$key
)
{
return
isset
(
$this
->
data
[
$key
]);
return
isset
(
$this
->
_
data
[
$key
]);
}
/**
...
...
@@ -344,7 +316,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
search
(
Doctrine_Entity
$record
)
{
return
array_search
(
$record
,
$this
->
data
,
true
);
return
array_search
(
$record
,
$this
->
_
data
,
true
);
}
/**
...
...
@@ -357,8 +329,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
get
(
$key
)
{
if
(
isset
(
$this
->
data
[
$key
]))
{
return
$this
->
data
[
$key
];
if
(
isset
(
$this
->
_
data
[
$key
]))
{
return
$this
->
_
data
[
$key
];
}
return
null
;
}
...
...
@@ -374,7 +346,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$list
=
array
();
$idFieldNames
=
(
array
)
$this
->
_mapper
->
getClassMetadata
()
->
getIdentifier
();
foreach
(
$this
->
data
as
$record
)
{
foreach
(
$this
->
_
data
as
$record
)
{
if
(
is_array
(
$record
))
{
if
(
count
(
$idFieldNames
)
>
1
)
{
$id
=
array
();
...
...
@@ -406,7 +378,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getKeys
()
{
return
array_keys
(
$this
->
data
);
return
array_keys
(
$this
->
_
data
);
}
/**
...
...
@@ -418,7 +390,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
count
()
{
return
count
(
$this
->
data
);
return
count
(
$this
->
_
data
);
}
/**
...
...
@@ -429,69 +401,49 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
* @internal Can't type-hint the second parameter to Doctrine_Entity because we need
* to adhere to the Doctrine_Access::set() signature.
*/
public
function
set
(
$key
,
$
entity
)
public
function
set
(
$key
,
$
value
)
{
if
(
!
$
entity
instanceof
Doctrine_Entity
)
{
if
(
!
$
value
instanceof
Doctrine_Entity
)
{
throw
new
Doctrine_Collection_Exception
(
'Value variable in set is not an instance of Doctrine_Entity'
);
}
if
(
isset
(
$this
->
referenceField
))
{
$entity
->
set
(
$this
->
referenceField
,
$this
->
_owner
,
false
);
}
$this
->
data
[
$key
]
=
$entity
;
$this
->
_data
[
$key
]
=
$value
;
}
/**
* adds a record to collection
*
* @param Doctrine_Entity $record record to be added
* @param string $key optional key for the record
* @return boolean
*/
public
function
add
(
$
record
,
$key
=
null
)
public
function
add
(
$
value
,
$key
=
null
)
{
/** @TODO Use raw getters/setters */
if
(
!
$
record
instanceof
Doctrine_Entity
)
{
if
(
!
$
value
instanceof
Doctrine_Entity
)
{
throw
new
Doctrine_Record_Exception
(
'Value variable in set is not an instance of Doctrine_Entity.'
);
}
if
(
isset
(
$this
->
referenceField
))
{
$value
=
$this
->
_owner
->
get
(
$this
->
relation
->
getLocalFieldName
());
if
(
$value
!==
null
)
{
$record
->
set
(
$this
->
referenceField
,
$value
,
false
);
}
else
{
$record
->
set
(
$this
->
referenceField
,
$this
->
_owner
,
false
);
}
}
/*
* for some weird reason in_array cannot be used here (php bug ?)
*
* if used it results in fatal error : [ nesting level too deep ]
*/
foreach
(
$this
->
data
as
$val
)
{
if
(
$val
===
$
record
)
{
foreach
(
$this
->
_
data
as
$val
)
{
if
(
$val
===
$
value
)
{
return
false
;
}
}
if
(
isset
(
$key
))
{
if
(
isset
(
$this
->
data
[
$key
]))
{
if
(
isset
(
$this
->
_
data
[
$key
]))
{
return
false
;
}
$this
->
data
[
$key
]
=
$record
;
$this
->
_data
[
$key
]
=
$value
;
return
true
;
}
// why is this not checked when the keyColumn is set?
if
(
isset
(
$this
->
_keyField
))
{
$value
=
$record
->
get
(
$this
->
_keyField
);
if
(
$value
===
null
)
{
throw
new
Doctrine_Collection_Exception
(
"Couldn't create collection index. Record field '"
.
$this
->
_keyField
.
"' was null."
);
}
$this
->
data
[
$value
]
=
$record
;
}
else
{
$this
->
data
[]
=
$record
;
}
$this
->
_data
[]
=
$value
;
return
true
;
}
...
...
@@ -509,7 +461,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$query
=
new
Doctrine_Query
(
$this
->
_mapper
->
getConnection
());
if
(
!
isset
(
$name
))
{
foreach
(
$this
->
data
as
$record
)
{
foreach
(
$this
->
_
data
as
$record
)
{
// FIXME: composite key support
$ids
=
$record
->
identifier
();
$value
=
count
(
$ids
)
>
0
?
array_pop
(
$ids
)
:
null
;
...
...
@@ -528,11 +480,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$rel
=
$this
->
_mapper
->
getTable
()
->
getRelation
(
$name
);
if
(
$rel
instanceof
Doctrine_Relation_LocalKey
||
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
foreach
(
$this
->
data
as
$record
)
{
foreach
(
$this
->
_
data
as
$record
)
{
$list
[]
=
$record
[
$rel
->
getLocal
()];
}
}
else
{
foreach
(
$this
->
data
as
$record
)
{
foreach
(
$this
->
_
data
as
$record
)
{
$ids
=
$record
->
identifier
();
$value
=
count
(
$ids
)
>
0
?
array_pop
(
$ids
)
:
null
;
if
(
$value
!==
null
)
{
...
...
@@ -563,15 +515,15 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$local
=
$rel
->
getLocal
();
if
(
$rel
instanceof
Doctrine_Relation_LocalKey
)
{
foreach
(
$this
->
data
as
$key
=>
$record
)
{
foreach
(
$this
->
_
data
as
$key
=>
$record
)
{
foreach
(
$coll
as
$k
=>
$related
)
{
if
(
$related
[
$foreign
]
==
$record
[
$local
])
{
$this
->
data
[
$key
]
->
_setRelated
(
$name
,
$related
);
$this
->
_
data
[
$key
]
->
_setRelated
(
$name
,
$related
);
}
}
}
}
else
if
(
$rel
instanceof
Doctrine_Relation_ForeignKey
)
{
foreach
(
$this
->
data
as
$key
=>
$record
)
{
foreach
(
$this
->
_
data
as
$key
=>
$record
)
{
if
(
!
$record
->
exists
())
{
continue
;
}
...
...
@@ -584,7 +536,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
}
$this
->
data
[
$key
]
->
_setRelated
(
$name
,
$sub
);
$this
->
_
data
[
$key
]
->
_setRelated
(
$name
,
$sub
);
}
}
else
if
(
$rel
instanceof
Doctrine_Relation_Association
)
{
// @TODO composite key support
...
...
@@ -592,7 +544,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$asf
=
$rel
->
getAssociationFactory
();
$name
=
$table
->
getComponentName
();
foreach
(
$this
->
data
as
$key
=>
$record
)
{
foreach
(
$this
->
_
data
as
$key
=>
$record
)
{
if
(
!
$record
->
exists
())
{
continue
;
}
...
...
@@ -603,7 +555,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
$sub
->
add
(
$related
->
get
(
$name
));
}
}
$this
->
data
[
$key
]
->
_setRelated
(
$name
,
$sub
);
$this
->
_
data
[
$key
]
->
_setRelated
(
$name
,
$sub
);
}
}
...
...
@@ -635,7 +587,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
takeSnapshot
()
{
$this
->
_snapshot
=
$this
->
data
;
$this
->
_snapshot
=
$this
->
_
data
;
return
$this
;
}
...
...
@@ -664,7 +616,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
processDiff
()
{
foreach
(
array_udiff
(
$this
->
_snapshot
,
$this
->
data
,
array
(
$this
,
"_compareRecords"
))
as
$record
)
{
foreach
(
array_udiff
(
$this
->
_snapshot
,
$this
->
_
data
,
array
(
$this
,
"_compareRecords"
))
as
$record
)
{
$record
->
delete
();
}
return
$this
;
...
...
@@ -686,6 +638,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
return
$data
;
}
public
function
isEmpty
()
{
return
$this
->
count
()
==
0
;
}
/**
* fromArray
...
...
@@ -776,7 +733,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getDeleteDiff
()
{
return
array_udiff
(
$this
->
_snapshot
,
$this
->
data
,
array
(
$this
,
"_compareRecords"
));
return
array_udiff
(
$this
->
_snapshot
,
$this
->
_
data
,
array
(
$this
,
"_compareRecords"
));
}
/**
...
...
@@ -786,7 +743,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getInsertDiff
()
{
return
array_udiff
(
$this
->
data
,
$this
->
_snapshot
,
array
(
$this
,
"_compareRecords"
));
return
array_udiff
(
$this
->
_
data
,
$this
->
_snapshot
,
array
(
$this
,
"_compareRecords"
));
}
/**
...
...
@@ -803,7 +760,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
/**
* save
* Saves all records of this collection and processes the
* difference of the last snapshot and the current data.
*
...
...
@@ -869,7 +825,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
}
}
$this
->
data
=
array
();
$this
->
_
data
=
array
();
if
(
$this
->
_owner
)
{
$this
->
_owner
->
free
(
$deep
);
...
...
@@ -884,7 +840,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
*/
public
function
getIterator
()
{
$data
=
$this
->
data
;
$data
=
$this
->
_
data
;
return
new
ArrayIterator
(
$data
);
}
...
...
@@ -907,6 +863,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator
public
function
clear
()
{
$this
->
data
=
array
();
$this
->
_
data
=
array
();
}
}
lib/Doctrine/Connection.php
View file @
d9975c36
...
...
@@ -138,8 +138,9 @@ abstract class Doctrine_Connection implements Countable
* @var array $properties
*/
protected
$properties
=
array
(
'sql_comments'
=>
array
(
array
(
'start'
=>
'--'
,
'end'
=>
"
\n
"
,
'escape'
=>
false
),
array
(
'start'
=>
'/*'
,
'end'
=>
'*/'
,
'escape'
=>
false
)),
'sql_comments'
=>
array
(
array
(
'start'
=>
'--'
,
'end'
=>
"
\n
"
,
'escape'
=>
false
),
array
(
'start'
=>
'/*'
,
'end'
=>
'*/'
,
'escape'
=>
false
)),
'identifier_quoting'
=>
array
(
'start'
=>
'"'
,
'end'
=>
'"'
,
'escape'
=>
'"'
),
'string_quoting'
=>
array
(
'start'
=>
"'"
,
'end'
=>
"'"
,
'escape'
=>
false
,
'escape_pattern'
=>
false
),
...
...
@@ -154,6 +155,8 @@ abstract class Doctrine_Connection implements Countable
/**
* The parameters used during creation of the Connection.
*
* @var array
*/
protected
$_params
=
array
();
...
...
@@ -364,24 +367,24 @@ abstract class Doctrine_Connection implements Countable
//$this->getListener()->preConnect($event);
// TODO: the extension_loaded check can happen earlier, maybe in the factory
if
(
extension_loaded
(
'pdo'
))
{
$driverOptions
=
isset
(
$this
->
_params
[
'driverOptions'
])
?
$this
->
_params
[
'driverOptions'
]
:
array
();
$user
=
isset
(
$this
->
_params
[
'user'
])
?
$this
->
_params
[
'user'
]
:
null
;
$password
=
isset
(
$this
->
_params
[
'password'
])
?
$this
->
_params
[
'password'
]
:
null
;
$this
->
_pdo
=
new
PDO
(
$this
->
_constructPdoDsn
(),
$user
,
$password
,
$driverOptions
);
$this
->
_pdo
->
setAttribute
(
PDO
::
ATTR_ERRMODE
,
PDO
::
ERRMODE_EXCEPTION
);
$this
->
_pdo
->
setAttribute
(
PDO
::
ATTR_CASE
,
PDO
::
CASE_LOWER
);
}
else
{
if
(
!
extension_loaded
(
'pdo'
))
{
throw
new
Doctrine_Connection_Exception
(
"Couldn't locate driver named "
.
$e
[
0
]);
}
$driverOptions
=
isset
(
$this
->
_params
[
'driverOptions'
])
?
$this
->
_params
[
'driverOptions'
]
:
array
();
$user
=
isset
(
$this
->
_params
[
'user'
])
?
$this
->
_params
[
'user'
]
:
null
;
$password
=
isset
(
$this
->
_params
[
'password'
])
?
$this
->
_params
[
'password'
]
:
null
;
$this
->
_pdo
=
new
PDO
(
$this
->
_constructPdoDsn
(),
$user
,
$password
,
$driverOptions
);
$this
->
_pdo
->
setAttribute
(
PDO
::
ATTR_ERRMODE
,
PDO
::
ERRMODE_EXCEPTION
);
$this
->
_pdo
->
setAttribute
(
PDO
::
ATTR_CASE
,
PDO
::
CASE_LOWER
);
// attach the pending attributes to adapter
/*foreach($this->pendingAttributes as $attr => $value) {
...
...
@@ -427,9 +430,8 @@ abstract class Doctrine_Connection implements Countable
*/
public
function
supports
(
$feature
)
{
return
(
isset
(
$this
->
supported
[
$feature
])
&&
(
$this
->
supported
[
$feature
]
===
'emulated'
||
$this
->
supported
[
$feature
]));
return
(
isset
(
$this
->
supported
[
$feature
])
&&
(
$this
->
supported
[
$feature
]
===
'emulated'
||
$this
->
supported
[
$feature
]));
}
/**
...
...
@@ -662,8 +664,7 @@ abstract class Doctrine_Connection implements Countable
}
/**
* quote
* quotes given input parameter
* Quotes given input parameter
*
* @param mixed $input parameter to be quoted
* @param string $type
...
...
@@ -794,10 +795,9 @@ abstract class Doctrine_Connection implements Countable
$this
->
getAttribute
(
Doctrine
::
ATTR_LISTENER
)
->
postPrepare
(
$event
);
return
new
Doctrine_Connection_Statement
(
$this
,
$stmt
);
}
catch
(
Doctrine_Adapter_Exception
$e
)
{
}
catch
(
PDOException
$e
)
{
}
$this
->
rethrowException
(
$e
,
$this
);
}
catch
(
PDOException
$e
)
{
$this
->
rethrowException
(
$e
,
$this
);
}
}
/**
...
...
@@ -859,10 +859,9 @@ abstract class Doctrine_Connection implements Countable
return
$stmt
;
}
}
catch
(
Doctrine_Adapter_Exception
$e
)
{
}
catch
(
PDOException
$e
)
{
}
$this
->
rethrowException
(
$e
,
$this
);
}
catch
(
PDOException
$e
)
{
$this
->
rethrowException
(
$e
,
$this
);
}
}
/**
...
...
@@ -894,10 +893,9 @@ abstract class Doctrine_Connection implements Countable
return
$count
;
}
}
catch
(
Doctrine_Adapter_Exception
$e
)
{
}
catch
(
PDOException
$e
)
{
}
$this
->
rethrowException
(
$e
,
$this
);
}
catch
(
PDOException
$e
)
{
$this
->
rethrowException
(
$e
,
$this
);
}
}
/**
...
...
@@ -923,7 +921,7 @@ abstract class Doctrine_Connection implements Countable
}
/**
*
rethrowException
*
Wraps the given exception into a driver-specific exception and rethrows it.
*
* @throws Doctrine_Connection_Exception
*/
...
...
@@ -940,9 +938,7 @@ abstract class Doctrine_Connection implements Countable
}
$exc
->
processErrorInfo
(
$e
->
errorInfo
);
if
(
$this
->
getAttribute
(
Doctrine
::
ATTR_THROW_EXCEPTIONS
))
{
throw
$exc
;
}
throw
$exc
;
//$this->getListener()->postError($event);
}
...
...
@@ -1178,7 +1174,6 @@ abstract class Doctrine_Connection implements Countable
}
}
public
function
getFormatter
()
{
if
(
!
$this
->
modules
[
'formatter'
])
{
...
...
@@ -1186,4 +1181,25 @@ abstract class Doctrine_Connection implements Countable
}
return
$this
->
modules
[
'formatter'
];
}
public
function
getSequenceModule
()
{
if
(
!
$this
->
modules
[
'sequence'
])
{
$class
=
"Doctrine_Sequence_"
.
$this
->
_driverName
;
$this
->
modules
[
'sequence'
]
=
new
$class
;
}
return
$this
->
modules
[
'sequence'
];
}
/**
* Gets the default (preferred) Id generation strategy of the database platform.
*
* @todo Sure, the id generator types are more ORM functionality but they're
* still kind of dbal related. Maybe we need another set of classes (DatabasePlatform?)
* but im not so sure...
*/
/*abstract*/
public
function
getDefaultIdGeneratorType
()
{
}
}
lib/Doctrine/Connection/UnitOfWork.php
View file @
d9975c36
...
...
@@ -21,42 +21,26 @@
#namespace Doctrine::ORM::Internal;
#use Doctrine::ORM::Entity;
#use Doctrine::ORM::EntityManager;
#use Doctrine::ORM::Exceptions::UnitOfWorkException;
/**
* The UnitOfWork is responsible for tracking changes to objects during an
* "object-level" transaction and for writing out changes to the database
at
* "object-level" transaction and for writing out changes to the database
* in the correct order.
*
* Some terminology:
*
* <b>New entity</b>: A new entity is an entity that already has an identity but
* is not yet persisted into the database. This is usually the case for all
* newly saved/persisted entities that use a SEQUENCE id generator. Entities with an
* IDENTITY id generator get persisted as soon as they're saved in order to
* obtain the identifier. Therefore entities that use an IDENTITY id generator
* never appear in the list of new entities of the UoW.
* New entities are inserted into the database when the is UnitOfWork committed.
*
* <b>Dirty entity</b>: A dirty entity is a managed entity whose values have
* been altered.
*
* <b>Removed entity</b>: A removed entity is a managed entity that is scheduled
* for deletion from the database.
*
* <b>Clean entity</b>: A clean entity is a managed entity that has been fetched
* from the database and whose values have not yet been altered.
*
* @package Doctrine
* @subpackage Connection
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 2.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @todo package:orm. Figure out a useful implementation.
* @todo Rename: Doctrine::ORM::(Internal::)UnitOfWork.
* @todo Turn connection exceptions into UnitOfWorkExceptions.
*/
class
Doctrine_Connection_UnitOfWork
{
{
/**
* The identity map that holds references to all managed entities that have
* an identity. The entities are grouped by their class name.
...
...
@@ -66,36 +50,50 @@ class Doctrine_Connection_UnitOfWork
* @var array
*/
protected
$_identityMap
=
array
();
/**
* A list of all new entities.
* A list of all new entities that need to be INSERTed.
*
* @var array
* @todo Index by class name.
* @todo Rename to _inserts?
*/
protected
$_newEntities
=
array
();
/**
* A list of all dirty entities.
*
* @var array
* @todo Rename to _updates?
*/
protected
$_dirtyEntities
=
array
();
/**
* A list of all removed entities.
* A list of all deleted entities.
* Removed entities are entities that are "scheduled for removal" but have
* not yet been removed from the database.
*
* @var array
* @todo Rename to _deletions?
*/
protected
$_
remov
edEntities
=
array
();
protected
$_
delet
edEntities
=
array
();
/**
* The EntityManager the UnitOfWork belongs to.
*
* @var Doctrine::ORM::EntityManager
*/
protected
$_em
;
/**
* The calculator used to calculate the order in which changes to
* entities need to be written to the database.
*
* @var
unknown_type
* @var
Doctrine::ORM::Internal::CommitOrderCalculator
* @todo Implementation. Replace buildFlushTree().
*/
protected
$_commitOrderCalculator
;
/**
* Constructor.
* Creates a new UnitOfWork.
...
...
@@ -105,344 +103,375 @@ class Doctrine_Connection_UnitOfWork
public
function
__construct
(
Doctrine_EntityManager
$em
)
{
$this
->
_em
=
$em
;
//TODO: any benefit with lazy init?
$this
->
_commitOrderCalculator
=
new
Doctrine_Internal_CommitOrderCalculator
();
}
/**
* Commits the unit of work, executing all operations that have been postponed
* up to this point.
*
*
* @todo Impl
*/
public
function
commit
()
{
$this
->
_orderCommits
();
$this
->
_insertNew
();
$this
->
_updateDirty
();
$this
->
_deleteRemoved
();
// Detect changes in managed entities (mark dirty)
//TODO: Consider using registerDirty() in Entity#set() instead if its
// more performant.
foreach
(
$this
->
_identityMap
as
$entities
)
{
foreach
(
$entities
as
$entity
)
{
if
(
$entity
->
_state
()
==
Doctrine_Entity
::
STATE_MANAGED
&&
$entity
->
isModified
())
{
$this
->
registerDirty
(
$entity
);
}
}
}
if
(
empty
(
$this
->
_newEntities
)
&&
empty
(
$this
->
_deletedEntities
)
&&
empty
(
$this
->
_dirtyEntities
))
{
return
;
// Nothing to do.
}
// Now we need a commit order to maintain referential integrity
$commitOrder
=
$this
->
_getCommitOrder
();
//TODO: begin transaction here?
foreach
(
$commitOrder
as
$class
)
{
$this
->
_executeInserts
(
$class
);
$this
->
_executeUpdates
(
$class
);
}
// Deletions come last and need to be in reverse commit order
for
(
$count
=
count
(
$commitOrder
),
$i
=
$count
-
1
;
$i
>=
0
;
$i
--
)
{
$this
->
_executeDeletions
(
$commitOrder
[
$i
]);
}
//TODO: commit transaction here?
// clear lists
$this
->
_newEntities
=
array
();
$this
->
_dirtyEntities
=
array
();
$this
->
_deletedEntities
=
array
();
}
private
function
_
orderCommits
(
)
private
function
_
executeInserts
(
$class
)
{
//TODO: Maybe $persister->addInsert($entity) in the loop and
// $persister->executeInserts() at the end to allow easy prepared
// statement reuse and maybe bulk operations in the persister.
// Same for update/delete.
$className
=
$class
->
getClassName
();
$persister
=
$this
->
_em
->
getEntityPersister
(
$className
);
foreach
(
$this
->
_newEntities
as
$entity
)
{
if
(
$entity
->
getClass
()
->
getClassName
()
==
$className
)
{
$persister
->
insert
(
$entity
);
}
}
}
private
function
_executeUpdates
(
$class
)
{
$className
=
$class
->
getClassName
();
$persister
=
$this
->
_em
->
getEntityPersister
(
$className
);
foreach
(
$this
->
_dirtyEntities
as
$entity
)
{
if
(
$entity
->
getClass
()
->
getClassName
()
==
$className
)
{
$persister
->
update
(
$entity
);
}
}
}
private
function
_executeDeletions
(
$class
)
{
$className
=
$class
->
getClassName
();
$persister
=
$this
->
_em
->
getEntityPersister
(
$className
);
foreach
(
$this
->
_deletedEntities
as
$entity
)
{
if
(
$entity
->
getClass
()
->
getClassName
()
==
$className
)
{
$persister
->
delete
(
$entity
);
}
}
}
/**
* Gets the commit order.
*
* @return array
*/
private
function
_getCommitOrder
()
{
//TODO: Once these 3 arrays are indexed by classname we can do this:
// Either way... do we need to care about duplicates?
/*$classesInChangeSet = array_merge(
array_keys($this->_newEntities),
array_keys($this->_dirtyEntities),
array_keys($this->_deletedEntities)
);*/
$entityChangeSet
=
array_merge
(
$this
->
_newEntities
,
$this
->
_dirtyEntities
,
$this
->
_deletedEntities
);
/* if (count($entityChangeSet) == 1) {
* return array($entityChangeSet[0]->getClass());
* }
*/
// See if there are any new classes in the changeset, that are not in the
// commit order graph yet (dont have a node).
$newNodes
=
array
();
foreach
(
$entityChangeSet
as
$entity
)
{
if
(
!
$this
->
_commitOrderCalculator
->
hasNodeWithKey
(
$entity
->
getClass
()
->
getClassName
()))
{
$this
->
_commitOrderCalculator
->
addNodeWithItem
(
$entity
->
getClass
()
->
getClassName
(),
// index/key
$entity
->
getClass
()
// item
);
$newNodes
[]
=
$this
->
_commitOrderCalculator
->
getNodeForKey
(
$entity
->
getClass
()
->
getClassName
());
}
}
// Calculate dependencies for new nodes
foreach
(
$newNodes
as
$node
)
{
foreach
(
$node
->
getClass
()
->
getAssociationMappings
()
as
$assocMapping
)
{
//TODO: should skip target classes that are not in the changeset.
if
(
$assocMapping
->
isOwningSide
())
{
$targetClass
=
$assocMapping
->
getTargetClass
();
$targetClassName
=
$targetClass
->
getClassName
();
// if the target class does not yet have a node, create it
if
(
!
$this
->
_commitOrderCalculator
->
hasNodeWithKey
(
$targetClassName
))
{
$this
->
_commitOrderCalculator
->
addNodeWithItem
(
$targetClassName
,
// index/key
$targetClass
// item
);
}
// add dependency
$otherNode
=
$this
->
_commitOrderCalculator
->
getNodeForKey
(
$targetClassName
);
$node
->
before
(
$otherNode
);
}
}
}
return
$this
->
_commitOrderCalculator
->
getCommitOrder
();
}
/**
* Register a new entity.
*
* @todo Rename to scheduleForInsert().
*/
public
function
registerNew
(
Doctrine_Entity
$entity
)
{
if
(
!
$entity
->
identifier
())
{
throw
new
Doctrine_Connection_Exception
(
"Entity without identity "
.
"can't be registered as new."
);
}
$oid
=
$entity
->
getOid
();
/*if ( ! $entity->_identifier()) {
throw new Doctrine_Connection_Exception("Entity without identity cant be registered as new.");
}*/
if
(
isset
(
$this
->
_dirtyEntities
[
$oid
]))
{
throw
new
Doctrine_Connection_Exception
(
"Dirty object can't be registered as new."
);
}
else
if
(
isset
(
$this
->
_removedEntities
[
$oid
]))
{
}
if
(
isset
(
$this
->
_deletedEntities
[
$oid
]))
{
throw
new
Doctrine_Connection_Exception
(
"Removed object can't be registered as new."
);
}
else
if
(
isset
(
$this
->
_newEntities
[
$oid
]))
{
}
if
(
isset
(
$this
->
_newEntities
[
$oid
]))
{
throw
new
Doctrine_Connection_Exception
(
"Object already registered as new. Can't register twice."
);
}
$this
->
registerIdentity
(
$entity
);
$this
->
_newEntities
[
$oid
]
=
$entity
;
if
(
$entity
->
_identifier
())
{
$this
->
addToIdentityMap
(
$entity
);
}
}
/**
* Checks whether an entity is registered as new on the unit of work.
*
* @param Doctrine_Entity $entity
* @return boolean
* @todo Rename to isScheduledForInsert().
*/
public
function
isRegisteredNew
(
Doctrine_Entity
$entity
)
{
return
isset
(
$this
->
_newEntities
[
$entity
->
getOid
()]);
}
/**
* Registers a clean entity.
* The entity is simply put into the identity map.
*
* @param Doctrine::ORM::Entity $entity
*/
public
function
registerClean
(
Doctrine_Entity
$entity
)
{
$this
->
registerIdentity
(
$entity
);
$this
->
addToIdentityMap
(
$entity
);
}
/**
* Registers a dirty entity.
*
* @param Doctrine::ORM::Entity $entity
* @todo Rename to scheduleForUpdate().
*/
public
function
registerDirty
(
Doctrine_Entity
$entity
)
{
if
(
!
$entity
->
identifier
())
{
$oid
=
$entity
->
getOid
();
if
(
!
$entity
->
_identifier
())
{
throw
new
Doctrine_Connection_Exception
(
"Entity without identity "
.
"can't be registered as dirty."
);
}
$oid
=
$entity
->
getOid
();
if
(
isset
(
$this
->
_removedEntities
[
$entity
->
getOid
()]))
{
if
(
isset
(
$this
->
_deletedEntities
[
$oid
]))
{
throw
new
Doctrine_Connection_Exception
(
"Removed object can't be registered as dirty."
);
}
if
(
!
isset
(
$this
->
_dirtyEntities
[
$oid
],
$this
->
_newEntities
[
$oid
]))
{
$this
->
_dirtyEntities
[
$entity
->
getOid
()]
=
$entity
;
if
(
!
isset
(
$this
->
_dirtyEntities
[
$oid
])
&&
!
isset
(
$this
->
_newEntities
[
$oid
]))
{
$this
->
_dirtyEntities
[
$oid
]
=
$entity
;
}
}
/**
* Checks whether an entity is registered as dirty in the unit of work.
* Note: Is not very useful currently as dirty entities are only registered
* at commit time.
*
* @param Doctrine_Entity $entity
* @return boolean
* @todo Rename to isScheduledForUpdate().
*/
public
function
isRegisteredDirty
(
Doctrine_Entity
$entity
)
{
return
isset
(
$this
->
_dirtyEntities
[
$entity
->
getOid
()]);
}
/**
/**
* Registers a deleted entity.
*
* @todo Rename to scheduleForDelete().
*/
public
function
register
Remov
ed
(
Doctrine_Entity
$entity
)
public
function
register
Delet
ed
(
Doctrine_Entity
$entity
)
{
if
(
$entity
->
isNew
())
{
$oid
=
$entity
->
getOid
();
if
(
!
$this
->
isInIdentityMap
(
$entity
))
{
return
;
}
$this
->
unregisterIdentity
(
$entity
);
$oid
=
$entity
->
getOid
();
$this
->
removeFromIdentityMap
(
$entity
);
if
(
isset
(
$this
->
_newEntities
[
$oid
]))
{
unset
(
$this
->
_newEntities
[
$oid
]);
return
;
}
if
(
isset
(
$this
->
_dirtyEntities
[
$oid
]))
{
unset
(
$this
->
_dirtyEntities
[
$oid
]);
return
;
// entity has not been persisted yet, so nothing more to do.
}
if
(
!
isset
(
$this
->
_removedEntities
[
$oid
]))
{
$this
->
_removedEntities
[
$oid
]
=
$entity
;
/* Seems unnecessary since _dirtyEntities is filled & cleared on commit, not earlier
if (isset($this->_dirtyEntities[$oid])) {
unset($this->_dirtyEntities[$oid]);
}*/
if
(
!
isset
(
$this
->
_deletedEntities
[
$oid
]))
{
$this
->
_deletedEntities
[
$oid
]
=
$entity
;
}
}
public
function
isRegisteredRemoved
(
Doctrine_Entity
$entity
)
{
return
isset
(
$this
->
_removedEntities
[
$entity
->
getOid
()]);
}
/**
* 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.
* Checks whether an entity is registered as removed/deleted with the unit
* of work.
*
* @param array $tables an array of Doctrine_Table objects or component names
* @return array an array of component names in flushing order
* @param Doctrine::ORM::Entity $entity
* @return boolean
* @todo Rename to isScheduledForDelete().
*/
public
function
buildFlushTree
(
array
$entityNames
)
public
function
isRegisteredRemoved
(
Doctrine_Entity
$entity
)
{
$tree
=
array
();
foreach
(
$entityNames
as
$k
=>
$entity
)
{
if
(
!
(
$mapper
instanceof
Doctrine_Mapper
))
{
$mapper
=
$this
->
conn
->
getMapper
(
$mapper
);
}
$nm
=
$mapper
->
getComponentName
();
$index
=
array_search
(
$nm
,
$tree
);
if
(
$index
===
false
)
{
$tree
[]
=
$nm
;
$index
=
max
(
array_keys
(
$tree
));
}
$rels
=
$mapper
->
getClassMetadata
()
->
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
;
}
}
else
if
(
$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
++
;
}
}
else
if
(
$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
$tree
;
return
isset
(
$this
->
_deletedEntities
[
$entity
->
getOid
()]);
}
/**
* persists all the pending records from all tables
* Detaches an entity from the persistence management. It's persistence will
* no longer be managed by Doctrine.
*
* @throws PDOException if something went wrong at database level
* @return void
* @deprecated
*/
/*public function saveAll()
{
$this->conn->beginInternalTransaction();
// get the flush tree
$tree = $this->buildFlushTree($this->conn->getMappers());
$tree = array_combine($tree, array_fill(0, count($tree), array()));
foreach ($this->_managedEntities as $oid => $entity) {
$className = $entity->getClassName();
$tree[$className][] = $entity;
}
// save all records
foreach ($tree as $className => $entities) {
$mapper = $this->conn->getMapper($className);
foreach ($entities as $entity) {
$mapper->saveSingleRecord($entity);
}
}
// save all associations
foreach ($tree as $className => $entities) {
$mapper = $this->conn->getMapper($className);
foreach ($entities as $entity) {
$mapper->saveAssociations($entity);
}
}
$this->conn->commit();
}*/
/**
* Adds an entity to the pool of managed entities.
* @deprecated
* @param integer $oid object identifier
* @return boolean whether ot not the operation was successful
*/
public
function
manage
(
Doctrine_Entity
$entity
)
public
function
detach
(
Doctrine_Entity
$entity
)
{
$oid
=
$entity
->
getOid
();
if
(
!
isset
(
$this
->
_managedEntities
[
$oid
]))
{
$this
->
_managedEntities
[
$oid
]
=
$entity
;
return
true
;
if
(
$this
->
isInIdentityMap
(
$entity
))
{
$this
->
removeFromIdentityMap
(
$entity
);
}
return
false
;
}
/**
* @param integer $oid object identifier
* @return boolean whether ot not the operation was successful
* @deprecated The new implementation of detach() should remove the entity
* from the identity map.
* Enter description here...
*
* @param Doctrine_Entity $entity
* @return unknown
* @todo Rename to isScheduled()
*/
public
function
detach
(
Doctrine_Entity
$entity
)
public
function
isEntityRegistered
(
Doctrine_Entity
$entity
)
{
$oid
=
$entity
->
getOid
();
if
(
!
isset
(
$this
->
_managedEntities
[
$oid
]))
{
return
false
;
}
unset
(
$this
->
_managedEntities
[
$oid
]);
return
true
;
return
isset
(
$this
->
_newEntities
[
$oid
])
||
//isset($this->_dirtyEntities[$oid]) ||
isset
(
$this
->
_deletedEntities
[
$oid
])
||
$this
->
isInIdentityMap
(
$entity
);
}
/**
* Detaches all currently managed entities.
* Alternatively, if an entity class name is given, all entities of that type
* (or subtypes) are detached. Don't forget that entities are registered in
* the identity map with the name of the root entity class. So calling detachAll()
* with a class name that is not the name of a root entity has no effect.
*
* @return integer The number of detached entities.
* @todo Deprecated. The new implementation should remove all entities from
* the identity map.
*/
public
function
detachAll
()
public
function
detachAll
(
$entityName
=
null
)
{
$numDetached
=
count
(
$this
->
_managedEntities
);
$this
->
_managedEntities
=
array
();
//TODO: what do do with new/dirty/removed lists?
$numDetached
=
0
;
if
(
$entityName
!==
null
&&
isset
(
$this
->
_identityMap
[
$entityName
]))
{
$numDetached
=
count
(
$this
->
_identityMap
[
$entityName
]);
$this
->
_identityMap
[
$entityName
]
=
array
();
}
else
{
$numDetached
=
count
(
$this
->
_identityMap
);
$this
->
_identityMap
=
array
();
}
return
$numDetached
;
}
/**
* Registers an entity in the identity map.
*
* Note that entities in a hierarchy are registered with the class name of
* the root entity.
*
* @param Doctrine::ORM::Entity $entity The entity to register.
* @return boolean TRUE if the registration was successful, FALSE if the identity of
* the entity in question is already managed.
* @throws Doctrine_Connection_Exception If the entity has no (database) identity.
*/
public
function
registerIdentity
(
Doctrine_Entity
$entity
)
public
function
addToIdentityMap
(
Doctrine_Entity
$entity
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
identifier
());
if
(
!
$idHash
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
_
identifier
());
if
(
$idHash
===
''
)
{
throw
new
Doctrine_Connection_Exception
(
"Entity with oid '"
.
$entity
->
getOid
()
.
"' has no identity and therefore can't be added to the identity map."
);
}
$className
=
$entity
->
getClass
Metadata
()
->
getRootClassName
();
$className
=
$entity
->
getClass
()
->
getRootClassName
();
if
(
isset
(
$this
->
_identityMap
[
$className
][
$idHash
]))
{
return
false
;
}
$this
->
_identityMap
[
$className
][
$idHash
]
=
$entity
;
$entity
->
_state
(
Doctrine_Entity
::
STATE_MANAGED
);
return
true
;
}
/**
* Enter description here...
*
* @param unknown_type $entityName
* @todo unify with detachAll()
*/
public
function
clearIdentitiesForEntity
(
$entityName
)
{
$this
->
_identityMap
[
$entityName
]
=
array
();
}
/**
* Removes an entity from the identity map.
*
* @param Doctrine_Entity $entity
* @return unknown
* @todo This will be the new detach().
*/
public
function
unregisterIdentity
(
Doctrine_Entity
$entity
)
public
function
removeFromIdentityMap
(
Doctrine_Entity
$entity
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
identifier
());
if
(
!
$idHash
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
_
identifier
());
if
(
$idHash
===
''
)
{
throw
new
Doctrine_Connection_Exception
(
"Entity with oid '"
.
$entity
->
getOid
()
.
"' has no identity and therefore can't be removed from the identity map."
);
}
$className
=
$entity
->
getClass
Metadata
()
->
getRootClassName
();
$className
=
$entity
->
getClass
()
->
getRootClassName
();
if
(
isset
(
$this
->
_identityMap
[
$className
][
$idHash
]))
{
unset
(
$this
->
_identityMap
[
$className
][
$idHash
]);
return
true
;
...
...
@@ -450,19 +479,18 @@ class Doctrine_Connection_UnitOfWork
return
false
;
}
/**
* Finds an entity in the identity map by its identifier hash.
*
* @param
unknown_type
$idHash
* @param
unknown_type
$rootClassName
* @return
unknown
* @param
string
$idHash
* @param
string
$rootClassName
* @return
Doctrine::ORM::Entity
*/
public
function
getByIdHash
(
$idHash
,
$rootClassName
)
{
return
$this
->
_identityMap
[
$rootClassName
][
$idHash
];
}
public
function
tryGetByIdHash
(
$idHash
,
$rootClassName
)
{
if
(
$this
->
containsIdHash
(
$idHash
,
$rootClassName
))
{
...
...
@@ -470,36 +498,43 @@ class Doctrine_Connection_UnitOfWork
}
return
false
;
}
/**
* Gets the identifier hash for a set of identifier values.
* The hash is just a concatenation of the identifier values.
* The identifiers are concatenated with a space.
*
* Note that this method always returns a string. If the given array is
* empty, an empty string is returned.
*
* @param array $id
* @return string
* @return string
The hash.
*/
public
function
getIdentifierHash
(
array
$id
)
{
return
implode
(
' '
,
$id
);
}
/**
* Checks whether an entity is registered in the identity map.
* Checks whether an entity is registered in the identity map of the
* UnitOfWork.
*
* @param Doctrine_Entity $entity
* @return boolean
*/
public
function
contains
(
Doctrine_Entity
$entity
)
public
function
isInIdentityMap
(
Doctrine_Entity
$entity
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
identifier
());
if
(
!
$idHash
)
{
$idHash
=
$this
->
getIdentifierHash
(
$entity
->
_identifier
());
if
(
$idHash
===
''
)
{
return
false
;
}
return
isset
(
$this
->
_identityMap
[
$entity
->
getClassMetadata
()
->
getRootClassName
()]
[
$idHash
]);
[
$entity
->
getClass
()
->
getRootClassName
()]
[
$idHash
]
);
}
/**
* Checks whether an identifier hash exists in the identity map.
*
...
...
@@ -511,46 +546,167 @@ class Doctrine_Connection_UnitOfWork
{
return
isset
(
$this
->
_identityMap
[
$rootClassName
][
$idHash
]);
}
/**
* Saves an entity as part of the current unit of work.
*
* @param Doctrine_Entity $entity The entity to save.
*/
public
function
save
(
Doctrine_Entity
$entity
)
{
{
$insertNow
=
array
();
$visited
=
array
();
$this
->
_doSave
(
$entity
,
$visited
,
$insertNow
);
if
(
!
empty
(
$insertNow
))
{
// We have no choice. This means that there are either new entities
// with an IDENTITY key generation or with a natural identifier.
// In both cases we must commit the inserts instantly.
//TODO: Isnt it enough to only execute the inserts instead of full flush?
$this
->
commit
();
}
}
/**
* Saves an entity as part of the current unit of work.
* This method is internally called during save() cascades as it tracks
* the already visited entities to prevent infinite recursions.
*
* @param Doctrine_Entity $entity The entity to save.
* @param array $visited The already visited entities.
*/
private
function
_doSave
(
Doctrine_Entity
$entity
,
array
&
$visited
,
array
&
$insertNow
)
{
if
(
isset
(
$visited
[
$entity
->
getOid
()]))
{
return
;
// Prevent infinite recursion
}
$visited
[
$entity
->
getOid
()]
=
$entity
;
// mark visited
$class
=
$entity
->
getClass
();
switch
(
$entity
->
_state
())
{
case
Doctrine_Entity
::
STATE_CLEAN
:
//nothing to do
// ignore $entity but cascade
case
Doctrine_Entity
::
STATE_MANAGED
:
// nothing to do for $entity
break
;
case
Doctrine_Entity
::
STATE_DIRTY
:
// update
$this
->
registerDirty
(
$entity
);
// todo:cascade
case
Doctrine_Entity
::
STATE_NEW
:
if
(
$class
->
isIdGeneratorIdentity
())
{
$insertNow
[
$entity
->
getOid
()]
=
$entity
;
$this
->
_newEntities
[
$entity
->
getOid
()]
=
$entity
;
}
else
if
(
!
$class
->
usesIdGenerator
())
{
$insertNow
[
$entity
->
getOid
()]
=
$entity
;
//...
}
else
if
(
$class
->
isIdGeneratorSequence
())
{
// Get the next sequence number
//TODO: sequence name?
$id
=
$this
->
_em
->
getConnection
()
->
getSequenceModule
()
->
nextId
(
"foo"
);
$entity
->
set
(
$class
->
getSingleIdentifierFieldName
(),
$id
);
$this
->
registerNew
(
$entity
);
}
else
{
throw
new
Doctrine_Exception
(
"Unable to handle ID generation of new entity."
);
}
break
;
case
Doctrine_Entity
::
STATE_TCLEAN
:
case
Doctrine_Entity
::
STATE_TDIRTY
:
// insert
// if identifier type IDENTITY:
// cascade
// if no transaction is started yet, do it
// force insert (directly to persister)
// else
// cascade
// get & assign the identifier, then registerNew()
case
Doctrine_Entity
::
STATE_DETACHED
:
//exception?
throw
new
Doctrine_Exception
(
"Behavior of save() for a detached entity "
.
"is not yet defined."
);
case
Doctrine_Entity
::
STATE_DELETED
:
// $entity becomes managed again
if
(
$this
->
isRegisteredRemoved
(
$entity
))
{
//TODO: better a method for this?
unset
(
$this
->
_deletedEntities
[
$entity
->
getOid
()]);
}
else
{
//FIXME: There's more to think of here...
$this
->
registerNew
(
$entity
);
}
break
;
default
:
//TODO: throw UnitOfWorkException::invalidEntityState()
throw
new
Doctrine_Exception
(
"Encountered invalid entity state."
);
}
$this
->
_cascadeSave
(
$entity
,
$visited
,
$insertNow
);
}
private
function
_cascadeSave
(
Doctrine_Entity
$entity
)
/**
* Deletes an entity as part of the current unit of work.
*
* @param Doctrine_Entity $entity
*/
public
function
delete
(
Doctrine_Entity
$entity
)
{
$this
->
_doDelete
(
$entity
,
array
());
}
private
function
_doDelete
(
Doctrine_Entity
$entity
,
array
&
$visited
)
{
if
(
isset
(
$visited
[
$entity
->
getOid
()]))
{
return
;
// Prevent infinite recursion
}
$visited
[
$entity
->
getOid
()]
=
$entity
;
// mark visited
$class
=
$entity
->
getClass
();
switch
(
$entity
->
_state
())
{
case
Doctrine_Entity
::
STATE_NEW
:
case
Doctrine_Entity
::
STATE_DELETED
:
// nothing to do for $entity
break
;
case
Doctrine_Entity
::
STATE_MANAGED
:
$this
->
registerDeleted
(
$entity
);
break
;
case
Doctrine_Entity
::
STATE_DETACHED
:
//exception?
throw
new
Doctrine_Exception
(
"A detached entity can't be deleted."
);
default
:
//TODO: throw UnitOfWorkException::invalidEntityState()
throw
new
Doctrine_Exception
(
"Encountered invalid entity state."
);
}
$this
->
_cascadeDelete
(
$entity
,
$visited
);
}
/**
* Cascades the save operation to associated entities.
*
* @param Doctrine_Entity $entity
* @param array $visited
*/
private
function
_cascadeSave
(
Doctrine_Entity
$entity
,
array
&
$visited
,
array
&
$insertNow
)
{
foreach
(
$entity
->
getClass
()
->
getAssociationMappings
()
as
$assocMapping
)
{
if
(
!
$assocMapping
->
isCascadeSave
())
{
continue
;
}
$relatedEntities
=
$entity
->
get
(
$assocMapping
->
getSourceFieldName
());
if
(
$relatedEntities
instanceof
Doctrine_Entity
)
{
$this
->
_doSave
(
$relatedEntities
,
$visited
,
$insertNow
);
}
else
if
(
$relatedEntities
instanceof
Doctrine_Collection
&&
count
(
$relatedEntities
)
>
0
)
{
foreach
(
$relatedEntities
as
$relatedEntity
)
{
$this
->
_doSave
(
$relatedEntity
,
$visited
,
$insertNow
);
}
}
}
}
private
function
_cascadeDelete
(
Doctrine_Entity
$entity
)
{
}
public
function
getCommitOrderCalculator
()
{
return
$this
->
_commitOrderCalculator
;
}
public
function
close
()
{
//...
$this
->
_commitOrderCalculator
->
clear
();
}
// Stuff from 0.11/1.0 that we will need later (need to modify it though)
/**
* Collects all records that need to be deleted by applying defined
* application-level delete cascades.
...
...
@@ -558,15 +714,15 @@ class Doctrine_Connection_UnitOfWork
* @param array $deletions Map of the records to delete. Keys=Oids Values=Records.
*/
/*private function _collectDeletions(Doctrine_Record $record, array &$deletions)
{
if ( ! $record->exists()) {
return;
}
{
if ( ! $record->exists()) {
return;
}
$deletions[$record->getOid()] = $record;
$this->_cascadeDelete($record, $deletions);
}*/
$deletions[$record->getOid()] = $record;
$this->_cascadeDelete($record, $deletions);
}*/
/**
* Cascades an ongoing delete operation to related objects. Applies only on relations
* that have 'delete' in their cascade options.
...
...
@@ -579,32 +735,32 @@ class Doctrine_Connection_UnitOfWork
* @throws PDOException If something went wrong at database level
* @return void
*/
/*protected function _cascadeDelete(Doctrine_Record $record, array &$deletions)
/*protected function _cascadeDelete(Doctrine_Record $record, array &$deletions)
{
foreach ($record->getTable()->getRelations() as $relation) {
if ($relation->isCascadeDelete()) {
$fieldName = $relation->getAlias();
// if it's a xToOne relation and the related object is already loaded
// we don't need to refresh.
if ( ! ($relation->getType() == Doctrine_Relation::ONE && isset($record->$fieldName))) {
$record->refreshRelated($relation->getAlias());
}
$relatedObjects = $record->get($relation->getAlias());
if ($relatedObjects instanceof Doctrine_Record && $relatedObjects->exists()
&& ! isset($deletions[$relatedObjects->getOid()])) {
$this->_collectDeletions($relatedObjects, $deletions);
} else if ($relatedObjects instanceof Doctrine_Collection && count($relatedObjects) > 0) {
// cascade the delete to the other objects
foreach ($relatedObjects as $object) {
if ( ! isset($deletions[$object->getOid()])) {
$this->_collectDeletions($object, $deletions);
}
}
}
}
}
foreach ($record->getTable()->getRelations() as $relation) {
if ($relation->isCascadeDelete()) {
$fieldName = $relation->getAlias();
// if it's a xToOne relation and the related object is already loaded
// we don't need to refresh.
if ( ! ($relation->getType() == Doctrine_Relation::ONE && isset($record->$fieldName))) {
$record->refreshRelated($relation->getAlias());
}
$relatedObjects = $record->get($relation->getAlias());
if ($relatedObjects instanceof Doctrine_Record && $relatedObjects->exists()
&& ! isset($deletions[$relatedObjects->getOid()])) {
$this->_collectDeletions($relatedObjects, $deletions);
} else if ($relatedObjects instanceof Doctrine_Collection && count($relatedObjects) > 0) {
// cascade the delete to the other objects
foreach ($relatedObjects as $object) {
if ( ! isset($deletions[$object->getOid()])) {
$this->_collectDeletions($object, $deletions);
}
}
}
}
}
}*/
/**
* Executes the deletions for all collected records during a delete operation
* (usually triggered through $record->delete()).
...
...
@@ -612,89 +768,89 @@ class Doctrine_Connection_UnitOfWork
* @param array $deletions Map of the records to delete. Keys=Oids Values=Records.
*/
/*private function _executeDeletions(array $deletions)
{
// collect class names
$classNames = array();
foreach ($deletions as $record) {
$classNames[] = $record->getTable()->getComponentName();
}
$classNames = array_unique($classNames);
// order deletes
$executionOrder = $this->buildFlushTree($classNames);
// execute
try {
$this->conn->beginInternalTransaction();
for ($i = count($executionOrder) - 1; $i >= 0; $i--) {
$className = $executionOrder[$i];
$table = $this->conn->getTable($className);
// collect identifiers
$identifierMaps = array();
$deletedRecords = array();
foreach ($deletions as $oid => $record) {
if ($record->getTable()->getComponentName() == $className) {
$veto = $this->_preDelete($record);
if ( ! $veto) {
$identifierMaps[] = $record->identifier();
$deletedRecords[] = $record;
unset($deletions[$oid]);
}
}
}
if (count($deletedRecords) < 1) {
continue;
}
// extract query parameters (only the identifier values are of interest)
$params = array();
$columnNames = array();
foreach ($identifierMaps as $idMap) {
while (list($fieldName, $value) = each($idMap)) {
$params[] = $value;
$columnNames[] = $table->getColumnName($fieldName);
}
}
$columnNames = array_unique($columnNames);
// delete
$tableName = $table->getTableName();
$sql = "DELETE FROM " . $this->conn->quoteIdentifier($tableName) . " WHERE ";
if ($table->isIdentifierComposite()) {
$sql .= $this->_buildSqlCompositeKeyCondition($columnNames, count($identifierMaps));
$this->conn->exec($sql, $params);
} else {
$sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params));
$this->conn->exec($sql, $params);
}
// adjust state, remove from identity map and inform postDelete listeners
foreach ($deletedRecords as $record) {
// currently just for bc!
$this->_deleteCTIParents($table, $record);
//--
$record->state(Doctrine_Record::STATE_TCLEAN);
$record->getTable()->removeRecord($record);
$this->_postDelete($record);
}
}
$this->conn->commit();
// trigger postDelete for records skipped during the deletion (veto!)
foreach ($deletions as $skippedRecord) {
$this->_postDelete($skippedRecord);
}
return true;
} catch (Exception $e) {
$this->conn->rollback();
throw $e;
}
}*/
{
// collect class names
$classNames = array();
foreach ($deletions as $record) {
$classNames[] = $record->getTable()->getComponentName();
}
$classNames = array_unique($classNames);
// order deletes
$executionOrder = $this->buildFlushTree($classNames);
// execute
try {
$this->conn->beginInternalTransaction();
for ($i = count($executionOrder) - 1; $i >= 0; $i--) {
$className = $executionOrder[$i];
$table = $this->conn->getTable($className);
// collect identifiers
$identifierMaps = array();
$deletedRecords = array();
foreach ($deletions as $oid => $record) {
if ($record->getTable()->getComponentName() == $className) {
$veto = $this->_preDelete($record);
if ( ! $veto) {
$identifierMaps[] = $record->identifier();
$deletedRecords[] = $record;
unset($deletions[$oid]);
}
}
}
if (count($deletedRecords) < 1) {
continue;
}
// extract query parameters (only the identifier values are of interest)
$params = array();
$columnNames = array();
foreach ($identifierMaps as $idMap) {
while (list($fieldName, $value) = each($idMap)) {
$params[] = $value;
$columnNames[] = $table->getColumnName($fieldName);
}
}
$columnNames = array_unique($columnNames);
// delete
$tableName = $table->getTableName();
$sql = "DELETE FROM " . $this->conn->quoteIdentifier($tableName) . " WHERE ";
if ($table->isIdentifierComposite()) {
$sql .= $this->_buildSqlCompositeKeyCondition($columnNames, count($identifierMaps));
$this->conn->exec($sql, $params);
} else {
$sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params));
$this->conn->exec($sql, $params);
}
// adjust state, remove from identity map and inform postDelete listeners
foreach ($deletedRecords as $record) {
// currently just for bc!
$this->_deleteCTIParents($table, $record);
//--
$record->state(Doctrine_Record::STATE_TCLEAN);
$record->getTable()->removeRecord($record);
$this->_postDelete($record);
}
}
$this->conn->commit();
// trigger postDelete for records skipped during the deletion (veto!)
foreach ($deletions as $skippedRecord) {
$this->_postDelete($skippedRecord);
}
return true;
} catch (Exception $e) {
$this->conn->rollback();
throw $e;
}
}*/
/**
* Builds the SQL condition to target multiple records who have a single-column
...
...
@@ -705,10 +861,10 @@ class Doctrine_Connection_UnitOfWork
* @return string The SQL condition "pk = ? OR pk = ? OR pk = ? ..."
*/
/*private function _buildSqlSingleKeyCondition($columnNames, $numRecords)
{
$idColumn = $this->conn->quoteIdentifier($columnNames[0]);
return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
}*/
{
$idColumn = $this->conn->quoteIdentifier($columnNames[0]);
return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
}*/
/**
* Builds the SQL condition to target multiple records who have a composite primary key.
...
...
@@ -718,21 +874,26 @@ class Doctrine_Connection_UnitOfWork
* @return string The SQL condition "(pk1 = ? AND pk2 = ?) OR (pk1 = ? AND pk2 = ?) ..."
*/
/*private function _buildSqlCompositeKeyCondition($columnNames, $numRecords)
{
$singleCondition = "";
foreach ($columnNames as $columnName) {
$columnName = $this->conn->quoteIdentifier($columnName);
if ($singleCondition === "") {
$singleCondition .= "($columnName = ?";
} else {
$singleCondition .= " AND $columnName = ?";
}
}
$singleCondition .= ")";
$fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
return $fullCondition;
}*/
{
$singleCondition = "";
foreach ($columnNames as $columnName) {
$columnName = $this->conn->quoteIdentifier($columnName);
if ($singleCondition === "") {
$singleCondition .= "($columnName = ?";
} else {
$singleCondition .= " AND $columnName = ?";
}
}
$singleCondition .= ")";
$fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
return $fullCondition;
}*/
public
function
getIdentityMap
()
{
return
$this
->
_identityMap
;
}
}
...
...
lib/Doctrine/Entity.php
View file @
d9975c36
...
...
@@ -37,45 +37,23 @@
* @link www.phpdoctrine.org
* @since 2.0
* @version $Revision: 4342 $
* @todo Split up into "Entity" and "ActiveEntity" (extends Entity)
* @todo Move entity states into a separate enumeration (EntityStates).
* They do not need to be exposed to users in such a way. The states are mainly
* for internal use.
* @todo Split up into "Entity" and "ActiveEntity" (extends Entity).
*/
abstract
class
Doctrine_Entity
extends
Doctrine_Access
implements
Serializable
{
/**
* DIRTY STATE
* An Entity is in dirty state when its properties are changed.
* MANAGED
* An Entity is in managed state when it has a primary key/identifier and is
* managed by an EntityManager (registered in the identity map).
*/
const
STATE_DIRTY
=
1
;
const
STATE_MANAGED_DIRTY
=
1
;
const
STATE_MANAGED
=
1
;
/**
*
TDIRTY STATE
* An Entity is
in transient dirty state when it is created and some of its
*
fields are modified but it is NOT yet persisted into database
.
*
NEW
* An Entity is
new if it does not yet have an identifier/primary key
*
and is not (yet) managed by an EntityManager
.
*/
const
STATE_TDIRTY
=
2
;
const
STATE_NEW_DIRTY
=
2
;
/**
* CLEAN STATE
* An Entity is in clean state when all of its properties are loaded from the database
* and none of its properties are changed.
*/
const
STATE_CLEAN
=
3
;
const
STATE_MANAGED_CLEAN
=
3
;
/**
* NEW TCLEAN
* An Entity is in transient clean state when it is created and none of its
* fields are modified.
* @todo Do we need this state? Just STATE_NEW may be enough without differentiating
* clean/dirty. A new entity is always "dirty".
*/
const
STATE_TCLEAN
=
5
;
const
STATE_NEW_CLEAN
=
5
;
const
STATE_NEW
=
2
;
/**
* LOCKED STATE
...
...
@@ -93,14 +71,14 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
* (or no longer) associated with an EntityManager (and a UnitOfWork).
* This means its no longer in the identity map.
*/
const
STATE_DETACHED
=
7
;
const
STATE_DETACHED
=
3
;
/**
* A removed Entity instance is an instance with a persistent identity,
* associated with an EntityManager, that is scheduled for removal from the
* database.
*/
const
STATE_DELETED
=
8
;
const
STATE_DELETED
=
4
;
/**
* Index used for creating object identifiers (oid's).
...
...
@@ -144,12 +122,6 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
private
$_entityName
;
/**
* @var Doctrine_Node_<TreeImpl> node object
* @todo Specific to the NestedSet Behavior plugin. Move outta here.
*/
//protected $_node;
/**
* The values that make up the ID/primary key of the entity.
*
...
...
@@ -167,16 +139,16 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* The state of the object.
*
* @var integer
* @see STATE_* constants
* @var integer
*/
private
$_state
;
/**
* The names of fields that have been modified but not yet persisted.
* Keys are field names, values oldValue => newValue tuples.
*
* @var array
* @todo
Better name? $_modifiedFields?
* @var array
* @todo
Rename to $_changeSet
*/
private
$_modified
=
array
();
...
...
@@ -204,6 +176,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* Constructor.
* Creates a new Entity instance.
*/
public
function
__construct
()
{
...
...
@@ -214,9 +187,9 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
$this
->
_data
=
$this
->
_em
->
_getTmpEntityData
();
if
(
$this
->
_data
)
{
$this
->
_extractIdentifier
();
$this
->
_state
=
self
::
STATE_
CLEAN
;
$this
->
_state
=
self
::
STATE_
MANAGED
;
}
else
{
$this
->
_state
=
self
::
STATE_
TCLEAN
;
$this
->
_state
=
self
::
STATE_
NEW
;
}
// @todo read from attribute the first time and move this initialization elsewhere.
...
...
@@ -265,7 +238,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}*/
/**
*
h
ydrates this object from given array
*
H
ydrates this object from given array
*
* @param array $data
* @return boolean
...
...
@@ -278,33 +251,26 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* Copies the identifier names and values from _data into _id.
*
* @param boolean $exists whether or not this record exists in persistent data store
* @return void
* @todo Looks like its better placed elsewhere (EntityManager?)
*/
private
function
_extractIdentifier
()
{
switch
(
$this
->
_class
->
getIdentifierType
())
{
case
Doctrine
::
IDENTIFIER_AUTOINC
:
case
Doctrine
::
IDENTIFIER_SEQUENCE
:
case
Doctrine
::
IDENTIFIER_NATURAL
:
$name
=
$this
->
_class
->
getIdentifier
();
$name
=
$name
[
0
];
if
(
isset
(
$this
->
_data
[
$name
])
&&
$this
->
_data
[
$name
]
!==
Doctrine_Null
::
$INSTANCE
)
{
if
(
!
$this
->
_class
->
isIdentifierComposite
())
{
// Single field identifier
$name
=
$this
->
_class
->
getIdentifier
();
$name
=
$name
[
0
];
if
(
isset
(
$this
->
_data
[
$name
])
&&
$this
->
_data
[
$name
]
!==
Doctrine_Null
::
$INSTANCE
)
{
$this
->
_id
[
$name
]
=
$this
->
_data
[
$name
];
}
}
else
{
// Composite identifier
$names
=
$this
->
_class
->
getIdentifier
();
foreach
(
$names
as
$name
)
{
if
(
$this
->
_data
[
$name
]
===
Doctrine_Null
::
$INSTANCE
)
{
$this
->
_id
[
$name
]
=
null
;
}
else
{
$this
->
_id
[
$name
]
=
$this
->
_data
[
$name
];
}
break
;
case
Doctrine
::
IDENTIFIER_COMPOSITE
:
$names
=
$this
->
_class
->
getIdentifier
();
foreach
(
$names
as
$name
)
{
if
(
$this
->
_data
[
$name
]
===
Doctrine_Null
::
$INSTANCE
)
{
$this
->
_id
[
$name
]
=
null
;
}
else
{
$this
->
_id
[
$name
]
=
$this
->
_data
[
$name
];
}
}
break
;
}
}
}
...
...
@@ -438,22 +404,16 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/* TODO: Do we really need this check? This is only for internal use after all. */
switch
(
$state
)
{
case
self
::
STATE_TCLEAN
:
case
self
::
STATE_CLEAN
:
case
self
::
STATE_TDIRTY
:
case
self
::
STATE_DIRTY
:
case
self
::
STATE_PROXY
:
case
self
::
STATE_MANAGED
:
case
self
::
STATE_DELETED
:
case
self
::
STATE_DETACHED
:
case
self
::
STATE_NEW
:
case
self
::
STATE_LOCKED
:
$this
->
_state
=
$state
;
break
;
default
:
throw
Doctrine_Entity_Exception
::
invalidState
(
$state
);
}
if
(
$this
->
_state
===
Doctrine_Entity
::
STATE_TCLEAN
||
$this
->
_state
===
Doctrine_Entity
::
STATE_CLEAN
)
{
$this
->
_modified
=
array
();
}
}
/**
...
...
@@ -878,23 +838,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
}*/
$old
=
isset
(
$this
->
_data
[
$fieldName
])
?
$this
->
_data
[
$fieldName
]
:
null
;
//FIXME: null == 0 => true
if
(
$old
!=
$value
)
{
$this
->
_data
[
$fieldName
]
=
$value
;
$this
->
_modified
[
$fieldName
]
=
array
(
$old
=>
$value
);
if
(
$this
->
isNew
()
&&
$this
->
_class
->
isIdentifier
(
$fieldName
))
{
$this
->
_id
[
$fieldName
]
=
$value
;
}
switch
(
$this
->
_state
)
{
case
Doctrine_Entity
::
STATE_CLEAN
:
$this
->
_state
=
Doctrine_Entity
::
STATE_DIRTY
;
break
;
case
Doctrine_Entity
::
STATE_TCLEAN
:
$this
->
_state
=
Doctrine_Entity
::
STATE_TDIRTY
;
break
;
}
}
}
else
if
(
$this
->
_class
->
hasRelation
(
$fieldName
))
{
$this
->
_rawSetReference
(
$fieldName
,
$value
);
...
...
@@ -1253,7 +1203,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
final
public
function
isNew
()
{
return
$this
->
_state
==
self
::
STATE_
TCLEAN
||
$this
->
_state
==
self
::
STATE_TDIRTY
;
return
$this
->
_state
==
self
::
STATE_
NEW
;
}
/**
...
...
@@ -1264,8 +1214,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
*/
final
public
function
isModified
()
{
return
(
$this
->
_state
===
Doctrine_Entity
::
STATE_DIRTY
||
$this
->
_state
===
Doctrine_Entity
::
STATE_TDIRTY
);
return
count
(
$this
->
_modified
)
>
0
;
}
/**
...
...
@@ -1349,44 +1298,33 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
/**
* INTERNAL:
* Assigns an identifier to the entity. This is only intended for use by
* the EntityPersisters or the UnitOfWork.
*
* @param integer $id
* @return void
* @todo Not sure this is the right place here.
* @param mixed $id
*/
final
public
function
assignIdentifier
(
$id
=
false
)
final
public
function
_assignIdentifier
(
$id
)
{
if
(
$id
===
false
)
{
$this
->
_id
=
array
();
$this
->
_state
=
Doctrine_Entity
::
STATE_TCLEAN
;
$this
->
_modified
=
array
();
}
else
if
(
$id
===
true
)
{
$this
->
_extractIdentifier
(
true
);
$this
->
_state
=
Doctrine_Entity
::
STATE_CLEAN
;
$this
->
_modified
=
array
();
}
else
{
if
(
is_array
(
$id
))
{
foreach
(
$id
as
$fieldName
=>
$value
)
{
$this
->
_id
[
$fieldName
]
=
$value
;
$this
->
_data
[
$fieldName
]
=
$value
;
}
}
else
{
$idFieldNames
=
$this
->
_class
->
getIdentifier
();
$name
=
$idFieldNames
[
0
];
$this
->
_id
[
$name
]
=
$id
;
$this
->
_data
[
$name
]
=
$id
;
if
(
is_array
(
$id
))
{
foreach
(
$id
as
$fieldName
=>
$value
)
{
$this
->
_id
[
$fieldName
]
=
$value
;
$this
->
_data
[
$fieldName
]
=
$value
;
}
$this
->
_state
=
self
::
STATE_CLEAN
;
$this
->
_modified
=
array
();
}
else
{
$name
=
$this
->
_class
->
getSingleIdentifierFieldName
();
$this
->
_id
[
$name
]
=
$id
;
$this
->
_data
[
$name
]
=
$id
;
}
$this
->
_modified
=
array
();
}
/**
* returns the primary keys of this object
* INTERNAL:
* Returns the primary keys of the entity (key => value pairs).
*
* @return array
*/
final
public
function
identifier
()
final
public
function
_
identifier
()
{
return
$this
->
_id
;
}
...
...
@@ -1656,13 +1594,13 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Serializable
{
$this->getNode()->delete();
}*/
/**
* Gets the ClassMetadata object that describes the entity class.
*
* @return Doctrine::ORM::Mapping::ClassMetadata
*/
final
public
function
getClass
Metadata
()
final
public
function
getClass
()
{
return
$this
->
_class
;
}
...
...
lib/Doctrine/EntityManager.php
View file @
d9975c36
...
...
@@ -42,6 +42,10 @@
*/
class
Doctrine_EntityManager
{
const
FLUSHMODE_AUTO
=
'auto'
;
const
FLUSHMODE_COMMIT
=
'commit'
;
const
FLUSHMODE_MANUAL
=
'manual'
;
/**
* The unique name of the EntityManager. The name is used to bind entity classes
* to certain EntityManagers.
...
...
@@ -70,11 +74,11 @@ class Doctrine_EntityManager
private
static
$_flushModes
=
array
(
// auto: Flush occurs automatically after each operation that issues database
// queries. No operations are queued.
'auto'
,
self
::
FLUSHMODE_AUTO
,
// commit: Flush occurs automatically at transaction commit.
'commit'
,
self
::
FLUSHMODE_COMMIT
,
// manual: Flush occurs never automatically.
'manual'
self
::
FLUSHMODE_MANUAL
);
/**
...
...
@@ -242,7 +246,7 @@ class Doctrine_EntityManager
*/
public
function
detach
(
Doctrine_Entity
$entity
)
{
return
$this
->
_unitOfWork
->
unregisterIdentity
(
$entity
);
return
$this
->
_unitOfWork
->
removeFromIdentityMap
(
$entity
);
}
/**
...
...
@@ -287,7 +291,7 @@ class Doctrine_EntityManager
*/
public
function
flush
()
{
$this
->
_unitOfWork
->
flush
();
$this
->
_unitOfWork
->
commit
();
}
/**
...
...
@@ -386,6 +390,9 @@ class Doctrine_EntityManager
public
function
save
(
Doctrine_Entity
$entity
)
{
$this
->
_unitOfWork
->
save
(
$entity
);
if
(
$this
->
_flushMode
==
self
::
FLUSHMODE_AUTO
)
{
$this
->
flush
();
}
}
/**
...
...
@@ -453,7 +460,7 @@ class Doctrine_EntityManager
return
$entity
;
}
else
{
$entity
=
new
$className
;
$this
->
_unitOfWork
->
registerIdentity
(
$entity
);
$this
->
_unitOfWork
->
addToIdentityMap
(
$entity
);
}
}
}
else
{
...
...
@@ -469,6 +476,17 @@ class Doctrine_EntityManager
return
$entity
;
}
/**
* Checks if the instance is managed by the EntityManager.
*
* @return boolean
*/
public
function
contains
(
Doctrine_Entity
$entity
)
{
return
$this
->
_unitOfWork
->
isInIdentityMap
(
$entity
)
&&
!
$this
->
_unitOfWork
->
isRegisteredRemoved
(
$entity
);
}
/**
* INTERNAL:
* For internal hydration purposes only.
...
...
@@ -546,7 +564,7 @@ class Doctrine_EntityManager
}
/**
* Gets the C
O
nfiguration used by the EntityManager.
* Gets the C
o
nfiguration used by the EntityManager.
*
* @return Configuration
*/
...
...
@@ -555,6 +573,16 @@ class Doctrine_EntityManager
return
$this
->
_config
;
}
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*
* @return Doctrine::ORM::UnitOfWork
*/
public
function
getUnitOfWork
()
{
return
$this
->
_unitOfWork
;
}
}
?>
\ No newline at end of file
lib/Doctrine/Event
Listen
er.php
→
lib/Doctrine/Event
Subscrib
er.php
View file @
d9975c36
<?php
/*
* $Id$
* $Id
: EventListener.php 4653 2008-07-10 17:17:58Z romanb
$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
...
...
@@ -20,18 +20,13 @@
*/
/**
* Doctrine_EventListener all event listeners extend this base class
* the empty methods allow child classes to only implement the methods they need to implement
*
* EventSubscriber.
*
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @package Doctrine
* @subpackage EventListener
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 2.0
* @version $Revision$
* @todo Remove. The 2.0 event system has no listener interfaces.
* @version $Revision: 4653 $
*/
interface
Doctrine_EventSubscriber
{
...
...
lib/Doctrine/Exception.php
View file @
d9975c36
...
...
@@ -31,7 +31,8 @@
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename to DoctrineException
*/
class
Doctrine_Exception
extends
Exception
{
...
...
lib/Doctrine/Hydrator/RecordDriver.php
View file @
d9975c36
...
...
@@ -72,7 +72,7 @@ class Doctrine_Hydrator_RecordDriver
public
function
initRelatedCollection
(
Doctrine_Entity
$entity
,
$name
)
{
if
(
!
isset
(
$this
->
_initializedRelations
[
$entity
->
getOid
()][
$name
]))
{
$relation
=
$entity
->
getClass
Metadata
()
->
getRelation
(
$name
);
$relation
=
$entity
->
getClass
()
->
getRelation
(
$name
);
$relatedClass
=
$relation
->
getTable
();
$coll
=
$this
->
getElementCollection
(
$relatedClass
->
getClassName
());
$coll
->
setReference
(
$entity
,
$relation
);
...
...
lib/Doctrine/HydratorNew.php
View file @
d9975c36
...
...
@@ -519,16 +519,12 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
* $table->prepareValue($field, $value); // Doctrine_Null
* </code>
*
* @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or
* @throws Doctrine_Table_Exception if uncompression of gzip typed column fails *
* @param string $field the name of the field
* @param string $value field value
* @param string $typeHint A hint on the type of the value. If provided, the type lookup
* for the field can be skipped. Used i.e. during hydration to
* improve performance on large and/or complex results.
* @return mixed prepared value
* @todo To EntityManager. Make private and use in createEntity().
* .. Or, maybe better: Move to hydrator for performance reasons.
*/
public
function
prepareValue
(
Doctrine_ClassMetadata
$class
,
$fieldName
,
$value
,
$typeHint
=
null
)
{
...
...
@@ -540,15 +536,17 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$type
=
is_null
(
$typeHint
)
?
$class
->
getTypeOf
(
$fieldName
)
:
$typeHint
;
switch
(
$type
)
{
case
'integer'
:
case
'string'
;
// don't do any casting here PHP INT_MAX is smaller than what the databases support
break
;
case
'string'
:
case
'enum'
:
return
$class
->
enumValue
(
$fieldName
,
$value
);
break
;
case
'boolean'
:
return
(
boolean
)
$value
;
// don't do any conversions on primitive types
break
;
//case 'enum':
// return $class->enumValue($fieldName, $value);
//break;
//case 'boolean':
// return (boolean) $value;
//break;
case
'array'
:
case
'object'
:
if
(
is_string
(
$value
))
{
...
...
lib/Doctrine/Internal/CommitOrderCalculator.php
0 → 100644
View file @
d9975c36
<?php
#namespace Doctrine::ORM::Internal;
/**
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
* correct order in which changes to entities need to be persisted.
*
* @since 2.0
* @todo Rename to: CommitOrderCalculator
* @author Roman Borschel <roman@code-factory.org>
*/
class
Doctrine_Internal_CommitOrderCalculator
{
private
$_currentTime
;
/**
* The node list used for sorting.
*
* @var array
*/
private
$_nodes
=
array
();
/**
* The topologically sorted list of items. Note that these are not nodes
* but the wrapped items.
*
* @var array
*/
private
$_sorted
;
/**
* Orders the given list of CommitOrderNodes based on their dependencies.
*
* Uses a depth-first search (DFS) to traverse the graph.
* The desired topological sorting is the reverse postorder of these searches.
*
* @param array $nodes The list of (unordered) CommitOrderNodes.
* @return array The list of ordered items. These are the items wrapped in the nodes.
*/
public
function
getCommitOrder
()
{
// Check whether we need to do anything. 0 or 1 node is easy.
$nodeCount
=
count
(
$this
->
_nodes
);
if
(
$nodeCount
==
0
)
{
return
array
();
}
else
if
(
$nodeCount
==
1
)
{
$node
=
array_pop
(
$this
->
_nodes
);
return
array
(
$node
->
getClass
());
}
$this
->
_sorted
=
array
();
// Init
foreach
(
$this
->
_nodes
as
$node
)
{
$node
->
markNotVisited
();
$node
->
setPredecessor
(
null
);
}
$this
->
_currentTime
=
0
;
// Go
foreach
(
$this
->
_nodes
as
$node
)
{
if
(
$node
->
isNotVisited
())
{
$node
->
visit
();
}
}
return
$this
->
_sorted
;
}
public
function
addNode
(
$key
,
$node
)
{
$this
->
_nodes
[
$key
]
=
$node
;
}
public
function
addNodeWithItem
(
$key
,
$item
)
{
$this
->
_nodes
[
$key
]
=
new
Doctrine_Internal_CommitOrderNode
(
$item
,
$this
);
}
public
function
getNodeForKey
(
$key
)
{
return
$this
->
_nodes
[
$key
];
}
public
function
hasNodeWithKey
(
$key
)
{
return
isset
(
$this
->
_nodes
[
$key
]);
}
public
function
clear
()
{
$this
->
_nodes
=
array
();
$this
->
_sorted
=
array
();
}
public
function
getNextTime
()
{
return
++
$this
->
_currentTime
;
}
public
function
prependNode
(
$node
)
{
array_unshift
(
$this
->
_sorted
,
$node
->
getClass
());
}
}
?>
\ No newline at end of file
lib/Doctrine/Internal/CommitOrderNode.php
0 → 100644
View file @
d9975c36
<?php
#namespace Doctrine::ORM::Internal;
#use Doctrine::ORM::Mapping::ClassMetadata;
/**
* A CommitOrderNode is a temporary wrapper around ClassMetadata objects
* that is used to sort the order of commits.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class
Doctrine_Internal_CommitOrderNode
{
const
NOT_VISITED
=
1
;
const
IN_PROGRESS
=
2
;
const
VISITED
=
3
;
private
$_traversalState
;
private
$_predecessor
;
private
$_status
;
private
$_calculator
;
private
$_relatedNodes
=
array
();
private
$_discoveryTime
;
private
$_finishingTime
;
private
$_wrappedObj
;
private
$_relationEdges
=
array
();
public
function
__construct
(
$wrappedObj
,
Doctrine_Internal_CommitOrderCalculator
$calc
)
{
$this
->
_wrappedObj
=
$wrappedObj
;
$this
->
_calculator
=
$calc
;
}
public
function
getClass
()
{
return
$this
->
_wrappedObj
;
}
public
function
setPredecessor
(
$node
)
{
$this
->
_predecessor
=
$node
;
}
public
function
getPredecessor
()
{
return
$this
->
_predecessor
;
}
public
function
markNotVisited
()
{
$this
->
_traversalState
=
self
::
NOT_VISITED
;
}
public
function
markInProgress
()
{
$this
->
_traversalState
=
self
::
IN_PROGRESS
;
}
public
function
markVisited
()
{
$this
->
_traversalState
=
self
::
VISITED
;
}
public
function
isNotVisited
()
{
return
$this
->
_traversalState
==
self
::
NOT_VISITED
;
}
public
function
isInProgress
()
{
return
$this
->
_traversalState
==
self
::
IN_PROGRESS
;
}
public
function
visit
()
{
$this
->
markInProgress
();
$this
->
setDiscoveryTime
(
$this
->
_calculator
->
getNextTime
());
foreach
(
$this
->
getRelatedNodes
()
as
$node
)
{
if
(
$node
->
isNotVisited
())
{
$node
->
setPredecessor
(
$this
);
$node
->
visit
();
}
if
(
$node
->
isInProgress
())
{
// back edge => cycle
//TODO: anything to do here?
}
}
$this
->
markVisited
();
$this
->
_calculator
->
prependNode
(
$this
);
$this
->
setFinishingTime
(
$this
->
_calculator
->
getNextTime
());
}
public
function
setDiscoveryTime
(
$time
)
{
$this
->
_discoveryTime
=
$time
;
}
public
function
setFinishingTime
(
$time
)
{
$this
->
_finishingTime
=
$time
;
}
public
function
getDiscoveryTime
()
{
return
$this
->
_discoveryTime
;
}
public
function
getFinishingTime
()
{
return
$this
->
_finishingTime
;
}
public
function
getRelatedNodes
()
{
return
$this
->
_relatedNodes
;
}
/**
* Adds a directed dependency (an edge). "$this -before-> $other".
*
* @param Doctrine_Internal_CommitOrderNode $node
*/
public
function
before
(
Doctrine_Internal_CommitOrderNode
$node
)
{
$this
->
_relatedNodes
[]
=
$node
;
}
}
?>
\ No newline at end of file
lib/Doctrine/MappingException.php
View file @
d9975c36
...
...
@@ -12,6 +12,22 @@ class Doctrine_MappingException extends Doctrine_Exception
return
new
self
(
"No identifier specified for Entity '
$entityName
'."
.
" Every Entity must have an identifier."
);
}
public
static
function
invalidInheritanceType
(
$type
)
{
return
new
self
(
"The inheritance type '
$type
' does not exist."
);
}
public
static
function
invalidInheritanceOption
(
$name
)
{
return
new
self
(
"The inheritance option '
$name
' does not exist."
);
}
public
static
function
generatorNotAllowedWithCompositeId
()
{
return
new
self
(
"Id generators can't be used with a composite id."
);
}
}
?>
\ No newline at end of file
lib/Doctrine/Overloadable.php
View file @
d9975c36
...
...
@@ -30,7 +30,7 @@
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Really needed?
* @todo Really needed?
Remove.
*/
interface
Doctrine_Overloadable
{
/**
...
...
lib/Doctrine/Tree.php
View file @
d9975c36
...
...
@@ -28,6 +28,7 @@
* @since 1.0
* @version $Revision$
* @author Joe Simms <joe.simms@websites4.com>
* @todo Move to NestedSet behavior.
*/
class
Doctrine_Tree
{
...
...
lib/Doctrine/Validator.php
View file @
d9975c36
...
...
@@ -33,6 +33,7 @@
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo Move to validator package.
*/
class
Doctrine_Validator
{
...
...
tests/Orm/Associations/CascadeTest.php
0 → 100644
View file @
d9975c36
<?php
require_once
'lib/DoctrineTestInit.php'
;
class
Orm_Associations_CascadeTest
extends
Doctrine_OrmTestCase
{
protected
function
setUp
()
{
;
}
protected
function
tearDown
()
{
;
}
public
function
testDeleteCascade
()
{
$container
=
array
();
$cascade
=
new
DeleteCascade
();
$cascade
->
cascade
(
$entity
,
$container
);
}
}
abstract
class
Cascade
{
public
function
cascade
(
Doctrine_Entity
$record
,
array
&
$container
)
{
if
(
$this
->
shouldCascadeTo
(
$record
))
{
$container
[
$record
->
getOid
()]
=
$record
;
}
foreach
(
$record
->
getTable
()
->
getRelations
()
as
$relation
)
{
if
(
$this
->
doCascade
(
$relation
))
{
$this
->
prepareCascade
(
$record
,
$relation
);
$relatedObjects
=
$record
->
get
(
$relation
->
getAlias
());
if
(
$relatedObjects
instanceof
Doctrine_Record
&&
$this
->
shouldCascadeTo
(
$relatedObjects
)
&&
!
isset
(
$container
[
$relatedObjects
->
getOid
()]))
{
$this
->
cascade
(
$relatedObjects
,
$container
);
}
else
if
(
$relatedObjects
instanceof
Doctrine_Collection
&&
count
(
$relatedObjects
)
>
0
)
{
foreach
(
$relatedObjects
as
$object
)
{
if
(
!
isset
(
$container
[
$object
->
getOid
()]))
{
$this
->
cascade
(
$object
,
$container
);
}
}
}
}
}
}
}
class
DeleteCascade
extends
Cascade
{
public
function
doCascade
(
$relation
)
{
return
$relation
->
isCascadeDelete
();
}
public
function
prepareCascade
(
$record
,
$relation
)
{
$fieldName
=
$relation
->
getAlias
();
// if it's a xToOne relation and the related object is already loaded
// we don't need to refresh, else we need to.
if
(
!
(
$relation
->
getType
()
==
Doctrine_Relation
::
ONE
&&
isset
(
$record
->
$fieldName
)))
{
$record
->
refreshRelated
(
$relation
->
getAlias
());
}
}
public
function
shouldCascadeTo
(
Doctrine_Entity
$entity
)
{
//TODO: also ignore removed Entities. incorporate that in exists() with a new
// state? (DELETED?)
return
!
$entity
->
exists
();
}
}
\ No newline at end of file
tests/Orm/Associations/OneToOneMappingTest.php
0 → 100644
View file @
d9975c36
<?php
require_once
'lib/DoctrineTestInit.php'
;
class
Orm_Associations_OneToOneMappingTest
extends
Doctrine_OrmTestCase
{
public
function
testCorrectOneToOneBidirectionalMapping
()
{
$owningSideMapping
=
array
(
'fieldName'
=>
'address'
,
'targetEntity'
=>
'Address'
,
'joinColumns'
=>
array
(
'address_id'
=>
'id'
),
'sourceEntity'
=>
'Person'
// This is normally filled by ClassMetadata
);
$oneToOneMapping
=
new
Doctrine_Association_OneToOne
(
$owningSideMapping
);
$this
->
assertEquals
(
array
(
'address_id'
=>
'id'
),
$oneToOneMapping
->
getSourceToTargetKeyColumns
());
$this
->
assertEquals
(
array
(
'id'
=>
'address_id'
),
$oneToOneMapping
->
getTargetToSourceKeyColumns
());
$this
->
assertEquals
(
'Address'
,
$oneToOneMapping
->
getTargetEntityName
());
$this
->
assertEquals
(
'Person'
,
$oneToOneMapping
->
getSourceEntityName
());
$this
->
assertEquals
(
'address'
,
$oneToOneMapping
->
getSourceFieldName
());
$this
->
assertTrue
(
$oneToOneMapping
->
isOwningSide
());
$inverseSideMapping
=
array
(
'mappedBy'
=>
'address'
);
$oneToOneMapping
=
new
Doctrine_Association_OneToOne
(
$inverseSideMapping
);
$this
->
assertEquals
(
'address'
,
$oneToOneMapping
->
getMappedByFieldName
());
$this
->
assertTrue
(
$oneToOneMapping
->
isInverseSide
());
}
}
?>
\ No newline at end of file
tests/Orm/Component/CollectionTest.php
View file @
d9975c36
...
...
@@ -106,17 +106,17 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
/**
* @test
*/
public
function
shouldSetKeyColumnWhenAddingNewRowAsArray
()
/*
public function shouldSetKeyColumnWhenAddingNewRowAsArray()
{
$this->assertTrue(isset($this->cmsColl['test']));
$this->assertEquals($this->cmsUser, $this->cmsColl['test']);
}
}
*/
/**
* @test
*/
public
function
shouldSerializeAndUnserializeCollectionWithData
()
/*
public function shouldSerializeAndUnserializeCollectionWithData()
{
$serialized = serialize($this->cmsColl);
$coll = unserialize($serialized);
...
...
@@ -126,6 +126,6 @@ class Orm_Component_CollectionTest extends Doctrine_OrmTestCase
$user = $coll['test'];
$this->assertTrue($user instanceOf CmsUser);
$this->assertEquals('test', $user['username']);
}
}
*/
}
tests/Orm/Entity/AccessorTest.php
View file @
d9975c36
...
...
@@ -22,12 +22,21 @@ class Orm_Entity_AccessorTest extends Doctrine_OrmTestCase
class
CustomAccessorMutatorTestEntity
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$
class
)
public
static
function
initMetadata
(
$
mapping
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
));
$class
->
mapColumn
(
'username'
,
'string'
,
50
,
array
(
'accessor'
=>
'getUsernameCustom'
,
'mutator'
=>
'setUsernameCustom'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'username'
,
'type'
=>
'string'
,
'length'
=>
50
,
'accessor'
=>
'getUsernameCustom'
,
'mutator'
=>
'setUsernameCustom'
));
}
public
function
getUsernameCustom
()
...
...
@@ -43,10 +52,19 @@ class CustomAccessorMutatorTestEntity extends Doctrine_Entity
class
MagicAccessorMutatorTestEntity
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$
class
)
public
static
function
initMetadata
(
$
mapping
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
));
$class
->
mapColumn
(
'username'
,
'string'
,
50
,
array
());
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'username'
,
'type'
=>
'string'
,
'length'
=>
50
));
}
public
function
getUsername
()
...
...
tests/Orm/Entity/ConstructorTest.php
View file @
d9975c36
...
...
@@ -22,10 +22,19 @@ class ConstructorTestEntity1 extends Doctrine_Entity
}
/* The mapping definition */
public
static
function
initMetadata
(
$
class
)
public
static
function
initMetadata
(
$
mapping
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
));
$class
->
mapColumn
(
'username'
,
'string'
,
50
,
array
());
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'username'
,
'type'
=>
'string'
,
'length'
=>
50
));
}
}
...
...
tests/Orm/UnitOfWorkTest.php
View file @
d9975c36
<?php
require_once
'lib/DoctrineTestInit.php'
;
require_once
'lib/mocks/Doctrine_EntityManagerMock.php'
;
require_once
'lib/mocks/Doctrine_ConnectionMock.php'
;
/**
* UnitOfWork tests.
* These tests run without a database through mocking the
* persister/connection/sequence used by the UnitOfWork.
*/
class
Orm_UnitOfWorkTest
extends
Doctrine_OrmTestCase
{
private
$_unitOfWork
;
private
$_user
;
// Mocks
// Provides a sequence mock to the UnitOfWork
private
$_connectionMock
;
// The sequence mock
private
$_sequenceMock
;
// The persister mock used by the UnitOfWork
private
$_persisterMock
;
// The EntityManager mock that provides the mock persister
private
$_emMock
;
protected
function
setUp
()
{
parent
::
setUp
();
$this
->
_user
=
new
ForumUser
();
$this
->
_unitOfWork
=
new
Doctrine_Connection_UnitOfWork
(
$this
->
_em
);
$this
->
_user
->
id
=
1
;
$this
->
_user
->
username
=
'romanb'
;
$this
->
_connectionMock
=
new
Doctrine_ConnectionMock
(
array
());
$this
->
_sequenceMock
=
$this
->
_connectionMock
->
getSequenceModule
();
$this
->
_emMock
=
new
Doctrine_EntityManagerMock
(
$this
->
_connectionMock
);
$this
->
_persisterMock
=
$this
->
_emMock
->
getEntityPersister
(
"ForumUser"
);
$this
->
_unitOfWork
=
$this
->
_emMock
->
getUnitOfWork
();
}
protected
function
tearDown
()
{
$this
->
_user
->
free
();
}
/* Basic registration tests */
public
function
testRegisterNew
()
{
$this
->
_user
->
username
=
'romanb'
;
$this
->
_user
->
id
=
1
;
// registerNew() is normally called in save()/persist()
$this
->
_unitOfWork
->
registerNew
(
$this
->
_user
);
$this
->
assertTrue
(
$this
->
_unitOfWork
->
isRegisteredNew
(
$this
->
_user
));
$this
->
assertTrue
(
$this
->
_unitOfWork
->
contains
(
$this
->
_user
));
$this
->
assertTrue
(
$this
->
_unitOfWork
->
isInIdentityMap
(
$this
->
_user
));
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredDirty
(
$this
->
_user
));
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredRemoved
(
$this
->
_user
));
}
/*public function testRegisterNewPerf() {
$s = microtime(true);
for ($i=1; $i<40000; $i++) {
$user = new ForumUser();
$user->id = $i;
$this->_unitOfWork->registerNew($user);
}
$e = microtime(true);
echo $e - $s . " seconds" . PHP_EOL;
}*/
public
function
testRegisterDirty
()
{
$this
->
_user
->
username
=
'romanb'
;
$this
->
_user
->
id
=
1
;
$this
->
assertEquals
(
Doctrine_Entity
::
STATE_TDIRTY
,
$this
->
_user
->
_state
());
$this
->
assertFalse
(
$this
->
_unitOfWork
->
contains
(
$this
->
_user
));
$this
->
assertEquals
(
Doctrine_Entity
::
STATE_NEW
,
$this
->
_user
->
_state
());
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isInIdentityMap
(
$this
->
_user
));
$this
->
_unitOfWork
->
registerDirty
(
$this
->
_user
);
$this
->
assertTrue
(
$this
->
_unitOfWork
->
isRegisteredDirty
(
$this
->
_user
));
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredNew
(
$this
->
_user
));
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredRemoved
(
$this
->
_user
));
}
public
function
testRegisterRemovedOn
Transient
EntityIsIgnored
()
public
function
testRegisterRemovedOn
New
EntityIsIgnored
()
{
$this
->
_user
->
username
=
'romanb'
;
$this
->
_user
->
id
=
1
;
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredRemoved
(
$this
->
_user
));
$this
->
_unitOfWork
->
register
Remov
ed
(
$this
->
_user
);
$this
->
_unitOfWork
->
register
Delet
ed
(
$this
->
_user
);
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredRemoved
(
$this
->
_user
));
}
/*public function testSavedEntityHasIdentityAndIsManaged()
/* Operational tests */
public
function
testSavingSingleEntityWithIdentityColumnForcesInsert
()
{
$this->_user->username = 'romanb';
$this->_user->save();
$this->assertTrue($this->_unitOfWork->hasIdentity($this->_user));
$this->assertTrue($this->_unitOfWork->isManaged($this->_user));
}*/
$this
->
assertEquals
(
Doctrine_Entity
::
STATE_NEW
,
$this
->
_user
->
_state
());
$this
->
_unitOfWork
->
save
(
$this
->
_user
);
$this
->
assertEquals
(
1
,
count
(
$this
->
_persisterMock
->
getInserts
()));
// insert forced
$this
->
assertEquals
(
0
,
count
(
$this
->
_persisterMock
->
getUpdates
()));
$this
->
assertEquals
(
0
,
count
(
$this
->
_persisterMock
->
getDeletes
()));
$this
->
assertTrue
(
$this
->
_unitOfWork
->
isInIdentityMap
(
$this
->
_user
));
$this
->
assertEquals
(
Doctrine_Entity
::
STATE_MANAGED
,
$this
->
_user
->
_state
());
// should no longer be scheduled for insert
$this
->
assertFalse
(
$this
->
_unitOfWork
->
isRegisteredNew
(
$this
->
_user
));
// should have an id
$this
->
assertTrue
(
is_numeric
(
$this
->
_user
->
id
));
// Now lets check whether a subsequence commit() does anything
$this
->
_persisterMock
->
reset
();
$this
->
_unitOfWork
->
commit
();
// shouldnt do anything
// verify that nothing happened
$this
->
assertEquals
(
0
,
count
(
$this
->
_persisterMock
->
getInserts
()));
$this
->
assertEquals
(
0
,
count
(
$this
->
_persisterMock
->
getUpdates
()));
$this
->
assertEquals
(
0
,
count
(
$this
->
_persisterMock
->
getDeletes
()));
}
public
function
testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert
()
{
//...
}
public
function
testSavingSingleEntityWithTableIdGeneratorSchedulesInsert
()
{
//...
}
public
function
testSavingSingleEntityWithSingleNaturalIdForcesInsert
()
{
//...
}
public
function
testSavingSingleEntityWithCompositeIdForcesInsert
()
{
//...
}
public
function
testSavingEntityGraphWithIdentityColumnsForcesInserts
()
{
//...
}
public
function
testSavingEntityGraphWithSequencesDelaysInserts
()
{
//...
}
public
function
testSavingEntityGraphWithNaturalIdsForcesInserts
()
{
//...
}
public
function
testSavingEntityGraphWithMixedIdGenerationStrategies
()
{
//...
}
}
\ No newline at end of file
tests/lib/mocks/Doctrine_ConnectionMock.php
0 → 100644
View file @
d9975c36
<?php
require_once
'lib/mocks/Doctrine_SequenceMock.php'
;
class
Doctrine_ConnectionMock
extends
Doctrine_Connection
{
protected
$_driverName
=
'Mysql'
;
private
$_sequenceModuleMock
;
public
function
getSequenceModule
()
{
if
(
!
$this
->
_sequenceModuleMock
)
{
$this
->
_sequenceModuleMock
=
new
Doctrine_SequenceMock
(
$this
);
}
return
$this
->
_sequenceModuleMock
;
}
}
?>
\ No newline at end of file
tests/lib/mocks/Doctrine_EntityManagerMock.php
0 → 100644
View file @
d9975c36
<?php
require_once
'lib/mocks/Doctrine_EntityPersisterMock.php'
;
class
Doctrine_EntityManagerMock
extends
Doctrine_EntityManager
{
private
$_persisterMock
;
/**
* Enter description here...
*
* @param unknown_type $entityName
* @override
*/
public
function
getEntityPersister
(
$entityName
)
{
if
(
!
$this
->
_persisterMock
)
{
$this
->
_persisterMock
=
new
Doctrine_EntityPersisterMock
(
$this
,
$this
->
getClassMetadata
(
$entityName
));
}
return
$this
->
_persisterMock
;
}
}
?>
\ No newline at end of file
tests/lib/mocks/Doctrine_EntityPersisterMock.php
0 → 100644
View file @
d9975c36
<?php
class
Doctrine_EntityPersisterMock
extends
Doctrine_EntityPersister_Standard
{
private
$_inserts
=
array
();
private
$_updates
=
array
();
private
$_deletes
=
array
();
private
$_identityColumnValueCounter
=
0
;
public
function
insert
(
$entity
)
{
if
(
$entity
->
getClass
()
->
isIdGeneratorIdentity
())
{
$entity
->
_assignIdentifier
(
$this
->
_identityColumnValueCounter
++
);
$this
->
_em
->
getUnitOfWork
()
->
addToIdentityMap
(
$entity
);
}
$this
->
_inserts
[]
=
$entity
;
}
public
function
update
(
$entity
)
{
$this
->
_updates
[]
=
$entity
;
}
public
function
delete
(
$entity
)
{
$this
->
_deletes
[]
=
$entity
;
}
public
function
getInserts
()
{
return
$this
->
_inserts
;
}
public
function
getUpdates
()
{
return
$this
->
_updates
;
}
public
function
getDeletes
()
{
return
$this
->
_deletes
;
}
public
function
reset
()
{
$this
->
_identityColumnValueCounter
=
0
;
$this
->
_inserts
=
array
();
$this
->
_updates
=
array
();
$this
->
_deletes
=
array
();
}
}
?>
\ No newline at end of file
tests/lib/mocks/Doctrine_SequenceMock.php
0 → 100644
View file @
d9975c36
<?php
class
Doctrine_SequenceMock
extends
Doctrine_Sequence
{
private
$_sequenceNumber
=
0
;
/**
* @override
*/
public
function
nextId
(
$seqName
,
$ondemand
=
true
)
{
return
$this
->
_sequenceNumber
++
;
}
/**
* @override
*/
public
function
lastInsertId
(
$table
=
null
,
$field
=
null
)
{
return
$this
->
_sequenceNumber
-
1
;
}
/**
* @override
*/
public
function
currId
(
$seqName
)
{
return
$this
->
_sequenceNumber
;
}
public
function
reset
()
{
$this
->
_sequenceNumber
=
0
;
}
}
?>
\ No newline at end of file
tests/models/cms/CmsArticle.php
View file @
d9975c36
<?php
#namespace Doctrine::Tests::ORM::Models::CMS;
#use Doctrine::ORM::Entity;
class
CmsArticle
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
=>
true
,
'autoincrement'
=>
true
));
$class
->
mapColumn
(
'topic'
,
'string'
,
255
);
$class
->
mapColumn
(
'text'
,
'string'
);
$class
->
mapColumn
(
'user_id'
,
'integer'
,
4
);
$class
->
hasMany
(
'CmsComment as comments'
,
array
(
'local'
=>
'id'
,
'foreign'
=>
'article_id'
));
}
#protected $id;
#protected $topic;
#protected $text;
#protected $user_id;
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
,
'generatorType'
=>
'auto'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'topic'
,
'type'
=>
'string'
,
'length'
=>
255
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'text'
,
'type'
=>
'string'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'user_id'
,
'type'
=>
'integer'
,
'length'
=>
4
));
$mapping
->
hasMany
(
'CmsComment as comments'
,
array
(
'local'
=>
'id'
,
'foreign'
=>
'article_id'
));
}
}
tests/models/cms/CmsComment.php
View file @
d9975c36
<?php
#namespace Doctrine::Tests::ORM::Models::CMS;
#use Doctrine::ORM::Entity;
class
CmsComment
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
=>
true
,
'autoincrement'
=>
true
));
$class
->
mapColumn
(
'topic'
,
'string'
,
255
);
$class
->
mapColumn
(
'text'
,
'string'
);
$class
->
mapColumn
(
'article_id'
,
'integer'
,
4
);
}
#protected $id;
#protected $topic;
#protected $text;
#protected $article_id;
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
,
'generatorType'
=>
'auto'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'topic'
,
'type'
=>
'string'
,
'length'
=>
255
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'text'
,
'type'
=>
'string'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'article_id'
,
'type'
=>
'integer'
,
'length'
=>
4
));
}
}
tests/models/cms/CmsPhonenumber.php
View file @
d9975c36
<?php
class
CmsPhonenumber
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
{
$class
->
mapColumn
(
'user_id'
,
'integer'
,
4
);
$class
->
mapColumn
(
'phonenumber'
,
'string'
,
50
,
array
(
'primary'
=>
true
));
}
#protected $user_id;
#protected $phonenumber;
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'user_id'
,
'type'
=>
'integer'
,
'length'
=>
4
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'phonenumber'
,
'type'
=>
'string'
,
'length'
=>
50
,
'id'
=>
true
));
}
}
tests/models/cms/CmsUser.php
View file @
d9975c36
<?php
#namespace Doctrine::Test::ORM::Models;
#use Doctrine::ORM::Entity;
class
CmsUser
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
=>
true
,
'autoincrement'
=>
true
));
$class
->
mapColumn
(
'status'
,
'string'
,
50
);
$class
->
mapColumn
(
'username'
,
'string'
,
255
);
$class
->
mapColumn
(
'name'
,
'string'
,
255
);
$class
->
hasMany
(
'CmsPhonenumber as phonenumbers'
,
array
(
#protected $id;
#protected $status;
#protected $username;
#protected $name;
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
,
'generatorType'
=>
'auto'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'status'
,
'type'
=>
'string'
,
'length'
=>
50
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'username'
,
'type'
=>
'string'
,
'length'
=>
255
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'name'
,
'type'
=>
'string'
,
'length'
=>
255
));
$mapping
->
hasMany
(
'CmsPhonenumber as phonenumbers'
,
array
(
'local'
=>
'id'
,
'foreign'
=>
'user_id'
));
$class
->
hasMany
(
'CmsArticle as articles'
,
array
(
$mapping
->
hasMany
(
'CmsArticle as articles'
,
array
(
'local'
=>
'id'
,
'foreign'
=>
'user_id'
));
}
}
}
tests/models/forum/ForumAdministrator.php
View file @
d9975c36
...
...
@@ -2,9 +2,14 @@
class
ForumAdministrator
extends
ForumUser
{
public
static
function
initMetadata
(
$
class
)
public
static
function
initMetadata
(
$
mapping
)
{
$class
->
mapColumn
(
'access_level as accessLevel'
,
'integer'
,
1
);
$mapping
->
mapField
(
array
(
'fieldName'
=>
'accessLevel'
,
'columnName'
=>
'access_level'
,
'type'
=>
'integer'
,
'length'
=>
1
));
}
public
function
banUser
(
ForumUser
$user
)
{}
...
...
tests/models/forum/ForumBoard.php
View file @
d9975c36
<?php
class
ForumBoard
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$metadata
)
{
class
ForumBoard
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$mapping
)
{
/*$metadata->mapField(array(
'fieldName' => 'id',
'id' => true,
...
...
@@ -8,10 +10,22 @@ class ForumBoard extends Doctrine_Entity {
'length' => 4
));
*/
$metadata
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
));
$metadata
->
mapColumn
(
'position'
,
'integer'
);
$metadata
->
mapColumn
(
'category_id'
,
'integer'
);
$metadata
->
hasOne
(
'ForumCategory as category'
,
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'position'
,
'type'
=>
'integer'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'category_id'
,
'type'
=>
'integer'
));
$mapping
->
hasOne
(
'ForumCategory as category'
,
array
(
'local'
=>
'category_id'
,
'foreign'
=>
'id'
));
/*
$metadata->mapOneToOne(array(
...
...
tests/models/forum/ForumCategory.php
View file @
d9975c36
<?php
class
ForumCategory
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
{
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
));
$class
->
mapColumn
(
'position'
,
'integer'
);
$class
->
mapColumn
(
'name'
,
'string'
,
255
);
$class
->
hasMany
(
'ForumBoard as boards'
,
array
(
class
ForumCategory
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'position'
,
'type'
=>
'integer'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'name'
,
'type'
=>
'string'
,
'length'
=>
255
));
$mapping
->
hasMany
(
'ForumBoard as boards'
,
array
(
'local'
=>
'id'
,
'foreign'
=>
'category_id'
));
}
}
tests/models/forum/ForumEntry.php
0 → 100644
View file @
d9975c36
<?php
#namespace Doctrine::Test::ORM::Models;
#use Doctrine::ORM::Entity;
class
ForumEntry
extends
Doctrine_Entity
{
#protected $id;
#protected $topic;
public
static
function
initMetadata
(
$mapping
)
{
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
,
'generatorType'
=>
'auto'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'topic'
,
'type'
=>
'string'
,
'length'
=>
50
));
}
}
?>
\ No newline at end of file
tests/models/forum/ForumUser.php
View file @
d9975c36
<?php
#namespace Doctrine::Tests::ORM::Models::Forum;
#use Doctrine::ORM::Entity;
class
ForumUser
extends
Doctrine_Entity
{
public
static
function
initMetadata
(
$class
)
#protected $dtype;
#protected $id;
#protected $username;
public
static
function
initMetadata
(
$mapping
)
{
// inheritance mapping
$
class
->
setInheritanceType
(
Doctrine
::
INHERITANCE_TYPE_JOINED
,
array
(
$
mapping
->
setInheritanceType
(
'joined'
,
array
(
'discriminatorColumn'
=>
'dtype'
,
'discriminatorMap'
=>
array
(
'user'
=>
'ForumUser'
,
'admin'
=>
'ForumAdministrator'
)
));
// register subclasses
$
class
->
setSubclasses
(
array
(
'ForumAdministrator'
));
$
mapping
->
setSubclasses
(
array
(
'ForumAdministrator'
));
// the discriminator column
$class
->
mapColumn
(
'dtype'
,
'string'
,
50
);
$mapping
->
mapField
(
array
(
'fieldName'
=>
'dtype'
,
'type'
=>
'string'
,
'length'
=>
50
));
// column-to-field mapping
$class
->
mapColumn
(
'id'
,
'integer'
,
4
,
array
(
'primary'
=>
true
,
'autoincrement'
=>
true
));
$class
->
mapColumn
(
'username'
,
'string'
,
50
,
array
());
$mapping
->
mapField
(
array
(
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'length'
=>
4
,
'id'
=>
true
,
'generatorType'
=>
'auto'
));
$mapping
->
mapField
(
array
(
'fieldName'
=>
'username'
,
'type'
=>
'string'
,
'length'
=>
50
));
}
...
...
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