Commit e0488ff8 authored by romanb's avatar romanb

[2.0] First draft of EntityManager#merge(). First draft of DynamicProxyGenerator.

parent 1da8f672
...@@ -95,6 +95,7 @@ class ClassLoader ...@@ -95,6 +95,7 @@ class ClassLoader
$prefix = substr($className, 0, strpos($className, $this->_namespaceSeparator)); $prefix = substr($className, 0, strpos($className, $this->_namespaceSeparator));
$class = ''; $class = '';
if (isset($this->_basePaths[$prefix])) { if (isset($this->_basePaths[$prefix])) {
$class .= $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR; $class .= $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR;
} }
......
...@@ -398,7 +398,7 @@ abstract class AbstractQuery ...@@ -398,7 +398,7 @@ abstract class AbstractQuery
if (count($result) > 1) { if (count($result) > 1) {
throw QueryException::nonUniqueResult(); throw QueryException::nonUniqueResult();
} }
return $result->getFirst(); return $result->first();
} }
return $result; return $result;
} }
......
...@@ -45,10 +45,21 @@ class Configuration extends \Doctrine\DBAL\Configuration ...@@ -45,10 +45,21 @@ class Configuration extends \Doctrine\DBAL\Configuration
'queryCacheImpl' => null, 'queryCacheImpl' => null,
'metadataCacheImpl' => null, 'metadataCacheImpl' => null,
'metadataDriverImpl' => new AnnotationDriver(), 'metadataDriverImpl' => new AnnotationDriver(),
'dqlClassAliasMap' => array() 'dqlClassAliasMap' => array(),
'cacheDir' => null
)); ));
} }
public function setCacheDir($dir)
{
$this->_attributes['cacheDir'] = $dir;
}
public function getCacheDir()
{
return $this->_attributes['cacheDir'];
}
public function getDqlClassAliasMap() public function getDqlClassAliasMap()
{ {
return $this->_attributes['dqlClassAliasMap']; return $this->_attributes['dqlClassAliasMap'];
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;
/**
* The DynamicProxyGenerator is used to generate proxy objects for entities.
* For that purpose he generates proxy class files on the fly as needed.
*
* @author Roman Borschel <roman@code-factory.org>
*/
class DynamicProxyGenerator
{
private $_cacheDir = '/Users/robo/dev/php/tmp/gen/';
private $_em;
public function __construct(EntityManager $em, $cacheDir = null)
{
$this->_em = $em;
if ($cacheDir === null) {
$cacheDir = sys_get_tmp_dir();
}
$this->_cacheDir = $cacheDir;
}
/**
*
*
* @param <type> $className
* @param <type> $identifier
* @return <type>
*/
public function getProxy($className, $identifier)
{
$proxyClassName = str_replace('\\', '_', $className) . 'Proxy';
if ( ! class_exists($proxyClassName, false)) {
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateProxyClass($className, $identifier, $proxyClassName, $fileName);
}
require $fileName;
}
$proxyClassName = '\Doctrine\Generated\Proxies\\' . $proxyClassName;
return new $proxyClassName($this->_em, $this->_em->getClassMetadata($className), $identifier);
}
/**
* Generates a proxy class.
*
* @param <type> $className
* @param <type> $id
* @param <type> $proxyClassName
* @param <type> $fileName
*/
private function _generateProxyClass($className, $id, $proxyClassName, $fileName)
{
$class = $this->_em->getClassMetadata($className);
$file = self::$_proxyClassTemplate;
if (is_array($id) && count($id) > 1) {
// it's a composite key. keys = field names, values = values.
$values = array_values($id);
$keys = array_keys($id);
} else {
$values = is_array($id) ? array_values($id) : array($id);
$keys = $class->getIdentifierFieldNames();
}
$paramIndex = 1;
$identifierCondition = 'prx.' . $keys[0] . ' = ?' . $paramIndex++;
for ($i=1, $c=count($keys); $i < $c; ++$i) {
$identifierCondition .= ' AND prx.' . $keys[$i] . ' = ?' . $paramIndex++;
}
$parameters = 'array(';
$first = true;
foreach ($values as $value) {
if ($first) {
$first = false;
} else {
$parameters = ', ';
}
$parameters .= "'" . $value . "'";
}
$parameters .= ')';
$hydrationSetters = '';
foreach ($class->getReflectionProperties() as $name => $prop) {
if ( ! $class->hasAssociation($name)) {
$hydrationSetters .= '$this->_class->setValue($this, \'' . $name . '\', $scalar[0][\'prx_' . $name . '\']);' . PHP_EOL;
}
}
$methods = '';
foreach ($class->getReflectionClass()->getMethods() as $method) {
if ($method->isPublic() && ! $method->isFinal()) {
$methods .= PHP_EOL . 'public function ' . $method->getName() . '(';
$firstParam = true;
$parameterString = '';
foreach ($method->getParameters() as $param) {
if ($firstParam) {
$firstParam = false;
} else {
$parameterString .= ', ';
}
$parameterString .= '$' . $param->getName();
}
$methods .= $parameterString . ') {' . PHP_EOL;
$methods .= '$this->_load();' . PHP_EOL;
$methods .= 'return parent::' . $method->getName() . '(' . $parameterString . ');';
$methods .= '}' . PHP_EOL;
}
}
$sleepImpl = '';
if ($class->getReflectionClass()->hasMethod('__sleep')) {
$sleepImpl .= 'return parent::__sleep();';
} else {
$sleepImpl .= 'return array(';
$first = true;
foreach ($class->getReflectionProperties() as $name => $prop) {
if ($first) {
$first = false;
} else {
$sleepImpl .= ', ';
}
$sleepImpl .= "'" . $name . "'";
}
$sleepImpl .= ');';
}
$placeholders = array(
'<proxyClassName>', '<className>', '<identifierCondition>',
'<parameters>', '<hydrationSetters>', '<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $identifierCondition, $parameters,
$hydrationSetters, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
}
/** Proxy class code template */
private static $_proxyClassTemplate =
'<?php
/** This class was generated by the Doctrine ORM. DO NOT EDIT THIS FILE. */
namespace Doctrine\Generated\Proxies {
class <proxyClassName> extends \<className> {
private $_em;
private $_class;
private $_loaded = false;
public function __construct($em, $class, $identifier) {
$this->_em = $em;
$this->_class = $class;
$this->_class->setIdentifierValues($this, $identifier);
}
private function _load() {
if ( ! $this->_loaded) {
$scalar = $this->_em->createQuery(\'select prx from <className> prx where <identifierCondition>\')->execute(<parameters>, \Doctrine\ORM\Query::HYDRATE_SCALAR);
<hydrationSetters>
unset($this->_em);
unset($this->_class);
$this->_loaded = true;
}
}
<methods>
public function __sleep() {
if (!$this->_loaded) {
throw new RuntimeException("Not fully loaded proxy can not be serialized.");
}
<sleepImpl>
}
}
}';
}
...@@ -42,22 +42,22 @@ class EntityManager ...@@ -42,22 +42,22 @@ class EntityManager
* IMMEDIATE: Flush occurs automatically after each operation that issues database * IMMEDIATE: Flush occurs automatically after each operation that issues database
* queries. No operations are queued. * queries. No operations are queued.
*/ */
const FLUSHMODE_IMMEDIATE = 'immediate'; const FLUSHMODE_IMMEDIATE = 1;
/** /**
* AUTO: Flush occurs automatically in the following situations: * AUTO: Flush occurs automatically in the following situations:
* - Before any query executions (to prevent getting stale data) * - Before any query executions (to prevent getting stale data)
* - On EntityManager#commit() * - On EntityManager#commit()
*/ */
const FLUSHMODE_AUTO = 'auto'; const FLUSHMODE_AUTO = 2;
/** /**
* COMMIT: Flush occurs automatically only on EntityManager#commit(). * COMMIT: Flush occurs automatically only on EntityManager#commit().
*/ */
const FLUSHMODE_COMMIT = 'commit'; const FLUSHMODE_COMMIT = 3;
/** /**
* MANUAL: Flush occurs never automatically. The only way to flush is * MANUAL: Flush occurs never automatically. The only way to flush is
* through EntityManager#flush(). * through EntityManager#flush().
*/ */
const FLUSHMODE_MANUAL = 'manual'; const FLUSHMODE_MANUAL = 4;
/** /**
* The used Configuration. * The used Configuration.
...@@ -92,7 +92,7 @@ class EntityManager ...@@ -92,7 +92,7 @@ class EntityManager
* *
* @var string * @var string
*/ */
private $_flushMode = 'commit'; private $_flushMode = self::FLUSHMODE_COMMIT;
/** /**
* The UnitOfWork used to coordinate object-level transactions. * The UnitOfWork used to coordinate object-level transactions.
...@@ -115,6 +115,13 @@ class EntityManager ...@@ -115,6 +115,13 @@ class EntityManager
*/ */
private $_hydrators = array(); private $_hydrators = array();
/**
* The proxy generator.
*
* @var DynamicProxyGenerator
*/
private $_proxyGenerator;
/** /**
* Whether the EntityManager is closed or not. * Whether the EntityManager is closed or not.
*/ */
...@@ -211,17 +218,6 @@ class EntityManager ...@@ -211,17 +218,6 @@ class EntityManager
return $query; return $query;
} }
/**
* Detaches an entity from the manager. It's lifecycle is no longer managed.
*
* @param object $entity
* @return boolean
*/
public function detach($entity)
{
return $this->_unitOfWork->removeFromIdentityMap($entity);
}
/** /**
* Creates a DQL query with the specified name. * Creates a DQL query with the specified name.
* *
...@@ -237,6 +233,7 @@ class EntityManager ...@@ -237,6 +233,7 @@ class EntityManager
* Creates a native SQL query. * Creates a native SQL query.
* *
* @param string $sql * @param string $sql
* @param ResultSetMapping $rsm The ResultSetMapping to use.
* @return Query * @return Query
*/ */
public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm) public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm)
...@@ -288,6 +285,23 @@ class EntityManager ...@@ -288,6 +285,23 @@ class EntityManager
return $this->getRepository($entityName)->find($identifier); return $this->getRepository($entityName)->find($identifier);
} }
/**
* Gets a reference to the entity identified by the given type and identifier
* without actually loading it. Only the identifier of the returned entity
* will be populated.
*
* NOTE: There is currently no magic proxying in place, that means the full state
* of the entity will not be loaded upon accessing it.
*
* @return object The entity reference.
*/
public function getReference($entityName, $identifier)
{
$entity = new $entityName;
$this->getClassMetadata($entityName)->setEntityIdentifier($entity, $identifier);
return $entity;
}
/** /**
* Sets the flush mode to use. * Sets the flush mode to use.
* *
...@@ -309,10 +323,7 @@ class EntityManager ...@@ -309,10 +323,7 @@ class EntityManager
*/ */
private function _isFlushMode($value) private function _isFlushMode($value)
{ {
return $value == self::FLUSHMODE_AUTO || return $value >= 1 && $value <= 4;
$value == self::FLUSHMODE_COMMIT ||
$value == self::FLUSHMODE_IMMEDIATE ||
$value == self::FLUSHMODE_MANUAL;
} }
/** /**
...@@ -326,7 +337,10 @@ class EntityManager ...@@ -326,7 +337,10 @@ class EntityManager
} }
/** /**
* Clears the persistence context, effectively detaching all managed entities. * Clears the EntityManager. All entities that are currently managed
* by this EntityManager become detached.
*
* @param string $entityName
*/ */
public function clear($entityName = null) public function clear($entityName = null)
{ {
...@@ -334,14 +348,18 @@ class EntityManager ...@@ -334,14 +348,18 @@ class EntityManager
$this->_unitOfWork->detachAll(); $this->_unitOfWork->detachAll();
} else { } else {
//TODO //TODO
throw DoctrineException::notImplemented();
} }
} }
/** /**
* Closes the EntityManager. * Closes the EntityManager. All entities that are currently managed
* by this EntityManager become detached. The EntityManager may no longer
* be used after it is closed.
*/ */
public function close() public function close()
{ {
$this->clear();
$this->_closed = true; $this->_closed = true;
} }
...@@ -378,51 +396,50 @@ class EntityManager ...@@ -378,51 +396,50 @@ class EntityManager
* overriding any local changes that have not yet been persisted. * overriding any local changes that have not yet been persisted.
* *
* @param object $entity * @param object $entity
* @todo FIX Impl * @todo Implemntation
*/ */
public function refresh($entity) public function refresh($entity)
{ {
throw DoctrineException::notImplemented();
/*$this->_mergeData($entity, $this->getRepository(get_class($entity))->find( /*$this->_mergeData($entity, $this->getRepository(get_class($entity))->find(
$entity->identifier(), Query::HYDRATE_ARRAY), $entity->identifier(), Query::HYDRATE_ARRAY),
true);*/ true);*/
} }
/** /**
* Creates a copy of the given entity. Can create a shallow or a deep copy. * Detaches an entity from the EntityManager. Its lifecycle is no longer managed.
* *
* @param object $entity The entity to copy. * @param object $entity The entity to detach.
* @return object The new entity. * @return boolean
*/ */
public function copy($entity, $deep = false) public function detach($entity)
{ {
//... return $this->_unitOfWork->removeFromIdentityMap($entity);
} }
/* /**
public function toArray($entity, $deep = false) * Merges the state of a detached entity into the persistence context
* of this EntityManager.
*
* @param object $entity The entity to merge into the persistence context.
* @return object The managed copy of the entity.
*/
public function merge($entity)
{ {
$array = array(); return $this->_unitOfWork->merge($entity);
foreach ($entity as $key => $value) {
if ($deep && is_object($value)) {
$array[$key] = $this->toArray($value, $deep);
} else if ( ! is_object($value)) {
$array[$key] = $value;
}
}
return $array;
} }
public function fromArray($entity, array $array, $deep = false) /**
* Creates a copy of the given entity. Can create a shallow or a deep copy.
*
* @param object $entity The entity to copy.
* @return object The new entity.
* @todo Implementation or remove.
*/
public function copy($entity, $deep = false)
{ {
foreach ($array as $key => $value) { throw DoctrineException::notImplemented();
if ($deep && is_array($value)) {
$entity->$key = $this->fromArray($entity, $value, $deep);
} else if ( ! is_array($value)) {
$entity->$key = $value;
}
} }
}
*/
/** /**
* Gets the repository for an entity class. * Gets the repository for an entity class.
...@@ -528,11 +545,11 @@ class EntityManager ...@@ -528,11 +545,11 @@ class EntityManager
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\NoneHydrator($this); $this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\NoneHydrator($this);
break; break;
default: default:
\Doctrine\Common\DoctrineException::updateMe("No hydrator found for hydration mode '$hydrationMode'."); throw DoctrineException::updateMe("No hydrator found for hydration mode '$hydrationMode'.");
} }
} else if ($this->_hydrators[$hydrationMode] instanceof Closure) { }/* else if ($this->_hydrators[$hydrationMode] instanceof Closure) {
$this->_hydrators[$hydrationMode] = $this->_hydrators[$hydrationMode]($this); $this->_hydrators[$hydrationMode] = $this->_hydrators[$hydrationMode]($this);
} }*/
return $this->_hydrators[$hydrationMode]; return $this->_hydrators[$hydrationMode];
} }
...@@ -540,13 +557,13 @@ class EntityManager ...@@ -540,13 +557,13 @@ class EntityManager
* Sets a hydrator for a hydration mode. * Sets a hydrator for a hydration mode.
* *
* @param mixed $hydrationMode * @param mixed $hydrationMode
* @param object $hydrator Either a hydrator instance or a closure that creates a * @param object $hydrator Either a hydrator instance or a Closure that creates a
* hydrator instance. * hydrator instance.
*/ */
public function setHydrator($hydrationMode, $hydrator) /*public function setHydrator($hydrationMode, $hydrator)
{ {
$this->_hydrators[$hydrationMode] = $hydrator; $this->_hydrators[$hydrationMode] = $hydrator;
} }*/
/** /**
* Factory method to create EntityManager instances. * Factory method to create EntityManager instances.
...@@ -563,7 +580,7 @@ class EntityManager ...@@ -563,7 +580,7 @@ class EntityManager
if (is_array($conn)) { if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager); $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
} else if ( ! $conn instanceof Connection) { } else if ( ! $conn instanceof Connection) {
\Doctrine\Common\DoctrineException::updateMe("Invalid parameter '$conn'."); throw DoctrineException::updateMe("Invalid parameter '$conn'.");
} }
if ($config === null) { if ($config === null) {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* This software consists of voluntary contributions made by many individuals * This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM; namespace Doctrine\ORM;
...@@ -80,10 +80,6 @@ class EntityRepository ...@@ -80,10 +80,6 @@ class EntityRepository
*/ */
public function find($id, $hydrationMode = null) public function find($id, $hydrationMode = null)
{ {
if ($id === null) {
return false;
}
if (is_array($id) && count($id) > 1) { if (is_array($id) && count($id) > 1) {
// it's a composite key. keys = field names, values = values. // it's a composite key. keys = field names, values = values.
$values = array_values($id); $values = array_values($id);
...@@ -98,9 +94,21 @@ class EntityRepository ...@@ -98,9 +94,21 @@ class EntityRepository
return $entity; // Hit! return $entity; // Hit!
} }
return $this->_createQuery() $dql = 'select e from ' . $this->_classMetadata->getClassName() . ' e where ';
->where(implode(' = ? AND ', $keys) . ' = ?') $conditionDql = '';
->fetchOne($values, $hydrationMode); $paramIndex = 1;
foreach ($keys as $key) {
if ($conditionDql != '') $conditionDql .= ' and ';
$conditionDql .= 'e.' . $key . ' = ?' . $paramIndex++;
}
$dql .= $conditionDql;
$q = $this->_em->createQuery($dql);
foreach ($values as $index => $value) {
$q->setParameter($index, $value);
}
return $q->getSingleResult($hydrationMode);
} }
/** /**
...@@ -172,7 +180,7 @@ class EntityRepository ...@@ -172,7 +180,7 @@ class EntityRepository
*/ */
public function findByDql($dql, array $params = array(), $hydrationMode = null) public function findByDql($dql, array $params = array(), $hydrationMode = null)
{ {
$query = new Doctrine_Query($this->_em); $query = new Query($this->_em);
$component = $this->getComponentName(); $component = $this->getComponentName();
$dql = 'FROM ' . $component . ' WHERE ' . $dql; $dql = 'FROM ' . $component . ' WHERE ' . $dql;
......
...@@ -103,6 +103,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -103,6 +103,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_classMetadatas = array(); $this->_classMetadatas = array();
$e = microtime(true); $e = microtime(true);
echo 'Hydration took: ' . ($e - $s) . PHP_EOL; echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
return $result; return $result;
......
...@@ -50,6 +50,7 @@ abstract class AssociationMapping ...@@ -50,6 +50,7 @@ abstract class AssociationMapping
protected $_isCascadeDelete; protected $_isCascadeDelete;
protected $_isCascadeSave; protected $_isCascadeSave;
protected $_isCascadeRefresh; protected $_isCascadeRefresh;
protected $_isCascadeMerge;
/** /**
* The fetch mode used for the association. * The fetch mode used for the association.
...@@ -208,6 +209,20 @@ abstract class AssociationMapping ...@@ -208,6 +209,20 @@ abstract class AssociationMapping
return $this->_isCascadeRefresh; return $this->_isCascadeRefresh;
} }
/**
* Whether the association cascades merge() operations from the source entity
* to the target entity/entities.
*
* @return boolean
*/
public function isCascadeMerge()
{
if ($this->_isCascadeMerge === null) {
$this->_isCascadeMerge = in_array('merge', $this->_cascades);
}
return $this->_isCascadeMerge;
}
/** /**
* Whether the target entity/entities of the association are eagerly fetched. * Whether the target entity/entities of the association are eagerly fetched.
* *
...@@ -262,6 +277,7 @@ abstract class AssociationMapping ...@@ -262,6 +277,7 @@ abstract class AssociationMapping
* Whether the association is optional (0..X), or not (1..X). * Whether the association is optional (0..X), or not (1..X).
* *
* @return boolean TRUE if the association is optional, FALSE otherwise. * @return boolean TRUE if the association is optional, FALSE otherwise.
* @todo Only applicable to OneToOne. Move there.
*/ */
public function isOptional() public function isOptional()
{ {
...@@ -320,25 +336,50 @@ abstract class AssociationMapping ...@@ -320,25 +336,50 @@ abstract class AssociationMapping
return $this->_mappedByFieldName; return $this->_mappedByFieldName;
} }
/**
* Whether the association is a one-to-one association.
*
* @return boolean
*/
public function isOneToOne() public function isOneToOne()
{ {
return false; return false;
} }
/**
* Whether the association is a one-to-many association.
*
* @return boolean
*/
public function isOneToMany() public function isOneToMany()
{ {
return false; return false;
} }
/**
* Whether the association is a many-to-many association.
*
* @return boolean
*/
public function isManyToMany() public function isManyToMany()
{ {
return false; return false;
} }
/**
* Whether the association uses a join table for the mapping.
*
* @return boolean
*/
public function usesJoinTable() public function usesJoinTable()
{ {
return (bool)$this->_joinTable; return (bool)$this->_joinTable;
} }
/**
*
* @param <type> $entity
* @param <type> $entityManager
*/
abstract public function lazyLoadFor($entity, $entityManager); abstract public function lazyLoadFor($entity, $entityManager);
} }
\ No newline at end of file
...@@ -886,6 +886,45 @@ final class ClassMetadata ...@@ -886,6 +886,45 @@ final class ClassMetadata
} }
} }
/**
* Extracts the identifier values of an entity of this class.
*
* @param object $entity
* @return mixed
*/
public function getIdentifierValues($entity)
{
if ($this->_isIdentifierComposite) {
$id = array();
foreach ($this->_identifier as $idField) {
$value = $this->_reflectionProperties[$idField]->getValue($entity);
if ($value !== null) {
$id[] = $value;
}
}
return $id;
} else {
return $this->_reflectionProperties[$this->_identifier[0]]->getValue($entity);
}
}
/**
* Populates the entity identifier of an entity.
*
* @param object $entity
* @param mixed $id
*/
public function setIdentifierValues($entity, $id)
{
if ($this->_isIdentifierComposite) {
foreach ((array)$id as $idField => $idValue) {
$this->_reflectionProperties[$idField]->setValue($entity, $idValue);
}
} else {
$this->_reflectionProperties[$this->_identifier[0]]->setValue($entity, $id);
}
}
/** /**
* Gets all field mappings. * Gets all field mappings.
* *
......
...@@ -58,20 +58,26 @@ final class DoctrineOneToOne extends \Addendum\Annotation { ...@@ -58,20 +58,26 @@ final class DoctrineOneToOne extends \Addendum\Annotation {
public $targetEntity; public $targetEntity;
public $mappedBy; public $mappedBy;
public $cascade; public $cascade;
public $fetch;
public $optional;
} }
final class DoctrineOneToMany extends \Addendum\Annotation { final class DoctrineOneToMany extends \Addendum\Annotation {
public $mappedBy; public $mappedBy;
public $targetEntity; public $targetEntity;
public $cascade; public $cascade;
public $fetch;
} }
final class DoctrineManyToOne extends \Addendum\Annotation { final class DoctrineManyToOne extends \Addendum\Annotation {
public $targetEntity; public $targetEntity;
public $cascade; public $cascade;
public $fetch;
public $optional;
} }
final class DoctrineManyToMany extends \Addendum\Annotation { final class DoctrineManyToMany extends \Addendum\Annotation {
public $targetEntity; public $targetEntity;
public $mappedBy; public $mappedBy;
public $cascade; public $cascade;
public $fetch;
} }
final class DoctrineElementCollection extends \Addendum\Annotation { final class DoctrineElementCollection extends \Addendum\Annotation {
public $tableName; public $tableName;
......
...@@ -146,7 +146,7 @@ abstract class AbstractCollectionPersister ...@@ -146,7 +146,7 @@ abstract class AbstractCollectionPersister
abstract protected function _getUpdateRowSql(PersistentCollection $coll); abstract protected function _getUpdateRowSql(PersistentCollection $coll);
/** /**
* Gets the SQL statement used for inserting a row from to the collection. * Gets the SQL statement used for inserting a row in the collection.
* *
* @param PersistentCollection $coll * @param PersistentCollection $coll
*/ */
......
...@@ -919,7 +919,7 @@ class Query extends AbstractQuery ...@@ -919,7 +919,7 @@ class Query extends AbstractQuery
$this->_dqlParts[$queryPartName] = array($queryPart); $this->_dqlParts[$queryPartName] = array($queryPart);
} }
$this->_state = Doctrine_ORM_Query::STATE_DIRTY; $this->_state = self::STATE_DIRTY;
return $this; return $this;
} }
......
...@@ -398,12 +398,14 @@ class UnitOfWork implements PropertyChangedListener ...@@ -398,12 +398,14 @@ class UnitOfWork implements PropertyChangedListener
) { ) {
//TODO: If $actualData[$name] is Collection then unwrap the array //TODO: If $actualData[$name] is Collection then unwrap the array
$assoc = $class->getAssociationMapping($name); $assoc = $class->getAssociationMapping($name);
echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL; //echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
// Inject PersistentCollection // Inject PersistentCollection
$coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(), $coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(),
$actualData[$name] ? $actualData[$name] : array()); $actualData[$name] ? $actualData[$name] : array());
$coll->setOwner($entity, $assoc); $coll->setOwner($entity, $assoc);
if ( ! $coll->isEmpty()) $coll->setDirty(true); if ( ! $coll->isEmpty()) {
$coll->setDirty(true);
}
$class->getReflectionProperty($name)->setValue($entity, $coll); $class->getReflectionProperty($name)->setValue($entity, $coll);
$actualData[$name] = $coll; $actualData[$name] = $coll;
} }
...@@ -428,7 +430,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -428,7 +430,7 @@ class UnitOfWork implements PropertyChangedListener
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (is_object($orgValue) && $orgValue !== $actualValue) { if (is_object($orgValue) && $orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue); $changeSet[$propName] = array($orgValue, $actualValue);
} else if ($orgValue != $actualValue || ($orgValue === null xor $actualValue === null)) { } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
$changeSet[$propName] = array($orgValue, $actualValue); $changeSet[$propName] = array($orgValue, $actualValue);
} }
...@@ -764,8 +766,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -764,8 +766,7 @@ class UnitOfWork implements PropertyChangedListener
* Detaches an entity from the persistence management. It's persistence will * Detaches an entity from the persistence management. It's persistence will
* no longer be managed by Doctrine. * no longer be managed by Doctrine.
* *
* @param integer $oid object identifier * @param object $entity The entity to detach.
* @return boolean whether ot not the operation was successful
*/ */
public function detach($entity) public function detach($entity)
{ {
...@@ -802,7 +803,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -802,7 +803,6 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function detachAll($entityName = null) public function detachAll($entityName = null)
{ {
//TODO: what do do with new/dirty/removed lists?
$numDetached = 0; $numDetached = 0;
if ($entityName !== null && isset($this->_identityMap[$entityName])) { if ($entityName !== null && isset($this->_identityMap[$entityName])) {
$numDetached = count($this->_identityMap[$entityName]); $numDetached = count($this->_identityMap[$entityName]);
...@@ -852,13 +852,20 @@ class UnitOfWork implements PropertyChangedListener ...@@ -852,13 +852,20 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Gets the state of an entity within the current unit of work. * Gets the state of an entity within the current unit of work.
* *
* @param Doctrine\ORM\Entity $entity * @param object $entity
* @return int * @return int
*/ */
public function getEntityState($entity) public function getEntityState($entity)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if ( ! isset($this->_entityStates[$oid])) { if ( ! isset($this->_entityStates[$oid])) {
/*if (isset($this->_entityInsertions[$oid])) {
$this->_entityStates[$oid] = self::STATE_NEW;
} else if ( ! isset($this->_entityIdentifiers[$oid])) {
// Either NEW (if no ID) or DETACHED (if ID)
} else {
$this->_entityStates[$oid] = self::STATE_DETACHED;
}*/
if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_entityInsertions[$oid])) { if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_entityInsertions[$oid])) {
$this->_entityStates[$oid] = self::STATE_DETACHED; $this->_entityStates[$oid] = self::STATE_DETACHED;
} else { } else {
...@@ -872,7 +879,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -872,7 +879,7 @@ class UnitOfWork implements PropertyChangedListener
* Removes an entity from the identity map. This effectively detaches the * Removes an entity from the identity map. This effectively detaches the
* entity from the persistence management of Doctrine. * entity from the persistence management of Doctrine.
* *
* @param Doctrine\ORM\Entity $entity * @param object $entity
* @return boolean * @return boolean
*/ */
public function removeFromIdentityMap($entity) public function removeFromIdentityMap($entity)
...@@ -1000,18 +1007,18 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1000,18 +1007,18 @@ class UnitOfWork implements PropertyChangedListener
return; // Prevent infinite recursion return; // Prevent infinite recursion
} }
$visited[$oid] = $entity; // mark visited $visited[$oid] = $entity; // Mark visited
$class = $this->_em->getClassMetadata(get_class($entity)); $class = $this->_em->getClassMetadata(get_class($entity));
switch ($this->getEntityState($entity)) { switch ($this->getEntityState($entity)) {
case self::STATE_MANAGED: case self::STATE_MANAGED:
// nothing to do, except if policy is "deferred explicit" // Nothing to do, except if policy is "deferred explicit"
if ($class->isChangeTrackingDeferredExplicit()) { if ($class->isChangeTrackingDeferredExplicit()) {
$this->scheduleForDirtyCheck($entity); $this->scheduleForDirtyCheck($entity);
} }
break; break;
case self::STATE_NEW: case self::STATE_NEW:
//TODO: Better defer insert for post-insert ID generators also. //TODO: Better defer insert for post-insert ID generators also?
$idGen = $class->getIdGenerator(); $idGen = $class->getIdGenerator();
if ($idGen->isPostInsertGenerator()) { if ($idGen->isPostInsertGenerator()) {
$insertNow[$oid] = $entity; $insertNow[$oid] = $entity;
...@@ -1020,22 +1027,19 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1020,22 +1027,19 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityStates[$oid] = self::STATE_MANAGED; $this->_entityStates[$oid] = self::STATE_MANAGED;
if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) { if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) {
$this->_entityIdentifiers[$oid] = array($idValue); $this->_entityIdentifiers[$oid] = array($idValue);
$class->getSingleIdReflectionProperty()->setValue($entity, $idValue); $class->setIdentifierValues($entity, $idValue);
} else { } else {
$this->_entityIdentifiers[$oid] = $idValue; $this->_entityIdentifiers[$oid] = $idValue;
} }
} }
//TODO: Calculate changeSet now instead of later to allow some optimizations
// in calculateChangeSets() (ie no need to consider NEW objects) ?
$this->registerNew($entity); $this->registerNew($entity);
break; break;
case self::STATE_DETACHED: case self::STATE_DETACHED:
throw DoctrineException::updateMe("Behavior of save() for a detached entity " throw DoctrineException::updateMe("Behavior of save() for a detached entity "
. "is not yet defined."); . "is not yet defined.");
case self::STATE_DELETED: case self::STATE_DELETED:
// entity becomes managed again // Entity becomes managed again
if ($this->isRegisteredRemoved($entity)) { if ($this->isRegisteredRemoved($entity)) {
//TODO: better a method for this?
unset($this->_entityDeletions[$oid]); unset($this->_entityDeletions[$oid]);
} else { } else {
//FIXME: There's more to think of here... //FIXME: There's more to think of here...
...@@ -1093,6 +1097,93 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1093,6 +1097,93 @@ class UnitOfWork implements PropertyChangedListener
$this->_cascadeDelete($entity, $visited); $this->_cascadeDelete($entity, $visited);
} }
/**
* Merges the state of the given detached entity into this UnitOfWork.
*
* @param object $entity
* @return object The managed copy of the entity.
*/
public function merge($entity)
{
$visited = array();
return $this->_doMerge($entity, $visited);
}
/**
* Executes a merge operation on an entity.
*
* @param object $entity
* @param array $visited
* @return object The managed copy of the entity.
*/
private function _doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
{
$class = $this->_em->getClassMetadata(get_class($entity));
$id = $class->getIdentifierValues($entity);
if ( ! $id) {
throw new InvalidArgumentException('New entity passed to merge().');
}
$managedCopy = $this->tryGetById($id, $class->getRootClassName());
if ($managedCopy) {
if ($this->getEntityState($managedCopy) == self::STATE_DELETED) {
throw new InvalidArgumentException('Can not merge with a deleted entity.');
}
} else {
$managedCopy = $this->_em->find($class->getClassName(), $id);
}
// Merge state of $entity into existing (managed) entity
foreach ($class->getReflectionProperties() as $name => $prop) {
if ( ! $class->hasAssociation($name)) {
$prop->setValue($managedCopy, $prop->getValue($entity));
}
if ($class->isChangeTrackingNotify()) {
//TODO
}
}
if ($class->isChangeTrackingDeferredExplicit()) {
//TODO
}
if ($prevManagedCopy !== null) {
$assocField = $assoc->getSourceFieldName();
$prevClass = $this->_em->getClassMetadata(get_class($prevManagedCopy));
if ($assoc->isOneToOne()) {
$prevClass->getReflectionProperty($assocField)->setValue($prevManagedCopy, $managedCopy);
} else {
$prevClass->getReflectionProperty($assocField)->getValue($prevManagedCopy)->add($managedCopy);
}
}
$this->_cascadeMerge($entity, $managedCopy, $visited);
return $managedCopy;
}
/**
* Cascades a merge operation to associated entities.
*/
private function _cascadeMerge($entity, $managedCopy, array &$visited)
{
$class = $this->_em->getClassMetadata(get_class($entity));
foreach ($class->getAssociationMappings() as $assocMapping) {
if ( ! $assocMapping->isCascadeMerge()) {
continue;
}
$relatedEntities = $class->getReflectionProperty($assocMapping->getSourceFieldName())
->getValue($entity);
if (($relatedEntities instanceof Collection) && count($relatedEntities) > 0) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doMerge($relatedEntity, $visited, $managedCopy, $assocMapping);
}
} else if (is_object($relatedEntities)) {
$this->_doMerge($relatedEntities, $visited, $managedCopy, $assocMapping);
}
}
}
/** /**
* Cascades the save operation to associated entities. * Cascades the save operation to associated entities.
* *
...@@ -1155,11 +1246,22 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1155,11 +1246,22 @@ class UnitOfWork implements PropertyChangedListener
} }
/** /**
* Closes the UnitOfWork. * Clears the UnitOfWork.
*/ */
public function close() public function clear()
{ {
//... $this->_identityMap = array();
$this->_entityIdentifiers = array();
$this->_originalEntityData = array();
$this->_entityChangeSets = array();
$this->_entityStates = array();
$this->_scheduledForDirtyCheck = array();
$this->_entityInsertions = array();
$this->_entityUpdates = array();
$this->_entityDeletions = array();
$this->_collectionDeletions = array();
$this->_collectionCreations = array();
$this->_collectionUpdates = array();
$this->_commitOrderCalculator->clear(); $this->_commitOrderCalculator->clear();
} }
...@@ -1221,8 +1323,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1221,8 +1323,8 @@ class UnitOfWork implements PropertyChangedListener
$entity = $this->tryGetByIdHash($idHash, $class->getRootClassName()); $entity = $this->tryGetByIdHash($idHash, $class->getRootClassName());
if ($entity) { if ($entity) {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$this->_mergeData($entity, $data, $class/*, $query->getHint('doctrine.refresh')*/); $overrideLocalChanges = false;
return $entity; //$overrideLocalChanges = $query->getHint('doctrine.refresh');
} else { } else {
$entity = new $className; $entity = new $className;
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
...@@ -1233,33 +1335,18 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1233,33 +1335,18 @@ class UnitOfWork implements PropertyChangedListener
$prop->setValue($entity, new \Doctrine\ORM\VirtualProxy($entity, $lazyAssoc, $prop)); $prop->setValue($entity, new \Doctrine\ORM\VirtualProxy($entity, $lazyAssoc, $prop));
} }
}*/ }*/
$this->_mergeData($entity, $data, $class, true);
$this->_entityIdentifiers[$oid] = $id; $this->_entityIdentifiers[$oid] = $id;
$this->_entityStates[$oid] = self::STATE_MANAGED; $this->_entityStates[$oid] = self::STATE_MANAGED;
$this->addToIdentityMap($entity);
}
$this->_originalEntityData[$oid] = $data; $this->_originalEntityData[$oid] = $data;
$this->addToIdentityMap($entity);
return $entity; $overrideLocalChanges = true;
} }
/**
* Merges the given data into the given entity, optionally overriding
* local changes.
*
* @param object $entity
* @param array $data
* @param boolean $overrideLocalChanges
* @todo Consider moving to ClassMetadata for a little performance improvement.
*/
private function _mergeData($entity, array $data, $class, $overrideLocalChanges = false) {
if ($overrideLocalChanges) { if ($overrideLocalChanges) {
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
$class->setValue($entity, $field, $value); $class->setValue($entity, $field, $value);
} }
} else { } else {
$oid = spl_object_hash($entity);
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
if ($class->hasField($field)) { if ($class->hasField($field)) {
$currentValue = $class->getReflectionProperty($field)->getValue($entity); $currentValue = $class->getReflectionProperty($field)->getValue($entity);
...@@ -1270,6 +1357,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1270,6 +1357,8 @@ class UnitOfWork implements PropertyChangedListener
} }
} }
} }
return $entity;
} }
/** /**
...@@ -1300,9 +1389,9 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1300,9 +1389,9 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* INTERNAL: * INTERNAL:
* For hydration purposes only. * For internal purposes only.
* *
* Sets a property of the original data array of an entity. * Sets a property value of the original data array of an entity.
* *
* @param string $oid * @param string $oid
* @param string $property * @param string $property
...@@ -1328,7 +1417,13 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1328,7 +1417,13 @@ class UnitOfWork implements PropertyChangedListener
} }
/** /**
* Tries to find an entity with the given identifier in the identity map of
* this UnitOfWork.
* *
* @param mixed $id The entity identifier to look for.
* @param string $rootClassName The name of the root class of the mapped entity hierarchy.
* @return mixed Returns the entity with the specified identifier if it exists in
* this UnitOfWork, FALSE otherwise.
*/ */
public function tryGetById($id, $rootClassName) public function tryGetById($id, $rootClassName)
{ {
...@@ -1429,6 +1524,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1429,6 +1524,7 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function propertyChanged($entity, $propertyName, $oldValue, $newValue) public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
{ {
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$class = $this->_em->getClassMetadata(get_class($entity)); $class = $this->_em->getClassMetadata(get_class($entity));
...@@ -1448,4 +1544,5 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1448,4 +1544,5 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityUpdates[$oid] = $entity; $this->_entityUpdates[$oid] = $entity;
} }
} }
}
} }
\ No newline at end of file
...@@ -26,7 +26,7 @@ use Doctrine\ORM\Mapping\AssociationMapping; ...@@ -26,7 +26,7 @@ use Doctrine\ORM\Mapping\AssociationMapping;
/** /**
* Represents a virtual proxy that is used for lazy to-one associations. * Represents a virtual proxy that is used for lazy to-one associations.
* *
* @author robo * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
class VirtualProxy class VirtualProxy
......
...@@ -46,6 +46,22 @@ class CmsUser ...@@ -46,6 +46,22 @@ class CmsUser
*/ */
public $groups; public $groups;
public function getId() {
return $this->id;
}
public function getStatus() {
return $this->status;
}
public function getUsername() {
return $this->username;
}
public function getName() {
return $this->name;
}
/** /**
* Adds a phonenumber to the user. * Adds a phonenumber to the user.
* *
...@@ -58,6 +74,10 @@ class CmsUser ...@@ -58,6 +74,10 @@ class CmsUser
} }
} }
public function getPhonenumbers() {
return $this->phonenumbers;
}
public function addArticle(CmsArticle $article) { public function addArticle(CmsArticle $article) {
$this->articles[] = $article; $this->articles[] = $article;
if ($article->user !== $this) { if ($article->user !== $this) {
......
...@@ -22,6 +22,7 @@ class AllTests ...@@ -22,6 +22,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DetachedEntityTest');
return $suite; return $suite;
} }
......
...@@ -306,5 +306,14 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -306,5 +306,14 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u inner join u.groups g"); $query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u inner join u.groups g");
$this->assertEquals(0, count($query->getResultList())); $this->assertEquals(0, count($query->getResultList()));
/* RB: TEST
\Doctrine\ORM\DynamicProxyGenerator::configure($this->_em);
$proxy = \Doctrine\ORM\DynamicProxyGenerator::getReferenceProxy('Doctrine\Tests\Models\CMS\CmsUser', 1);
echo $proxy->getId();
var_dump(serialize($proxy));
*/
} }
} }
\ No newline at end of file
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../TestInit.php';
/**
* Description of DetachedEntityTest
*
* @author robo
*/
class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
}
public function testSimpleDetachMerge() {
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$this->_em->save($user);
$this->_em->flush();
$this->_em->clear();
// $user is now detached
$this->assertFalse($this->_em->contains($user));
$user->name = 'Roman B.';
//$this->assertEquals(UnitOfWork::STATE_DETACHED, $this->_em->getUnitOfWork()->getEntityState($user));
$user2 = $this->_em->merge($user);
$this->assertFalse($user === $user2);
$this->assertTrue($this->_em->contains($user2));
$this->assertEquals('Roman B.', $user2->name);
}
}
...@@ -693,16 +693,18 @@ class ObjectHydratorTest extends HydrationTest ...@@ -693,16 +693,18 @@ class ObjectHydratorTest extends HydrationTest
{ {
$rsm = new ResultSetMapping; $rsm = new ResultSetMapping;
$rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u'); $rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u');
$rsm->addJoinedEntityResult( /*$rsm->addJoinedEntityResult(
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'),
'p', 'p',
'u', 'u',
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers')
); );*/
$rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status'); $rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper'); $rsm->addFieldResult('u', 'u__username', 'username');
$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); $rsm->addFieldResult('u', 'u__name', 'name');
//$rsm->addScalarResult('sclr0', 'nameUpper');
//$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
// Faked result set // Faked result set
$resultSet = array( $resultSet = array(
...@@ -710,29 +712,37 @@ class ObjectHydratorTest extends HydrationTest ...@@ -710,29 +712,37 @@ class ObjectHydratorTest extends HydrationTest
array( array(
'u__id' => '1', 'u__id' => '1',
'u__status' => 'developer', 'u__status' => 'developer',
'sclr0' => 'ROMANB', 'u__username' => 'romanb',
'p__phonenumber' => '42', 'u__name' => 'Roman',
//'sclr0' => 'ROMANB',
//'p__phonenumber' => '42',
), ),
array( array(
'u__id' => '1', 'u__id' => '1',
'u__status' => 'developer', 'u__status' => 'developer',
'sclr0' => 'ROMANB', 'u__username' => 'romanb',
'p__phonenumber' => '43', 'u__name' => 'Roman',
//'sclr0' => 'ROMANB',
//'p__phonenumber' => '43',
), ),
array( array(
'u__id' => '2', 'u__id' => '2',
'u__status' => 'developer', 'u__status' => 'developer',
'sclr0' => 'JWAGE', 'u__username' => 'romanb',
'p__phonenumber' => '91' 'u__name' => 'Roman',
//'sclr0' => 'JWAGE',
//'p__phonenumber' => '91'
) )
); );
for ($i = 4; $i < 300; $i++) { for ($i = 4; $i < 1000; ++$i) {
$resultSet[] = array( $resultSet[] = array(
'u__id' => $i, 'u__id' => $i,
'u__status' => 'developer', 'u__status' => 'developer',
'sclr0' => 'JWAGE' . $i, 'u__username' => 'jwage',
'p__phonenumber' => '91' 'u__name' => 'Jonathan',
//'sclr0' => 'JWAGE' . $i,
//'p__phonenumber' => '91'
); );
} }
......
...@@ -20,13 +20,3 @@ set_include_path( ...@@ -20,13 +20,3 @@ set_include_path(
. PATH_SEPARATOR . $modelDir . DIRECTORY_SEPARATOR . 'forum' . PATH_SEPARATOR . $modelDir . DIRECTORY_SEPARATOR . 'forum'
); );
// Some of these classes depend on Doctrine_* classes
/*require_once 'DoctrineTestCase.php';
require_once 'TestUtil.php';
require_once 'DbalTestCase.php';
require_once 'OrmTestCase.php';
require_once 'OrmFunctionalTestCase.php';
require_once 'DoctrineTestSuite.php';
require_once 'OrmTestSuite.php';
require_once 'OrmFunctionalTestSuite.php';
require_once 'DbalTestSuite.php';*/
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