From 0f8e9e0ba9033b3adc9cef7252c1f801a76bcd05 Mon Sep 17 00:00:00 2001 From: romanb Date: Fri, 22 Aug 2008 09:05:14 +0000 Subject: [PATCH] refactorings. made basic one-one, one-many joins work. --- lib/Doctrine/Association.php | 1 + lib/Doctrine/Association/OneToMany.php | 74 +++++- lib/Doctrine/Cache/Apc.php | 4 +- lib/Doctrine/Cache/Memcache.php | 12 +- lib/Doctrine/Cache/Xcache.php | 2 +- lib/Doctrine/ClassMetadata.php | 234 ++++++++---------- lib/Doctrine/ClassMetadata/Factory.php | 21 +- lib/Doctrine/Collection.php | 30 ++- lib/Doctrine/Connection.php | 2 +- lib/Doctrine/Connection/UnitOfWork.php | 4 +- lib/Doctrine/DataDict/Informix.php | 3 +- .../DatabasePlatform/MySqlPlatform.php | 20 ++ lib/Doctrine/Entity.php | 231 +++++------------ lib/Doctrine/EntityManager.php | 7 +- lib/Doctrine/EntityPersister/Abstract.php | 146 +++++++---- .../EntityPersister/JoinedSubclass.php | 94 ++++--- lib/Doctrine/Hook/Equal.php | 6 +- lib/Doctrine/Hook/Integer.php | 6 +- lib/Doctrine/Hook/Parser.php | 4 +- lib/Doctrine/Hook/Parser/Complex.php | 6 +- lib/Doctrine/Hook/WordLike.php | 6 +- lib/Doctrine/Query/Production/Join.php | 62 +++-- .../Production/JoinVariableDeclaration.php | 2 +- lib/Doctrine/RawSql/Exception.php | 7 +- lib/Doctrine/Record/Exception.php | 5 +- lib/Doctrine/Record/Filter.php | 3 +- lib/Doctrine/Record/Filter/Compound.php | 4 +- lib/Doctrine/Record/Filter/Standard.php | 3 +- lib/Doctrine/Record/Generator.php | 3 +- lib/Doctrine/Record/Iterator.php | 3 +- lib/Doctrine/Record/Listener.php | 3 +- lib/Doctrine/Record/Listener/Chain.php | 5 +- lib/Doctrine/Record/Listener/Interface.php | 3 +- lib/Doctrine/Record/State/Exception.php | 5 +- lib/Doctrine/Relation.php | 3 +- lib/Doctrine/Relation/Association.php | 5 +- lib/Doctrine/Relation/Association/Self.php | 3 +- lib/Doctrine/Relation/ForeignKey.php | 3 +- lib/Doctrine/Relation/LocalKey.php | 3 +- lib/Doctrine/Relation/Nest.php | 5 +- lib/Doctrine/Relation/Parser.php | 3 +- lib/Doctrine/Relation/Parser/Exception.php | 5 +- lib/Doctrine/Sequence/Mysql.php | 2 +- lib/Doctrine/Sequence/Sqlite.php | 2 +- tests/Orm/EntityPersisterTest.php | 34 ++- tests/Orm/Query/SelectSqlGenerationTest.php | 2 +- tests/Orm/UnitOfWorkTest.php | 17 ++ tests/lib/mocks/Doctrine_SequenceMock.php | 5 + tests/models/cms/CmsArticle.php | 9 +- tests/models/cms/CmsPhonenumber.php | 6 + tests/models/cms/CmsUser.php | 8 +- tests/models/forum/ForumUser.php | 7 +- 52 files changed, 617 insertions(+), 526 deletions(-) diff --git a/lib/Doctrine/Association.php b/lib/Doctrine/Association.php index bbc2aad58..4666938a0 100644 --- a/lib/Doctrine/Association.php +++ b/lib/Doctrine/Association.php @@ -159,6 +159,7 @@ class Doctrine_Association implements Serializable $this->_customAccessor = isset($mapping['accessor']) ? $mapping['accessor'] : null; $this->_customMutator = isset($mapping['mutator']) ? $mapping['mutator'] : null; $this->_isOptional = isset($mapping['isOptional']) ? (bool)$mapping['isOptional'] : true; + $this->_cascades = isset($mapping['cascade']) ? (array)$mapping['cascade'] : array(); return $mapping; } diff --git a/lib/Doctrine/Association/OneToMany.php b/lib/Doctrine/Association/OneToMany.php index 8a76636bc..f5f1b5248 100644 --- a/lib/Doctrine/Association/OneToMany.php +++ b/lib/Doctrine/Association/OneToMany.php @@ -1,28 +1,94 @@ . + */ #namespace Doctrine::ORM::Mappings; +/** + * Represents a one-to-many mapping. + * + * NOTE: One-to-many mappings can currently not be uni-directional (one -> many). + * They must either be bidirectional (one <-> many) or unidirectional (many -> one). + * In other words, the many-side MUST be the owning side and the one-side MUST be + * the inverse side. + * + * @author Roman Borschel + * @since 2.0 + * @todo Rename to OneToManyMapping + */ class Doctrine_Association_OneToMany extends Doctrine_Association { /** The target foreign key columns that reference the sourceKeyColumns. */ - protected $_targetForeignKeyColumns; + /* NOTE: Currently not used because uni-directional one-many not supported atm. */ + //protected $_targetForeignKeyColumns; /** The (typically primary) source key columns that are referenced by the targetForeignKeyColumns. */ - protected $_sourceKeyColumns; + /* NOTE: Currently not used because uni-directional one-many not supported atm. */ + //protected $_sourceKeyColumns; /** This maps the target foreign key columns to the corresponding (primary) source key columns. */ - protected $_targetForeignKeysToSourceKeys; + /* NOTE: Currently not used because uni-directional one-many not supported atm. */ + //protected $_targetForeignKeysToSourceKeys; /** This maps the (primary) source key columns to the corresponding target foreign key columns. */ - protected $_sourceKeysToTargetForeignKeys; + /* NOTE: Currently not used because uni-directional one-many not supported atm. */ + //protected $_sourceKeysToTargetForeignKeys; /** Whether to delete orphaned elements (removed from the collection) */ protected $_deleteOrphans = false; + /** + * Constructor. + * Creates a new OneToManyMapping. + * + * @param array $mapping The mapping info. + */ + public function __construct(array $mapping) + { + parent::__construct($mapping); + // one side in one-many can currently never be owning side, we may support that later + $this->_isOwningSide = false; + } + + /** + * Whether orphaned elements (removed from the collection) should be deleted. + * + * @return boolean TRUE if orphaned elements should be deleted, FALSE otherwise. + */ public function shouldDeleteOrphans() { return $this->_deleteOrphans; } + + /** + * Whether the association is one-to-many. + * + * @return boolean TRUE if the association is one-to-many, FALSE otherwise. + * @override + */ + public function isOneToMany() + { + return true; + } + + } ?> \ No newline at end of file diff --git a/lib/Doctrine/Cache/Apc.php b/lib/Doctrine/Cache/Apc.php index 39698a7ad..4bcf520f3 100644 --- a/lib/Doctrine/Cache/Apc.php +++ b/lib/Doctrine/Cache/Apc.php @@ -83,9 +83,7 @@ class Doctrine_Cache_Apc extends Doctrine_Cache_Driver */ public function save($id, $data, $lifeTime = false) { - $lifeTime = $this->getLifeTime($lifeTime); - - return (bool) apc_store($id, array($data, time()), $lifeTime); + return (bool) apc_store($id, $data, $lifeTime); } /** diff --git a/lib/Doctrine/Cache/Memcache.php b/lib/Doctrine/Cache/Memcache.php index 8afa9896b..2c287b67f 100644 --- a/lib/Doctrine/Cache/Memcache.php +++ b/lib/Doctrine/Cache/Memcache.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Cache_Driver'); + /** * Doctrine_Cache_Memcache * @@ -80,15 +80,7 @@ class Doctrine_Cache_Memcache extends Doctrine_Cache_Driver */ public function fetch($id, $testCacheValidity = true) { - $tmp = $this->_memcache->get($id); - - if (is_array($tmp)) { - return $tmp[0]; - } else if (is_string($tmp)) { - return $tmp; - } - - return false; + return $this->_memcache->get($id); } /** diff --git a/lib/Doctrine/Cache/Xcache.php b/lib/Doctrine/Cache/Xcache.php index 9b23f6add..798c5d315 100644 --- a/lib/Doctrine/Cache/Xcache.php +++ b/lib/Doctrine/Cache/Xcache.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Cache_Driver'); + /** * Doctrine_Cache_Xcache * diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php index b0b24169f..d1b0d295d 100644 --- a/lib/Doctrine/ClassMetadata.php +++ b/lib/Doctrine/ClassMetadata.php @@ -25,26 +25,65 @@ /** * A ClassMetadata instance holds all the information (metadata) of an entity and - * it's associations and how they're mapped to the relational database. + * it's associations and how they're mapped to a relational database. * It is the backbone of Doctrine's metadata mapping. * * @author Roman Borschel * @since 2.0 - * @todo Rename to ClassDescriptor? + * @todo Rename to ClassDescriptor. */ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable { /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ const INHERITANCE_TYPE_NONE = 'none'; + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ const INHERITANCE_TYPE_JOINED = 'joined'; + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ const INHERITANCE_TYPE_SINGLE_TABLE = 'singleTable'; + /** + * TABLE_PER_CLASS means the class will be persisted according to the rules + * of Concrete Table Inheritance. + */ const INHERITANCE_TYPE_TABLE_PER_CLASS = 'tablePerClass'; - /* The Id generator types. TODO: belongs more in a DBAL class */ + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ const GENERATOR_TYPE_AUTO = 'auto'; + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ const GENERATOR_TYPE_SEQUENCE = 'sequence'; + /** + * TABLE means a separate table is used for id generation. + * Offers full portability. + */ const GENERATOR_TYPE_TABLE = 'table'; + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ const GENERATOR_TYPE_IDENTITY = 'identity'; + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural id. + */ const GENERATOR_TYPE_NONE = 'none'; /* The Entity types */ @@ -53,7 +92,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable */ const ENTITY_TYPE_REGULAR = 'regular'; /** - * A transient entity is ignored by Doctrine. + * A transient entity is ignored by Doctrine (so ... it's not an entity really). */ const ENTITY_TYPE_TRANSIENT = 'transient'; /** @@ -216,14 +255,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable */ protected $_lcColumnToFieldNames = array(); - /** - * Relation parser object. Manages the relations for the class. - * - * @var Doctrine_Relation_Parser $_parser - * @todo Remove. - */ - protected $_parser; - /** * Inheritance options. */ @@ -315,8 +346,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable $this->_entityName = $entityName; $this->_rootEntityName = $entityName; $this->_em = $em; - - $this->_parser = new Doctrine_Relation_Parser($this); } /** @@ -503,6 +532,11 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable return $this->_fieldMappings[$fieldName]; } + public function addFieldMapping($fieldName, array $mapping) + { + $this->_fieldMappings[$fieldName] = $mapping; + } + /** * Gets the mapping of an association. * @@ -519,6 +553,11 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable return $this->_associationMappings[$fieldName]; } + public function addAssociationMapping($fieldName, Doctrine_Association $assoc) + { + $this->_associationMappings[$fieldName] = $assoc; + } + /** * Gets all association mappings of the class. * @@ -879,6 +918,21 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable return $this->_generatorType != self::GENERATOR_TYPE_NONE; } + public function isInheritanceTypeJoined() + { + return $this->_inheritanceType == self::INHERITANCE_TYPE_JOINED; + } + + public function isInheritanceTypeSingleTable() + { + return $this->_inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + public function isInheritanceTypeTablePerClass() + { + return $this->_inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + /** * Checks whether the class uses an identity column for the Id generation. * @@ -1008,6 +1062,29 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable { return $this->_subClasses; } + + /** + * Gets the name of the class in the entity hierarchy that owns the field with + * the given name. The owning class is the one that defines the field. + * + * @param string $fieldName + * @return string + * @todo Consider using 'inherited' => 'ClassName' to make the lookup simpler. + */ + public function getOwningClass($fieldName) + { + if ($this->_inheritanceType == self::INHERITANCE_TYPE_NONE) { + return $this; + } else { + foreach ($this->_parentClasses as $parentClass) { + if ( ! $this->_em->getClassMetadata($parentClass)->isInheritedField($fieldName)) { + return $parentClass; + } + } + } + + throw new Doctrine_ClassMetadata_Exception("Unable to find defining class of field '$fieldName'."); + } /** * Checks whether the class has any persistent subclasses. @@ -1408,9 +1485,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } /** - * Adds a one-to-one association mapping. + * Adds a one-to-one mapping. * - * @todo Implementation. + * @param array $mapping The mapping. */ public function mapOneToOne(array $mapping) { @@ -1425,7 +1502,9 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } /** - * @todo Implementation. + * Adds a one-to-many mapping. + * + * @param array $mapping The mapping. */ public function mapOneToMany(array $mapping) { @@ -1440,11 +1519,14 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } /** - * @todo Implementation. + * Adds a many-to-one mapping. + * + * @param array $mapping The mapping. */ public function mapManyToOne(array $mapping) { - + // A many-to-one mapping is simply a one-one backreference + $this->mapOneToOne($mapping); } /** @@ -1585,6 +1667,8 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable /** * Completes the identifier mapping of the class. * NOTE: Should only be called by the ClassMetadataFactory! + * + * @return void */ public function completeIdentifierMapping() { @@ -1603,7 +1687,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable /** * @todo Implementation. Immutable entities can not be updated or deleted once * they are created. This means the entity can only be modified as long as it's - * in transient state (TCLEAN, TDIRTY). + * new (STATE_NEW). */ public function isImmutable() { @@ -1634,121 +1718,11 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable } } - /* The following stuff needs to be touched for the association mapping rewrite */ - - /** - * hasOne - * binds One-to-One aggregate relation - * - * @param string $componentName the name of the related component - * @param string $options relation options - * @see Doctrine_Relation::_$definition - * @return Doctrine_Entity this object - * @deprecated - */ - public function hasOne() - { - $this->bind(func_get_args(), Doctrine_Relation::ONE_AGGREGATE); - - return $this; - } - - /** - * hasMany - * binds One-to-Many / Many-to-Many aggregate relation - * - * @param string $componentName the name of the related component - * @param string $options relation options - * @see Doctrine_Relation::_$definition - * @return Doctrine_Entity this object - * @deprecated - */ - public function hasMany() - { - $this->bind(func_get_args(), Doctrine_Relation::MANY_AGGREGATE); - - return $this; - } - - /** - * @deprecated - */ - public function bindRelation($args, $type) - { - return $this->bind($args, $type); - } - - /** - * @todo Relation mapping rewrite. - * @deprecated - */ - public function bind($args, $type) - { - $options = array(); - $options['type'] = $type; - - if ( ! isset($args[1])) { - $args[1] = array(); - } - if ( ! is_array($args[1])) { - try { - throw new Exception(); - } catch (Exception $e) { - echo $e->getTraceAsString(); - } - } - $options = array_merge($args[1], $options); - $this->_parser->bind($args[0], $options); - } - - /** - * hasRelation - * - * @param string $alias the relation to check if exists - * @return boolean true if the relation exists otherwise false - * @deprecated - */ - public function hasRelation($alias) - { - return $this->_parser->hasRelation($alias); - } - public function hasAssociation($fieldName) { return isset($this->_associationMappings[$fieldName]); } - /** - * getRelation - * - * @param string $alias relation alias - * @deprecated - */ - public function getRelation($alias, $recursive = true) - { - return $this->_parser->getRelation($alias, $recursive); - } - - /** - * @deprecated - */ - public function getRelationParser() - { - return $this->_parser; - } - - /** - * getRelations - * returns an array containing all relation objects - * - * @return array an array of Doctrine_Relation objects - * @deprecated - */ - public function getRelations() - { - return $this->_parser->getRelations(); - } - /** * */ diff --git a/lib/Doctrine/ClassMetadata/Factory.php b/lib/Doctrine/ClassMetadata/Factory.php index c0ff594c6..b3287e7ef 100644 --- a/lib/Doctrine/ClassMetadata/Factory.php +++ b/lib/Doctrine/ClassMetadata/Factory.php @@ -31,7 +31,7 @@ * @version $Revision$ * @link www.phpdoctrine.org * @since 2.0 - * @todo Rename to ClassMetadataFactory. + * @todo Rename to ClassDescriptorFactory. */ class Doctrine_ClassMetadata_Factory { @@ -114,6 +114,7 @@ class Doctrine_ClassMetadata_Factory // load metadata of subclasses // -> child1 -> child2 -> $name + // Move down the hierarchy of parent classes, starting from the topmost class $parent = $class; foreach ($parentClasses as $subclassName) { $subClass = new Doctrine_ClassMetadata($subclassName, $this->_em); @@ -121,7 +122,7 @@ class Doctrine_ClassMetadata_Factory $this->_addInheritedFields($subClass, $parent); $this->_addInheritedRelations($subClass, $parent); $this->_loadMetadata($subClass, $subclassName); - if ($parent->getInheritanceType() == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) { + if ($parent->isInheritanceTypeSingleTable()) { $subClass->setTableName($parent->getTableName()); } $classes[$subclassName] = $subClass; @@ -132,15 +133,17 @@ class Doctrine_ClassMetadata_Factory /** * Adds inherited fields to the subclass mapping. * - * @param unknown_type $subClass - * @param unknown_type $parentClass + * @param Doctrine::ORM::Mapping::ClassMetadata $subClass + * @param Doctrine::ORM::Mapping::ClassMetadata $parentClass + * @return void */ protected function _addInheritedFields($subClass, $parentClass) { foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) { - $fullName = "$name as " . $parentClass->getFieldName($name); - $mapping['inherited'] = true; - $subClass->mapField($mapping); + if ( ! isset($mapping['inherited'])) { + $mapping['inherited'] = $parentClass->getClassName(); + } + $subClass->addFieldMapping($fieldName, $mapping); } } @@ -152,8 +155,8 @@ class Doctrine_ClassMetadata_Factory */ protected function _addInheritedRelations($subClass, $parentClass) { - foreach ($parentClass->getRelationParser()->getRelationDefinitions() as $name => $definition) { - $subClass->getRelationParser()->addRelationDefinition($name, $definition); + foreach ($parentClass->getAssociationMappings() as $fieldName => $mapping) { + $subClass->addAssociationMapping($name, $mapping); } } diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index f35fcdc21..60edbfe4d 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -83,7 +83,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, * * @var Doctrine::ORM::Mapping::AssociationMapping */ - protected $_associationMapping; + protected $_association; /** * The name of the column that is used for collection key mapping. @@ -99,8 +99,6 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, */ //protected static $null; - protected $_mapping; - /** * The EntityManager. * @@ -261,10 +259,10 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, * * @return void */ - public function setReference(Doctrine_Entity $entity, $relation) + public function setReference(Doctrine_Entity $entity, Doctrine_Association $relation) { $this->_owner = $entity; - //$this->relation = $relation; + $this->_association = $relation; /*if ($relation instanceof Doctrine_Relation_ForeignKey || $relation instanceof Doctrine_Relation_LocalKey) { @@ -304,6 +302,12 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, $removed = $this->_data[$key]; unset($this->_data[$key]); //TODO: Register collection as dirty with the UoW if necessary + //$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this); + //TODO: delete entity if shouldDeleteOrphans + /*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans()) { + $this->_em->delete($removed); + }*/ + return $removed; } @@ -323,7 +327,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, * * @param string $name * @since 1.0 - * @return void + * @return mixed */ public function __unset($key) { @@ -508,7 +512,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, throw new Doctrine_Record_Exception('Value variable in set is not an instance of Doctrine_Entity.'); } - // Neither Maps nor Lists allow duplicates, both are Sets + // TODO: Really prohibit duplicates? if (in_array($value, $this->_data, true)) { return false; } @@ -956,7 +960,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, public function mapElements($lambda) { $result = array(); foreach ($this->_data as $key => $entity) { - list($key, $value) = $lambda($key, $entity); + list($key, $value) = each($lambda($key, $entity)); $result[$key] = $value; } return $result; @@ -969,14 +973,20 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, */ public function clear() { - $this->_data = array(); //TODO: Register collection as dirty with the UoW if necessary + //TODO: If oneToMany() && shouldDeleteOrphan() delete entities + /*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans()) { + foreach ($this->_data as $entity) { + $this->_em->delete($entity); + } + }*/ + $this->_data = array(); } private function _changed() { /*if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) { $this->_em->getUnitOfWork()->scheduleCollectionUpdate($this); - }*/ + }*/ } } diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index b80910b55..06a416416 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -932,7 +932,7 @@ abstract class Doctrine_Connection */ public function lastInsertId($table = null, $field = null) { - return $this->sequence->lastInsertId($table, $field); + return $this->getSequenceManager()->lastInsertId($table, $field); } /** diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php index 5a50e7618..37ac8c265 100644 --- a/lib/Doctrine/Connection/UnitOfWork.php +++ b/lib/Doctrine/Connection/UnitOfWork.php @@ -160,6 +160,8 @@ class Doctrine_Connection_UnitOfWork foreach ($commitOrder as $class) { $this->_executeInserts($class); + } + foreach ($commitOrder as $class) { $this->_executeUpdates($class); } @@ -268,7 +270,7 @@ class Doctrine_Connection_UnitOfWork } // add dependency $otherNode = $this->_commitOrderCalculator->getNodeForKey($targetClassName); - $node->before($otherNode); + $otherNode->before($node); } } } diff --git a/lib/Doctrine/DataDict/Informix.php b/lib/Doctrine/DataDict/Informix.php index ab56a3e54..e2d5e93f9 100644 --- a/lib/Doctrine/DataDict/Informix.php +++ b/lib/Doctrine/DataDict/Informix.php @@ -27,7 +27,8 @@ * @author Lukas Smith (PEAR MDB2 library) * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Remove. */ class Doctrine_DataDict_Informix extends Doctrine_DataDict { diff --git a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php index d82de8496..90cd6503e 100644 --- a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php +++ b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php @@ -227,6 +227,26 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform return 'CONCAT(' . join(', ', (array) $args) . ')'; } + /** + * @TEST + */ + public function getVarcharDeclaration(array $field) + { + if ( ! isset($field['length'])) { + if (array_key_exists('default', $field)) { + $field['length'] = $this->getVarcharMaxLength(); + } else { + $field['length'] = false; + } + } + + $length = ($field['length'] <= $this->getVarcharMaxLength()) ? $field['length'] : false; + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } + /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. diff --git a/lib/Doctrine/Entity.php b/lib/Doctrine/Entity.php index e5c3d7d15..a0edd66ac 100644 --- a/lib/Doctrine/Entity.php +++ b/lib/Doctrine/Entity.php @@ -143,6 +143,14 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable * @var integer */ private $_state; + + /** + * Name => Value map of join columns. + * + * @var array + * @todo Not yet clear if needed. + */ + //private $_joinColumns = array(); /** * The changes that happened to fields of the entity. @@ -189,14 +197,6 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable */ private $_oid; - /** - * Flag that indicates whether the entity is dirty. - * (which means it has local changes) - * - * @var boolean - */ - //private $_isDirty = false; - /** * Constructor. * Creates a new Entity instance. @@ -444,6 +444,7 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable $this->_internalSetReference($fieldName, $value); $this->_referenceChangeSet[$fieldName] = array($old => $value); $this->_registerDirty(); + //TODO: Allow arrays in $value. Wrap them in a collection transparently. if ($old instanceof Doctrine_Collection) { $this->_em->getUnitOfWork()->scheduleCollectionDeletion($old); } @@ -524,67 +525,6 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable * * @param string $fieldName * @param mixed $value - * @todo Refactor. - */ - final public function _internalSetReference_OLD($name, $value) - { - if ($value === Doctrine_Null::$INSTANCE) { - $this->_references[$name] = $value; - return; - } - - $rel = $this->_class->getRelation($name); - - // one-to-many or one-to-one relation - if ($rel instanceof Doctrine_Relation_ForeignKey || - $rel instanceof Doctrine_Relation_LocalKey) { - if ( ! $rel->isOneToOne()) { - // one-to-many relation - if ( ! $value instanceof Doctrine_Collection) { - throw Doctrine_Entity_Exception::invalidValueForOneToManyReference(); - } - if (isset($this->_references[$name])) { - $this->_references[$name]->setData($value->getData()); - return; - } - } else { - $relatedTable = $value->getTable(); - $foreignFieldName = $rel->getForeignFieldName(); - $localFieldName = $rel->getLocalFieldName(); - - // one-to-one relation found - if ( ! ($value instanceof Doctrine_Entity)) { - throw Doctrine_Entity_Exception::invalidValueForOneToOneReference(); - } - if ($rel instanceof Doctrine_Relation_LocalKey) { - $idFieldNames = $value->getTable()->getIdentifier(); - if ( ! empty($foreignFieldName) && $foreignFieldName != $idFieldNames[0]) { - $this->set($localFieldName, $value->_get($foreignFieldName)); - } else { - $this->set($localFieldName, $value); - } - } else { - $value->set($foreignFieldName, $this); - } - } - } else if ($rel instanceof Doctrine_Relation_Association) { - if ( ! ($value instanceof Doctrine_Collection)) { - throw Doctrine_Entity_Exception::invalidValueForManyToManyReference(); - } - } - - $this->_references[$name] = $value; - } - - /** - * INTERNAL: - * Sets a reference to another entity or a collection of entities. - * - * NOTE: Use of this method in userland code is strongly discouraged. - * - * @param string $fieldName - * @param mixed $value - * @todo Refactor. */ final public function _internalSetReference($name, $value) { @@ -595,31 +535,14 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable $rel = $this->_class->getAssociationMapping($name); - // one-to-many or one-to-one relation - if ($rel->isOneToOne() || $rel->isOneToMany()) { - if ( ! $rel->isOneToOne()) { - // one-to-many relation - if ( ! $value instanceof Doctrine_Collection) { - throw Doctrine_Entity_Exception::invalidValueForOneToManyReference(); - } - if (isset($this->_references[$name])) { - $this->_references[$name]->setData($value->getData()); - return; - } - } else { - $relatedClass = $value->getClass(); - //$foreignFieldName = $rel->getForeignFieldName(); - //$localFieldName = $rel->getLocalFieldName(); - - // one-to-one relation found - if ( ! ($value instanceof Doctrine_Entity)) { - throw Doctrine_Entity_Exception::invalidValueForOneToOneReference(); - } - } - } else if ($rel instanceof Doctrine_Relation_Association) { - if ( ! ($value instanceof Doctrine_Collection)) { - throw Doctrine_Entity_Exception::invalidValueForManyToManyReference(); - } + if ($rel->isOneToOne() && ! $value instanceof Doctrine_Entity) { + throw Doctrine_Entity_Exception::invalidValueForOneToOneReference(); + } + if ($rel->isOneToMany() && ! $value instanceof Doctrine_Collection) { + throw Doctrine_Entity_Exception::invalidValueForOneToManyReference(); + } + if ($rel->isManyToMany() && ! $value instanceof Doctrine_Collection) { + throw Doctrine_Entity_Exception::invalidValueForManyToManyReference(); } $this->_references[$name] = $value; @@ -734,7 +657,7 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable * @param string $name * @return boolean */ - final public function contains($fieldName) + private function _contains($fieldName) { if (isset($this->_data[$fieldName])) { if ($this->_data[$fieldName] === Doctrine_Null::$INSTANCE) { @@ -755,28 +678,29 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable /** * Clears the value of a field. * - * Invoked by Doctrine::ORM::Access#__unset(). - * * @param string $name * @return void */ - final public function remove($fieldName) + private function _unset($fieldName) { if (isset($this->_data[$fieldName])) { - $this->_data[$fieldName] = array(); + $this->_data[$fieldName] = null; } else if (isset($this->_references[$fieldName])) { - if ($this->_references[$fieldName] instanceof Doctrine_Entity) { - // todo: delete related record when saving $this (ONLY WITH deleteOrphans!) - $this->_references[$fieldName] = Doctrine_Null::$INSTANCE; - } else if ($this->_references[$fieldName] instanceof Doctrine_Collection) { - $this->_references[$fieldName]->setData(array()); + $assoc = $this->_class->getAssociationMapping($fieldName); + if ($assoc->isOneToOne() && $assoc->shouldDeleteOrphans()) { + $this->_em->delete($this->_references[$fieldName]); + } else if ($assoc->isOneToMany() && $assoc->shouldDeleteOrphans()) { + foreach ($this->_references[$fieldName] as $entity) { + $this->_em->delete($entity); + } } + $this->_references[$fieldName] = null; } } /** * INTERNAL: - * Gets the changeset of the entities persistent state. + * Gets the changeset of the entities data. * * @return array */ @@ -785,6 +709,12 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable return $this->_dataChangeSet; } + /** + * INTERNAL: + * Gets the changeset of the entities references to other entities. + * + * @return array + */ final public function _getReferenceChangeSet() { return $this->_referenceChangeSet; @@ -831,11 +761,28 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable $this->_data[$name] = $id; } $this->_dataChangeSet = array(); + $this->_referenceChangeSet = array(); } + + /** + * @todo Not yet clear if needed. + */ + /*final public function _setJoinColumn($columnName, $value) + { + $this->_joinColumns[$columnName] = $value; + }*/ + + /** + * @todo Not yet clear if needed. + */ + /*final public function _getJoinColumn($columnName) + { + return $this->_joinColumns[$columnName]; + }*/ /** * INTERNAL: - * Returns the primary keys of the entity (key => value pairs). + * Returns the primary keys of the entity (field => value pairs). * * @return array */ @@ -844,32 +791,6 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable return $this->_id; } - /** - * hasRefence - * @param string $name - * @return boolean - * @todo Better name? hasAssociation() ? Remove? - */ - final public function hasReference($name) - { - return isset($this->_references[$name]); - } - - /** - * obtainReference - * - * @param string $name - * @throws Doctrine_Record_Exception if trying to get an unknown related component - * @todo Better name? Remove? - */ - final public function obtainReference($name) - { - if (isset($this->_references[$name])) { - return $this->_references[$name]; - } - throw new Doctrine_Record_Exception("Unknown reference $name."); - } - /** * INTERNAL: * @@ -881,19 +802,6 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable return $this->_references; } - /** - * INTERNAL: - * setRelated - * - * @param string $alias - * @param Doctrine_Access $coll - * @todo Name? Remove? - */ - final public function _setRelated($alias, Doctrine_Access $coll) - { - $this->_references[$alias] = $coll; - } - /** * Gets the ClassMetadata object that describes the entity class. * @@ -925,23 +833,6 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable return $this->_em->getRepository($this->_entityName); } - /** - * @todo Why toString() and __toString() ? - */ - public function toString() - { - return Doctrine::dump(get_object_vars($this)); - } - - /** - * returns a string representation of this object - * @todo Why toString() and __toString() ? - */ - public function __toString() - { - return (string)$this->_oid; - } - /** * Helps freeing the memory occupied by the entity. * Cuts all references the entity has to other entities and removes the entity @@ -980,7 +871,7 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable */ public function offsetExists($offset) { - return $this->contains($offset); + return $this->_contains($offset); } /** @@ -1020,7 +911,7 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable */ public function offsetUnset($offset) { - return $this->remove($offset); + return $this->_unset($offset); } /** @@ -1058,7 +949,7 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable */ public function __isset($name) { - return $this->contains($name); + return $this->_contains($name); } /** @@ -1070,6 +961,14 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable */ public function __unset($name) { - return $this->remove($name); + return $this->_unset($name); + } + + /** + * returns a string representation of this object + */ + public function __toString() + { + return (string)$this->_oid; } } diff --git a/lib/Doctrine/EntityManager.php b/lib/Doctrine/EntityManager.php index d3f8610ef..eaeedf0fd 100644 --- a/lib/Doctrine/EntityManager.php +++ b/lib/Doctrine/EntityManager.php @@ -44,7 +44,7 @@ class Doctrine_EntityManager * IMMEDIATE: Flush occurs automatically after each operation that issues database * queries. No operations are queued. */ - const FLUSHMODE_IMMEDIATE = 'immediated'; + const FLUSHMODE_IMMEDIATE = 'immediate'; /** * AUTO: Flush occurs automatically in the following situations: * - Before any query executions (to prevent getting stale data) @@ -86,7 +86,7 @@ class Doctrine_EntityManager /** * The metadata factory, used to retrieve the metadata of entity classes. * - * @var Doctrine_ClassMetadata_Factory + * @var Doctrine::ORM::Mapping::ClassMetadataFactory */ private $_metadataFactory; @@ -539,7 +539,8 @@ class Doctrine_EntityManager } } else { foreach ($data as $field => $value) { - if ( ! $entity->contains($field) || $entity->_internalGetField($field) === null) { + $currentValue = $entity->get($field); + if ( ! isset($currentValue) || $entity->_internalGetField($field) === null) { $entity->_internalSetField($field, $value); } } diff --git a/lib/Doctrine/EntityPersister/Abstract.php b/lib/Doctrine/EntityPersister/Abstract.php index 818495947..d5738800a 100644 --- a/lib/Doctrine/EntityPersister/Abstract.php +++ b/lib/Doctrine/EntityPersister/Abstract.php @@ -72,7 +72,7 @@ abstract class Doctrine_EntityPersister_Abstract private $_nullObject; /** - * Constructs a new persister. + * Constructs a new EntityPersister. */ public function __construct(Doctrine_EntityManager $em, Doctrine_ClassMetadata $classMetadata) { @@ -83,37 +83,78 @@ abstract class Doctrine_EntityPersister_Abstract $this->_nullObject = Doctrine_Null::$INSTANCE; } + /** + * Inserts an entity. + * + * @param Doctrine::ORM::Entity $entity The entity to insert. + * @return void + */ public function insert(Doctrine_Entity $entity) { $insertData = array(); - $dataChangeSet = $entity->_getDataChangeSet(); + $class = $entity->getClass(); + $referenceChangeSet = $entity->_getReferenceChangeSet(); - foreach ($referenceChangeSet as $field => $change) { list($old, $new) = each($change); - $assocMapping = $entity->getClass()->getAssociationMapping($field); - if ($assocMapping->isOneToOne()) { - if ($assocMapping->isInverseSide()) { - //echo "INVERSE!"; - continue; // ignore inverse side - } - foreach ($assocMapping->getSourceToTargetKeyColumns() as $localColumn => $foreignColumn) { - //echo "$localColumn -- $foreignColumn
"; - //$insertData[$localColumn] = 1; //FIX - } - // ... set the foreign key column to the id of the related entity ($new) + $assocMapping = $class->getAssociationMapping($field); + if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) { + //echo "NOT TO-ONE OR INVERSE!"; + continue; + } + + foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) { + //TODO: What if both join columns (local/foreign) are just db-only + // columns (no fields in models) ? Currently we assume the foreign column + // is mapped to a field in the foreign entity. + $insertData[$sourceColumn] = $new->_internalGetField( + $new->getClass()->getFieldName($targetColumn) + ); } //... } - foreach ($dataChangeSet as $field => $change) { - $insertData[$entity->getClass()->getColumnName($field)] = current($change); - } + $this->_prepareData($entity, $insertData, true); //TODO: perform insert - $this->_conn->insert($entity->getClass()->getTableName(), $insertData); + $this->_conn->insert($class->getTableName(), $insertData); + + //TODO: if IDENTITY pk, assign it + if ($class->isIdGeneratorIdentity()) { + //TODO: Postgres IDENTITY columns (SERIAL) use a sequence, so we need to pass the + // sequence name to lastInsertId(). + $entity->_assignIdentifier($this->_conn->lastInsertId()); + } } + /*protected function _fillJoinColumns($entity, array &$data) + { + $referenceChangeSet = $entity->_getReferenceChangeSet(); + foreach ($referenceChangeSet as $field => $change) { + list($old, $new) = each($change); + $assocMapping = $entity->getClass()->getAssociationMapping($field); + if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) { + //echo "NOT TO-ONE OR INVERSE!"; + continue; + } + + foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) { + //TODO: What if both join columns (local/foreign) are just db-only + // columns (no fields in models) ? Currently we assume the foreign column + // is mapped to a field in the foreign entity. + $insertData[$sourceColumn] = $new->_internalGetField( + $new->getClass()->getFieldName($targetColumn) + ); + } + } + }*/ + + /** + * Updates an entity. + * + * @param Doctrine::ORM::Entity $entity The entity to update. + * @return void + */ public function update(Doctrine_Entity $entity) { $dataChangeSet = $entity->_getDataChangeSet(); @@ -134,6 +175,12 @@ abstract class Doctrine_EntityPersister_Abstract //TODO: perform update } + /** + * Deletes an entity. + * + * @param Doctrine::ORM::Entity $entity The entity to delete. + * @return void + */ public function delete(Doctrine_Entity $entity) { //TODO: perform delete @@ -177,6 +224,9 @@ abstract class Doctrine_EntityPersister_Abstract return $this->_classMetadata; } + /** + * @todo Move to ClassMetadata? + */ public function getFieldNames() { if ($this->_fieldNames) { @@ -186,6 +236,9 @@ abstract class Doctrine_EntityPersister_Abstract return $this->_fieldNames; } + /** + * @todo Move to ClassMetadata? + */ public function getOwningClass($fieldName) { return $this->_classMetadata; @@ -193,6 +246,7 @@ abstract class Doctrine_EntityPersister_Abstract /** * Callback that is invoked during the SQL construction process. + * @todo Move to ClassMetadata? */ public function getCustomJoins() { @@ -201,6 +255,7 @@ abstract class Doctrine_EntityPersister_Abstract /** * Callback that is invoked during the SQL construction process. + * @todo Move to ClassMetadata? */ public function getCustomFields() { @@ -213,7 +268,7 @@ abstract class Doctrine_EntityPersister_Abstract * * @return array */ - protected function _convertFieldToColumnNames(array $fields, Doctrine_ClassMetadata $class) + /*protected function _convertFieldToColumnNames(array $fields, Doctrine_ClassMetadata $class) { $converted = array(); foreach ($fields as $fieldName => $value) { @@ -221,65 +276,52 @@ abstract class Doctrine_EntityPersister_Abstract } return $converted; - } + }*/ /** * Returns an array of modified fields and values with data preparation * adds column aggregation inheritance and converts Records into primary key values * * @param array $array - * @return array + * @return void * @todo Move to EntityPersister. There call _getChangeSet() and apply this logic. */ - public function prepareData(array $data = array()) + protected function _prepareData($entity, array &$result, $isInsert = false) { - $dataSet = array(); + foreach ($entity->_getDataChangeSet() as $field => $change) { + list ($oldVal, $newVal) = each($change); + $type = $entity->getClass()->getTypeOfField($field); + $columnName = $entity->getClass()->getColumnName($field); - $modifiedFields = $fields; - - foreach ($data as $field => $value) { - $type = $this->_classMetadata->getTypeOfField($field); - - if ($value === Doctrine_Null::$INSTANCE) { - $dataSet[$field] = null; + if ($newVal === Doctrine_Null::$INSTANCE) { + $result[$columnName] = null; continue; } switch ($type) { case 'array': case 'object': - $dataSet[$field] = serialize($value); + $result[$columnName] = serialize($newVal); break; case 'gzip': - $dataSet[$field] = gzcompress($value, 5); + $result[$columnName] = gzcompress($newVal, 5); break; case 'boolean': - $dataSet[$field] = $this->_em->getConnection() - ->convertBooleans($value); + $result[$columnName] = $this->_em->getConnection()->convertBooleans($newVal); break; - case 'enum': - $dataSet[$field] = $this->_class->enumIndex($field, $value); - break; default: - $dataSet[$field] = $value; + $result[$columnName] = $newVal; } } - // @todo cleanup - // populates the discriminator field in Single & Class Table Inheritance - if ($this->_classMetadata->getInheritanceType() == Doctrine_ClassMetadata::INHERITANCE_TYPE_JOINED || - $this->_class->getInheritanceType() == Doctrine_ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE) { - $discCol = $this->_classMetadata->getInheritanceOption('discriminatorColumn'); - $discMap = $this->_classMetadata->getInheritanceOption('discriminatorMap'); - $old = $this->get($discCol, false); - $discValue = array_search($this->_entityName, $discMap); - if ((string) $old !== (string) $discValue || $old === null) { - $dataSet[$discCol] = $discValue; - //$this->_data[$discCol] = $discValue; - } + // @todo Cleanup + // populates the discriminator column on insert in Single & Class Table Inheritance + if ($isInsert && ($entity->getClass()->isInheritanceTypeJoined() || + $entity->getClass()->isInheritanceTypeSingleTable())) { + $discColumn = $entity->getClass()->getInheritanceOption('discriminatorColumn'); + $discMap = $entity->getClass()->getInheritanceOption('discriminatorMap'); + $result[$discColumn] = array_search($this->_entityName, $discMap); } - - return $dataSet; } diff --git a/lib/Doctrine/EntityPersister/JoinedSubclass.php b/lib/Doctrine/EntityPersister/JoinedSubclass.php index 1e48ce1a6..b144eea3d 100644 --- a/lib/Doctrine/EntityPersister/JoinedSubclass.php +++ b/lib/Doctrine/EntityPersister/JoinedSubclass.php @@ -30,58 +30,53 @@ * @since 2.0 */ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_Abstract -{ - //protected $_columnNameFieldNameMap = array(); - +{ /** * Inserts an entity that is part of a Class Table Inheritance hierarchy. * * @param Doctrine_Entity $record record to be inserted * @return boolean + * @override */ - protected function _doInsert(Doctrine_Entity $record) + public function insert(Doctrine_Entity $entity) { - $class = $this->_classMetadata; - $conn = $this->_conn; - - $dataSet = $this->_groupFieldsByDefiningClass($record); + $class = $entity->getClass(); + + $dataSet = array(); + + $this->_prepareData($entity, $dataSet, true); + + $dataSet = $this->_groupFieldsByDefiningClass($class, $dataSet); + $component = $class->getClassName(); $classes = $class->getParentClasses(); array_unshift($classes, $component); - try { - $conn->beginInternalTransaction(); - $identifier = null; - foreach (array_reverse($classes) as $k => $parent) { - $parentClass = $conn->getClassMetadata($parent); - if ($k == 0) { - $identifierType = $parentClass->getIdentifierType(); - if ($identifierType == Doctrine::IDENTIFIER_AUTOINC) { - $this->_insertRow($parentClass->getTableName(), $dataSet[$parent]); - $identifier = $conn->sequence->lastInsertId(); - } else if ($identifierType == Doctrine::IDENTIFIER_SEQUENCE) { - $seq = $record->getClassMetadata()->getTableOption('sequenceName'); - if ( ! empty($seq)) { - $id = $conn->sequence->nextId($seq); - $identifierFields = (array)$parentClass->getIdentifier(); - $dataSet[$parent][$identifierFields[0]] = $id; - $this->_insertRow($parentClass->getTableName(), $dataSet[$parent]); - } - } else { - throw new Doctrine_Mapper_Exception("Unsupported identifier type '$identifierType'."); - } - $record->assignIdentifier($identifier); - } else { - foreach ((array) $record->identifier() as $id => $value) { - $dataSet[$parent][$parentClass->getColumnName($id)] = $value; - } + $identifier = null; + foreach (array_reverse($classes) as $k => $parent) { + $parentClass = $this->_em->getClassMetadata($parent); + if ($k == 0) { + if ($parentClass->isIdGeneratorIdentity()) { $this->_insertRow($parentClass->getTableName(), $dataSet[$parent]); + $identifier = $this->_conn->lastInsertId(); + } else if ($parentClass->isIdGeneratorSequence()) { + $seq = $entity->getClassMetadata()->getTableOption('sequenceName'); + if ( ! empty($seq)) { + $id = $this->_conn->getSequenceManager()->nextId($seq); + $identifierFields = $parentClass->getIdentifier(); + $dataSet[$parent][$identifierFields[0]] = $id; + $this->_insertRow($parentClass->getTableName(), $dataSet[$parent]); + } + } else { + throw new Doctrine_Mapper_Exception("Unsupported identifier type '$identifierType'."); } + $entity->_assignIdentifier($identifier); + } else { + foreach ($entity->_identifier() as $id => $value) { + $dataSet[$parent][$parentClass->getColumnName($id)] = $value; + } + $this->_insertRow($parentClass->getTableName(), $dataSet[$parent]); } - $conn->commit(); - } catch (Exception $e) { - $conn->rollback(); - throw $e; } return true; @@ -255,29 +250,26 @@ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_A * * @return array */ - protected function _groupFieldsByDefiningClass(Doctrine_Entity $record) + protected function _groupFieldsByDefiningClass(Doctrine_ClassMetadata $class, array $fields) { - $conn = $this->_conn; - $classMetadata = $this->_classMetadata; $dataSet = array(); - $component = $classMetadata->getClassName(); - $array = $record->getPrepared(); + $component = $class->getClassName(); - $classes = array_merge(array($component), $classMetadata->getParentClasses()); + $classes = array_merge(array($component), $class->getParentClasses()); foreach ($classes as $class) { $dataSet[$class] = array(); - $parentClassMetadata = $conn->getClassMetadata($class); - foreach ($parentClassMetadata->getColumns() as $columnName => $definition) { - if ((isset($definition['primary']) && $definition['primary'] === true) || - (isset($definition['inherited']) && $definition['inherited'] === true)) { + $parentClassMetadata = $this->_em->getClassMetadata($class); + foreach ($parentClassMetadata->getFieldMappings() as $fieldName => $mapping) { + if ((isset($mapping['id']) && $mapping['id'] === true) || + (isset($mapping['inherited']) && $mapping['inherited'] === true)) { continue; } - $fieldName = $classMetadata->getFieldName($columnName); - if ( ! array_key_exists($fieldName, $array)) { + if ( ! array_key_exists($fieldName, $fields)) { continue; } - $dataSet[$class][$columnName] = $array[$fieldName]; + $columnName = $parentClassMetadata->getColumnName($fieldName); + $dataSet[$class][$columnName] = $fields[$fieldName]; } } diff --git a/lib/Doctrine/Hook/Equal.php b/lib/Doctrine/Hook/Equal.php index f6a100a72..5b8b509e0 100644 --- a/lib/Doctrine/Hook/Equal.php +++ b/lib/Doctrine/Hook/Equal.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Hook_Parser'); + /** * Doctrine_Hook_Equal * @@ -28,7 +28,9 @@ Doctrine::autoload('Doctrine_Hook_Parser'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Can be removed? + * @deprecated */ class Doctrine_Hook_Equal extends Doctrine_Hook_Parser { diff --git a/lib/Doctrine/Hook/Integer.php b/lib/Doctrine/Hook/Integer.php index 141641ec2..a181f2cd0 100644 --- a/lib/Doctrine/Hook/Integer.php +++ b/lib/Doctrine/Hook/Integer.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Hook_Parser_Complex'); + /** * Doctrine_Hook_Integer * @@ -28,7 +28,9 @@ Doctrine::autoload('Doctrine_Hook_Parser_Complex'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Can be removed? + * @deprecated */ class Doctrine_Hook_Integer extends Doctrine_Hook_Parser_Complex { diff --git a/lib/Doctrine/Hook/Parser.php b/lib/Doctrine/Hook/Parser.php index b97bb246f..4562dcdf8 100644 --- a/lib/Doctrine/Hook/Parser.php +++ b/lib/Doctrine/Hook/Parser.php @@ -28,7 +28,9 @@ * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Can be removed? + * @deprecated */ abstract class Doctrine_Hook_Parser { diff --git a/lib/Doctrine/Hook/Parser/Complex.php b/lib/Doctrine/Hook/Parser/Complex.php index 826a4cd8a..68302117c 100644 --- a/lib/Doctrine/Hook/Parser/Complex.php +++ b/lib/Doctrine/Hook/Parser/Complex.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Hook_Parser'); + /** * Doctrine_Hook_Parser_Complex * @@ -28,7 +28,9 @@ Doctrine::autoload('Doctrine_Hook_Parser'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Can be removed? + * @deprecated */ abstract class Doctrine_Hook_Parser_Complex extends Doctrine_Hook_Parser { diff --git a/lib/Doctrine/Hook/WordLike.php b/lib/Doctrine/Hook/WordLike.php index 9d9d1dd00..c6e85203d 100644 --- a/lib/Doctrine/Hook/WordLike.php +++ b/lib/Doctrine/Hook/WordLike.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Hook_Parser'); + /** * Doctrine_Hook_WordLike * @@ -28,7 +28,9 @@ Doctrine::autoload('Doctrine_Hook_Parser'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated + * @todo Can be removed? */ class Doctrine_Hook_WordLike extends Doctrine_Hook_Parser_Complex { diff --git a/lib/Doctrine/Query/Production/Join.php b/lib/Doctrine/Query/Production/Join.php index 5c9930fda..4af2f8ee6 100644 --- a/lib/Doctrine/Query/Production/Join.php +++ b/lib/Doctrine/Query/Production/Join.php @@ -88,33 +88,51 @@ class Doctrine_Query_Production_Join extends Doctrine_Query_Production return $sql . ' ON ' . $conditionExpression; } - // We need to build the relationship conditions. Retrieving AssociationMapping - $queryComponent = $this->_rangeVariableDeclaration->getQueryComponent(); - $relationColumns = $queryComponent['relation']->getSourceToTargetKeyColumns(); - $relationConditionExpression = ''; + // We need to build the join conditions. Retrieving AssociationMapping + $queryComponent = $this->_rangeVariableDeclaration->getQueryComponent(); + $association = $queryComponent['relation']; + $joinColumns = array(); + + if ($association->isOneToMany() || $association->isOneToOne()) { + if ($association->isInverseSide()) { + // joinColumns are found on the other (owning) side + $targetClass = $this->_em->getClassMetadata($association->getTargetEntityName()); + $joinColumns = $targetClass->getAssociationMapping($association->getMappedByFieldName()) + ->getTargetToSourceKeyColumns(); + } else { + $joinColumns = $association->getSourceToTargetKeyColumns(); + } + } else { + //TODO: many-many + } + + $relationConditionExpression = ''; - // We have an array('localColumn' => 'foreignColumn', ...) here - foreach ($relationColumns as $localColumn => $foreignColumn) { - // leftExpression = rightExpression + // We have an array('localColumn' => 'foreignColumn', ...) here + foreach ($joinColumns as $localColumn => $foreignColumn) { + // leftExpression = rightExpression - // Defining leftExpression - $leftExpression = $conn->quoteIdentifier( - $parserResult->getTableAliasFromComponentAlias($queryComponent['parent']) . '.' . $localColumn - ); + // Defining leftExpression + $leftExpression = $conn->quoteIdentifier( + $parserResult->getTableAliasFromComponentAlias($queryComponent['parent']) . '.' . $localColumn + ); - // Defining rightExpression - $rightExpression = $conn->quoteIdentifier( - $parserResult->getTableAliasFromComponentAlias( - $this->_rangeVariableDeclaration->getIdentificationVariable() - ) . '.' . $foreignColumn - ); + // Defining rightExpression + $rightExpression = $conn->quoteIdentifier( + $parserResult->getTableAliasFromComponentAlias( + $this->_rangeVariableDeclaration->getIdentificationVariable() + ) . '.' . $foreignColumn + ); - // Building the relation - $relationConditionExpression .= (($relationConditionExpression != '') ? ' AND ' : '') - . $leftExpression . ' = ' . $rightExpression; - } + // Building the relation + $relationConditionExpression .= (($relationConditionExpression != '') ? ' AND ' : '') + . $leftExpression . ' = ' . $rightExpression; + } + + $sql .= ' ON ' . $relationConditionExpression; + $sql .= empty($conditionExpression) ? '' : ' AND (' . $conditionExpression . ')'; - return $sql . ' ON ' . $relationConditionExpression . ' AND (' . $conditionExpression . ')'; + return $sql; } diff --git a/lib/Doctrine/Query/Production/JoinVariableDeclaration.php b/lib/Doctrine/Query/Production/JoinVariableDeclaration.php index 9c28a3823..433c17209 100755 --- a/lib/Doctrine/Query/Production/JoinVariableDeclaration.php +++ b/lib/Doctrine/Query/Production/JoinVariableDeclaration.php @@ -52,7 +52,7 @@ class Doctrine_Query_Production_JoinVariableDeclaration extends Doctrine_Query_P public function buildSql() { - return $this->_join->buildSql() . ' ' . (isset($this->_indexby) ? $this->_indexby->buildSql() . ' ' : ''); + return $this->_join->buildSql() . (isset($this->_indexby) ? $this->_indexby->buildSql() . ' ' : ''); } diff --git a/lib/Doctrine/RawSql/Exception.php b/lib/Doctrine/RawSql/Exception.php index 8870151eb..6c9d0b68b 100644 --- a/lib/Doctrine/RawSql/Exception.php +++ b/lib/Doctrine/RawSql/Exception.php @@ -18,7 +18,9 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Exception'); + +#namespace Doctrine::ORM::Exceptions; + /** * Doctrine_RawSql_Exception * @@ -28,7 +30,8 @@ Doctrine::autoload('Doctrine_Exception'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Rename to NativeSqlException or maybe remove. */ class Doctrine_RawSql_Exception extends Doctrine_Exception { } \ No newline at end of file diff --git a/lib/Doctrine/Record/Exception.php b/lib/Doctrine/Record/Exception.php index 4ffb1c1f3..e6013702f 100644 --- a/lib/Doctrine/Record/Exception.php +++ b/lib/Doctrine/Record/Exception.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Exception'); + /** * Doctrine_Exception * @@ -28,7 +28,8 @@ Doctrine::autoload('Doctrine_Exception'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated Remove. */ class Doctrine_Record_Exception extends Doctrine_Exception { } \ No newline at end of file diff --git a/lib/Doctrine/Record/Filter.php b/lib/Doctrine/Record/Filter.php index b0d6bb2e4..adba321d8 100644 --- a/lib/Doctrine/Record/Filter.php +++ b/lib/Doctrine/Record/Filter.php @@ -29,7 +29,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision: 1298 $ + * @version $Revision: 1298 $ + * @deprecated Remove. */ abstract class Doctrine_Record_Filter { diff --git a/lib/Doctrine/Record/Filter/Compound.php b/lib/Doctrine/Record/Filter/Compound.php index db435a02a..d2dac6c17 100644 --- a/lib/Doctrine/Record/Filter/Compound.php +++ b/lib/Doctrine/Record/Filter/Compound.php @@ -28,7 +28,9 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision: 1298 $ + * @version $Revision: 1298 $ + * @todo Remove. + * @deprecated */ class Doctrine_Record_Filter_Compound extends Doctrine_Record_Filter { diff --git a/lib/Doctrine/Record/Filter/Standard.php b/lib/Doctrine/Record/Filter/Standard.php index fb23b1200..42daf0052 100644 --- a/lib/Doctrine/Record/Filter/Standard.php +++ b/lib/Doctrine/Record/Filter/Standard.php @@ -29,7 +29,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision: 1298 $ + * @version $Revision: 1298 $ + * @deprecated Remove. */ class Doctrine_Record_Filter_Standard extends Doctrine_Record_Filter { diff --git a/lib/Doctrine/Record/Generator.php b/lib/Doctrine/Record/Generator.php index 4c95e8d5e..35f31ca2a 100644 --- a/lib/Doctrine/Record/Generator.php +++ b/lib/Doctrine/Record/Generator.php @@ -28,7 +28,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @version $Revision$ * @link www.phpdoctrine.org - * @since 1.0 + * @since 1.0 + * @todo Rename. Move. Reimpl? */ abstract class Doctrine_Record_Generator { diff --git a/lib/Doctrine/Record/Iterator.php b/lib/Doctrine/Record/Iterator.php index be3d1beaa..e53a5fca8 100644 --- a/lib/Doctrine/Record/Iterator.php +++ b/lib/Doctrine/Record/Iterator.php @@ -28,7 +28,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated Remove. */ class Doctrine_Record_Iterator extends ArrayIterator { diff --git a/lib/Doctrine/Record/Listener.php b/lib/Doctrine/Record/Listener.php index c2cf57b63..26df8c87e 100644 --- a/lib/Doctrine/Record/Listener.php +++ b/lib/Doctrine/Record/Listener.php @@ -28,7 +28,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated Remove. */ class Doctrine_Record_Listener implements Doctrine_Record_Listener_Interface { diff --git a/lib/Doctrine/Record/Listener/Chain.php b/lib/Doctrine/Record/Listener/Chain.php index 73534565c..8a0a2b71a 100644 --- a/lib/Doctrine/Record/Listener/Chain.php +++ b/lib/Doctrine/Record/Listener/Chain.php @@ -18,7 +18,6 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Access'); /** * Doctrine_Record_Listener_Chain @@ -31,7 +30,9 @@ Doctrine::autoload('Doctrine_Access'); * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated + * @todo Remove */ class Doctrine_Record_Listener_Chain extends Doctrine_Access implements Doctrine_Record_Listener_Interface { diff --git a/lib/Doctrine/Record/Listener/Interface.php b/lib/Doctrine/Record/Listener/Interface.php index 2ddc470b1..d900c60ef 100644 --- a/lib/Doctrine/Record/Listener/Interface.php +++ b/lib/Doctrine/Record/Listener/Interface.php @@ -28,7 +28,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated Remove. */ interface Doctrine_Record_Listener_Interface { diff --git a/lib/Doctrine/Record/State/Exception.php b/lib/Doctrine/Record/State/Exception.php index fd2ad4322..56cd5338d 100644 --- a/lib/Doctrine/Record/State/Exception.php +++ b/lib/Doctrine/Record/State/Exception.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Record_Exception'); + /** * Doctrine_Exception * @@ -28,7 +28,8 @@ Doctrine::autoload('Doctrine_Record_Exception'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @todo Deprecated. Remove */ class Doctrine_Record_State_Exception extends Doctrine_Record_Exception { } \ No newline at end of file diff --git a/lib/Doctrine/Relation.php b/lib/Doctrine/Relation.php index 84d6cb509..3a945a0cb 100644 --- a/lib/Doctrine/Relation.php +++ b/lib/Doctrine/Relation.php @@ -29,7 +29,8 @@ * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated */ abstract class Doctrine_Relation implements ArrayAccess { diff --git a/lib/Doctrine/Relation/Association.php b/lib/Doctrine/Relation/Association.php index c43c3f598..b1b681fdd 100644 --- a/lib/Doctrine/Relation/Association.php +++ b/lib/Doctrine/Relation/Association.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Relation'); + /** * Doctrine_Relation_Association * @@ -30,7 +30,8 @@ Doctrine::autoload('Doctrine_Relation'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated */ class Doctrine_Relation_Association extends Doctrine_Relation { diff --git a/lib/Doctrine/Relation/Association/Self.php b/lib/Doctrine/Relation/Association/Self.php index 12209c9ef..6a63d298d 100644 --- a/lib/Doctrine/Relation/Association/Self.php +++ b/lib/Doctrine/Relation/Association/Self.php @@ -28,7 +28,8 @@ * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated */ class Doctrine_Relation_Association_Self extends Doctrine_Relation_Association { diff --git a/lib/Doctrine/Relation/ForeignKey.php b/lib/Doctrine/Relation/ForeignKey.php index 0da32cd80..33018dbe2 100644 --- a/lib/Doctrine/Relation/ForeignKey.php +++ b/lib/Doctrine/Relation/ForeignKey.php @@ -29,7 +29,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated */ class Doctrine_Relation_ForeignKey extends Doctrine_Relation { diff --git a/lib/Doctrine/Relation/LocalKey.php b/lib/Doctrine/Relation/LocalKey.php index 9590b2069..16c241f86 100644 --- a/lib/Doctrine/Relation/LocalKey.php +++ b/lib/Doctrine/Relation/LocalKey.php @@ -29,7 +29,8 @@ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.phpdoctrine.org * @since 1.0 - * @version $Revision$ + * @version $Revision$ + * @deprecated */ class Doctrine_Relation_LocalKey extends Doctrine_Relation { diff --git a/lib/Doctrine/Relation/Nest.php b/lib/Doctrine/Relation/Nest.php index ff22c20c6..489ac856b 100644 --- a/lib/Doctrine/Relation/Nest.php +++ b/lib/Doctrine/Relation/Nest.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Relation_Association'); + /** * Doctrine_Relation_Association_Self * @@ -28,7 +28,8 @@ Doctrine::autoload('Doctrine_Relation_Association'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision: 1434 $ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated */ class Doctrine_Relation_Nest extends Doctrine_Relation_Association { diff --git a/lib/Doctrine/Relation/Parser.php b/lib/Doctrine/Relation/Parser.php index 7332ab3ac..da78957da 100644 --- a/lib/Doctrine/Relation/Parser.php +++ b/lib/Doctrine/Relation/Parser.php @@ -30,7 +30,8 @@ * @link www.phpdoctrine.org * @since 1.0 * @todo Composite key support? - * @todo Remove. Association mapping needs a reimplementation. + * @todo Remove. Association mapping is being reimplemented. + * @deprecated */ class Doctrine_Relation_Parser { diff --git a/lib/Doctrine/Relation/Parser/Exception.php b/lib/Doctrine/Relation/Parser/Exception.php index 49802dd85..14544c6cf 100644 --- a/lib/Doctrine/Relation/Parser/Exception.php +++ b/lib/Doctrine/Relation/Parser/Exception.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Relation_Exception'); + /** * Doctrine_Relation_Parser_Exception * @@ -28,7 +28,8 @@ Doctrine::autoload('Doctrine_Relation_Exception'); * @link www.phpdoctrine.org * @since 1.0 * @version $Revision$ - * @author Konsta Vesterinen + * @author Konsta Vesterinen + * @deprecated Remove. */ class Doctrine_Relation_Parser_Exception extends Doctrine_Relation_Exception { } \ No newline at end of file diff --git a/lib/Doctrine/Sequence/Mysql.php b/lib/Doctrine/Sequence/Mysql.php index c4edbbf35..ca96f6d07 100644 --- a/lib/Doctrine/Sequence/Mysql.php +++ b/lib/Doctrine/Sequence/Mysql.php @@ -83,7 +83,7 @@ class Doctrine_Sequence_Mysql extends Doctrine_Sequence */ public function lastInsertId($table = null, $field = null) { - return $this->conn->getDbh()->lastInsertId(); + return $this->conn->getPdo()->lastInsertId(); } /** diff --git a/lib/Doctrine/Sequence/Sqlite.php b/lib/Doctrine/Sequence/Sqlite.php index 23d7e1440..bc34a15ef 100644 --- a/lib/Doctrine/Sequence/Sqlite.php +++ b/lib/Doctrine/Sequence/Sqlite.php @@ -84,7 +84,7 @@ class Doctrine_Sequence_Sqlite extends Doctrine_Sequence */ public function lastInsertId($table = null, $field = null) { - return $this->conn->getDbh()->lastInsertId(); + return $this->conn->getPdo()->lastInsertId(); } /** diff --git a/tests/Orm/EntityPersisterTest.php b/tests/Orm/EntityPersisterTest.php index cfff88c57..d738673ff 100644 --- a/tests/Orm/EntityPersisterTest.php +++ b/tests/Orm/EntityPersisterTest.php @@ -11,32 +11,54 @@ class Orm_EntityPersisterTest extends Doctrine_OrmTestCase private $_persister; // SUT private $_connMock; private $_emMock; + private $_seqManagerMock; protected function setUp() { parent::setUp(); $this->_connMock = new Doctrine_ConnectionMock(array()); $this->_emMock = new Doctrine_EntityManagerMock($this->_connMock); + $this->_seqManagerMock = new Doctrine_SequenceMock($this->_connMock); + $this->_connMock->setDatabasePlatform(new Doctrine_DatabasePlatformMock()); + $this->_connMock->setSequenceManager($this->_seqManagerMock); $this->_persister = new Doctrine_EntityPersister_Standard( $this->_emMock, $this->_emMock->getClassMetadata("ForumUser")); } - public function testTest() { + public function testInsert() { $user = new ForumUser(); $user->username = "romanb"; - $user->avatar = new ForumAvatar(); - $this->_persister->insert($user); - + $this->_seqManagerMock->autoinc(); //fake identity column autoinc + $this->_persister->insert($user->avatar); $inserts = $this->_connMock->getInserts(); - //var_dump($inserts); + //check + $this->assertEquals(1, count($inserts)); + $this->assertEquals(0, $user->avatar->id); + $this->assertTrue(isset($inserts['forum_avatar'])); + $this->assertEquals(1, count($inserts['forum_avatar'])); + $this->assertTrue(empty($inserts['forum_avatar'][0])); + + $this->_seqManagerMock->autoinc(); //fake identity column autoinc + $this->_persister->insert($user); + $inserts = $this->_connMock->getInserts(); + //check + $this->assertEquals(2, count($inserts)); + $this->assertEquals(1, $user->id); $this->assertTrue(isset($inserts['forum_user'])); $this->assertEquals(1, count($inserts['forum_user'])); - $this->assertEquals(1, count($inserts['forum_user'][0])); + $this->assertEquals(3, count($inserts['forum_user'][0])); + //username column $this->assertTrue(isset($inserts['forum_user'][0]['username'])); $this->assertEquals('romanb', $inserts['forum_user'][0]['username']); + //avatar_id join column + $this->assertTrue(isset($inserts['forum_user'][0]['avatar_id'])); + $this->assertEquals(0, $inserts['forum_user'][0]['avatar_id']); + //dtype discriminator column + $this->assertTrue(isset($inserts['forum_user'][0]['dtype'])); + $this->assertEquals('user', $inserts['forum_user'][0]['dtype']); } } \ No newline at end of file diff --git a/tests/Orm/Query/SelectSqlGenerationTest.php b/tests/Orm/Query/SelectSqlGenerationTest.php index 49f582ff6..df63bbde1 100755 --- a/tests/Orm/Query/SelectSqlGenerationTest.php +++ b/tests/Orm/Query/SelectSqlGenerationTest.php @@ -172,7 +172,7 @@ class Orm_Query_SelectSqlGenerationTest extends Doctrine_OrmTestCase { $this->assertSqlGeneration( 'SELECT u.id, a.id FROM CmsUser u LEFT JOIN u.articles a', - 'SELECT cu.id AS cu__id, ca.id AS ca__id FROM cms_user cu LEFT JOIN cms_article ca ON ca.user_id = cu.id WHERE 1 = 1' + 'SELECT cu.id AS cu__id, ca.id AS ca__id FROM cms_user cu LEFT JOIN cms_article ca ON cu.id = ca.user_id WHERE 1 = 1' ); } diff --git a/tests/Orm/UnitOfWorkTest.php b/tests/Orm/UnitOfWorkTest.php index c86e4a440..27dbb52c2 100644 --- a/tests/Orm/UnitOfWorkTest.php +++ b/tests/Orm/UnitOfWorkTest.php @@ -126,6 +126,23 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase $this->assertEquals(0, count($this->_persisterMock->getDeletes())); } + public function testCommitOrder() + { + $avatar = new ForumAvatar(); + $this->_user->avatar = $avatar; + $this->_unitOfWork->save($this->_user); // save cascaded to avatar + + $this->assertEquals(2, count($this->_persisterMock->getInserts())); // insert forced + $this->assertEquals(0, count($this->_persisterMock->getUpdates())); + $this->assertEquals(0, count($this->_persisterMock->getDeletes())); + // verify order of inserts()s + $inserts = $this->_persisterMock->getInserts(); + $this->assertSame($avatar, $inserts[0]); + $this->assertSame($this->_user, $inserts[1]); + + //... + } + public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert() { //... diff --git a/tests/lib/mocks/Doctrine_SequenceMock.php b/tests/lib/mocks/Doctrine_SequenceMock.php index 8933f7b56..9b2fd1e00 100644 --- a/tests/lib/mocks/Doctrine_SequenceMock.php +++ b/tests/lib/mocks/Doctrine_SequenceMock.php @@ -34,6 +34,11 @@ class Doctrine_SequenceMock extends Doctrine_Sequence { $this->_sequenceNumber = 0; } + + public function autoinc() + { + $this->_sequenceNumber++; + } } ?> \ No newline at end of file diff --git a/tests/models/cms/CmsArticle.php b/tests/models/cms/CmsArticle.php index e04a9b442..98714e049 100755 --- a/tests/models/cms/CmsArticle.php +++ b/tests/models/cms/CmsArticle.php @@ -35,17 +35,16 @@ class CmsArticle extends Doctrine_Entity 'length' => 4 )); - /*$mapping->hasMany('CmsComment as comments', array( - 'local' => 'id', 'foreign' => 'article_id'));*/ $mapping->mapOneToMany(array( 'fieldName' => 'comments', 'targetEntity' => 'CmsComment', )); - /*$mapping->mapManyToOne(array( - 'fieldName' => 'author', + $mapping->mapManyToOne(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', 'joinColumns' => array('user_id' => 'id') - ));*/ + )); } } diff --git a/tests/models/cms/CmsPhonenumber.php b/tests/models/cms/CmsPhonenumber.php index 6e7d22a3d..481d1172b 100755 --- a/tests/models/cms/CmsPhonenumber.php +++ b/tests/models/cms/CmsPhonenumber.php @@ -17,5 +17,11 @@ class CmsPhonenumber extends Doctrine_Entity 'length' => 50, 'id' => true )); + + $mapping->mapManyToOne(array( + 'fieldName' => 'user', + 'targetEntity' => 'CmsUser', + 'joinColumns' => array('user_id' => 'id') + )); } } diff --git a/tests/models/cms/CmsUser.php b/tests/models/cms/CmsUser.php index 74f51938b..babc2acf3 100644 --- a/tests/models/cms/CmsUser.php +++ b/tests/models/cms/CmsUser.php @@ -35,21 +35,17 @@ class CmsUser extends Doctrine_Entity 'type' => 'string', 'length' => 255 )); - - /*$mapping->hasMany('CmsPhonenumber as phonenumbers', array( - 'local' => 'id', 'foreign' => 'user_id')); - $mapping->hasMany('CmsArticle as articles', array( - 'local' => 'id', 'foreign' => 'user_id'));*/ $mapping->mapOneToMany(array( 'fieldName' => 'phonenumbers', 'targetEntity' => 'CmsPhonenumber', - + 'mappedBy' => 'user' )); $mapping->mapOneToMany(array( 'fieldName' => 'articles', 'targetEntity' => 'CmsArticle', + 'mappedBy' => 'user' )); } diff --git a/tests/models/forum/ForumUser.php b/tests/models/forum/ForumUser.php index 668397364..55c5b5cef 100644 --- a/tests/models/forum/ForumUser.php +++ b/tests/models/forum/ForumUser.php @@ -21,12 +21,6 @@ class ForumUser extends Doctrine_Entity )); // register subclasses $mapping->setSubclasses(array('ForumAdministrator')); - // the discriminator column - $mapping->mapField(array( - 'fieldName' => 'dtype', - 'type' => 'string', - 'length' => 50 - )); // column-to-field mapping $mapping->mapField(array( @@ -46,6 +40,7 @@ class ForumUser extends Doctrine_Entity 'fieldName' => 'avatar', 'targetEntity' => 'ForumAvatar', 'joinColumns' => array('avatar_id' => 'id'), + 'cascade' => array('save') )); }