[2.0] First draft of EntityManager#merge(). First draft of DynamicProxyGenerator.
This commit is contained in:
parent
1da8f672ce
commit
e0488ff8fc
@ -95,6 +95,7 @@ class ClassLoader
|
||||
|
||||
$prefix = substr($className, 0, strpos($className, $this->_namespaceSeparator));
|
||||
$class = '';
|
||||
|
||||
if (isset($this->_basePaths[$prefix])) {
|
||||
$class .= $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ abstract class AbstractQuery
|
||||
if (count($result) > 1) {
|
||||
throw QueryException::nonUniqueResult();
|
||||
}
|
||||
return $result->getFirst();
|
||||
return $result->first();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
@ -45,10 +45,21 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
'queryCacheImpl' => null,
|
||||
'metadataCacheImpl' => null,
|
||||
'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()
|
||||
{
|
||||
return $this->_attributes['dqlClassAliasMap'];
|
||||
|
197
lib/Doctrine/ORM/DynamicProxyGenerator.php
Normal file
197
lib/Doctrine/ORM/DynamicProxyGenerator.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?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
|
||||
* IMMEDIATE: Flush occurs automatically after each operation that issues database
|
||||
* queries. No operations are queued.
|
||||
*/
|
||||
const FLUSHMODE_IMMEDIATE = 'immediate';
|
||||
const FLUSHMODE_IMMEDIATE = 1;
|
||||
/**
|
||||
* AUTO: Flush occurs automatically in the following situations:
|
||||
* - Before any query executions (to prevent getting stale data)
|
||||
* - On EntityManager#commit()
|
||||
*/
|
||||
const FLUSHMODE_AUTO = 'auto';
|
||||
const FLUSHMODE_AUTO = 2;
|
||||
/**
|
||||
* 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
|
||||
* through EntityManager#flush().
|
||||
*/
|
||||
const FLUSHMODE_MANUAL = 'manual';
|
||||
const FLUSHMODE_MANUAL = 4;
|
||||
|
||||
/**
|
||||
* The used Configuration.
|
||||
@ -92,7 +92,7 @@ class EntityManager
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_flushMode = 'commit';
|
||||
private $_flushMode = self::FLUSHMODE_COMMIT;
|
||||
|
||||
/**
|
||||
* The UnitOfWork used to coordinate object-level transactions.
|
||||
@ -115,6 +115,13 @@ class EntityManager
|
||||
*/
|
||||
private $_hydrators = array();
|
||||
|
||||
/**
|
||||
* The proxy generator.
|
||||
*
|
||||
* @var DynamicProxyGenerator
|
||||
*/
|
||||
private $_proxyGenerator;
|
||||
|
||||
/**
|
||||
* Whether the EntityManager is closed or not.
|
||||
*/
|
||||
@ -211,17 +218,6 @@ class EntityManager
|
||||
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.
|
||||
*
|
||||
@ -237,6 +233,7 @@ class EntityManager
|
||||
* Creates a native SQL query.
|
||||
*
|
||||
* @param string $sql
|
||||
* @param ResultSetMapping $rsm The ResultSetMapping to use.
|
||||
* @return Query
|
||||
*/
|
||||
public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm)
|
||||
@ -287,6 +284,23 @@ class EntityManager
|
||||
{
|
||||
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.
|
||||
@ -309,10 +323,7 @@ class EntityManager
|
||||
*/
|
||||
private function _isFlushMode($value)
|
||||
{
|
||||
return $value == self::FLUSHMODE_AUTO ||
|
||||
$value == self::FLUSHMODE_COMMIT ||
|
||||
$value == self::FLUSHMODE_IMMEDIATE ||
|
||||
$value == self::FLUSHMODE_MANUAL;
|
||||
return $value >= 1 && $value <= 4;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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)
|
||||
{
|
||||
@ -334,14 +348,18 @@ class EntityManager
|
||||
$this->_unitOfWork->detachAll();
|
||||
} else {
|
||||
//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()
|
||||
{
|
||||
$this->clear();
|
||||
$this->_closed = true;
|
||||
}
|
||||
|
||||
@ -378,52 +396,51 @@ class EntityManager
|
||||
* overriding any local changes that have not yet been persisted.
|
||||
*
|
||||
* @param object $entity
|
||||
* @todo FIX Impl
|
||||
* @todo Implemntation
|
||||
*/
|
||||
public function refresh($entity)
|
||||
{
|
||||
throw DoctrineException::notImplemented();
|
||||
/*$this->_mergeData($entity, $this->getRepository(get_class($entity))->find(
|
||||
$entity->identifier(), Query::HYDRATE_ARRAY),
|
||||
true);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches an entity from the EntityManager. Its lifecycle is no longer managed.
|
||||
*
|
||||
* @param object $entity The entity to detach.
|
||||
* @return boolean
|
||||
*/
|
||||
public function detach($entity)
|
||||
{
|
||||
return $this->_unitOfWork->removeFromIdentityMap($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return $this->_unitOfWork->merge($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
//...
|
||||
throw DoctrineException::notImplemented();
|
||||
}
|
||||
|
||||
/*
|
||||
public function toArray($entity, $deep = false)
|
||||
{
|
||||
$array = array();
|
||||
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)
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
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.
|
||||
*
|
||||
@ -528,11 +545,11 @@ class EntityManager
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\NoneHydrator($this);
|
||||
break;
|
||||
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);
|
||||
}
|
||||
}*/
|
||||
return $this->_hydrators[$hydrationMode];
|
||||
}
|
||||
|
||||
@ -540,13 +557,13 @@ class EntityManager
|
||||
* Sets a hydrator for a hydration mode.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function setHydrator($hydrationMode, $hydrator)
|
||||
/*public function setHydrator($hydrationMode, $hydrator)
|
||||
{
|
||||
$this->_hydrators[$hydrationMode] = $hydrator;
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Factory method to create EntityManager instances.
|
||||
@ -563,7 +580,7 @@ class EntityManager
|
||||
if (is_array($conn)) {
|
||||
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
|
||||
} else if ( ! $conn instanceof Connection) {
|
||||
\Doctrine\Common\DoctrineException::updateMe("Invalid parameter '$conn'.");
|
||||
throw DoctrineException::updateMe("Invalid parameter '$conn'.");
|
||||
}
|
||||
|
||||
if ($config === null) {
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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;
|
||||
@ -80,10 +80,6 @@ class EntityRepository
|
||||
*/
|
||||
public function find($id, $hydrationMode = null)
|
||||
{
|
||||
if ($id === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($id) && count($id) > 1) {
|
||||
// it's a composite key. keys = field names, values = values.
|
||||
$values = array_values($id);
|
||||
@ -98,9 +94,21 @@ class EntityRepository
|
||||
return $entity; // Hit!
|
||||
}
|
||||
|
||||
return $this->_createQuery()
|
||||
->where(implode(' = ? AND ', $keys) . ' = ?')
|
||||
->fetchOne($values, $hydrationMode);
|
||||
$dql = 'select e from ' . $this->_classMetadata->getClassName() . ' e where ';
|
||||
$conditionDql = '';
|
||||
$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
|
||||
*/
|
||||
public function findByDql($dql, array $params = array(), $hydrationMode = null)
|
||||
{
|
||||
$query = new Doctrine_Query($this->_em);
|
||||
$query = new Query($this->_em);
|
||||
$component = $this->getComponentName();
|
||||
$dql = 'FROM ' . $component . ' WHERE ' . $dql;
|
||||
|
||||
|
@ -103,6 +103,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_classMetadatas = array();
|
||||
|
||||
$e = microtime(true);
|
||||
|
||||
echo 'Hydration took: ' . ($e - $s) . PHP_EOL;
|
||||
|
||||
return $result;
|
||||
|
@ -50,6 +50,7 @@ abstract class AssociationMapping
|
||||
protected $_isCascadeDelete;
|
||||
protected $_isCascadeSave;
|
||||
protected $_isCascadeRefresh;
|
||||
protected $_isCascadeMerge;
|
||||
|
||||
/**
|
||||
* The fetch mode used for the association.
|
||||
@ -207,6 +208,20 @@ abstract class AssociationMapping
|
||||
}
|
||||
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.
|
||||
@ -262,6 +277,7 @@ abstract class AssociationMapping
|
||||
* Whether the association is optional (0..X), or not (1..X).
|
||||
*
|
||||
* @return boolean TRUE if the association is optional, FALSE otherwise.
|
||||
* @todo Only applicable to OneToOne. Move there.
|
||||
*/
|
||||
public function isOptional()
|
||||
{
|
||||
@ -319,26 +335,51 @@ abstract class AssociationMapping
|
||||
{
|
||||
return $this->_mappedByFieldName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the association is a one-to-one association.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isOneToOne()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the association is a one-to-many association.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isOneToMany()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the association is a many-to-many association.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isManyToMany()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the association uses a join table for the mapping.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function usesJoinTable()
|
||||
{
|
||||
return (bool)$this->_joinTable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <type> $entity
|
||||
* @param <type> $entityManager
|
||||
*/
|
||||
abstract public function lazyLoadFor($entity, $entityManager);
|
||||
}
|
@ -885,6 +885,45 @@ final class ClassMetadata
|
||||
$this->_reflectionProperties[$field]->setValue($entity, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -58,20 +58,26 @@ final class DoctrineOneToOne extends \Addendum\Annotation {
|
||||
public $targetEntity;
|
||||
public $mappedBy;
|
||||
public $cascade;
|
||||
public $fetch;
|
||||
public $optional;
|
||||
}
|
||||
final class DoctrineOneToMany extends \Addendum\Annotation {
|
||||
public $mappedBy;
|
||||
public $targetEntity;
|
||||
public $cascade;
|
||||
public $fetch;
|
||||
}
|
||||
final class DoctrineManyToOne extends \Addendum\Annotation {
|
||||
public $targetEntity;
|
||||
public $cascade;
|
||||
public $fetch;
|
||||
public $optional;
|
||||
}
|
||||
final class DoctrineManyToMany extends \Addendum\Annotation {
|
||||
public $targetEntity;
|
||||
public $mappedBy;
|
||||
public $cascade;
|
||||
public $fetch;
|
||||
}
|
||||
final class DoctrineElementCollection extends \Addendum\Annotation {
|
||||
public $tableName;
|
||||
|
@ -146,7 +146,7 @@ abstract class AbstractCollectionPersister
|
||||
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
|
||||
*/
|
||||
|
@ -919,7 +919,7 @@ class Query extends AbstractQuery
|
||||
$this->_dqlParts[$queryPartName] = array($queryPart);
|
||||
}
|
||||
|
||||
$this->_state = Doctrine_ORM_Query::STATE_DIRTY;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -398,12 +398,14 @@ class UnitOfWork implements PropertyChangedListener
|
||||
) {
|
||||
//TODO: If $actualData[$name] is Collection then unwrap the array
|
||||
$assoc = $class->getAssociationMapping($name);
|
||||
echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
|
||||
//echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
|
||||
// Inject PersistentCollection
|
||||
$coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(),
|
||||
$actualData[$name] ? $actualData[$name] : array());
|
||||
$coll->setOwner($entity, $assoc);
|
||||
if ( ! $coll->isEmpty()) $coll->setDirty(true);
|
||||
if ( ! $coll->isEmpty()) {
|
||||
$coll->setDirty(true);
|
||||
}
|
||||
$class->getReflectionProperty($name)->setValue($entity, $coll);
|
||||
$actualData[$name] = $coll;
|
||||
}
|
||||
@ -428,7 +430,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
|
||||
if (is_object($orgValue) && $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);
|
||||
}
|
||||
|
||||
@ -764,8 +766,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* Detaches an entity from the persistence management. It's persistence will
|
||||
* no longer be managed by Doctrine.
|
||||
*
|
||||
* @param integer $oid object identifier
|
||||
* @return boolean whether ot not the operation was successful
|
||||
* @param object $entity The entity to detach.
|
||||
*/
|
||||
public function detach($entity)
|
||||
{
|
||||
@ -802,7 +803,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
public function detachAll($entityName = null)
|
||||
{
|
||||
//TODO: what do do with new/dirty/removed lists?
|
||||
$numDetached = 0;
|
||||
if ($entityName !== null && isset($this->_identityMap[$entityName])) {
|
||||
$numDetached = count($this->_identityMap[$entityName]);
|
||||
@ -852,13 +852,20 @@ class UnitOfWork implements PropertyChangedListener
|
||||
/**
|
||||
* Gets the state of an entity within the current unit of work.
|
||||
*
|
||||
* @param Doctrine\ORM\Entity $entity
|
||||
* @param object $entity
|
||||
* @return int
|
||||
*/
|
||||
public function getEntityState($entity)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
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])) {
|
||||
$this->_entityStates[$oid] = self::STATE_DETACHED;
|
||||
} else {
|
||||
@ -872,7 +879,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* Removes an entity from the identity map. This effectively detaches the
|
||||
* entity from the persistence management of Doctrine.
|
||||
*
|
||||
* @param Doctrine\ORM\Entity $entity
|
||||
* @param object $entity
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeFromIdentityMap($entity)
|
||||
@ -1000,18 +1007,18 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return; // Prevent infinite recursion
|
||||
}
|
||||
|
||||
$visited[$oid] = $entity; // mark visited
|
||||
$visited[$oid] = $entity; // Mark visited
|
||||
|
||||
$class = $this->_em->getClassMetadata(get_class($entity));
|
||||
switch ($this->getEntityState($entity)) {
|
||||
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()) {
|
||||
$this->scheduleForDirtyCheck($entity);
|
||||
}
|
||||
break;
|
||||
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();
|
||||
if ($idGen->isPostInsertGenerator()) {
|
||||
$insertNow[$oid] = $entity;
|
||||
@ -1020,22 +1027,19 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$this->_entityStates[$oid] = self::STATE_MANAGED;
|
||||
if ( ! $idGen instanceof \Doctrine\ORM\Id\Assigned) {
|
||||
$this->_entityIdentifiers[$oid] = array($idValue);
|
||||
$class->getSingleIdReflectionProperty()->setValue($entity, $idValue);
|
||||
$class->setIdentifierValues($entity, $idValue);
|
||||
} else {
|
||||
$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);
|
||||
break;
|
||||
case self::STATE_DETACHED:
|
||||
throw DoctrineException::updateMe("Behavior of save() for a detached entity "
|
||||
. "is not yet defined.");
|
||||
case self::STATE_DELETED:
|
||||
// entity becomes managed again
|
||||
// Entity becomes managed again
|
||||
if ($this->isRegisteredRemoved($entity)) {
|
||||
//TODO: better a method for this?
|
||||
unset($this->_entityDeletions[$oid]);
|
||||
} else {
|
||||
//FIXME: There's more to think of here...
|
||||
@ -1093,6 +1097,93 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$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.
|
||||
*
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -1221,8 +1323,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$entity = $this->tryGetByIdHash($idHash, $class->getRootClassName());
|
||||
if ($entity) {
|
||||
$oid = spl_object_hash($entity);
|
||||
$this->_mergeData($entity, $data, $class/*, $query->getHint('doctrine.refresh')*/);
|
||||
return $entity;
|
||||
$overrideLocalChanges = false;
|
||||
//$overrideLocalChanges = $query->getHint('doctrine.refresh');
|
||||
} else {
|
||||
$entity = new $className;
|
||||
$oid = spl_object_hash($entity);
|
||||
@ -1233,33 +1335,18 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$prop->setValue($entity, new \Doctrine\ORM\VirtualProxy($entity, $lazyAssoc, $prop));
|
||||
}
|
||||
}*/
|
||||
$this->_mergeData($entity, $data, $class, true);
|
||||
$this->_entityIdentifiers[$oid] = $id;
|
||||
$this->_entityStates[$oid] = self::STATE_MANAGED;
|
||||
$this->_originalEntityData[$oid] = $data;
|
||||
$this->addToIdentityMap($entity);
|
||||
$overrideLocalChanges = true;
|
||||
}
|
||||
|
||||
$this->_originalEntityData[$oid] = $data;
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
foreach ($data as $field => $value) {
|
||||
$class->setValue($entity, $field, $value);
|
||||
}
|
||||
} else {
|
||||
$oid = spl_object_hash($entity);
|
||||
foreach ($data as $field => $value) {
|
||||
if ($class->hasField($field)) {
|
||||
$currentValue = $class->getReflectionProperty($field)->getValue($entity);
|
||||
@ -1270,6 +1357,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1300,9 +1389,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
/**
|
||||
* 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 $property
|
||||
@ -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)
|
||||
{
|
||||
@ -1429,23 +1524,25 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
$class = $this->_em->getClassMetadata(get_class($entity));
|
||||
|
||||
$this->_entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
|
||||
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
|
||||
$oid = spl_object_hash($entity);
|
||||
$class = $this->_em->getClassMetadata(get_class($entity));
|
||||
|
||||
if ($class->hasAssociation($propertyName)) {
|
||||
$assoc = $class->getAssociationMapping($name);
|
||||
if ($assoc->isOneToOne() && $assoc->isOwningSide()) {
|
||||
$this->_entityUpdates[$oid] = $entity;
|
||||
} else if ($oldValue instanceof PersistentCollection) {
|
||||
// A PersistentCollection was de-referenced, so delete it.
|
||||
if ( ! in_array($orgValue, $this->_collectionDeletions, true)) {
|
||||
$this->_collectionDeletions[] = $orgValue;
|
||||
$this->_entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
|
||||
|
||||
if ($class->hasAssociation($propertyName)) {
|
||||
$assoc = $class->getAssociationMapping($name);
|
||||
if ($assoc->isOneToOne() && $assoc->isOwningSide()) {
|
||||
$this->_entityUpdates[$oid] = $entity;
|
||||
} else if ($oldValue instanceof PersistentCollection) {
|
||||
// A PersistentCollection was de-referenced, so delete it.
|
||||
if ( ! in_array($orgValue, $this->_collectionDeletions, true)) {
|
||||
$this->_collectionDeletions[] = $orgValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->_entityUpdates[$oid] = $entity;
|
||||
}
|
||||
} else {
|
||||
$this->_entityUpdates[$oid] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
/**
|
||||
* Represents a virtual proxy that is used for lazy to-one associations.
|
||||
*
|
||||
* @author robo
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class VirtualProxy
|
||||
|
@ -46,6 +46,22 @@ class CmsUser
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -58,6 +74,10 @@ class CmsUser
|
||||
}
|
||||
}
|
||||
|
||||
public function getPhonenumbers() {
|
||||
return $this->phonenumbers;
|
||||
}
|
||||
|
||||
public function addArticle(CmsArticle $article) {
|
||||
$this->articles[] = $article;
|
||||
if ($article->user !== $this) {
|
||||
|
@ -22,6 +22,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\BasicFunctionalTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\NativeQueryTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\SingleTableInheritanceTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DetachedEntityTest');
|
||||
|
||||
return $suite;
|
||||
}
|
||||
|
@ -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");
|
||||
$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));
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
}
|
46
tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php
Normal file
46
tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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
|
||||
{
|
||||
$rsm = new ResultSetMapping;
|
||||
$rsm->addEntityResult($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), 'u');
|
||||
$rsm->addJoinedEntityResult(
|
||||
/*$rsm->addJoinedEntityResult(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'),
|
||||
'p',
|
||||
'u',
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers')
|
||||
);
|
||||
);*/
|
||||
$rsm->addFieldResult('u', 'u__id', 'id');
|
||||
$rsm->addFieldResult('u', 'u__status', 'status');
|
||||
$rsm->addScalarResult('sclr0', 'nameUpper');
|
||||
$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
|
||||
$rsm->addFieldResult('u', 'u__username', 'username');
|
||||
$rsm->addFieldResult('u', 'u__name', 'name');
|
||||
//$rsm->addScalarResult('sclr0', 'nameUpper');
|
||||
//$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
|
||||
|
||||
// Faked result set
|
||||
$resultSet = array(
|
||||
@ -710,29 +712,37 @@ class ObjectHydratorTest extends HydrationTest
|
||||
array(
|
||||
'u__id' => '1',
|
||||
'u__status' => 'developer',
|
||||
'sclr0' => 'ROMANB',
|
||||
'p__phonenumber' => '42',
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
//'sclr0' => 'ROMANB',
|
||||
//'p__phonenumber' => '42',
|
||||
),
|
||||
array(
|
||||
'u__id' => '1',
|
||||
'u__status' => 'developer',
|
||||
'sclr0' => 'ROMANB',
|
||||
'p__phonenumber' => '43',
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
//'sclr0' => 'ROMANB',
|
||||
//'p__phonenumber' => '43',
|
||||
),
|
||||
array(
|
||||
'u__id' => '2',
|
||||
'u__status' => 'developer',
|
||||
'sclr0' => 'JWAGE',
|
||||
'p__phonenumber' => '91'
|
||||
'u__username' => 'romanb',
|
||||
'u__name' => 'Roman',
|
||||
//'sclr0' => 'JWAGE',
|
||||
//'p__phonenumber' => '91'
|
||||
)
|
||||
);
|
||||
|
||||
for ($i = 4; $i < 300; $i++) {
|
||||
for ($i = 4; $i < 1000; ++$i) {
|
||||
$resultSet[] = array(
|
||||
'u__id' => $i,
|
||||
'u__status' => 'developer',
|
||||
'sclr0' => 'JWAGE' . $i,
|
||||
'p__phonenumber' => '91'
|
||||
'u__username' => 'jwage',
|
||||
'u__name' => 'Jonathan',
|
||||
//'sclr0' => 'JWAGE' . $i,
|
||||
//'p__phonenumber' => '91'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -20,13 +20,3 @@ set_include_path(
|
||||
. 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';*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user