Commit 2395888f authored by romanb's avatar romanb

General work. Now using spl_object_hash.

parent 6be6f40e
<?php
/**
* A class loader used to load class files on demand.
*
* Usage recommendation:
* 1) Use only 1 class loader instance.
* 2) Prepend the base paths to your class libraries (including Doctrine's) to your include path.
* 2) Set the base paths to your class libraries (including Doctrine's) through
* $classLoader->setBasePath($prefix, $basePath);
* Example:
* $classLoader->setBasePath('Doctrine', '/usr/local/phplibs/doctrine/lib');
* Then, when trying to load the class Doctrine\ORM\EntityManager, for example
* the classloader will look for /usr/local/phplibs/doctrine/lib/Doctrine/ORM/EntityManager.php
*
* 3) DO NOT setCheckFileExists(true). Doing so is expensive in terms of performance.
* 4) Use an opcode-cache (i.e. APC) (STRONGLY RECOMMENDED).
*
* @since 2.0
* @author romanb <roman@code-factory.org>
......@@ -16,7 +22,7 @@ class Doctrine_Common_ClassLoader
private $_namespaceSeparator = '_';
private $_fileExtension = '.php';
private $_checkFileExists = false;
private $_basePath;
private $_basePaths = array();
public function __construct()
{
......@@ -38,19 +44,20 @@ class Doctrine_Common_ClassLoader
}
/**
* Sets a static base path that is prepended to the path derived from the class itself.
* Sets a static base path for classes with a certain prefix that is prepended
* to the path derived from the class itself.
*
* @param string $basePath
*/
public function setBasePath($basePath)
public function setBasePath($classPrefix, $basePath)
{
$this->_basePath = $basePath;
$this->_basePaths[$classPrefix] = $basePath;
}
/**
* Loads the given class or interface.
*
* @param string $classname The name of the class to load.
* @param string $classname The name of the class to load.
* @return boolean TRUE if the class has been successfully loaded, FALSE otherwise.
*/
public function loadClass($className)
......@@ -59,9 +66,10 @@ class Doctrine_Common_ClassLoader
return false;
}
$prefix = substr($className, 0, strpos($className, $this->_namespaceSeparator));
$class = '';
if ($this->_basePath) {
$class .= $this->_basePath . DIRECTORY_SEPARATOR;
if (isset($this->_basePaths[$prefix])) {
$class .= $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR;
}
$class .= str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $className)
. $this->_fileExtension;
......
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::Common;
#namespace Doctrine\Common;
/**
* The EventManager is the central point of Doctrine's event listener system.
......@@ -101,7 +101,7 @@ class Doctrine_Common_EventManager
* Adds an EventSubscriber. The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param Doctrine::Common::Event::EventSubscriber $subscriber The subscriber.
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
*/
public function addEventSubscriber(Doctrine_Common_EventSubscriber $subscriber)
{
......
......@@ -22,8 +22,7 @@
#namespace Doctrine\DBAL;
/**
* The Configuration is the container for all configuration options of Doctrine.
* It combines all configuration options from DBAL & ORM.
* Configuration container for the Doctrine DBAL.
*
* INTERNAL: When adding a new configuration option just write a getter/setter
* pair and add the option to the _attributes array with a proper default value.
......
<?php
/**
* PDO implementation of the driver Connection interface.
* Used by all PDO-based drivers.
......@@ -16,5 +15,3 @@ class Doctrine_DBAL_Driver_PDOConnection extends PDO implements Doctrine_DBAL_Dr
$this->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
}
}
?>
\ No newline at end of file
<?php
#namespace Doctrine::DBAL::Driver::PDOMySql;
#namespace Doctrine\DBAL\Driver\PDOMySql;
#use Doctrine::DBAL::Driver;
#use Doctrine\DBAL\Driver;
class Doctrine_DBAL_Driver_PDOMySql_Driver implements Doctrine_DBAL_Driver
{
......
......@@ -58,8 +58,8 @@ class Doctrine_DBAL_Driver_PDOSqlite_Driver implements Doctrine_DBAL_Driver
/**
* Gets the schema manager that is relevant for this driver.
*
* @param Doctrine::DBAL::Connection $conn
* @return Doctrine::DBAL::Schema::AbstractSchemaManager
* @param Doctrine\DBAL\Connection $conn
* @return Doctrine\DBAL\Schema\AbstractSchemaManager
*/
public function getSchemaManager(Doctrine_DBAL_Connection $conn)
{
......
......@@ -2,5 +2,3 @@
class Doctrine_DBAL_Driver_PDOStatement extends PDOStatement implements Doctrine_DBAL_Driver_Statement
{}
?>
\ No newline at end of file
......@@ -18,16 +18,16 @@
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::DBAL::Driver;
#namespace Doctrine\DBAL\Driver;
/**
* Statement interface.
* Drivers must implement this interface.
*
* This resembles the PDOStatement interface.
* Statement interface.
* Drivers must implement this interface.
*
* This resembles the PDOStatement interface.
*
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
......
......@@ -19,10 +19,10 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::DBAL;
#namespace Doctrine\DBAL;
/**
* Factory for creating Doctrine::DBAL::Connection instances.
* Factory for creating Doctrine\DBAL\Connection instances.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
......@@ -43,12 +43,13 @@ final class Doctrine_DBAL_DriverManager
'pdo_firebird' => 'Doctrine_DBAL_Driver_PDOFirebird_Driver',
'pdo_informix' => 'Doctrine_DBAL_Driver_PDOInformix_Driver',
);
/** Private constructor. This class cannot be instantiated. */
private function __construct() {}
/**
* Creates a connection object based on the specified parameters.
* This method returns a Doctrine::DBAL::Connection which wraps the underlying
* This method returns a Doctrine\DBAL\Connection which wraps the underlying
* driver connection.
*
* $params must contain at least one of the following.
......@@ -79,16 +80,16 @@ final class Doctrine_DBAL_DriverManager
*
* <b>pdo</b>:
* You can pass an existing PDO instance through this parameter. The PDO
* instance will be wrapped in a Doctrine::DBAL::Connection.
* instance will be wrapped in a Doctrine\DBAL\Connection.
*
* <b>wrapperClass</b>:
* You may specify a custom wrapper class through the 'wrapperClass'
* parameter but this class MUST inherit from Doctrine::DBAL::Connection.
* parameter but this class MUST inherit from Doctrine\DBAL\Connection.
*
* @param array $params The parameters.
* @param Doctrine::Common::Configuration The configuration to use.
* @param Doctrine::Common::EventManager The event manager to use.
* @return Doctrine::DBAL::Connection
* @param Doctrine\DBAL\Configuration The configuration to use.
* @param Doctrine\Common\EventManager The event manager to use.
* @return Doctrine\DBAL\Connection
*/
public static function getConnection(array $params,
Doctrine_DBAL_Configuration $config = null,
......
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::DBAL::Platforms;
#namespace Doctrine\DBAL\Platforms;
/**
* Base class for all DatabasePlatforms. The DatabasePlatforms are the central
......
......@@ -19,10 +19,10 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM;
#namespace Doctrine\ORM;
/**
* A persistent collection.
* A persistent collection wrapper.
*
* A collection object is strongly typed in the sense that it can only contain
* entities of a specific type or one of it's subtypes. A collection object is
......@@ -73,7 +73,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
protected $_snapshot = array();
/**
* This entity that owns this collection.
* The entity that owns this collection.
*
* @var Doctrine\ORM\Entity
*/
......@@ -468,7 +468,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
public function add($value, $key = null)
{
if ( ! $value instanceof $this->_entityBaseType) {
throw new Doctrine_Record_Exception('Value variable in collection is not an instance of Doctrine_Entity.');
throw new Doctrine_Exception('Invalid instance.');
}
// TODO: Really prohibit duplicates?
......@@ -511,121 +511,6 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
//TODO: Register collection as dirty with the UoW if necessary
//$this->_changed();
}
/**
* INTERNAL:
* loadRelated
*
* @param mixed $name
* @return boolean
* @todo New implementation & maybe move elsewhere.
*/
/*public function loadRelated($name = null)
{
$list = array();
$query = new Doctrine_Query($this->_mapper->getConnection());
if ( ! isset($name)) {
foreach ($this->_data as $record) {
// FIXME: composite key support
$ids = $record->identifier();
$value = count($ids) > 0 ? array_pop($ids) : null;
if ($value !== null) {
$list[] = $value;
}
}
$query->from($this->_mapper->getComponentName()
. '(' . implode(", ",$this->_mapper->getTable()->getPrimaryKeys()) . ')');
$query->where($this->_mapper->getComponentName()
. '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')');
return $query;
}
$rel = $this->_mapper->getTable()->getRelation($name);
if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) {
foreach ($this->_data as $record) {
$list[] = $record[$rel->getLocal()];
}
} else {
foreach ($this->_data as $record) {
$ids = $record->identifier();
$value = count($ids) > 0 ? array_pop($ids) : null;
if ($value !== null) {
$list[] = $value;
}
}
}
$dql = $rel->getRelationDql(count($list), 'collection');
$coll = $query->query($dql, $list);
$this->populateRelated($name, $coll);
}*/
/**
* INTERNAL:
* populateRelated
*
* @param string $name
* @param Doctrine_Collection $coll
* @return void
* @todo New implementation & maybe move elsewhere.
*/
/*protected function populateRelated($name, Doctrine_Collection $coll)
{
$rel = $this->_mapper->getTable()->getRelation($name);
$table = $rel->getTable();
$foreign = $rel->getForeign();
$local = $rel->getLocal();
if ($rel instanceof Doctrine_Relation_LocalKey) {
foreach ($this->_data as $key => $record) {
foreach ($coll as $k => $related) {
if ($related[$foreign] == $record[$local]) {
$this->_data[$key]->_setRelated($name, $related);
}
}
}
} else if ($rel instanceof Doctrine_Relation_ForeignKey) {
foreach ($this->_data as $key => $record) {
if ( ! $record->exists()) {
continue;
}
$sub = new Doctrine_Collection($rel->getForeignComponentName());
foreach ($coll as $k => $related) {
if ($related[$foreign] == $record[$local]) {
$sub->add($related);
$coll->remove($k);
}
}
$this->_data[$key]->_setRelated($name, $sub);
}
} else if ($rel instanceof Doctrine_Relation_Association) {
// @TODO composite key support
$identifier = (array)$this->_mapper->getClassMetadata()->getIdentifier();
$asf = $rel->getAssociationFactory();
$name = $table->getComponentName();
foreach ($this->_data as $key => $record) {
if ( ! $record->exists()) {
continue;
}
$sub = new Doctrine_Collection($rel->getForeignComponentName());
foreach ($coll as $k => $related) {
$idField = $identifier[0];
if ($related->get($local) == $record[$idField]) {
$sub->add($related->get($name));
}
}
$this->_data[$key]->_setRelated($name, $sub);
}
}
}*/
/**
* INTERNAL:
......@@ -661,7 +546,8 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
}
/**
* INTERNAL: Returns the data of the last snapshot.
* INTERNAL:
* Returns the data of the last snapshot.
*
* @return array returns the data in last snapshot
*/
......@@ -671,7 +557,8 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
}
/**
* INTERNAL: Processes the difference of the last snapshot and the current data.
* INTERNAL:
* Processes the difference of the last snapshot and the current data.
*
* an example:
* Snapshot with the objects 1, 2 and 4
......@@ -719,7 +606,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
}
/**
* Populate a Doctrine_Collection from an array of data.
* Populate a Collection from an array of data.
*
* @param string $array
* @return void
......@@ -733,7 +620,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
}
/**
* Synchronizes a Doctrine_Collection with data from an array.
* Synchronizes a Collection with data from an array.
*
* it expects an array representation of a Doctrine_Collection similar to the return
* value of the toArray() method. It will create Dectrine_Records that don't exist
......@@ -760,7 +647,8 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
}
/**
* INTERNAL: getDeleteDiff
* INTERNAL:
* getDeleteDiff
*
* @return array
*/
......@@ -786,17 +674,17 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
*/
protected function _compareRecords($a, $b)
{
if ($a->getOid() == $b->getOid()) {
if ($a === $b) {
return 0;
}
return ($a->getOid() > $b->getOid()) ? 1 : -1;
return 1;
}
/**
*
* @param <type> $deep
*/
public function free($deep = false)
/*public function free($deep = false)
{
foreach ($this->getData() as $key => $record) {
if ( ! ($record instanceof Doctrine_Null)) {
......@@ -810,8 +698,7 @@ class Doctrine_ORM_Collection implements Countable, IteratorAggregate, Serializa
$this->_owner->free($deep);
$this->_owner = null;
}
}
}*/
/**
* getIterator
......
......@@ -24,7 +24,7 @@
#use Doctrine\DBAL\Configuration;
/**
* The Configuration is the container for all configuration options of Doctrine.
* Configuration container for all configuration options of Doctrine.
* It combines all configuration options from DBAL & ORM.
*
* INTERNAL: When adding a new configuration option just write a getter/setter
......@@ -72,7 +72,7 @@ class Doctrine_ORM_Configuration extends Doctrine_DBAL_Configuration
return $this->_attributes['metadataCacheImpl'];
}
public function setMetadataCacheImpl(Doctrine_Cache_Interface $cacheImpl)
public function setMetadataCacheImpl($cacheImpl)
{
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
}
......
......@@ -21,8 +21,6 @@
#namespace Doctrine\ORM;
#use \Serializable;
/**
* Entity marker interface.
*
......
......@@ -86,14 +86,14 @@ class Doctrine_ORM_EntityManager
/**
* The database connection used by the EntityManager.
*
* @var Doctrine_Connection
* @var Connection
*/
private $_conn;
/**
* The metadata factory, used to retrieve the metadata of entity classes.
*
* @var Doctrine::ORM::Mapping::ClassMetadataFactory
* @var Doctrine\ORM\Mapping\ClassMetadataFactory
*/
private $_metadataFactory;
......@@ -131,19 +131,8 @@ class Doctrine_ORM_EntityManager
* @var EventManager
*/
private $_eventManager;
/**
* Container that is used temporarily during hydration.
*
* @var array
*/
private $_tmpEntityData = array();
private $_idGenerators = array();
private $_closed = false;
private $_originalEntityData = array();
/**
* Creates a new EntityManager that operates on the given database connection.
......@@ -220,6 +209,7 @@ class Doctrine_ORM_EntityManager
* Returns the metadata for a class.
*
* @return Doctrine_Metadata
* @internal Performance-sensitive method.
*/
public function getClassMetadata($className)
{
......@@ -262,8 +252,7 @@ class Doctrine_ORM_EntityManager
* Creates a new Query object.
*
* @param string The DQL string.
* @return Doctrine::ORM::Query
* @todo package:orm
* @return Doctrine\ORM\Query
*/
public function createQuery($dql = "")
{
......@@ -271,7 +260,6 @@ class Doctrine_ORM_EntityManager
if ( ! empty($dql)) {
$query->setDql($dql);
}
return $query;
}
......@@ -281,16 +269,16 @@ class Doctrine_ORM_EntityManager
* This is usually not of interest for users, mainly for internal use.
*
* @param string $entityName The name of the Entity.
* @return Doctrine::ORM::Internal::EntityPersister
* @return Doctrine\ORM\Persister\AbstractEntityPersister
*/
public function getEntityPersister($entityName)
{
if ( ! isset($this->_persisters[$entityName])) {
$class = $this->getClassMetadata($entityName);
if ($class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED) {
if ($class->getInheritanceType() == Doctrine_ORM_Mapping_ClassMetadata::INHERITANCE_TYPE_JOINED) {
$persister = new Doctrine_EntityPersister_JoinedSubclass($this, $class);
} else {
$persister = new Doctrine_EntityPersister_Standard($this, $class);
$persister = new Doctrine_ORM_Persisters_StandardEntityPersister($this, $class);
}
$this->_persisters[$entityName] = $persister;
}
......@@ -444,7 +432,7 @@ class Doctrine_ORM_EntityManager
/**
* Removes the given entity from the persistent store.
*
* @param Doctrine::ORM::Entity $entity
* @param Doctrine\ORM\Entity $entity
* @return void
*/
public function delete(Doctrine_ORM_Entity $entity)
......@@ -506,93 +494,6 @@ class Doctrine_ORM_EntityManager
return $repository;
}
/**
* Creates an entity. Used for reconstitution as well as initial creation.
*
* @param string $className The name of the entity class.
* @param array $data The data for the entity.
* @return Doctrine\ORM\Entity
*/
public function createEntity($className, array $data, Doctrine_Query $query = null)
{
$this->_errorIfNotActiveOrClosed();
$this->_tmpEntityData = $data;
$className = $this->_inferCorrectClassName($data, $className);
$classMetadata = $this->getClassMetadata($className);
if ( ! empty($data)) {
$identifierFieldNames = $classMetadata->getIdentifier();
$isNew = false;
foreach ($identifierFieldNames as $fieldName) {
if ( ! isset($data[$fieldName])) {
// id field not found return new entity
$isNew = true;
break;
}
$id[] = $data[$fieldName];
}
if ($isNew) {
$entity = new $className;
} else {
$idHash = $this->_unitOfWork->getIdentifierHash($id);
$entity = $this->_unitOfWork->tryGetByIdHash($idHash, $classMetadata->getRootClassName());
if ($entity) {
$this->_mergeData($entity, $data/*, $classMetadata, $query->getHint('doctrine.refresh')*/);
return $entity;
} else {
$entity = new $className;
$this->_unitOfWork->addToIdentityMap($entity);
}
}
} else {
$entity = new $className;
}
//$this->_originalEntityData[$entity->getOid()] = $data;
return $entity;
}
/**
* Merges the given data into the given entity, optionally overriding
* local changes.
*
* @param Doctrine\ORM\Entity $entity
* @param array $data
* @param boolean $overrideLocalChanges
* @return void
*/
private function _mergeData(Doctrine_ORM_Entity $entity, /*$class,*/ array $data, $overrideLocalChanges = false) {
if ($overrideLocalChanges) {
foreach ($data as $field => $value) {
$entity->_internalSetField($field, $value);
}
} else {
foreach ($data as $field => $value) {
$currentValue = $entity->get($field);
if ( ! isset($currentValue) || $entity->_internalGetField($field) === null) {
$entity->_internalSetField($field, $value);
}
}
}
// NEW
/*if ($overrideLocalChanges) {
foreach ($data as $field => $value) {
$class->getReflectionProperty($field)->setValue($entity, $value);
}
} else {
foreach ($data as $field => $value) {
$currentValue = $class->getReflectionProperty($field)->getValue($entity);
if ( ! isset($this->_originalEntityData[$entity->getOid()]) ||
$currentValue == $this->_originalEntityData[$entity->getOid()]) {
$class->getReflectionProperty($field)->setValue($entity, $value);
}
}
}*/
}
/**
* Checks if the instance is managed by the EntityManager.
*
......@@ -606,51 +507,10 @@ class Doctrine_ORM_EntityManager
! $this->_unitOfWork->isRegisteredRemoved($entity);
}
/**
* INTERNAL: For internal hydration purposes only.
*
* Gets the temporarily stored entity data.
*
* @return array
*/
public function _getTmpEntityData()
{
$data = $this->_tmpEntityData;
$this->_tmpEntityData = array();
return $data;
}
/**
* Check the dataset for a discriminator column to determine the correct
* class to instantiate. If no discriminator column is found, the given
* classname will be returned.
*
* @param array $data
* @param string $className
* @return string The name of the class to instantiate.
*/
private function _inferCorrectClassName(array $data, $className)
{
$class = $this->getClassMetadata($className);
$discCol = $class->getInheritanceOption('discriminatorColumn');
if ( ! $discCol) {
return $className;
}
$discMap = $class->getInheritanceOption('discriminatorMap');
if (isset($data[$discCol], $discMap[$data[$discCol]])) {
return $discMap[$data[$discCol]];
} else {
return $className;
}
}
/**
* Gets the EventManager used by the EntityManager.
*
* @return Doctrine::Common::EventManager
* @return Doctrine\Common\EventManager
*/
public function getEventManager()
{
......@@ -660,7 +520,7 @@ class Doctrine_ORM_EntityManager
/**
* Gets the Configuration used by the EntityManager.
*
* @return Doctrine::Common::Configuration
* @return Doctrine\ORM\Configuration
*/
public function getConfiguration()
{
......@@ -682,7 +542,7 @@ class Doctrine_ORM_EntityManager
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*
* @return Doctrine::ORM::UnitOfWork
* @return Doctrine\ORM\UnitOfWork
*/
public function getUnitOfWork()
{
......@@ -747,7 +607,7 @@ class Doctrine_ORM_EntityManager
/**
* Static lookup to get the currently active EntityManager.
*
* @return Doctrine::ORM::EntityManager
* @return Doctrine\ORM\EntityManager
*/
public static function getActiveEntityManager()
{
......
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM;
#namespace Doctrine\ORM;
/**
* A repository provides the illusion of an in-memory Entity store.
......
<?php
#namespace Doctrine::ORM::Id;
#namespace Doctrine\ORM\Id;
/**
* Enter description here...
......@@ -8,17 +8,33 @@
* @todo Rename to AbstractIdGenerator
*/
abstract class Doctrine_ORM_Id_AbstractIdGenerator
{
const POST_INSERT_INDICATOR = 'POST_INSERT_INDICATOR';
{
protected $_em;
public function __construct(Doctrine_ORM_EntityManager $em)
{
$this->_em = $em;
}
/**
* Generates an identifier for an entity.
*
* @param Doctrine\ORM\Entity $entity
* @return mixed
*/
abstract public function generate($entity);
/**
* Gets whether this generator is a post-insert generator which means that
* {@link generate()} must be called after the entity has been inserted
* into the database.
* By default, this method returns FALSE. Generators that have this requirement
* must override this method and return TRUE.
*
* @return boolean
*/
public function isPostInsertGenerator() {
return false;
}
}
?>
\ No newline at end of file
......@@ -4,23 +4,43 @@
* Special generator for application-assigned identifiers (doesnt really generate anything).
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
*/
class Doctrine_ORM_Id_Assigned extends Doctrine_ORM_Id_AbstractIdGenerator
{
/**
* Enter description here...
* Returns the identifier assigned to the given entity.
*
* @param Doctrine_ORM_Entity $entity
* @return unknown
* @param Doctrine\ORM\Entity $entity
* @return mixed
* @override
*/
public function generate($entity)
{
if ( ! $entity->_identifier()) {
throw new Doctrine_Exception("Entity '$entity' is missing an assigned Id");
$class = $this->_em->getClassMetadata(get_class($entity));
if ($class->isIdentifierComposite()) {
$identifier = array();
$idFields = $class->getIdentifierFieldNames();
foreach ($idFields as $idField) {
$identifier[] =
$value = $class->getReflectionProperty($idField)->getValue($entity);
if (isset($value)) {
$identifier[] = $value;
}
}
} else {
$value = $class->getReflectionProperty($class->getSingleIdentifierFieldName())
->getValue($entity);
if (isset($value)) {
$identifier = array($value);
}
}
return $entity->_identifier();
if ( ! $identifier) {
throw new Doctrine_Exception("Entity '$entity' is missing an assigned ID.");
}
return $identifier;
}
}
?>
\ No newline at end of file
......@@ -10,14 +10,16 @@ class Doctrine_ORM_Id_IdentityGenerator extends Doctrine_ORM_Id_AbstractIdGenera
* @override
*/
public function generate($entity)
{
return self::POST_INSERT_INDICATOR;
}
public function getPostInsertId()
{
return $this->_em->getConnection()->lastInsertId();
}
/**
* @return boolean
* @override
*/
public function isPostInsertGenerator() {
return true;
}
}
?>
\ No newline at end of file
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal;
#namespace Doctrine\ORM\Internal;
/**
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
......
......@@ -19,11 +19,11 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal;
#namespace Doctrine\ORM\Internal;
/**
* A CommitOrderNode is a temporary wrapper around ClassMetadata instances
* that is used to sort the order of commits.
* that is used to sort the order of commits in a UnitOfWork.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
......@@ -53,7 +53,7 @@ class Doctrine_ORM_Internal_CommitOrderNode
* Creates a new node.
*
* @param mixed $wrappedObj The object to wrap.
* @param Doctrine::ORM::Internal::CommitOrderCalculator $calc The calculator.
* @param Doctrine\ORM\Internal\CommitOrderCalculator $calc The calculator.
*/
public function __construct($wrappedObj, Doctrine_ORM_Internal_CommitOrderCalculator $calc)
{
......@@ -155,7 +155,7 @@ class Doctrine_ORM_Internal_CommitOrderNode
/**
* Adds a directed dependency (an edge on the graph). "$this -before-> $other".
*
* @param Doctrine::ORM::Internal::CommitOrderNode $node
* @param Doctrine\ORM\Internal\CommitOrderNode $node
*/
public function before(Doctrine_ORM_Internal_CommitOrderNode $node)
{
......@@ -163,4 +163,3 @@ class Doctrine_ORM_Internal_CommitOrderNode
}
}
?>
\ No newline at end of file
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal::Hydration;
#namespace Doctrine\ORM\Internal\Hydration;
/**
* Defines an array hydration strategy.
......@@ -32,8 +32,7 @@
* @author Roman Borschel <roman@code-factory.org>
*/
class Doctrine_ORM_Internal_Hydration_ArrayDriver
{
{
/**
*
*/
......@@ -66,8 +65,7 @@ class Doctrine_ORM_Internal_Hydration_ArrayDriver
}
}
public function addRelatedIndexedElement(array &$entity1, $property, array &$entity2,
$indexField)
public function addRelatedIndexedElement(array &$entity1, $property, array &$entity2, $indexField)
{
$entity1[$property][$entity2[$indexField]] = $entity2;
}
......@@ -134,7 +132,7 @@ class Doctrine_ORM_Internal_Hydration_ArrayDriver
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param array|Collection $coll The element.
* @param array $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
......
......@@ -16,20 +16,21 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
#namespace Doctrine\ORM\Internal\Hydration;
/**
* Hydration strategy used for creating graphs of entities.
* Defines the object hydration strategy.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @link www.doctrine-project.org
* @since 1.0
* @version $Revision$
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @internal All the methods in this class are performance-sentitive.
*/
class Doctrine_ORM_Internal_Hydration_ObjectDriver
{
......@@ -38,15 +39,18 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
/** Memory for initialized relations */
private $_initializedRelations = array();
/** Null object */
private $_nullObject;
//private $_nullObject;
/** The EntityManager */
private $_em;
private $_uow;
private $_metadataMap = array();
private $_entityData = array();
public function __construct(Doctrine_ORM_EntityManager $em)
{
$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
//$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
$this->_em = $em;
$this->_uow = $this->_em->getUnitOfWork();
}
public function getElementCollection($component)
......@@ -59,7 +63,7 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
public function getLastKey($coll)
{
// check needed because of mixed results.
// is_object instead of is_array because is_array is slow.
// is_object instead of is_array because is_array is slow on large arrays.
if (is_object($coll)) {
$coll->end();
return $coll->key();
......@@ -71,10 +75,8 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
public function initRelatedCollection($entity, $name)
{
//$class = get_class($entity);
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
$classMetadata = $this->_metadataMap[$oid];
//$classMetadata = $this->_em->getClassMetadata(get_class($entity));
if ( ! isset($this->_initializedRelations[$oid][$name])) {
$relation = $classMetadata->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
......@@ -83,6 +85,7 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
$coll->_setHydrationFlag(true);
$classMetadata->getReflectionProperty($name)->setValue($entity, $coll);
$this->_initializedRelations[$oid][$name] = true;
$this->_uow->setOriginalEntityProperty($oid, $name, $coll);
}
}
......@@ -93,91 +96,102 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
public function getNullPointer()
{
return $this->_nullObject;
//TODO: Return VirtualProxy if lazy association
return null;
}
public function getElement(array $data, $className)
{
$entity = $this->_em->getUnitOfWork()->createEntity($className, $data);
$this->_metadataMap[spl_object_id($entity)] = $this->_em->getClassMetadata($className);
$oid = spl_object_hash($entity);
$this->_metadataMap[$oid] = $this->_em->getClassMetadata($className);
return $entity;
}
/**
* Adds an element to an indexed collection-valued property.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
* @param <type> $indexField
*/
public function addRelatedIndexedElement($entity1, $property, $entity2, $indexField)
{
$classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
$classMetadata2 = $this->_metadataMap[spl_object_id($entity2)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
//$classMetadata2 = $this->_em->getClassMetadata(get_class($entity2));
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)];
$classMetadata2 = $this->_metadataMap[spl_object_hash($entity2)];
$indexValue = $classMetadata2->getReflectionProperty($indexField)->getValue($entity2);
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2, $indexValue);
}
/**
* Adds an element to a collection-valued property.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
*/
public function addRelatedElement($entity1, $property, $entity2)
{
$classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
$classMetadata1->getReflectionProperty($property)
->getValue($entity1)->add($entity2);
$classMetadata1 = $this->_metadataMap[spl_object_hash($entity1)];
$classMetadata1->getReflectionProperty($property)->getValue($entity1)->add($entity2);
}
/**
* Sets a related element.
*
* @param <type> $entity1
* @param <type> $property
* @param <type> $entity2
*/
public function setRelatedElement($entity1, $property, $entity2)
{
$classMetadata1 = $this->_metadataMap[spl_object_id($entity1)];
//$classMetadata1 = $this->_em->getClassMetadata(get_class($entity1));
$classMetadata1->getReflectionProperty($property)
->setValue($entity1, $entity2);
$oid = spl_object_hash($entity1);
$classMetadata1 = $this->_metadataMap[$oid];
$classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty($oid, $property, $entity2);
$relation = $classMetadata1->getAssociationMapping($property);
if ($relation->isOneToOne()) {
$targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
if ($relation->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional
if ($targetClass->hasInverseAssociationMapping($property)) {
$refProp = $targetClass->getReflectionProperty(
$targetClass->getInverseAssociationMapping($fieldName)
->getSourceFieldName());
$refProp->setValue($entity2, $entity1);
$oid2 = spl_object_hash($entity2);
$sourceProp = $targetClass->getInverseAssociationMapping($fieldName)->getSourceFieldName();
$targetClass->getReflectionProperty($sourceProp)->setValue($entity2, $entity1);
$this->_entityData[$oid2][$sourceProp] = $entity1;
}
} else {
// for sure bidirectional, as there is no inverse side in unidirectional
$targetClass->getReflectionProperty($relation->getMappedByFieldName())
->setValue($entity2, $entity1);
$mappedByProp = $relation->getMappedByFieldName();
$targetClass->getReflectionProperty($mappedByProp)->setValue($entity2, $entity1);
$this->_entityData[spl_object_hash($entity2)][$mappedByProp] = $entity1;
}
}
}
public function isIndexKeyInUse($entity, $assocField, $indexField)
{
return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($assocField)
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($assocField)
->getValue($entity)->containsKey($indexField);*/
}
public function isFieldSet($entity, $field)
{
return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($field)
->getValue($entity) !== null;
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity) !== null;*/
}
public function getFieldValue($entity, $field)
{
return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($field)
->getValue($entity);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity);*/
}
public function getReferenceValue($entity, $field)
{
return $this->_metadataMap[spl_object_id($entity)]->getReflectionProperty($field)
return $this->_metadataMap[spl_object_hash($entity)]->getReflectionProperty($field)
->getValue($entity);
/*return $this->_em->getClassMetadata(get_class($entity))->getReflectionProperty($field)
->getValue($entity);*/
}
public function addElementToIndexedCollection($coll, $entity, $keyField)
......@@ -195,14 +209,15 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
* last seen instance of each Entity type. This is used for graph construction.
*
* @param array $resultPointers The result pointers.
* @param array|Collection $coll The element.
* @param Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
public function updateResultPointer(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne)
{
if ($coll === $this->_nullObject) {
if ($coll === /*$this->_nullObject*/null) {
echo "HERE!";
unset($resultPointers[$dqlAlias]); // Ticket #1228
return;
}
......@@ -230,10 +245,13 @@ class Doctrine_ORM_Internal_Hydration_ObjectDriver
foreach ($this->_collections as $coll) {
$coll->_takeSnapshot();
$coll->_setHydrationFlag(false);
$this->_uow->addManagedCollection($coll);
}
// clean up
$this->_collections = array();
$this->_initializedRelations = array();
$this->_metadataMap = array();
$this->_entityData = array();
}
}
......@@ -22,7 +22,7 @@
#namespace Doctrine\ORM\Internal\Hydration;
/**
* The hydrator has the tedious to process result sets returned by the database
* The hydrator has the tedious task to process result sets returned by the database
* and turn them into useable structures.
*
* Runtime complexity: The following gives the overall number of iterations
......@@ -63,10 +63,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
*
* This is method defines the core of Doctrine's object population algorithm.
*
* @todo: Detailed documentation. Refactor (too long & nesting level).
*
* @param mixed $stmt
* @param array $tableAliases Array that maps table aliases (SQL alias => DQL alias)
* @param array $aliasMap Array that maps DQL aliases to their components
* (DQL alias => array(
* 'metadata' => Table object,
......@@ -134,8 +130,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
// Initialize
foreach ($this->_queryComponents as $dqlAlias => $component) {
// disable lazy-loading of related elements during hydration
//$component['metadata']->setAttribute('loadReferences', false);
$identifierMap[$dqlAlias] = array();
$resultPointers[$dqlAlias] = array();
$idTemplate[$dqlAlias] = '';
......@@ -152,7 +146,8 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$result = $this->_gatherScalarRowData($result[0], $cache);
return array_shift($result);
}
$resultCounter = 0;
// Process result set
while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Evaluate HYDRATE_SCALAR
......@@ -178,12 +173,14 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$result[] = array(
$driver->getFieldValue($element, $field) => $element
);
++$resultCounter;
} else {
$driver->addElementToIndexedCollection($result, $element, $field);
}
} else {
if ($parserResult->isMixedQuery()) {
$result[] = array($element);
++$resultCounter;
} else {
$driver->addElementToCollection($result, $element);
}
......@@ -226,7 +223,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
continue;
}
// check the type of the relation (many or single-valued)
// Check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) {
// x-to-many relation
$oneToOne = false;
......@@ -270,7 +267,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$coll =& $baseElement[$relationAlias];
} else {
$coll = $driver->getReferenceValue($baseElement, $relationAlias);
//$baseElement->_internalGetReference($relationAlias);
}
if ($coll !== null) {
......@@ -279,11 +275,9 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
}
// Append scalar values to mixed result sets
//TODO: we dont need to count every time here, instead count with the loop
if (isset($scalars)) {
$rowNumber = count($result) - 1;
foreach ($scalars as $name => $value) {
$result[$rowNumber][$name] = $value;
$result[$resultCounter - 1][$name] = $value;
}
}
}
......@@ -291,11 +285,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
$stmt->closeCursor();
$driver->flush();
/*// re-enable lazy loading
foreach ($this->_queryComponents as $dqlAlias => $data) {
$data['metadata']->setAttribute('loadReferences', true);
}*/
$e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
......@@ -514,7 +503,6 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
}
/**
* prepareValue
* this method performs special data preparation depending on
* the type of the given column
*
......@@ -536,6 +524,7 @@ class Doctrine_ORM_Internal_Hydration_StandardHydrator extends Doctrine_ORM_Inte
* 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 Remove. Should be handled by the Type classes. No need for this switch stuff.
*/
public function prepareValue(Doctrine_ClassMetadata $class, $fieldName, $value, $typeHint = null)
{
......
......@@ -18,20 +18,20 @@
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Internal;
#namespace Doctrine\ORM\Internal;
/**
* Null class representing a null value that has been fetched from
* the database or a fetched, empty association. This is for internal use only.
* User code should never deal with this null object.
*
* Semantics are as follows:
*
* Regular PHP null : Value is undefined. When a field with that value is accessed
* and lazy loading is used the database is queried.
*
* Null object: Null valued of a field or empty association that has already been loaded.
* Null class representing a null value that has been fetched from
* the database or a fetched, empty association. This is for internal use only.
* User code should never deal with this null object.
*
* Semantics are as follows:
*
* Regular PHP null : Value is undefined. When a field with that value is accessed
* and lazy loading is used the database is queried.
*
* Null object: Null valued of a field or empty association that has already been loaded.
* On access, the database is not queried.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
......@@ -39,6 +39,7 @@
* @since 1.0
* @version $Revision: 4723 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @todo No longer needed?
*/
// static initializer
Doctrine_ORM_Internal_Null::$INSTANCE = new Doctrine_ORM_Internal_Null();
......
......@@ -28,7 +28,7 @@
* @since 2.0
* @todo Rename to AssociationMapping.
*/
class Doctrine_ORM_Mapping_AssociationMapping implements Serializable
abstract class Doctrine_ORM_Mapping_AssociationMapping implements Serializable
{
const FETCH_MANUAL = 1;
const FETCH_LAZY = 2;
......@@ -443,6 +443,8 @@ class Doctrine_ORM_Mapping_AssociationMapping implements Serializable
{
return false;
}
abstract public function lazyLoadFor($entity);
/* Serializable implementation */
......
......@@ -31,10 +31,8 @@
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Rename to ClassDescriptor.
*/
class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
implements Doctrine_Common_Configurable, Serializable
class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata implements Serializable
{
/* The inheritance mapping types */
/**
......@@ -136,7 +134,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
/**
* The field names of all fields that are part of the identifier/primary key
* of the described entity class.
* of the mapped entity class.
*
* @var array
*/
......@@ -331,26 +329,49 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
$prop->setAccessible(true);
$this->_reflectionProperties[$prop->getName()] = $prop;
}
//$this->_isVirtualPropertyObject = is_subclass_of($entityName, 'Doctrine\Common\VirtualPropertyObject');
}
/**
* Gets the ReflectionClass instance of the mapped class.
*
* @return ReflectionClass
*/
public function getReflectionClass()
{
return $this->_reflectionClass;
}
/**
* Gets the ReflectionPropertys of the mapped class.
*
* @return array An array of ReflectionProperty instances.
*/
public function getReflectionProperties()
{
return $this->_reflectionProperties;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
* @param string $name
* @return ReflectionProperty
*/
public function getReflectionProperty($name)
{
return $this->_reflectionProperties[$name];
}
public function getSingleIdReflectionProperty()
{
if ($this->_isIdentifierComposite) {
throw new Doctrine_Exception("getSingleIdReflectionProperty called on entity with composite key.");
}
return $this->_reflectionProperties[$this->_identifier[0]];
}
/**
* getComponentName
* Gets the name of the mapped class.
*
* @return string
*/
......@@ -360,11 +381,11 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Gets the name of the root class of the entity hierarchy. If the entity described
* by the ClassMetadata is not participating in a hierarchy, this is the same as the
* Gets the name of the root class of the mapped entity hierarchy. If the entity described
* by this ClassMetadata instance is not participating in a hierarchy, this is the same as the
* name returned by {@link getClassName()}.
*
* @return string
* @return string The name of the root class of the entity hierarchy.
*/
public function getRootClassName()
{
......@@ -387,7 +408,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Check if the class has a composite identifier.
* Checks if the class has a composite identifier.
*
* @param string $fieldName The field name
* @return boolean TRUE if the identifier is composite, FALSE otherwise.
......@@ -449,12 +470,10 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
if ( ! array_key_exists($name, $this->_tableOptions)) {
throw new Doctrine_ClassMetadata_Exception("Unknown table option: '$name'.");
}
return $this->_tableOptions[$name];
}
/**
* getTableOptions
* returns all table options.
*
* @return array all options and their values
......@@ -469,7 +488,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
* If the column name for the field cannot be found, the given field name
* is returned.
*
* @param string $alias The field name.
* @param string $fieldName The field name.
* @return string The column name.
*/
public function getColumnName($fieldName)
......@@ -479,7 +498,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Gets the mapping of a (regular) fields that holds some data but not a
* Gets the mapping of a (regular) field that holds some data but not a
* reference to another object.
*
* @param string $fieldName The field name.
......@@ -490,7 +509,6 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
if ( ! isset($this->_fieldMappings[$fieldName])) {
throw Doctrine_MappingException::mappingNotFound($fieldName);
}
return $this->_fieldMappings[$fieldName];
}
......@@ -499,14 +517,13 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
*
* @param string $fieldName The field name that represents the association in
* the object model.
* @return Doctrine::ORM::Mapping::AssociationMapping The mapping.
* @return Doctrine\ORM\Mapping\AssociationMapping The mapping.
*/
public function getAssociationMapping($fieldName)
{
if ( ! isset($this->_associationMappings[$fieldName])) {
throw new Doctrine_Exception("Mapping not found: $fieldName");
}
return $this->_associationMappings[$fieldName];
}
......@@ -514,7 +531,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
* Gets the inverse association mapping for the given fieldname.
*
* @param string $mappedByFieldName
* @return Doctrine::ORM::Mapping::AssociationMapping The mapping.
* @return Doctrine\ORM\Mapping\AssociationMapping The mapping.
*/
public function getInverseAssociationMapping($mappedByFieldName)
{
......@@ -608,7 +625,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Validates & completes the field mapping. Default values are applied here.
* Validates & completes the field mapping.
*
* @param array $mapping The field mapping to validated & complete.
* @return array The validated and completed field mapping.
......@@ -689,59 +706,6 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
//...
}
private $_entityIdentifiers = array();
/**
* Gets the identifier of an entity.
*
* @param object $entity
* @return array Map of identifier field names to values.
*/
public function getEntityIdentifier($entity)
{
$oid = spl_object_id($entity);
if ( ! isset($this->_entityIdentifiers[$oid])) {
if ( ! $this->isIdentifierComposite()) {
$idField = $this->_identifier[0];
$idValue = $this->_reflectionProperties[$idField]->getValue($entity);
if (isset($idValue)) {
//return array($idField => $idValue);
$this->_entityIdentifiers[$oid] = array($idField => $idValue);
} else {
return false;
}
//$this->_entityIdentifiers[$oid] = false;
} else {
$id = array();
foreach ($this->getIdentifierFieldNames() as $idFieldName) {
$idValue = $this->_reflectionProperties[$idFieldName]->getValue($entity);
if (isset($idValue)) {
$id[$idFieldName] = $idValue;
}
}
//return $id;
$this->_entityIdentifiers[$oid] = $id;
}
}
return $this->_entityIdentifiers[$oid];
}
/**
*
*
* @param <type> $entity
* @param <type> $identifier
*/
public function setEntityIdentifier($entity, $identifier)
{
if (is_array($identifier)) {
foreach ($identifier as $fieldName => $value) {
$this->_reflectionProperties[$fieldName]->setValue($entity, $value);
}
} else {
$this->_reflectionProperties[$this->_identifier[0]]->setValue($entity, $identifier);
}
}
/**
* Gets the identifier (primary key) field names of the class.
*
......@@ -852,7 +816,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Sets the type of Id generator to use for this class.
* Sets the type of Id generator to use for the mapped class.
*/
public function setIdGeneratorType($generatorType)
{
......@@ -860,9 +824,9 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Checks whether the class uses an Id generator.
* Checks whether the mapped class uses an Id generator.
*
* @return boolean TRUE if the class uses an Id generator, FALSE otherwise.
* @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
*/
public function usesIdGenerator()
{
......@@ -1010,7 +974,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Gets the inheritance mapping type used by the class.
* Gets the inheritance mapping type used by the mapped class.
*
* @return string
*/
......@@ -1020,7 +984,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
}
/**
* Sets the subclasses of the class.
* Sets the subclasses of the mapped class.
* All entity classes that participate in a hierarchy and have subclasses
* need to declare them this way.
*
......@@ -1136,10 +1100,6 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
*/
public function getInheritanceOption($name)
{
/*if ( ! array_key_exists($name, $this->_inheritanceOptions)) {
throw new Doctrine_ClassMetadata_Exception("Unknown inheritance option: '$name'.");
}*/
return $this->_inheritanceOptions[$name];
}
......@@ -1179,7 +1139,6 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
throw Doctrine_MappingException::invalidInheritanceOption($name);
}
}
$this->_inheritanceOptions[$name] = $value;
}
......@@ -1350,7 +1309,6 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
public function serialize()
{
//$contents = get_object_vars($this);
/* @TODO How to handle $this->_em and $this->_parser ? */
//return serialize($contents);
return "";
}
......@@ -1506,7 +1464,7 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
*/
public function setCustomRepositoryClass($repositoryClassName)
{
if ( ! is_subclass_of($repositoryClassName, 'Doctrine_EntityRepository')) {
if ( ! is_subclass_of($repositoryClassName, 'Doctrine\ORM\EntityRepository')) {
throw new Doctrine_ClassMetadata_Exception("The custom repository must be a subclass"
. " of Doctrine_EntityRepository.");
}
......@@ -1640,37 +1598,15 @@ class Doctrine_ORM_Mapping_ClassMetadata extends Doctrine_Common_ClassMetadata
{
return $columnName === $this->_inheritanceOptions['discriminatorColumn'];
}
public function hasAttribute($name)
{
return isset($this->_attributes[$name]);
}
public function getAttribute($name)
{
if ($this->hasAttribute($name)) {
return $this->_attributes[$name];
}
}
public function setAttribute($name, $value)
{
if ($this->hasAttribute($name)) {
$this->_attributes[$name] = $value;
}
}
public function hasAssociation($fieldName)
{
return isset($this->_associationMappings[$fieldName]);
}
/**
*
*/
public function __toString()
{
return spl_object_hash($this);
return __CLASS__ . '@' . spl_object_hash($this);
}
}
......@@ -19,7 +19,7 @@
* <http://www.phpdoctrine.org>.
*/
#namespace Doctrine::ORM::Mapping;
#namespace Doctrine\ORM\Mapping;
/**
* Represents a one-to-many mapping.
......@@ -66,7 +66,7 @@ class Doctrine_ORM_Mapping_OneToManyMapping extends Doctrine_ORM_Mapping_Associa
}
/**
* Validates and completed the mapping.
* Validates and completes the mapping.
*
* @param array $mapping The mapping to validate and complete.
* @return array The validated and completed mapping.
......@@ -106,6 +106,15 @@ class Doctrine_ORM_Mapping_OneToManyMapping extends Doctrine_ORM_Mapping_Associa
return true;
}
/**
*
* @param <type> $entity
* @override
*/
public function lazyLoadFor($entity)
{
}
}
......
......@@ -130,10 +130,10 @@ class Doctrine_ORM_Mapping_OneToOneMapping extends Doctrine_ORM_Mapping_Associat
/**
* Lazy-loads the associated entity for a given entity.
*
* @param Doctrine::ORM::Entity $entity
* @param Doctrine\ORM\Entity $entity
* @return void
*/
public function lazyLoadFor(Doctrine_ORM_Entity $entity)
public function lazyLoadFor($entity)
{
if ($entity->getClassName() != $this->_sourceClass->getClassName()) {
//error?
......
......@@ -41,7 +41,7 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
/**
* Metadata object that descibes the mapping of the mapped entity class.
*
* @var Doctrine_ClassMetadata
* @var Doctrine\ORM\Mapping\ClassMetadata
*/
protected $_classMetadata;
......@@ -53,26 +53,28 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
protected $_entityName;
/**
* The Doctrine_Connection object that the database connection of this mapper.
* The Connection instance.
*
* @var Doctrine::DBAL::Connection $conn
* @var Doctrine\DBAL\Connection $conn
*/
protected $_conn;
/**
* The EntityManager.
* The EntityManager instance.
*
* @var Doctrine::ORM::EntityManager
* @var Doctrine\ORM\EntityManager
*/
protected $_em;
/**
* Null object.
*/
private $_nullObject;
//private $_nullObject;
/**
* Constructs a new EntityPersister.
* Initializes a new instance of a class derived from AbstractEntityPersister
* that uses the given EntityManager and persists instances of the class described
* by the given class metadata descriptor.
*/
public function __construct(Doctrine_ORM_EntityManager $em, Doctrine_ORM_Mapping_ClassMetadata $classMetadata)
{
......@@ -80,31 +82,36 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
$this->_entityName = $classMetadata->getClassName();
$this->_conn = $em->getConnection();
$this->_classMetadata = $classMetadata;
$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
//$this->_nullObject = Doctrine_ORM_Internal_Null::$INSTANCE;
}
/**
* Inserts an entity.
*
* @param Doctrine::ORM::Entity $entity The entity to insert.
* @return void
* @param Doctrine\ORM\Entity $entity The entity to insert.
* @return mixed
*/
public function insert($entity)
{
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$this->_conn->insert($this->_classMetadata->getTableName(), $insertData);
$idGen = $this->_em->getIdGenerator($this->_classMetadata->getClassName());
if ($idGen->isPostInsertGenerator()) {
return $idGen->generate($entity);
}
return null;
}
/**
* Updates an entity.
*
* @param Doctrine::ORM::Entity $entity The entity to update.
* @param Doctrine\ORM\Entity $entity The entity to update.
* @return void
*/
public function update(Doctrine_ORM_Entity $entity)
{
$dataChangeSet = $entity->_getDataChangeSet();
/*$dataChangeSet = $entity->_getDataChangeSet();
$referenceChangeSet = $entity->_getReferenceChangeSet();
foreach ($referenceChangeSet as $field => $change) {
......@@ -118,14 +125,14 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
}
//...
}
*/
//TODO: perform update
}
/**
* Deletes an entity.
*
* @param Doctrine::ORM::Entity $entity The entity to delete.
* @param Doctrine\ORM\Entity $entity The entity to delete.
* @return void
*/
public function delete(Doctrine_ORM_Entity $entity)
......@@ -209,7 +216,6 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
/**
* Callback that is invoked during the SQL construction process.
* @todo Move to ClassMetadata?
*/
public function getCustomJoins()
{
......@@ -218,7 +224,6 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
/**
* Callback that is invoked during the SQL construction process.
* @todo Move to ClassMetadata?
*/
public function getCustomFields()
{
......@@ -226,24 +231,7 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
}
/**
* Assumes that the keys of the given field array are field names and converts
* them to column names.
*
* @return array
*/
/*protected function _convertFieldToColumnNames(array $fields, Doctrine_ClassMetadata $class)
{
$converted = array();
foreach ($fields as $fieldName => $value) {
$converted[$class->getColumnName($fieldName)] = $value;
}
return $converted;
}*/
/**
* Returns an array of modified fields and values with data preparation
* adds column aggregation inheritance and converts Records into primary key values
* Prepares all the entity data for insertion into the database.
*
* @param array $array
* @return void
......@@ -255,7 +243,7 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
$type = $this->_classMetadata->getTypeOfField($field);
$columnName = $this->_classMetadata->getColumnName($field);
if ($newVal === Doctrine_ORM_Internal_Null::$INSTANCE) {
if (is_null($newVal)) {
$result[$columnName] = null;
} else if (is_object($newVal)) {
$assocMapping = $this->_classMetadata->getAssociationMapping($field);
......@@ -301,336 +289,7 @@ abstract class Doctrine_ORM_Persisters_AbstractEntityPersister
$result[$discColumn] = array_search($this->_entityName, $discMap);
}
}
#############################################################
# The following is old code that needs to be removed/ported
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
* @throws PDOException if something went wrong at database level
* @return void
*/
protected function _deleteComposites(Doctrine_ORM_Entity $record)
{
$classMetadata = $this->_classMetadata;
foreach ($classMetadata->getRelations() as $fk) {
if ($fk->isComposite()) {
$obj = $record->get($fk->getAlias());
if ($obj instanceof Doctrine_ORM_Entity &&
$obj->_state() != Doctrine_ORM_Entity::STATE_LOCKED) {
$obj->delete($this->_mapper->getConnection());
}
}
}
}
/**
* Returns the connection the mapper is currently using.
*
* @return Doctrine_Connection|null The connection object.
*/
public function getConnection()
{
return $this->_conn;
}
public function getEntityManager()
{
return $this->_em;
}
/**
* getComponentName
*
* @return void
* @deprecated Use getMappedClassName()
*/
public function getComponentName()
{
return $this->_domainClassName;
}
/**
* Saves an entity.
*
* @param Doctrine_Entity $record The entity to save.
* @param Doctrine_Connection $conn The connection to use. Will default to the mapper's
* connection.
*/
public function save(Doctrine_ORM_Entity $record)
{
if ( ! ($record instanceof $this->_domainClassName)) {
throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . "
can't save instances of type" . get_class($record) . ".");
}
if ($conn === null) {
$conn = $this->_conn;
}
$state = $record->_state();
if ($state === Doctrine_ORM_Entity::STATE_LOCKED) {
return false;
}
$record->_state(Doctrine_ORM_Entity::STATE_LOCKED);
try {
$conn->beginInternalTransaction();
$saveLater = $this->_saveRelated($record);
$record->_state($state);
if ($record->isValid()) {
$this->_insertOrUpdate($record);
} else {
$conn->getTransaction()->addInvalid($record);
}
$state = $record->_state();
$record->_state(Doctrine_ORM_Entity::STATE_LOCKED);
foreach ($saveLater as $fk) {
$alias = $fk->getAlias();
if ($record->hasReference($alias)) {
$obj = $record->$alias;
// check that the related object is not an instance of Doctrine_Null
if ( ! ($obj instanceof Doctrine_Null)) {
$obj->save($conn);
}
}
}
// save the MANY-TO-MANY associations
$this->saveAssociations($record);
// reset state
$record->_state($state);
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return true;
}
/**
* Inserts or updates an entity, depending on it's state.
*
* @param Doctrine_Entity $record The entity to insert/update.
*/
protected function _insertOrUpdate(Doctrine_ORM_Entity $record)
{
//$record->preSave();
//$this->notifyEntityListeners($record, 'preSave', Doctrine_Event::RECORD_SAVE);
switch ($record->_state()) {
case Doctrine_ORM_Entity::STATE_TDIRTY:
$this->_insert($record);
break;
case Doctrine_ORM_Entity::STATE_DIRTY:
case Doctrine_ORM_Entity::STATE_PROXY:
$this->_update($record);
break;
case Doctrine_ORM_Entity::STATE_CLEAN:
case Doctrine_ORM_Entity::STATE_TCLEAN:
// do nothing
break;
}
//$record->postSave();
//$this->notifyEntityListeners($record, 'postSave', Doctrine_Event::RECORD_SAVE);
}
/**
* saves the given record
*
* @param Doctrine_Entity $record
* @return void
*/
public function saveSingleRecord(Doctrine_ORM_Entity $record)
{
$this->_insertOrUpdate($record);
}
/**
* saves all related records to $record
*
* @throws PDOException if something went wrong at database level
* @param Doctrine_Entity $record
*/
protected function _saveRelated(Doctrine_ORM_Entity $record)
{
$saveLater = array();
foreach ($record->_getReferences() as $k => $v) {
$rel = $record->getTable()->getRelation($k);
$local = $rel->getLocal();
$foreign = $rel->getForeign();
if ($rel instanceof Doctrine_Relation_ForeignKey) {
$saveLater[$k] = $rel;
} else if ($rel instanceof Doctrine_Relation_LocalKey) {
// ONE-TO-ONE relationship
$obj = $record->get($rel->getAlias());
// Protection against infinite function recursion before attempting to save
if ($obj instanceof Doctrine_ORM_Entity && $obj->isModified()) {
$obj->save();
/** Can this be removed?
$id = array_values($obj->identifier());
foreach ((array) $rel->getLocal() as $k => $field) {
$record->set($field, $id[$k]);
}
*/
}
}
}
return $saveLater;
}
/**
* saveAssociations
*
* this method takes a diff of one-to-many / many-to-many original and
* current collections and applies the changes
*
* for example if original many-to-many related collection has records with
* primary keys 1,2 and 3 and the new collection has records with primary keys
* 3, 4 and 5, this method would first destroy the associations to 1 and 2 and then
* save new associations to 4 and 5
*
* @throws Doctrine_Connection_Exception if something went wrong at database level
* @param Doctrine_Entity $record
* @return void
*/
public function saveAssociations(Doctrine_ORM_Entity $record)
{
foreach ($record->_getReferences() as $relationName => $relatedObject) {
if ($relatedObject === Doctrine_Null::$INSTANCE) {
continue;
}
$rel = $record->getTable()->getRelation($relationName);
if ($rel instanceof Doctrine_Relation_Association) {
$relatedObject->save($this->_conn);
$assocTable = $rel->getAssociationTable();
foreach ($relatedObject->getDeleteDiff() as $r) {
$query = 'DELETE FROM ' . $assocTable->getTableName()
. ' WHERE ' . $rel->getForeign() . ' = ?'
. ' AND ' . $rel->getLocal() . ' = ?';
// FIXME: composite key support
$ids1 = $r->identifier();
$id1 = count($ids1) > 0 ? array_pop($ids1) : null;
$ids2 = $record->identifier();
$id2 = count($ids2) > 0 ? array_pop($ids2) : null;
$this->_conn->execute($query, array($id1, $id2));
}
$assocMapper = $this->_conn->getMapper($assocTable->getComponentName());
foreach ($relatedObject->getInsertDiff() as $r) {
$assocRecord = $assocMapper->create();
$assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r);
$assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record);
$assocMapper->save($assocRecord);
}
}
}
}
/**
* Updates an entity.
*
* @param Doctrine_Entity $record record to be updated
* @return boolean whether or not the update was successful
* @todo Move to Doctrine_Table (which will become Doctrine_Mapper).
*/
protected function _update(Doctrine_ORM_Entity $record)
{
$record->preUpdate();
$this->notifyEntityListeners($record, 'preUpdate', Doctrine_Event::RECORD_UPDATE);
$table = $this->_classMetadata;
$this->_doUpdate($record);
$record->postUpdate();
$this->notifyEntityListeners($record, 'postUpdate', Doctrine_Event::RECORD_UPDATE);
return true;
}
abstract protected function _doUpdate(Doctrine_ORM_Entity $entity);
/**
* Inserts an entity.
*
* @param Doctrine_Entity $record record to be inserted
* @return boolean
*/
protected function _insert(Doctrine_ORM_Entity $record)
{
//$record->preInsert();
//$this->notifyEntityListeners($record, 'preInsert', Doctrine_Event::RECORD_INSERT);
$this->_doInsert($record);
$this->addRecord($record);
//$record->postInsert();
//$this->notifyEntityListeners($record, 'postInsert', Doctrine_Event::RECORD_INSERT);
return true;
}
abstract protected function _doInsert(Doctrine_ORM_Entity $entity);
/**
* Deletes given entity and all it's related entities.
*
* Triggered Events: onPreDelete, onDelete.
*
* @return boolean true on success, false on failure
* @throws Doctrine_Mapper_Exception
*/
public function delete_old(Doctrine_ORM_Entity $record)
{
if ( ! $record->exists()) {
return false;
}
if ( ! ($record instanceof $this->_domainClassName)) {
throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . "
can't save instances of type" . get_class($record) . ".");
}
if ($conn == null) {
$conn = $this->_conn;
}
$record->preDelete();
$this->notifyEntityListeners($record, 'preDelete', Doctrine_Event::RECORD_DELETE);
$table = $this->_classMetadata;
$state = $record->_state();
$record->_state(Doctrine_ORM_Entity::STATE_LOCKED);
$this->_doDelete($record);
$record->postDelete();
$this->notifyEntityListeners($record, 'postDelete', Doctrine_Event::RECORD_DELETE);
return true;
}
}
......@@ -30,7 +30,7 @@
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version $Revision$
* @link www.phpdoctrine.org
* @link www.doctrine-project.org
* @since 2.0
*/
class Doctrine_ORM_Persisters_StandardEntityPersister extends Doctrine_ORM_Persisters_AbstractEntityPersister
......@@ -40,10 +40,8 @@ class Doctrine_ORM_Persisters_StandardEntityPersister extends Doctrine_ORM_Persi
*/
protected function _doDelete(Doctrine_ORM_Entity $record)
{
$conn = $this->_conn;
$metadata = $this->_classMetadata;
try {
$conn->beginInternalTransaction();
/*try {
$this->_conn->beginInternalTransaction();
$this->_deleteComposites($record);
$record->_state(Doctrine_ORM_Entity::STATE_TDIRTY);
......@@ -57,18 +55,16 @@ class Doctrine_ORM_Persisters_StandardEntityPersister extends Doctrine_ORM_Persi
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
}*/
}
/**
* Inserts a single entity into the database, without any related entities.
* Inserts a single entity into the database.
*
* @param Doctrine_Entity $record The entity to insert.
* @param Doctrine\ORM\Entity $entity The entity to insert.
*/
protected function _doInsert(Doctrine_ORM_Entity $record)
{
$conn = $this->_conn;
$fields = $record->getPrepared();
if (empty($fields)) {
return false;
......
......@@ -32,11 +32,8 @@
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 2.0
* @version $Revision: 4947 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @todo Rename: Doctrine::ORM::UnitOfWork.
* @todo Turn connection exceptions into UnitOfWorkExceptions.
*/
class Doctrine_ORM_UnitOfWork
{
......@@ -89,6 +86,13 @@ class Doctrine_ORM_UnitOfWork
*/
protected $_identityMap = array();
/**
* Map of all identifiers. Keys are object ids.
*
* @var array
*/
private $_entityIdentifiers = array();
/**
* Map of the original entity data of entities fetched from the database.
* Keys are object ids. This is used for calculating changesets at commit time.
......@@ -199,7 +203,7 @@ class Doctrine_ORM_UnitOfWork
public function commit()
{
// Compute changes in managed entities
$this->_computeDataChangeSet();
$this->computeDataChangeSet();
if (empty($this->_newEntities) &&
empty($this->_deletedEntities) &&
......@@ -244,7 +248,7 @@ class Doctrine_ORM_UnitOfWork
*/
public function getDataChangeSet($entity)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
if (isset($this->_dataChangeSets[$oid])) {
return $this->_dataChangeSets[$oid];
}
......@@ -261,7 +265,7 @@ class Doctrine_ORM_UnitOfWork
* map are computed.
* @return void
*/
private function _computeDataChangeSet(array $entities = null)
public function computeDataChangeSet(array $entities = null)
{
$entitySet = array();
if ( ! is_null($entities)) {
......@@ -279,7 +283,7 @@ class Doctrine_ORM_UnitOfWork
foreach ($entitySet as $className => $entities) {
$class = $this->_em->getClassMetadata($className);
foreach ($entities as $entity) {
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->_em->getClassMetadata(get_class($entity));
......@@ -317,7 +321,7 @@ class Doctrine_ORM_UnitOfWork
/**
* Executes all entity insertions for entities of the specified type.
*
* @param Doctrine::ORM::Mapping::ClassMetadata $class
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function _executeInserts($class)
{
......@@ -329,11 +333,14 @@ class Doctrine_ORM_UnitOfWork
$persister = $this->_em->getEntityPersister($className);
foreach ($this->_newEntities as $entity) {
if (get_class($entity) == $className) {
$persister->insert($entity);
if ($class->isIdGeneratorIdentity()) {
$id = $this->_em->getIdGenerator($class->getIdGeneratorType());
$class->setEntityIdentifier($entity, $id);
$this->_entityStates[spl_object_id($oid)] = self::STATE_MANAGED;
$returnVal = $persister->insert($entity);
if ( ! is_null($returnVal)) {
$oid = spl_object_hash($entity);
$class->getReflectionProperty($class->getSingleIdentifierFieldName())
->setValue($entity, $returnVal);
$this->_entityIdentifiers[$oid] = array($returnVal);
$this->_entityStates[$oid] = self::STATE_MANAGED;
$this->addToIdentityMap($entity);
}
}
}
......@@ -342,7 +349,7 @@ class Doctrine_ORM_UnitOfWork
/**
* Executes all entity updates for entities of the specified type.
*
* @param Doctrine::ORM::Mapping::ClassMetadata $class
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function _executeUpdates($class)
{
......@@ -358,7 +365,7 @@ class Doctrine_ORM_UnitOfWork
/**
* Executes all entity deletions for entities of the specified type.
*
* @param Doctrine::ORM::Mapping::ClassMetadata $class
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function _executeDeletions($class)
{
......@@ -441,11 +448,8 @@ class Doctrine_ORM_UnitOfWork
*/
public function registerNew($entity)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
/*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.");
}
......@@ -457,7 +461,7 @@ class Doctrine_ORM_UnitOfWork
}
$this->_newEntities[$oid] = $entity;
if ($this->_em->getClassMetadata(get_class($entity))->getEntityIdentifier($entity)) {
if (isset($this->_entityIdentifiers[$oid])) {
$this->addToIdentityMap($entity);
}
}
......@@ -465,36 +469,25 @@ class Doctrine_ORM_UnitOfWork
/**
* Checks whether an entity is registered as new on the unit of work.
*
* @param Doctrine_ORM_Entity $entity
* @param Doctrine\ORM\Entity $entity
* @return boolean
* @todo Rename to isScheduledForInsert().
*/
public function isRegisteredNew($entity)
{
return isset($this->_newEntities[spl_object_id($entity)]);
}
/**
* Registers a clean entity.
* The entity is simply put into the identity map.
*
* @param object $entity
*/
public function registerClean($entity)
{
$this->addToIdentityMap($entity);
return isset($this->_newEntities[spl_object_hash($entity)]);
}
/**
* Registers a dirty entity.
*
* @param Doctrine::ORM::Entity $entity
* @param Doctrine\ORM\Entity $entity
* @todo Rename to scheduleForUpdate().
*/
public function registerDirty($entity)
{
$oid = spl_object_id($entity);
if ( ! $entity->_identifier()) {
$oid = spl_object_hash($entity);
if ( ! isset($this->_entityIdentifiers[$oid])) {
throw new Doctrine_Exception("Entity without identity "
. "can't be registered as dirty.");
}
......@@ -518,7 +511,7 @@ class Doctrine_ORM_UnitOfWork
*/
public function isRegisteredDirty($entity)
{
return isset($this->_dirtyEntities[spl_object_id($entity)]);
return isset($this->_dirtyEntities[spl_object_hash($entity)]);
}
/**
......@@ -528,12 +521,13 @@ class Doctrine_ORM_UnitOfWork
*/
public function registerDeleted($entity)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
if ( ! $this->isInIdentityMap($entity)) {
return;
}
$this->removeFromIdentityMap($entity);
$className = get_class($entity);
if (isset($this->_newEntities[$oid])) {
unset($this->_newEntities[$oid]);
......@@ -552,13 +546,13 @@ class Doctrine_ORM_UnitOfWork
* Checks whether an entity is registered as removed/deleted with the unit
* of work.
*
* @param Doctrine::ORM::Entity $entity
* @param Doctrine\ORM\Entity $entity
* @return boolean
* @todo Rename to isScheduledForDelete().
*/
public function isRegisteredRemoved($entity)
{
return isset($this->_deletedEntities[spl_object_id($entity)]);
return isset($this->_deletedEntities[spl_object_hash($entity)]);
}
/**
......@@ -570,25 +564,26 @@ class Doctrine_ORM_UnitOfWork
*/
public function detach($entity)
{
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
}
$oid = spl_object_hash($entity);
$this->removeFromIdentityMap($entity);
unset($this->_newEntities[$oid], $this->_dirtyEntities[$oid],
$this->_deletedEntities[$oid], $this->_entityIdentifiers[$oid],
$this->_entityStates[$oid]);
}
/**
* Enter description here...
*
* @param Doctrine_ORM_Entity $entity
* @param Doctrine\ORM\Entity $entity
* @return unknown
* @todo Rename to isScheduled()
*/
public function isEntityRegistered($entity)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
return isset($this->_newEntities[$oid]) ||
isset($this->_dirtyEntities[$oid]) ||
isset($this->_deletedEntities[$oid]) ||
$this->isInIdentityMap($entity);
isset($this->_deletedEntities[$oid]);
}
/**
......@@ -606,10 +601,16 @@ class Doctrine_ORM_UnitOfWork
$numDetached = 0;
if ($entityName !== null && isset($this->_identityMap[$entityName])) {
$numDetached = count($this->_identityMap[$entityName]);
foreach ($this->_identityMap[$entityName] as $entity) {
$this->detach($entity);
}
$this->_identityMap[$entityName] = array();
} else {
$numDetached = count($this->_identityMap);
$this->_identityMap = array();
$this->_newEntities = array();
$this->_dirtyEntities = array();
$this->_deletedEntities = array();
}
return $numDetached;
......@@ -627,9 +628,9 @@ class Doctrine_ORM_UnitOfWork
public function addToIdentityMap($entity)
{
$classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
$idHash = $this->getIdentifierHash($this->_entityIdentifiers[spl_object_hash($entity)]);
if ($idHash === '') {
throw new Doctrine_Exception("Entity with oid '" . spl_object_id($entity)
throw new Doctrine_Exception("Entity with oid '" . spl_object_hash($entity)
. "' has no identity and therefore can't be added to the identity map.");
}
$className = $classMetadata->getRootClassName();
......@@ -648,23 +649,22 @@ class Doctrine_ORM_UnitOfWork
*/
public function getEntityState($entity)
{
$oid = spl_object_id($entity);
return isset($this->_entityStates[$oid]) ? $this->_entityStates[$oid] :
self::STATE_NEW;
$oid = spl_object_hash($entity);
return isset($this->_entityStates[$oid]) ? $this->_entityStates[$oid] : self::STATE_NEW;
}
/**
* Removes an entity from the identity map.
*
* @param Doctrine_ORM_Entity $entity
* @return unknown
* @param Doctrine\ORM\Entity $entity
* @return boolean
*/
public function removeFromIdentityMap($entity)
{
$classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
$idHash = $this->getIdentifierHash($this->_entityIdentifiers[spl_object_hash($entity)]);
if ($idHash === '') {
throw new Doctrine_Exception("Entity with oid '" . spl_object_id($entity)
throw new Doctrine_Exception("Entity with oid '" . spl_object_hash($entity)
. "' has no identity and therefore can't be removed from the identity map.");
}
$className = $classMetadata->getRootClassName();
......@@ -681,7 +681,7 @@ class Doctrine_ORM_UnitOfWork
*
* @param string $idHash
* @param string $rootClassName
* @return Doctrine::ORM::Entity
* @return Doctrine\ORM\Entity
*/
public function getByIdHash($idHash, $rootClassName)
{
......@@ -729,8 +729,12 @@ class Doctrine_ORM_UnitOfWork
*/
public function isInIdentityMap($entity)
{
$oid = spl_object_hash($entity);
if ( ! isset($this->_entityIdentifiers[$oid])) {
return false;
}
$classMetadata = $this->_em->getClassMetadata(get_class($entity));
$idHash = $this->getIdentifierHash($classMetadata->getEntityIdentifier($entity));
$idHash = $this->getIdentifierHash($this->_entityIdentifiers[$oid]);
if ($idHash === '') {
return false;
}
......@@ -765,15 +769,15 @@ class Doctrine_ORM_UnitOfWork
$this->_doSave($entity, $visited, $insertNow);
if ( ! empty($insertNow)) {
// We have no choice. This means that there are new entities
// with an IDENTITY column key generation.
$this->_computeDataChangeSet($insertNow);
// with an IDENTITY column key generation strategy.
$this->computeDataChangeSet($insertNow);
$commitOrder = $this->_getCommitOrder($insertNow);
foreach ($commitOrder as $class) {
$this->_executeInserts($class);
}
// remove them from _newEntities
$this->_newEntities = array_diff_key($this->_newEntities, $insertNow);
$this->_dataChangeSets = array();
$this->_dataChangeSets = array_diff_key($this->_dataChangeSets, $insertNow);
}
}
......@@ -787,7 +791,7 @@ class Doctrine_ORM_UnitOfWork
*/
private function _doSave($entity, array &$visited, array &$insertNow)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion
}
......@@ -800,12 +804,16 @@ class Doctrine_ORM_UnitOfWork
// nothing to do
break;
case self::STATE_NEW:
$result = $this->_em->getIdGenerator($class->getClassName())->generate($entity);
if ($result == Doctrine_ORM_Id_AbstractIdGenerator::POST_INSERT_INDICATOR) {
$idGen = $this->_em->getIdGenerator($class->getClassName());
if ($idGen->isPostInsertGenerator()) {
$insertNow[$oid] = $entity;
} else {
$class->setEntityIdentifier($entity, $result);
$idValue = $idGen->generate($entity);
$this->_entityIdentifiers[$oid] = array($idValue);
$this->_entityStates[$oid] = self::STATE_MANAGED;
if ( ! $idGen instanceof Doctrine_ORM_Id_Assigned) {
$class->getSingleIdReflectionProperty()->setValue($entity, $idValue);
}
}
$this->registerNew($entity);
break;
......@@ -844,19 +852,18 @@ class Doctrine_ORM_UnitOfWork
/**
* Enter description here...
*
* @param Doctrine_ORM_Entity $entity
* @param Doctrine\ORM\Entity $entity
* @param array $visited
*/
private function _doDelete($entity, array &$visited)
{
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion
}
$visited[$oid] = $entity; // mark visited
//$class = $entity->getClass();
switch ($this->getEntityState($entity)) {
case self::STATE_NEW:
case self::STATE_DELETED:
......@@ -951,47 +958,43 @@ class Doctrine_ORM_UnitOfWork
}
/**
* Creates an entity. Used for reconstitution as well as initial creation.
* Creates an entity. Used for reconstitution of entities during hydration.
*
* @param string $className The name of the entity class.
* @param array $data The data for the entity.
* @return Doctrine\ORM\Entity
* @internal Performance-sensitive method.
*/
public function createEntity($className, array $data, Doctrine_Query $query = null)
public function createEntity($className, array $data, $query = null)
{
$className = $this->_inferCorrectClassName($data, $className);
$classMetadata = $this->_em->getClassMetadata($className);
if ( ! empty($data)) {
$id = array();
if ($classMetadata->isIdentifierComposite()) {
$identifierFieldNames = $classMetadata->getIdentifier();
$isNew = false;
foreach ($identifierFieldNames as $fieldName) {
if ( ! isset($data[$fieldName])) {
// id field not found return new entity
$isNew = true;
break;
}
$id[] = $data[$fieldName];
}
if ($isNew) {
$entity = new $className;
} else {
$idHash = $this->getIdentifierHash($id);
$entity = $this->tryGetByIdHash($idHash, $classMetadata->getRootClassName());
if ($entity) {
$this->_mergeData($entity, $data, $classMetadata/*, $query->getHint('doctrine.refresh')*/);
return $entity;
} else {
$entity = new $className;
$this->_mergeData($entity, $data, $classMetadata, true);
$this->addToIdentityMap($entity);
}
}
$idHash = $this->getIdentifierHash($id);
} else {
$id = array($data[$classMetadata->getSingleIdentifierFieldName()]);
$idHash = $id[0];
}
$entity = $this->tryGetByIdHash($idHash, $classMetadata->getRootClassName());
if ($entity) {
$oid = spl_object_hash($entity);
$this->_mergeData($entity, $data, $classMetadata/*, $query->getHint('doctrine.refresh')*/);
return $entity;
} else {
$entity = new $className;
$oid = spl_object_hash($entity);
$this->_mergeData($entity, $data, $classMetadata, true);
$this->_entityIdentifiers[$oid] = $id;
$this->addToIdentityMap($entity);
}
$this->_originalEntityData[spl_object_id($entity)] = $data;
$this->_originalEntityData[$oid] = $data;
return $entity;
}
......@@ -1011,11 +1014,11 @@ class Doctrine_ORM_UnitOfWork
$class->getReflectionProperty($field)->setValue($entity, $value);
}
} else {
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
foreach ($data as $field => $value) {
$currentValue = $class->getReflectionProperty($field)->getValue($entity);
if ( ! isset($this->_originalEntityData[$oid]) ||
$currentValue == $this->_originalEntityData[$oid]) {
if ( ! isset($this->_originalEntityData[$oid][$field]) ||
$currentValue == $this->_originalEntityData[$oid][$field]) {
$class->getReflectionProperty($field)->setValue($entity, $value);
}
}
......@@ -1058,6 +1061,61 @@ class Doctrine_ORM_UnitOfWork
{
return $this->_identityMap;
}
/**
* Gets the original data of an entity. The original data is the data that was
* present at the time the entity was reconstituted from the database.
*
* @param object $entity
* @return array
*/
public function getOriginalEntityData($entity)
{
$oid = spl_object_hash($entity);
if (isset($this->_originalEntityData[$oid])) {
return $this->_originalEntityData[$oid];
}
return array();
}
/**
* INTERNAL:
* For hydration purposes only.
*
* Sets a property of the original data array of an entity.
*
* @param string $oid
* @param string $property
* @param mixed $value
*/
public function setOriginalEntityProperty($oid, $property, $value)
{
$this->_originalEntityData[$oid][$property] = $value;
}
/**
* INTERNAL:
* For hydration purposes only.
*
* Adds a managed collection to the UnitOfWork.
*
* @param Doctrine\ORM\Collection $coll
*/
public function addManagedCollection(Doctrine_ORM_Collection $coll)
{
}
/**
* Gets the identifier of an entity.
*
* @param object $entity
* @return array The identifier values.
*/
public function getEntityIdentifier($entity)
{
return $this->_entityIdentifiers[spl_object_hash($entity)];
}
}
......
......@@ -871,6 +871,7 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase
$this->assertEquals(3, count($result[0]->boards));
$this->assertTrue(isset($result[1]->boards));
$this->assertEquals(1, count($result[1]->boards));
} else if ($hydrationMode == Doctrine_ORM_Query::HYDRATE_SCALAR) {
//...
}
......
......@@ -2,7 +2,8 @@
require_once 'lib/DoctrineTestInit.php';
require_once 'lib/mocks/Doctrine_EntityManagerMock.php';
require_once 'lib/mocks/Doctrine_ConnectionMock.php';
require_once 'lib/mocks/Doctrine_ClassMetadataMock.php';
require_once 'lib/mocks/Doctrine_UnitOfWorkMock.php';
require_once 'lib/mocks/Doctrine_IdentityIdGeneratorMock.php';
/**
* UnitOfWork tests.
......@@ -12,16 +13,11 @@ require_once 'lib/mocks/Doctrine_ClassMetadataMock.php';
class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
{
private $_unitOfWork;
private $_user;
// Mocks
// Provides a sequence mock to the UnitOfWork
private $_connectionMock;
// The sequence mock
private $_idGeneratorMock;
// The persister mock used by the UnitOfWork
private $_persisterMock;
// The EntityManager mock that provides the mock persister
private $_emMock;
......@@ -30,106 +26,141 @@ class Orm_UnitOfWorkTest extends Doctrine_OrmTestCase
$this->_connectionMock = new Doctrine_ConnectionMock(array());
$this->_emMock = Doctrine_EntityManagerMock::create($this->_connectionMock, "uowMockEm");
$this->_idGeneratorMock = new Doctrine_SequenceMock($this->_emMock);
$this->_emMock->setIdGenerator('ForumUser', $this->_idGeneratorMock);
$this->_emMock->setIdGenerator('ForumAvatar', $this->_idGeneratorMock);
$this->_persisterMock = new Doctrine_EntityPersisterMock(
$this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
$this->_emMock->setEntityPersister($this->_persisterMock);
$this->_emMock->activate();
// SUT
$this->_unitOfWork = $this->_emMock->getUnitOfWork();
$this->_user = new ForumUser();
$this->_user->id = 1;
$this->_user->username = 'romanb';
$this->_unitOfWork = new Doctrine_UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_unitOfWork);
}
protected function tearDown() {
//$this->_user->free();
}
/* Basic registration tests */
public function testRegisterNew()
{
// registerNew() is normally called in save()/persist()
$this->_unitOfWork->registerNew($this->_user);
$this->assertTrue($this->_unitOfWork->isRegisteredNew($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 testRegisterRemovedOnNewEntityIsIgnored()
{
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
$this->_unitOfWork->registerDeleted($this->_user);
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user));
$user = new ForumUser();
$user->username = 'romanb';
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($user));
$this->_unitOfWork->registerDeleted($user);
$this->assertFalse($this->_unitOfWork->isRegisteredRemoved($user));
}
/* Operational tests */
public function testSavingSingleEntityWithIdentityColumnForcesInsert()
{
$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));
{
// Setup fake persister and id generator for identity generation
$userPersister = new Doctrine_EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
$this->_emMock->setEntityPersister('ForumUser', $userPersister);
$idGeneratorMock = new Doctrine_IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('ForumUser', $idGeneratorMock);
$userPersister->setMockIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY);
// Test
$user = new ForumUser();
$user->username = 'romanb';
$this->_unitOfWork->save($user);
// Check
$this->assertEquals(1, count($userPersister->getInserts())); // insert forced
$this->assertEquals(0, count($userPersister->getUpdates()));
$this->assertEquals(0, count($userPersister->getDeletes()));
$this->assertTrue($this->_unitOfWork->isInIdentityMap($user));
// should no longer be scheduled for insert
$this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user));
$this->assertFalse($this->_unitOfWork->isRegisteredNew($user));
// should have an id
$this->assertTrue(is_numeric($this->_user->id));
$this->assertTrue(is_numeric($user->id));
// Now lets check whether a subsequent commit() does anything
$this->_persisterMock->reset();
$userPersister->reset();
// Test
$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()));
// Check. Verify that nothing happened.
$this->assertEquals(0, count($userPersister->getInserts()));
$this->assertEquals(0, count($userPersister->getUpdates()));
$this->assertEquals(0, count($userPersister->getDeletes()));
}
public function testCommitOrder()
/**
* Tests a scenario where a save() operation is cascaded from a ForumUser
* to its associated ForumAvatar, both entities using IDENTITY id generation.
*/
public function testCascadedIdentityColumnInsert()
{
// Setup fake persister and id generator for identity generation
//ForumUser
$userPersister = new Doctrine_EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("ForumUser"));
$this->_emMock->setEntityPersister('ForumUser', $userPersister);
$userIdGeneratorMock = new Doctrine_IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('ForumUser', $userIdGeneratorMock);
$userPersister->setMockIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY);
// ForumAvatar
$avatarPersister = new Doctrine_EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("ForumAvatar"));
$this->_emMock->setEntityPersister('ForumAvatar', $avatarPersister);
$avatarIdGeneratorMock = new Doctrine_IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('ForumAvatar', $avatarIdGeneratorMock);
$avatarPersister->setMockIdGeneratorType(Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY);
// Test
$user = new ForumUser();
$user->username = 'romanb';
$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]);
//...
$user->avatar = $avatar;
$this->_unitOfWork->save($user); // save cascaded to avatar
$this->assertTrue(is_numeric($user->id));
$this->assertTrue(is_numeric($avatar->id));
$this->assertEquals(1, count($userPersister->getInserts())); // insert forced
$this->assertEquals(0, count($userPersister->getUpdates()));
$this->assertEquals(0, count($userPersister->getDeletes()));
$this->assertEquals(1, count($avatarPersister->getInserts())); // insert forced
$this->assertEquals(0, count($avatarPersister->getUpdates()));
$this->assertEquals(0, count($avatarPersister->getDeletes()));
}
public function testComputeDataChangeSet()
{
$user1 = new ForumUser();
$user1->id = 1;
$user1->username = "romanb";
$user1->avatar = new ForumAvatar();
// Fake managed state
$this->_unitOfWork->setEntityState($user1, Doctrine_ORM_UnitOfWork::STATE_MANAGED);
$user2 = new ForumUser();
$user2->id = 2;
$user2->username = "jwage";
$this->_unitOfWork->setEntityState($user2, Doctrine_ORM_UnitOfWork::STATE_MANAGED);
$this->_unitOfWork->setOriginalEntityData($user1, array(
'id' => 1, 'username' => 'roman'
));
$this->_unitOfWork->setOriginalEntityData($user2, array(
'id' => 2, 'username' => 'jon'
));
$this->_unitOfWork->computeDataChangeSet(array($user1, $user2));
$user1ChangeSet = $this->_unitOfWork->getDataChangeSet($user1);
$this->assertTrue(is_array($user1ChangeSet));
$this->assertEquals(2, count($user1ChangeSet));
$this->assertTrue(isset($user1ChangeSet['username']));
$this->assertEquals(array('roman' => 'romanb'), $user1ChangeSet['username']);
$this->assertTrue(isset($user1ChangeSet['avatar']));
$this->assertSame(array(null => $user1->avatar), $user1ChangeSet['avatar']);
$user2ChangeSet = $this->_unitOfWork->getDataChangeSet($user2);
$this->assertTrue(is_array($user2ChangeSet));
$this->assertEquals(1, count($user2ChangeSet));
$this->assertTrue(isset($user2ChangeSet['username']));
$this->assertEquals(array('jon' => 'jwage'), $user2ChangeSet['username']);
}
/*
public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert()
{
......
......@@ -2,6 +2,9 @@
require_once 'lib/mocks/Doctrine_EntityPersisterMock.php';
/**
* Special EntityManager mock used for testing purposes.
*/
class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager
{
private $_persisterMock;
......@@ -13,8 +16,8 @@ class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager
*/
public function getEntityPersister($entityName)
{
return isset($this->_persisterMock) ? $this->_persisterMock :
parent::getEntityPersister($entityName);
return isset($this->_persisterMock[$entityName]) ?
$this->_persisterMock[$entityName] : parent::getEntityPersister($entityName);
}
/**
......@@ -27,24 +30,36 @@ class Doctrine_EntityManagerMock extends Doctrine_ORM_EntityManager
/* Mock API */
/**
* Sets a (mock) UnitOfWork that will be returned when getUnitOfWork() is called.
*
* @param <type> $uow
*/
public function setUnitOfWork($uow)
{
$this->_uowMock = $uow;
}
public function setEntityPersister($persister)
/**
* Sets a (mock) persister for an entity class that will be returned when
* getEntityPersister() is invoked for that class.
*
* @param <type> $entityName
* @param <type> $persister
*/
public function setEntityPersister($entityName, $persister)
{
$this->_persisterMock = $persister;
$this->_persisterMock[$entityName] = $persister;
}
/**
* Mock factory method.
* Mock factory method to create an EntityManager.
*
* @param unknown_type $conn
* @param unknown_type $name
* @param Doctrine_Configuration $config
* @param Doctrine_EventManager $eventManager
* @return unknown
* @return Doctrine\ORM\EntityManager
*/
public static function create($conn, $name, Doctrine_ORM_Configuration $config = null,
Doctrine_Common_EventManager $eventManager = null)
......
<?php
/**
* EntityPersister implementation used for mocking during tests.
*/
class Doctrine_EntityPersisterMock extends Doctrine_ORM_Persisters_StandardEntityPersister
{
private $_inserts = array();
private $_updates = array();
private $_deletes = array();
private $_identityColumnValueCounter = 0;
private $_mockIdGeneratorType;
/**
* @param <type> $entity
* @return <type>
* @override
*/
public function insert($entity)
{
$class = $this->_em->getClassMetadata(get_class($entity));
if ($class->isIdGeneratorIdentity()) {
$class->setEntityIdentifier($entity, $this->_identityColumnValueCounter++);
$this->_em->getUnitOfWork()->addToIdentityMap($entity);
}
$this->_inserts[] = $entity;
if ( ! is_null($this->_mockIdGeneratorType) && $this->_mockIdGeneratorType == Doctrine_ORM_Mapping_ClassMetadata::GENERATOR_TYPE_IDENTITY
|| $this->_classMetadata->isIdGeneratorIdentity()) {
return $this->_identityColumnValueCounter++;
}
return null;
}
public function setMockIdGeneratorType($genType) {
$this->_mockIdGeneratorType = $genType;
}
public function update(Doctrine_ORM_Entity $entity)
......@@ -51,7 +61,5 @@ class Doctrine_EntityPersisterMock extends Doctrine_ORM_Persisters_StandardEntit
$this->_updates = array();
$this->_deletes = array();
}
}
?>
\ No newline at end of file
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Description of Doctrine_IdentityIdGeneratorMock
*
* @author robo
*/
class Doctrine_IdentityIdGeneratorMock extends Doctrine_ORM_Id_IdentityGenerator
{
private $_mockPostInsertId;
public function setMockPostInsertId($id) {
$this->_mockPostInsertId = $id;
}
}
?>
......@@ -15,8 +15,6 @@ class Doctrine_SequenceMock extends Doctrine_ORM_Id_SequenceGenerator
return $this->_sequenceNumber++;
}
/**
* @override
*/
......
......@@ -17,7 +17,7 @@ class Doctrine_UnitOfWorkMock extends Doctrine_ORM_UnitOfWork {
* @override
*/
public function getDataChangeSet($entity) {
$oid = spl_object_id($entity);
$oid = spl_object_hash($entity);
return isset($this->_mockDataChangeSets[$oid]) ?
$this->_mockDataChangeSets[$oid] : parent::getDataChangeSet($entity);
}
......@@ -25,7 +25,17 @@ class Doctrine_UnitOfWorkMock extends Doctrine_ORM_UnitOfWork {
/* MOCK API */
public function setDataChangeSet($entity, array $mockChangeSet) {
$this->_mockDataChangeSets[spl_object_id($entity)] = $mockChangeSet;
$this->_mockDataChangeSets[spl_object_hash($entity)] = $mockChangeSet;
}
public function setEntityState($entity, $state)
{
$this->_entityStates[spl_object_hash($entity)] = $state;
}
public function setOriginalEntityData($entity, array $originalData)
{
$this->_originalEntityData[spl_object_hash($entity)] = $originalData;
}
}
......@@ -13,6 +13,18 @@ class ForumUser
public static function initMetadata($mapping)
{
/*$mapping->setClassMetadata(array(
'doctrine.inheritanceType' => 'joined',
'doctrine.discriminatorColumn' => 'dtype',
'doctrine.discriminatorMap' => array('user' => 'ForumUser', 'admin' => 'ForumAdministrator'),
'doctrine.subclasses' => array('ForumAdministrator')
));
$mapping->setFieldMetadata('id', array(
'doctrine.type' => 'integer',
'doctrine.id' => true,
'doctrine.idGenerator' => 'auto'
));*/
// inheritance mapping
$mapping->setInheritanceType('joined', array(
'discriminatorColumn' => 'dtype',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment