1
0
mirror of synced 2025-01-18 22:41:43 +03:00

Merge branch 'master' of git://github.com/doctrine/doctrine2

This commit is contained in:
Benjamin Eberlei 2011-10-25 22:55:08 +02:00
commit cfe7ab46f2
73 changed files with 2739 additions and 249 deletions

View File

@ -1,6 +1,6 @@
# Doctrine 2 ORM # Doctrine 2 ORM
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.0+ that provides transparent persistence Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility

View File

@ -1,3 +1,14 @@
# EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they
haven't been in the identity map before. This means objects of this kind never lead to changes
in the UnitOfWork.
# Fields omitted in a partial DQL query or a native query are never updated
Fields of an entity that are not returned from a partial DQL Query or native SQL query
will never be updated through an UPDATE statement.
# Removed support for onUpdate in @JoinColumn # Removed support for onUpdate in @JoinColumn
The onUpdate foreign key handling makes absolutly no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed. The onUpdate foreign key handling makes absolutly no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.

20
composer.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "doctrine/orm",
"type": "library",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
"license": "LGPL",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": "master-dev",
"doctrine/dbal": "master-dev"
}
}

View File

@ -150,7 +150,7 @@
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:enumeration value="CASCADE"/> <xs:enumeration value="CASCADE"/>
<xs:enumeration value="RESTRICT"/> <xs:enumeration value="RESTRICT"/>
<xs:enumeration value="SET_NULL"/> <xs:enumeration value="SET NULL"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

View File

@ -325,13 +325,17 @@ class EntityManager implements ObjectManager
* This effectively synchronizes the in-memory state of managed objects with the * This effectively synchronizes the in-memory state of managed objects with the
* database. * database.
* *
* If an entity is explicitly passed to this method only this entity and
* the cascade-persist semantics + scheduled inserts/removals are synchronized.
*
* @param object $entity
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that * @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
* makes use of optimistic locking fails. * makes use of optimistic locking fails.
*/ */
public function flush() public function flush($entity = null)
{ {
$this->errorIfClosed(); $this->errorIfClosed();
$this->unitOfWork->commit(); $this->unitOfWork->commit($entity);
} }
/** /**
@ -413,6 +417,7 @@ class EntityManager implements ObjectManager
$entity = $class->newInstance(); $entity = $class->newInstance();
$class->setIdentifierValues($entity, $identifier); $class->setIdentifierValues($entity, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array()); $this->unitOfWork->registerManaged($entity, $identifier, array());
$this->unitOfWork->markReadOnly($entity);
return $entity; return $entity;
} }
@ -421,16 +426,11 @@ class EntityManager implements ObjectManager
* Clears the EntityManager. All entities that are currently managed * Clears the EntityManager. All entities that are currently managed
* by this EntityManager become detached. * by this EntityManager become detached.
* *
* @param string $entityName * @param string $entityName if given, only entities of this type will get detached
*/ */
public function clear($entityName = null) public function clear($entityName = null)
{ {
if ($entityName === null) { $this->unitOfWork->clear($entityName);
$this->unitOfWork->clear();
} else {
//TODO
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
}
} }
/** /**
@ -713,6 +713,18 @@ class EntityManager implements ObjectManager
return $this->proxyFactory; return $this->proxyFactory;
} }
/**
* Helper method to initialize a lazy loading proxy or persistent collection.
*
* This method is a no-op for other objects
*
* @param object $obj
*/
public function initializeObject($obj)
{
$this->unitOfWork->initializeObject($obj);
}
/** /**
* Factory method to create EntityManager instances. * Factory method to create EntityManager instances.
* *

View File

@ -178,7 +178,7 @@ class EntityRepository implements ObjectRepository
*/ */
public function findOneBy(array $criteria) public function findOneBy(array $criteria)
{ {
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria); return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria, null, null, array(), 0, 1);
} }
/** /**
@ -204,8 +204,7 @@ class EntityRepository implements ObjectRepository
); );
} }
if ( !isset($arguments[0])) { if (empty($arguments)) {
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
throw ORMException::findByRequiresParameter($method.$by); throw ORMException::findByRequiresParameter($method.$by);
} }

View File

@ -0,0 +1,110 @@
<?php
/*
* 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\Event;
use \Doctrine\Common\EventSubscriber;
use \LogicException;
/**
* Delegate events only for certain entities they are registered for.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.2
*/
class EntityEventDelegator implements EventSubscriber
{
/**
* Keeps track of all the event listeners.
*
* @var array
*/
private $listeners = array();
/**
* If frozen no new event listeners can be added.
*
* @var bool
*/
private $frozen = false;
/**
* Adds an event listener that listens on the specified events.
*
* @param string|array $events The event(s) to listen on.
* @param string|array $entities The entities to trigger this listener for
* @param object $listener The listener object.
*/
public function addEventListener($events, $entities, $listener)
{
if ($this->frozen) {
throw new LogicException("Cannot add event listeners after EntityEventDelegator::getSubscribedEvents() " .
"is called once. This happens when you register the delegator with the event manager.");
}
// Picks the hash code related to that listener
$hash = spl_object_hash($listener);
foreach ((array) $events as $event) {
// Overrides listener if a previous one was associated already
// Prevents duplicate listeners on same event (same instance only)
$this->listeners[$event][$hash] = array('listener' => $listener, 'entities' => array_flip((array)$entities));
}
}
/**
* Adds an EventSubscriber. The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
*/
public function addEventSubscriber(EventSubscriber $subscriber, $entities)
{
$this->addEventListener($subscriber->getSubscribedEvents(), $entities, $subscriber);
}
/**
* Returns an array of events this subscriber wants to listen to.
*
* @return array
*/
public function getSubscribedEvents()
{
$this->frozen = true;
return array_keys($this->listeners);
}
/**
* Delegate the event to an appropriate listener
*
* @param $eventName
* @param $event
* @return void
*/
public function __call($eventName, $args)
{
$event = $args[0];
foreach ($this->listeners[$eventName] AS $listenerData) {
$class = get_class($event->getEntity());
if (isset($listenerData['entities'][$class])) {
$listenerData['listener']->$eventName($event);
}
}
}
}

View File

@ -36,12 +36,18 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
*/ */
private $em; private $em;
/**
* @var string
*/
private $entityClass;
/** /**
* @param \Doctrine\ORM\EntityManager $em * @param \Doctrine\ORM\EntityManager $em
*/ */
public function __construct($em) public function __construct($em, $entityClass = null)
{ {
$this->em = $em; $this->em = $em;
$this->entityClass = $entityClass;
} }
/** /**
@ -51,4 +57,24 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
{ {
return $this->em; return $this->em;
} }
/**
* Name of the entity class that is cleared, or empty if all are cleared.
*
* @return string
*/
public function getEntityClass()
{
return $this->entityClass;
}
/**
* Check if event clears all entities.
*
* @return bool
*/
public function clearsAllEntities()
{
return $this->entityClass === null;
}
} }

View File

@ -0,0 +1,57 @@
<?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\Event;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\EventArgs;
/**
* Provides event arguments for the postFlush event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision$
* @author Daniel Freudenberger <df@rebuy.de>
*/
class PostFlushEventArgs extends EventArgs
{
/**
* @var EntityManager
*/
private $em;
/**
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
}

View File

@ -90,7 +90,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
if (!isset($this->_entityChangeSet[$field])) { if (!isset($this->_entityChangeSet[$field])) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
"Field '".$field."' is not a valid field of the entity ". "Field '".$field."' is not a valid field of the entity ".
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs." "'".get_class($this->getEntity())."' in PreUpdateEventArgs."
); );
} }
} }

View File

@ -120,6 +120,17 @@ final class Events
*/ */
const onFlush = 'onFlush'; const onFlush = 'onFlush';
/**
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
* after all actual database operations are executed successfully. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the postFlush event is not raised. The event won't be raised if an error occurs during the
* flush operation.
*
* @var string
*/
const postFlush = 'postFlush';
/** /**
* The onClear event occurs when the EntityManager#clear() operation is invoked, * The onClear event occurs when the EntityManager#clear() operation is invoked,
* after all references to entities have been removed from the unit of work. * after all references to entities have been removed from the unit of work.

View File

@ -60,7 +60,7 @@ class AssignedGenerator extends AbstractIdGenerator
$identifier[$idField] = $value; $identifier[$idField] = $value;
} }
} else { } else {
throw ORMException::entityMissingAssignedId($entity); throw ORMException::entityMissingAssignedIdForField($entity, $idField);
} }
} }
} else { } else {
@ -78,7 +78,7 @@ class AssignedGenerator extends AbstractIdGenerator
$identifier[$idField] = $value; $identifier[$idField] = $value;
} }
} else { } else {
throw ORMException::entityMissingAssignedId($entity); throw ORMException::entityMissingAssignedIdForField($entity, $idField);
} }
} }

View File

@ -164,6 +164,11 @@ abstract class AbstractHydrator
* field names during this procedure as well as any necessary conversions on * field names during this procedure as well as any necessary conversions on
* the values applied. * the values applied.
* *
* @param array $data SQL Result Row
* @param array &$cache Cache for column to field result information
* @param array &$id Dql-Alias => ID-Hash
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
*
* @return array An array with all the fields (name => value) of the data row, * @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias. * grouped by their component alias.
*/ */

View File

@ -92,6 +92,11 @@ class ArrayHydrator extends AbstractHydrator
$parent = $this->_rsm->parentAliasMap[$dqlAlias]; $parent = $this->_rsm->parentAliasMap[$dqlAlias];
$path = $parent . '.' . $dqlAlias; $path = $parent . '.' . $dqlAlias;
// missing parent data, skipping as RIGHT JOIN hydration is not supported.
if ( ! isset($nonemptyComponents[$parent]) ) {
continue;
}
// Get a reference to the right element in the result tree. // Get a reference to the right element in the result tree.
// This element will get the associated element attached. // This element will get the associated element attached.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
@ -155,6 +160,17 @@ class ArrayHydrator extends AbstractHydrator
$this->_rootAliases[$dqlAlias] = true; // Mark as root $this->_rootAliases[$dqlAlias] = true; // Mark as root
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array(0 => null);
} else {
$result[] = null;
}
++$this->_resultCounter;
continue;
}
// Check for an existing element // Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias]; $element = $rowData[$dqlAlias];

View File

@ -302,6 +302,12 @@ class ObjectHydrator extends AbstractHydrator
// seen for this parent-child relationship // seen for this parent-child relationship
$path = $parentAlias . '.' . $dqlAlias; $path = $parentAlias . '.' . $dqlAlias;
// We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
if (!isset($nonemptyComponents[$parentAlias])) {
// TODO: Add special case code where we hydrate the right join objects into identity map at least
continue;
}
// Get a reference to the parent object to which the joined element belongs. // Get a reference to the parent object to which the joined element belongs.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) { if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
$first = reset($this->_resultPointers); $first = reset($this->_resultPointers);
@ -408,6 +414,18 @@ class ObjectHydrator extends AbstractHydrator
// PATH C: Its a root result element // PATH C: Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias $this->_rootAliases[$dqlAlias] = true; // Mark as root alias
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array(0 => null);
} else {
$result[] = null;
}
++$this->_resultCounter;
continue;
}
// check for existing result from the iterations before
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias); $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) { if (isset($this->_rsm->indexByMap[$dqlAlias])) {

View File

@ -50,7 +50,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
private $targetPlatform; private $targetPlatform;
/** /**
* @var Driver\Driver * @var \Doctrine\ORM\Mapping\Driver\Driver
*/ */
private $driver; private $driver;
@ -309,6 +309,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->setPrimaryTable($parent->table); $class->setPrimaryTable($parent->table);
} }
if ($parent && $parent->containsForeignIdentifier) {
$class->containsForeignIdentifier = true;
}
$class->setParentClasses($visited); $class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) { if ($this->evm->hasListeners(Events::loadClassMetadata)) {
@ -481,4 +485,19 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
throw new ORMException("Unknown generator type: " . $class->generatorType); throw new ORMException("Unknown generator type: " . $class->generatorType);
} }
} }
/**
* Check if this class is mapped by this EntityManager + ClassMetadata configuration
*
* @param $class
* @return bool
*/
public function isTransient($class)
{
if ( ! $this->initialized) {
$this->initialize();
}
return $this->driver->isTransient($class);
}
} }

View File

@ -318,7 +318,7 @@ class ClassMetadataInfo implements ClassMetadata
public $discriminatorMap = array(); public $discriminatorMap = array();
/** /**
* READ-ONLY: The definition of the descriminator column used in JOINED and SINGLE_TABLE * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
* inheritance mappings. * inheritance mappings.
* *
* @var array * @var array

View File

@ -0,0 +1,176 @@
<?php
/*
* 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\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
/**
* XmlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license MIT
*/
class SimplifiedXmlDriver extends XmlDriver
{
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.xml';
public function __construct($prefixes)
{
$this->addNamespacePrefixes($prefixes);
}
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function addNamespacePrefixes($prefixes)
{
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
$this->addPaths(array_flip($prefixes));
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
{
if (null === $this->_classCache) {
$this->initialize();
}
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
return array_merge($classes, array_keys($this->_classCache));
}
public function getElement($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
if (!isset($this->_classCache[$className])) {
$this->_classCache[$className] = parent::getElement($className);
}
return $this->_classCache[$className];
}
protected function initialize()
{
$this->_classCache = array();
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (is_file($filename)) {
return $filename;
}
throw MappingException::mappingFileNotFound($className, $filename);
}
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
}
}

View File

@ -0,0 +1,182 @@
<?php
/*
* 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\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
/**
* YamlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license MIT
*/
class SimplifiedYamlDriver extends YamlDriver
{
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.yml';
public function __construct($prefixes)
{
$this->addNamespacePrefixes($prefixes);
}
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function addNamespacePrefixes($prefixes)
{
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
$this->addPaths(array_flip($prefixes));
}
public function addNamespacePrefix($prefix, $path)
{
$this->_prefixes[$path] = $prefix;
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
{
if (null === $this->_classCache) {
$this->initialize();
}
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
return array_merge($classes, array_keys($this->_classCache));
}
public function getElement($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
if (!isset($this->_classCache[$className])) {
$this->_classCache[$className] = parent::getElement($className);
}
return $this->_classCache[$className];
}
protected function initialize()
{
$this->_classCache = array();
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (is_file($filename)) {
return $filename;
}
throw MappingException::mappingFileNotFound($className, $filename);
}
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
}
}

View File

@ -288,7 +288,7 @@ class MappingException extends \Doctrine\ORM\ORMException
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName) public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
{ {
return new self( return new self(
"Entity '" . $className . "' has to be part of the descriminator map of '" . $rootClassName . "' " . "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " .
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " . "to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
"to avoid this exception from occuring." "to avoid this exception from occuring."
); );

View File

@ -46,15 +46,14 @@ class ORMException extends Exception
); );
} }
public static function entityMissingAssignedId($entity) public static function entityMissingAssignedIdForField($entity, $field)
{ {
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " . return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " .
"The identifier generation strategy for this entity requires the ID field to be populated before ". "The identifier generation strategy for this entity requires the ID field to be populated before ".
"EntityManager#persist() is called. If you want automatically generated identifiers instead " . "EntityManager#persist() is called. If you want automatically generated identifiers instead " .
"you need to adjust the metadata mapping accordingly." "you need to adjust the metadata mapping accordingly."
); );
} }
public static function unrecognizedField($field) public static function unrecognizedField($field)
{ {
return new self("Unrecognized field: $field"); return new self("Unrecognized field: $field");

View File

@ -580,12 +580,13 @@ class BasicEntityPersister
* @param $assoc The association that connects the entity to load to another entity, if any. * @param $assoc The association that connects the entity to load to another entity, if any.
* @param array $hints Hints for entity creation. * @param array $hints Hints for entity creation.
* @param int $lockMode * @param int $lockMode
* @param int $limit Limit number of results
* @return object The loaded and managed entity instance or NULL if the entity can not be found. * @return object The loaded and managed entity instance or NULL if the entity can not be found.
* @todo Check identity map? loadById method? Try to guess whether $criteria is the id? * @todo Check identity map? loadById method? Try to guess whether $criteria is the id?
*/ */
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0) public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null)
{ {
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode); $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
@ -747,6 +748,7 @@ class BasicEntityPersister
* *
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
*
* @return array * @return array
*/ */
private function loadArrayFromStatement($assoc, $stmt) private function loadArrayFromStatement($assoc, $stmt)
@ -771,6 +773,8 @@ class BasicEntityPersister
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
* @param PersistentCollection $coll * @param PersistentCollection $coll
*
* @return array
*/ */
private function loadCollectionFromStatement($assoc, $stmt, $coll) private function loadCollectionFromStatement($assoc, $stmt, $coll)
{ {
@ -784,7 +788,8 @@ class BasicEntityPersister
} }
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $rsm, $hints);
return $hydrator->hydrateAll($stmt, $rsm, $hints);
} }
/** /**
@ -819,7 +824,7 @@ class BasicEntityPersister
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
} }
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
@ -843,7 +848,7 @@ class BasicEntityPersister
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
} }
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
@ -1056,10 +1061,10 @@ class BasicEntityPersister
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $columnAlias;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true); $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
} }
} }
@ -1179,6 +1184,7 @@ class BasicEntityPersister
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
. '.' . $class->getQuotedColumnName($field, $this->_platform); . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field); $this->_rsm->addFieldResult($alias, $columnAlias, $field);
return $sql . ' AS ' . $columnAlias; return $sql . ' AS ' . $columnAlias;
@ -1228,7 +1234,9 @@ class BasicEntityPersister
$sql = 'SELECT 1 ' $sql = 'SELECT 1 '
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
} }
@ -1320,7 +1328,8 @@ class BasicEntityPersister
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{ {
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
$this->loadCollectionFromStatement($assoc, $stmt, $coll);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
} }
/** /**
@ -1347,7 +1356,7 @@ class BasicEntityPersister
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
} }
$criteria[$tableAlias . "." . $targetKeyColumn] = $value; $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
@ -1478,6 +1487,7 @@ class BasicEntityPersister
public function exists($entity, array $extraConditions = array()) public function exists($entity, array $extraConditions = array())
{ {
$criteria = $this->_class->getIdentifierValues($entity); $criteria = $this->_class->getIdentifierValues($entity);
if ($extraConditions) { if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions); $criteria = array_merge($criteria, $extraConditions);
} }
@ -1486,6 +1496,8 @@ class BasicEntityPersister
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name) . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name)
. ' WHERE ' . $this->_getSelectConditionSQL($criteria); . ' WHERE ' . $this->_getSelectConditionSQL($criteria);
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria)); list($params, $types) = $this->expandParameters($criteria);
return (bool) $this->_conn->fetchColumn($sql, $params);
} }
} }

View File

@ -41,6 +41,10 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
/** {@inheritdoc} */ /** {@inheritdoc} */
protected function _getSelectColumnListSQL() protected function _getSelectColumnListSQL()
{ {
if ($this->_selectColumnListSql !== null) {
return $this->_selectColumnListSql;
}
$columnList = parent::_getSelectColumnListSQL(); $columnList = parent::_getSelectColumnListSQL();
// Append discriminator column // Append discriminator column
@ -81,7 +85,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
} }
} }
return $columnList; $this->_selectColumnListSql = $columnList;
return $this->_selectColumnListSql;
} }
/** {@inheritdoc} */ /** {@inheritdoc} */

View File

@ -209,6 +209,11 @@ class ProxyFactory
$methods .= $parameterString . ')'; $methods .= $parameterString . ')';
$methods .= "\n" . ' {' . "\n"; $methods .= "\n" . ' {' . "\n";
if ($this->isShortIdentifierGetter($method, $class)) {
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
$methods .= ' return $this->_identifier["' . lcfirst(substr($method->getName(), 3)) . '"];' . "\n";
$methods .= ' }' . "\n";
}
$methods .= ' $this->__load();' . "\n"; $methods .= ' $this->__load();' . "\n";
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= "\n" . ' }' . "\n"; $methods .= "\n" . ' }' . "\n";
@ -218,6 +223,23 @@ class ProxyFactory
return $methods; return $methods;
} }
/**
* @param ReflectionMethod $method
* @param ClassMetadata $class
* @return bool
*/
private function isShortIdentifierGetter($method, $class)
{
$identifier = lcfirst(substr($method->getName(), 3));
return (
$method->getNumberOfParameters() == 0 &&
substr($method->getName(), 0, 3) == "get" &&
in_array($identifier, $class->identifier, true) &&
$class->hasField($identifier) &&
(($method->getEndLine() - $method->getStartLine()) <= 4)
);
}
/** /**
* Generates the code for the __sleep method for a proxy class. * Generates the code for the __sleep method for a proxy class.
* *

View File

@ -23,7 +23,7 @@ namespace Doctrine\ORM\Query\AST;
/** /**
* SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable]
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
@ -37,11 +37,13 @@ class SelectExpression extends Node
{ {
public $expression; public $expression;
public $fieldIdentificationVariable; public $fieldIdentificationVariable;
public $hiddenAliasResultVariable;
public function __construct($expression, $fieldIdentificationVariable) public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false)
{ {
$this->expression = $expression; $this->expression = $expression;
$this->fieldIdentificationVariable = $fieldIdentificationVariable; $this->fieldIdentificationVariable = $fieldIdentificationVariable;
$this->hiddenAliasResultVariable = $hiddenAliasResultVariable;
} }
public function dispatch($sqlWalker) public function dispatch($sqlWalker)

View File

@ -98,7 +98,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
} }
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
} }
/** /**

View File

@ -137,7 +137,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
} }
/** /**

View File

@ -40,10 +40,12 @@ class Expr
* *
* [php] * [php]
* // (u.type = ?1) AND (u.role = ?2) * // (u.type = ?1) AND (u.role = ?2)
* $expr->andX('u.type = ?1', 'u.role = ?2')); * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));
* *
* @param mixed $x Optional clause. Defaults = null, but requires * @param Doctrine\ORM\Query\Expr\Comparison |
* at least one defined when converting to string. * Doctrine\ORM\Query\Expr\Func |
* Doctrine\ORM\Query\Expr\Orx
* $x Optional clause. Defaults = null, but requires at least one defined when converting to string.
* @return Expr\Andx * @return Expr\Andx
*/ */
public function andX($x = null) public function andX($x = null)

View File

@ -51,11 +51,13 @@ abstract class Base
foreach ((array) $args as $arg) { foreach ((array) $args as $arg) {
$this->add($arg); $this->add($arg);
} }
return $this;
} }
public function add($arg) public function add($arg)
{ {
if ( $arg !== null || ($arg instanceof self && $arg->count() > 0)) { if ( $arg !== null ) {
// If we decide to keep Expr\Base instances, we can use this check // If we decide to keep Expr\Base instances, we can use this check
if ( ! is_string($arg)) { if ( ! is_string($arg)) {
$class = get_class($arg); $class = get_class($arg);
@ -67,6 +69,8 @@ abstract class Base
$this->_parts[] = $arg; $this->_parts[] = $arg;
} }
return $this;
} }
public function count() public function count()

View File

@ -34,27 +34,55 @@ namespace Doctrine\ORM\Query\Expr;
*/ */
class From class From
{ {
/**
* @var string
*/
private $_from; private $_from;
/**
* @var string
*/
private $_alias; private $_alias;
public function __construct($from, $alias) /**
* @var string
*/
private $_indexBy;
/**
* @param string $from The class name.
* @param string $alias The alias of the class.
* @param string $indexBy The index for the from.
*/
public function __construct($from, $alias, $indexBy = null)
{ {
$this->_from = $from; $this->_from = $from;
$this->_alias = $alias; $this->_alias = $alias;
$this->_indexBy = $indexBy;
} }
/**
* @return string
*/
public function getFrom() public function getFrom()
{ {
return $this->_from; return $this->_from;
} }
/**
* @return string
*/
public function getAlias() public function getAlias()
{ {
return $this->_alias; return $this->_alias;
} }
/**
* @return string
*/
public function __toString() public function __toString()
{ {
return $this->_from . ' ' . $this->_alias; return $this->_from . ' ' . $this->_alias .
($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
} }
} }

View File

@ -76,39 +76,40 @@ class Lexer extends \Doctrine\Common\Lexer
const T_FROM = 122; const T_FROM = 122;
const T_GROUP = 123; const T_GROUP = 123;
const T_HAVING = 124; const T_HAVING = 124;
const T_IN = 125; const T_HIDDEN = 125;
const T_INDEX = 126; const T_IN = 126;
const T_INNER = 127; const T_INDEX = 127;
const T_INSTANCE = 128; const T_INNER = 128;
const T_IS = 129; const T_INSTANCE = 129;
const T_JOIN = 130; const T_IS = 130;
const T_LEADING = 131; const T_JOIN = 131;
const T_LEFT = 132; const T_LEADING = 132;
const T_LIKE = 133; const T_LEFT = 133;
const T_MAX = 134; const T_LIKE = 134;
const T_MEMBER = 135; const T_MAX = 135;
const T_MIN = 136; const T_MEMBER = 136;
const T_NOT = 137; const T_MIN = 137;
const T_NULL = 138; const T_NOT = 138;
const T_NULLIF = 139; const T_NULL = 139;
const T_OF = 140; const T_NULLIF = 140;
const T_OR = 141; const T_OF = 141;
const T_ORDER = 142; const T_OR = 142;
const T_OUTER = 143; const T_ORDER = 143;
const T_SELECT = 144; const T_OUTER = 144;
const T_SET = 145; const T_SELECT = 145;
const T_SIZE = 146; const T_SET = 146;
const T_SOME = 147; const T_SIZE = 147;
const T_SUM = 148; const T_SOME = 148;
const T_THEN = 149; const T_SUM = 149;
const T_TRAILING = 150; const T_THEN = 150;
const T_TRUE = 151; const T_TRAILING = 151;
const T_UPDATE = 152; const T_TRUE = 152;
const T_WHEN = 153; const T_UPDATE = 153;
const T_WHERE = 154; const T_WHEN = 154;
const T_WITH = 155; const T_WHERE = 155;
const T_PARTIAL = 156; const T_WITH = 156;
const T_MOD = 157; const T_PARTIAL = 157;
const T_MOD = 158;
/** /**
* Creates a new query scanner object. * Creates a new query scanner object.

View File

@ -1328,7 +1328,10 @@ class Parser
// We need to check if we are in a IdentificationVariable or SingleValuedPathExpression // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
$glimpse = $this->_lexer->glimpse(); $glimpse = $this->_lexer->glimpse();
if ($glimpse['type'] != Lexer::T_DOT) { if ($glimpse['type'] == Lexer::T_DOT) {
return $this->SingleValuedPathExpression();
}
$token = $this->_lexer->lookahead; $token = $this->_lexer->lookahead;
$identVariable = $this->IdentificationVariable(); $identVariable = $this->IdentificationVariable();
@ -1339,9 +1342,6 @@ class Parser
return $identVariable; return $identVariable;
} }
return $this->SingleValuedPathExpression();
}
/** /**
* OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"] * OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"]
* *
@ -1354,12 +1354,9 @@ class Parser
// We need to check if we are in a ResultVariable or StateFieldPathExpression // We need to check if we are in a ResultVariable or StateFieldPathExpression
$glimpse = $this->_lexer->glimpse(); $glimpse = $this->_lexer->glimpse();
if ($glimpse['type'] != Lexer::T_DOT) { $expr = ($glimpse['type'] != Lexer::T_DOT)
$token = $this->_lexer->lookahead; ? $this->ResultVariable()
$expr = $this->ResultVariable(); : $this->SingleValuedPathExpression();
} else {
$expr = $this->SingleValuedPathExpression();
}
$item = new AST\OrderByItem($expr); $item = new AST\OrderByItem($expr);
@ -1831,7 +1828,7 @@ class Parser
/** /**
* SelectExpression ::= * SelectExpression ::=
* IdentificationVariable | StateFieldPathExpression | * IdentificationVariable | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] * (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
* *
* @return Doctrine\ORM\Query\AST\SelectExpression * @return Doctrine\ORM\Query\AST\SelectExpression
*/ */
@ -1839,6 +1836,7 @@ class Parser
{ {
$expression = null; $expression = null;
$identVariable = null; $identVariable = null;
$hiddenAliasResultVariable = false;
$fieldAliasIdentificationVariable = null; $fieldAliasIdentificationVariable = null;
$peek = $this->_lexer->glimpse(); $peek = $this->_lexer->glimpse();
@ -1901,6 +1899,12 @@ class Parser
$this->match(Lexer::T_AS); $this->match(Lexer::T_AS);
} }
if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) {
$this->match(Lexer::T_HIDDEN);
$hiddenAliasResultVariable = true;
}
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead; $token = $this->_lexer->lookahead;
$fieldAliasIdentificationVariable = $this->AliasResultVariable(); $fieldAliasIdentificationVariable = $this->AliasResultVariable();
@ -1914,10 +1918,12 @@ class Parser
} }
} }
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); $expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable, $hiddenAliasResultVariable);
if ( ! $supportsAlias) { if ( ! $supportsAlias) {
$this->_identVariableExpressions[$identVariable] = $expr; $this->_identVariableExpressions[$identVariable] = $expr;
} }
return $expr; return $expr;
} }

View File

@ -977,6 +977,7 @@ class SqlWalker implements TreeWalker
{ {
$sql = ''; $sql = '';
$expr = $selectExpression->expression; $expr = $selectExpression->expression;
$hidden = $selectExpression->hiddenAliasResultVariable;
if ($expr instanceof AST\PathExpression) { if ($expr instanceof AST\PathExpression) {
if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) { if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
@ -1006,7 +1007,10 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->getSQLColumnAlias($columnName); $columnAlias = $this->getSQLColumnAlias($columnName);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\AggregateExpression) { } else if ($expr instanceof AST\AggregateExpression) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1019,7 +1023,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Subselect) { } else if ($expr instanceof AST\Subselect) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1032,7 +1039,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Functions\FunctionNode) { } else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1045,7 +1055,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ( } else if (
$expr instanceof AST\SimpleArithmeticExpression || $expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm || $expr instanceof AST\ArithmeticTerm ||
@ -1070,7 +1083,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ( } else if (
$expr instanceof AST\NullIfExpression || $expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression || $expr instanceof AST\CoalesceExpression ||
@ -1090,7 +1106,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else { } else {
// IdentificationVariable or PartialObjectExpression // IdentificationVariable or PartialObjectExpression
if ($expr instanceof AST\PartialObjectExpression) { if ($expr instanceof AST\PartialObjectExpression) {
@ -1109,6 +1128,7 @@ class SqlWalker implements TreeWalker
} }
$beginning = true; $beginning = true;
// Select all fields from the queried class // Select all fields from the queried class
foreach ($class->fieldMappings as $fieldName => $mapping) { foreach ($class->fieldMappings as $fieldName => $mapping) {
if ($partialFieldSet && !in_array($fieldName, $partialFieldSet)) { if ($partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
@ -1127,6 +1147,7 @@ class SqlWalker implements TreeWalker
. ' AS ' . $columnAlias; . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
} }
@ -1138,6 +1159,7 @@ class SqlWalker implements TreeWalker
foreach ($class->subClasses as $subClassName) { foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName); $subClass = $this->_em->getClassMetadata($subClassName);
$sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
foreach ($subClass->fieldMappings as $fieldName => $mapping) { foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
continue; continue;
@ -1159,8 +1181,10 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($beginning) $beginning = false; else $sql .= ', '; if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSQLColumnAlias($srcColumn); $columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn); $this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
} }
} }
@ -1826,12 +1850,16 @@ class SqlWalker implements TreeWalker
switch ($literal->type) { switch ($literal->type) {
case AST\Literal::STRING: case AST\Literal::STRING:
return $this->_conn->quote($literal->value); return $this->_conn->quote($literal->value);
case AST\Literal::BOOLEAN: case AST\Literal::BOOLEAN:
$bool = strtolower($literal->value) == 'true' ? true : false; $bool = strtolower($literal->value) == 'true' ? true : false;
$boolVal = $this->_conn->getDatabasePlatform()->convertBooleans($bool); $boolVal = $this->_conn->getDatabasePlatform()->convertBooleans($bool);
return is_string($boolVal) ? $this->_conn->quote($boolVal) : $boolVal;
return $boolVal;
case AST\Literal::NUMERIC: case AST\Literal::NUMERIC:
return $literal->value; return $literal->value;
default: default:
throw QueryException::invalidLiteral($literal); throw QueryException::invalidLiteral($literal);
} }

View File

@ -595,11 +595,12 @@ class QueryBuilder
* *
* @param string $from The class name. * @param string $from The class name.
* @param string $alias The alias of the class. * @param string $alias The alias of the class.
* @param string $indexBy The index for the from.
* @return QueryBuilder This QueryBuilder instance. * @return QueryBuilder This QueryBuilder instance.
*/ */
public function from($from, $alias) public function from($from, $alias, $indexBy = null)
{ {
return $this->add('from', new Expr\From($from, $alias), true); return $this->add('from', new Expr\From($from, $alias, $indexBy), true);
} }
/** /**

View File

@ -21,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console; Symfony\Component\Console,
Doctrine\Common\Cache;
/** /**
* Command to clear the metadata cache of the various cache drivers. * Command to clear the metadata cache of the various cache drivers.
@ -84,6 +85,10 @@ EOT
throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.'); throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.');
} }
if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Metadata cache entries' . PHP_EOL); $output->write('Clearing ALL Metadata cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll(); $result = $cacheDriver->deleteAll();

View File

@ -21,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console; Symfony\Component\Console,
Doctrine\Common\Cache;
/** /**
* Command to clear the query cache of the various cache drivers. * Command to clear the query cache of the various cache drivers.
@ -84,6 +85,10 @@ EOT
throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
} }
if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Query cache entries' . PHP_EOL); $output->write('Clearing ALL Query cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll(); $result = $cacheDriver->deleteAll();

View File

@ -21,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Component\Console\Input\InputArgument, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console; Symfony\Component\Console,
Doctrine\Common\Cache;
/** /**
* Command to clear the result cache of the various cache drivers. * Command to clear the result cache of the various cache drivers.
@ -78,13 +79,17 @@ EOT
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{ {
$em = $this->getHelper('em')->getEntityManager(); $em = $this->getHelper('em')->getEntityManager();
$cacheDriver = $em->getConfiguration()->getQueryCacheImpl(); $cacheDriver = $em->getConfiguration()->getResultCacheImpl();
if ( ! $cacheDriver) { if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.');
} }
$output->write('Clearing ALL Query cache entries' . PHP_EOL); if ($cacheDriver instanceof Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Result cache entries' . PHP_EOL);
$result = $cacheDriver->deleteAll(); $result = $cacheDriver->deleteAll();
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';

View File

@ -115,10 +115,12 @@ public function <methodName>()
* <description> * <description>
* *
* @param <variableType>$<variableName> * @param <variableType>$<variableName>
* @return <entity>
*/ */
public function <methodName>(<methodTypeHint>$<variableName>) public function <methodName>(<methodTypeHint>$<variableName>)
{ {
<spaces>$this-><fieldName> = $<variableName>; <spaces>$this-><fieldName> = $<variableName>;
<spaces>return $this;
}'; }';
private static $_addMethodTemplate = private static $_addMethodTemplate =
@ -150,7 +152,7 @@ public function <methodName>()
public function __construct() public function __construct()
{ {
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) { if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->_annotationsPrefix = 'ORM\\'; $this->_annotationsPrefix = 'ORM\\';
} }
} }
@ -302,9 +304,6 @@ public function <methodName>()
*/ */
public function setAnnotationPrefix($prefix) public function setAnnotationPrefix($prefix)
{ {
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
return;
}
$this->_annotationsPrefix = $prefix; $this->_annotationsPrefix = $prefix;
} }
@ -399,14 +398,17 @@ public function <methodName>()
} }
$collections = array(); $collections = array();
foreach ($metadata->associationMappings AS $mapping) { foreach ($metadata->associationMappings AS $mapping) {
if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
$collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
} }
} }
if ($collections) { if ($collections) {
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate)); return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate));
} }
return ''; return '';
} }
@ -734,7 +736,8 @@ public function <methodName>()
'<variableType>' => $variableType, '<variableType>' => $variableType,
'<variableName>' => Inflector::camelize($fieldName), '<variableName>' => Inflector::camelize($fieldName),
'<methodName>' => $methodName, '<methodName>' => $methodName,
'<fieldName>' => $fieldName '<fieldName>' => $fieldName,
'<entity>' => $this->_getClassName($metadata)
); );
$method = str_replace( $method = str_replace(
@ -788,7 +791,7 @@ public function <methodName>()
} }
if (isset($joinColumn['onDelete'])) { if (isset($joinColumn['onDelete'])) {
$joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false'); $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
} }
if (isset($joinColumn['columnDefinition'])) { if (isset($joinColumn['columnDefinition'])) {

View File

@ -139,6 +139,9 @@ class XmlExporter extends AbstractExporter
if (isset($field['columnName'])) { if (isset($field['columnName'])) {
$idXml->addAttribute('column', $field['columnName']); $idXml->addAttribute('column', $field['columnName']);
} }
if (isset($field['associationKey']) && $field['associationKey']) {
$idXml->addAttribute('association-key', 'true');
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$generatorXml = $idXml->addChild('generator'); $generatorXml = $idXml->addChild('generator');
$generatorXml->addAttribute('strategy', $idGeneratorType); $generatorXml->addAttribute('strategy', $idGeneratorType);

View File

@ -152,6 +152,17 @@ class SchemaValidator
"has to be a primary key column."; "has to be a primary key column.";
} }
} }
if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) {
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the target entity '". $targetClass->name . "'";
}
if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) {
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) { } else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($assoc['joinColumns'] AS $joinColumn) { foreach ($assoc['joinColumns'] AS $joinColumn) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']); $targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
@ -167,6 +178,11 @@ class SchemaValidator
"has to be a primary key column."; "has to be a primary key column.";
} }
} }
if (count($class->identifier) != count($assoc['joinColumns'])) {
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
}
} }
} }

View File

@ -97,7 +97,7 @@ class Setup
require_once $directory . "/Doctrine/Common/ClassLoader.php"; require_once $directory . "/Doctrine/Common/ClassLoader.php";
} }
$loader = new ClassLoader("Doctrine"); $loader = new ClassLoader("Doctrine", $directory);
$loader->register(); $loader->register();
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine"); $loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");

View File

@ -216,7 +216,12 @@ class UnitOfWork implements PropertyChangedListener
*/ */
private $orphanRemovals = array(); private $orphanRemovals = array();
//private $_readOnlyObjects = array(); /**
* Read-Only objects are never evaluated
*
* @var array
*/
private $readOnlyObjects = array();
/** /**
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
@ -249,11 +254,17 @@ class UnitOfWork implements PropertyChangedListener
* 4) All collection updates * 4) All collection updates
* 5) All entity deletions * 5) All entity deletions
* *
* @param object $entity
* @return void
*/ */
public function commit() public function commit($entity = null)
{ {
// Compute changes done since last commit. // Compute changes done since last commit.
if ($entity === null) {
$this->computeChangeSets(); $this->computeChangeSets();
} else {
$this->computeSingleEntityChangeSet($entity);
}
if ( ! ($this->entityInsertions || if ( ! ($this->entityInsertions ||
$this->entityDeletions || $this->entityDeletions ||
@ -329,6 +340,11 @@ class UnitOfWork implements PropertyChangedListener
$coll->takeSnapshot(); $coll->takeSnapshot();
} }
// Raise postFlush
if ($this->evm->hasListeners(Events::postFlush)) {
$this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em));
}
// Clear up // Clear up
$this->entityInsertions = $this->entityInsertions =
$this->entityUpdates = $this->entityUpdates =
@ -342,6 +358,61 @@ class UnitOfWork implements PropertyChangedListener
$this->orphanRemovals = array(); $this->orphanRemovals = array();
} }
/**
* Compute the changesets of all entities scheduled for insertion
*
* @return void
*/
private function computeScheduleInsertsChangeSets()
{
foreach ($this->entityInsertions as $entity) {
$class = $this->em->getClassMetadata(get_class($entity));
$this->computeChangeSet($class, $entity);
}
}
/**
* Only flush the given entity according to a ruleset that keeps the UoW consistent.
*
* 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
* 2. Read Only entities are skipped.
* 3. Proxies are skipped.
* 4. Only if entity is properly managed.
*
* @param object $entity
* @return void
*/
private function computeSingleEntityChangeSet($entity)
{
if ( ! $this->isInIdentityMap($entity) ) {
throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity));
}
$class = $this->em->getClassMetadata(get_class($entity));
if ($class->isChangeTrackingDeferredImplicit()) {
$this->persist($entity);
}
// Compute changes for INSERTed entities first. This must always happen even in this case.
$this->computeScheduleInsertsChangeSets();
if ( $class->isReadOnly ) {
return;
}
// Ignore uninitialized proxy objects
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
return;
}
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
$oid = spl_object_hash($entity);
if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) {
$this->computeChangeSet($class, $entity);
}
}
/** /**
* Executes any extra updates that have been scheduled. * Executes any extra updates that have been scheduled.
*/ */
@ -393,6 +464,8 @@ class UnitOfWork implements PropertyChangedListener
* If a PersistentCollection has been de-referenced in a fully MANAGED entity, * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
* then this collection is marked for deletion. * then this collection is marked for deletion.
* *
* @ignore
* @internal Don't call from the outside.
* @param ClassMetadata $class The class descriptor of the entity. * @param ClassMetadata $class The class descriptor of the entity.
* @param object $entity The entity for which to compute the changes. * @param object $entity The entity for which to compute the changes.
*/ */
@ -403,6 +476,11 @@ class UnitOfWork implements PropertyChangedListener
} }
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if (isset($this->readOnlyObjects[$oid])) {
return;
}
$actualData = array(); $actualData = array();
foreach ($class->reflFields as $name => $refProp) { foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity); $value = $refProp->getValue($entity);
@ -458,7 +536,15 @@ class UnitOfWork implements PropertyChangedListener
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array(); $changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
foreach ($actualData as $propName => $actualValue) { foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (isset($originalData[$propName])) {
$orgValue = $originalData[$propName];
} else if (array_key_exists($propName, $originalData)) {
$orgValue = null;
} else {
// skip field, its a partially omitted one!
continue;
}
if (isset($class->associationMappings[$propName])) { if (isset($class->associationMappings[$propName])) {
$assoc = $class->associationMappings[$propName]; $assoc = $class->associationMappings[$propName];
if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) { if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) {
@ -470,8 +556,9 @@ class UnitOfWork implements PropertyChangedListener
} }
} else if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) { } else if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) {
// A PersistentCollection was de-referenced, so delete it. // A PersistentCollection was de-referenced, so delete it.
if ( ! in_array($orgValue, $this->collectionDeletions, true)) { $coid = spl_object_hash($orgValue);
$this->collectionDeletions[] = $orgValue; if ( ! isset($this->collectionDeletions[$coid]) ) {
$this->collectionDeletions[$coid] = $orgValue;
$changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
} }
} }
@ -505,10 +592,7 @@ class UnitOfWork implements PropertyChangedListener
public function computeChangeSets() public function computeChangeSets()
{ {
// Compute changes for INSERTed entities first. This must always happen. // Compute changes for INSERTed entities first. This must always happen.
foreach ($this->entityInsertions as $entity) { $this->computeScheduleInsertsChangeSets();
$class = $this->em->getClassMetadata(get_class($entity));
$this->computeChangeSet($class, $entity);
}
// Compute changes for other MANAGED entities. Change tracking policies take effect here. // Compute changes for other MANAGED entities. Change tracking policies take effect here.
foreach ($this->identityMap as $className => $entities) { foreach ($this->identityMap as $className => $entities) {
@ -528,7 +612,7 @@ class UnitOfWork implements PropertyChangedListener
foreach ($entitiesToProcess as $entity) { foreach ($entitiesToProcess as $entity) {
// Ignore uninitialized proxy objects // Ignore uninitialized proxy objects
if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) { if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
continue; continue;
} }
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
@ -549,10 +633,11 @@ class UnitOfWork implements PropertyChangedListener
private function computeAssociationChanges($assoc, $value) private function computeAssociationChanges($assoc, $value)
{ {
if ($value instanceof PersistentCollection && $value->isDirty()) { if ($value instanceof PersistentCollection && $value->isDirty()) {
$coid = spl_object_hash($value);
if ($assoc['isOwningSide']) { if ($assoc['isOwningSide']) {
$this->collectionUpdates[] = $value; $this->collectionUpdates[$coid] = $value;
} }
$this->visitedCollections[] = $value; $this->visitedCollections[$coid] = $value;
} }
// Look through the entities, and in any of their associations, for transient (new) // Look through the entities, and in any of their associations, for transient (new)
@ -635,7 +720,7 @@ class UnitOfWork implements PropertyChangedListener
* @param object $entity The entity for which to (re)calculate the change set. * @param object $entity The entity for which to (re)calculate the change set.
* @throws InvalidArgumentException If the passed entity is not MANAGED. * @throws InvalidArgumentException If the passed entity is not MANAGED.
*/ */
public function recomputeSingleEntityChangeSet($class, $entity) public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
@ -853,6 +938,7 @@ class UnitOfWork implements PropertyChangedListener
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']); $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
if ( ! $calc->hasClass($targetClass->name)) { if ( ! $calc->hasClass($targetClass->name)) {
$calc->addClass($targetClass); $calc->addClass($targetClass);
$newNodes[] = $targetClass;
} }
$calc->addDependency($targetClass, $class); $calc->addDependency($targetClass, $class);
// If the target class has mapped subclasses, // If the target class has mapped subclasses,
@ -1387,6 +1473,10 @@ class UnitOfWork implements PropertyChangedListener
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) { if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
$managedCopy = $entity; $managedCopy = $entity;
} else { } else {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__load();
}
// Try to look the entity up in the identity map. // Try to look the entity up in the identity map.
$id = $class->getIdentifierValues($entity); $id = $class->getIdentifierValues($entity);
@ -1448,11 +1538,17 @@ class UnitOfWork implements PropertyChangedListener
if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) { if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) {
$prop->setValue($managedCopy, $other); $prop->setValue($managedCopy, $other);
} else { } else {
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
$id = $targetClass->getIdentifierValues($other); $relatedId = $targetClass->getIdentifierValues($other);
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id);
if ($targetClass->subClasses) {
$entity = $this->em->find($targetClass->name, $relatedId);
} else {
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
$prop->setValue($managedCopy, $proxy); $prop->setValue($managedCopy, $proxy);
$this->registerManaged($proxy, $id, array()); $this->registerManaged($proxy, $relatedId, array());
}
} }
} }
} else { } else {
@ -1534,8 +1630,9 @@ class UnitOfWork implements PropertyChangedListener
* *
* @param object $entity * @param object $entity
* @param array $visited * @param array $visited
* @param boolean $noCascade if true, don't cascade detach operation
*/ */
private function doDetach($entity, array &$visited) private function doDetach($entity, array &$visited, $noCascade = false)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if (isset($visited[$oid])) { if (isset($visited[$oid])) {
@ -1558,8 +1655,10 @@ class UnitOfWork implements PropertyChangedListener
return; return;
} }
if (!$noCascade) {
$this->cascadeDetach($entity, $visited); $this->cascadeDetach($entity, $visited);
} }
}
/** /**
* Refreshes the state of the given entity from the database, overwriting * Refreshes the state of the given entity from the database, overwriting
@ -1809,9 +1908,12 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Clears the UnitOfWork. * Clears the UnitOfWork.
*
* @param string $entityName if given, only entities of this type will get detached
*/ */
public function clear() public function clear($entityName = null)
{ {
if ($entityName === null) {
$this->identityMap = $this->identityMap =
$this->entityIdentifiers = $this->entityIdentifiers =
$this->originalEntityData = $this->originalEntityData =
@ -1828,9 +1930,19 @@ class UnitOfWork implements PropertyChangedListener
if ($this->commitOrderCalculator !== null) { if ($this->commitOrderCalculator !== null) {
$this->commitOrderCalculator->clear(); $this->commitOrderCalculator->clear();
} }
} else {
$visited = array();
foreach ($this->identityMap as $className => $entities) {
if ($className === $entityName) {
foreach ($entities as $entity) {
$this->doDetach($entity, $visited, true);
}
}
}
}
if ($this->evm->hasListeners(Events::onClear)) { if ($this->evm->hasListeners(Events::onClear)) {
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
} }
} }
@ -1858,12 +1970,12 @@ class UnitOfWork implements PropertyChangedListener
{ {
//TODO: if $coll is already scheduled for recreation ... what to do? //TODO: if $coll is already scheduled for recreation ... what to do?
// Just remove $coll from the scheduled recreations? // Just remove $coll from the scheduled recreations?
$this->collectionDeletions[] = $coll; $this->collectionDeletions[spl_object_hash($coll)] = $coll;
} }
public function isCollectionScheduledForDeletion(PersistentCollection $coll) public function isCollectionScheduledForDeletion(PersistentCollection $coll)
{ {
return in_array($coll, $this->collectionsDeletions, true); return isset( $this->collectionsDeletions[spl_object_hash($coll)] );
} }
/** /**
@ -2403,4 +2515,37 @@ class UnitOfWork implements PropertyChangedListener
{ {
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
} }
/**
* Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
*
* This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
* on this object that might be necessary to perform a correct udpate.
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function markReadOnly($object)
{
if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
throw new InvalidArgumentException("Managed entity required");
}
$this->readOnlyObjects[spl_object_hash($object)] = true;
}
/**
* Is this entity read only?
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function isReadOnly($object)
{
if ( ! is_object($object) ) {
throw new InvalidArgumentException("Managed entity required");
}
return isset($this->readOnlyObjects[spl_object_hash($object)]);
}
} }

@ -1 +1 @@
Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2 Subproject commit b2fd909b4b5476df01744c9d34c7a23723a687b6

View File

@ -19,7 +19,7 @@ class CmsUser
*/ */
public $id; public $id;
/** /**
* @Column(type="string", length=50) * @Column(type="string", length=50, nullable=true)
*/ */
public $status; public $status;
/** /**
@ -35,7 +35,7 @@ class CmsUser
*/ */
public $phonenumbers; public $phonenumbers;
/** /**
* @OneToMany(targetEntity="CmsArticle", mappedBy="user") * @OneToMany(targetEntity="CmsArticle", mappedBy="user", cascade={"detach"})
*/ */
public $articles; public $articles;
/** /**
@ -48,7 +48,7 @@ class CmsUser
*/ */
public $email; public $email;
/** /**
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"}) * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge", "detach"})
* @JoinTable(name="cms_users_groups", * @JoinTable(name="cms_users_groups",
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}

View File

@ -7,7 +7,11 @@ namespace Doctrine\Tests\Models\Company;
* @Table(name="company_contracts") * @Table(name="company_contracts")
* @InheritanceType("SINGLE_TABLE") * @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"fix" = "CompanyFixContract", "flexible" = "CompanyFlexContract", "flexultra" = "CompanyFlexUltraContract"}) * @DiscriminatorMap({
* "fix" = "CompanyFixContract",
* "flexible" = "CompanyFlexContract",
* "flexultra" = "CompanyFlexUltraContract"
* })
*/ */
abstract class CompanyContract abstract class CompanyContract
{ {

View File

@ -0,0 +1,91 @@
<?php
namespace Doctrine\Tests\ORM\Event;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* @group DDC-1415
*/
class EntityEventDelegatorTest extends \Doctrine\Tests\OrmTestCase
{
/**
* @var \Doctrine\ORM\Event\EntityEventDelegator
*/
private $delegator;
public function setUp()
{
$this->delegator = new \Doctrine\ORM\Event\EntityEventDelegator();
}
public function testGetSubscribedEventsWhenEmpty()
{
$this->assertEquals(array(), $this->delegator->getSubscribedEvents());
}
public function testAddListener()
{
$this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener());
$this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents());
}
public function testAddSubscriber()
{
$this->delegator->addEventSubscriber(new DelegateeEventListener(), 'stdClass');
$this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents());
}
public function testAddListenerAfterFrozenThrowsException()
{
$this->delegator->getSubscribedEvents(); // freezes
$this->setExpectedException("LogicException", "Cannot add event listeners aft");
$this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener());
}
public function testDelegateEvent()
{
$delegatee = new DelegateeEventListener();
$this->delegator->addEventListener('postLoad', 'stdClass', $delegatee);
$event = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager());
$this->delegator->postLoad($event);
$this->delegator->postLoad($event);
$this->assertEquals(2, $delegatee->postLoad);
}
public function testDelegatePickEntity()
{
$delegatee = new DelegateeEventListener();
$this->delegator->addEventListener('postLoad', 'stdClass', $delegatee);
$event1 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager());
$event2 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \Doctrine\Tests\Models\CMS\CmsUser(), $this->_getTestEntityManager());
$this->delegator->postLoad($event1);
$this->delegator->postLoad($event2);
$this->assertEquals(1, $delegatee->postLoad);
}
}
class DelegateeEventListener implements \Doctrine\Common\EventSubscriber
{
public $postLoad = 0;
public function postLoad($args)
{
$this->postLoad++;
}
/**
* Returns an array of events this subscriber wants to listen to.
*
* @return array
*/
function getSubscribedEvents()
{
return array('postLoad');
}
}

View File

@ -863,7 +863,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt() public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt()
{ {
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
$user = new CmsUser(); $user = new CmsUser();
$user->username = "beberlei"; $user->username = "beberlei";
$user->name = "Benjamin E."; $user->name = "Benjamin E.";
@ -882,7 +881,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush(); $this->_em->flush();
$this->_em->clear(); $this->_em->clear();
$this->assertEquals('Stephan', $this->_em->find(get_class($user), $userId)->name); $this->assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name);
} }
public function testMergePersistsNewEntities() public function testMergePersistsNewEntities()
@ -981,4 +980,223 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($article->user->__isInitialized__, "...but its initialized!"); $this->assertTrue($article->user->__isInitialized__, "...but its initialized!");
$this->assertEquals($qc+2, $this->getCurrentQueryCount()); $this->assertEquals($qc+2, $this->getCurrentQueryCount());
} }
/**
* @group DDC-1278
*/
public function testClearWithEntityName()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$address = new CmsAddress();
$address->city = "Springfield";
$address->zip = "12354";
$address->country = "Germany";
$address->street = "Foo Street";
$address->user = $user;
$user->address = $address;
$article1 = new CmsArticle();
$article1->topic = 'Foo';
$article1->text = 'Foo Text';
$article2 = new CmsArticle();
$article2->topic = 'Bar';
$article2->text = 'Bar Text';
$user->addArticle($article1);
$user->addArticle($article2);
$this->_em->persist($article1);
$this->_em->persist($article2);
$this->_em->persist($address);
$this->_em->persist($user);
$this->_em->flush();
$unitOfWork = $this->_em->getUnitOfWork();
$this->_em->clear('Doctrine\Tests\Models\CMS\CmsUser');
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($address));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article1));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article2));
$this->_em->clear();
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address));
}
/**
* @group DDC-720
*/
public function testFlushSingleManagedEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$user->status = 'administrator';
$this->_em->flush($user);
$this->_em->clear();
$user = $this->_em->find(get_class($user), $user->id);
$this->assertEquals('administrator', $user->status);
}
/**
* @group DDC-720
*/
public function testFlushSingleUnmanagedEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->setExpectedException('InvalidArgumentException', 'Entity has to be managed for single computation');
$this->_em->flush($user);
}
/**
* @group DDC-720
*/
public function testFlushSingleAndNewEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$otherUser = new CmsUser;
$otherUser->name = 'Dominik2';
$otherUser->username = 'domnikl2';
$otherUser->status = 'developer';
$user->status = 'administrator';
$this->_em->persist($otherUser);
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushAndCascadePersist()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$address = new CmsAddress();
$address->city = "Springfield";
$address->zip = "12354";
$address->country = "Germany";
$address->street = "Foo Street";
$address->user = $user;
$user->address = $address;
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($address), "Other user is contained in EntityManager");
$this->assertTrue($address->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushSingleAndNoCascade()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$article1 = new CmsArticle();
$article1->topic = 'Foo';
$article1->text = 'Foo Text';
$article1->author = $user;
$user->articles[] = $article1;
$this->setExpectedException('InvalidArgumentException', "A new entity was found through the relationship 'Doctrine\Tests\Models\CMS\CmsUser#articles'");
$this->_em->flush($user);
}
/**
* @group DDC-720
*/
public function testProxyIsIgnored()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->getReference(get_class($user), $user->id);
$otherUser = new CmsUser;
$otherUser->name = 'Dominik2';
$otherUser->username = 'domnikl2';
$otherUser->status = 'developer';
$this->_em->persist($otherUser);
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushSingleSaveOnlySingle()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$user2 = new CmsUser;
$user2->name = 'Dominik';
$user2->username = 'domnikl2';
$user2->status = 'developer';
$this->_em->persist($user2);
$this->_em->flush();
$user->status = 'admin';
$user2->status = 'admin';
$this->_em->flush($user);
$this->_em->clear();
$user2 = $this->_em->find(get_class($user2), $user2->id);
$this->assertEquals('developer', $user2->status);
}
} }

View File

@ -98,7 +98,7 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers entity was not detected.'); $this->assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers entity was not detected.');
$this->assertArrayHasKey('CmsGroups', $metadatas, 'CmsGroups entity was not detected.'); $this->assertArrayHasKey('CmsGroups', $metadatas, 'CmsGroups entity was not detected.');
$this->assertEquals(1, count($metadatas['CmsUsers']->associationMappings)); $this->assertEquals(2, count($metadatas['CmsUsers']->associationMappings));
$this->assertArrayHasKey('group', $metadatas['CmsUsers']->associationMappings); $this->assertArrayHasKey('group', $metadatas['CmsUsers']->associationMappings);
$this->assertEquals(1, count($metadatas['CmsGroups']->associationMappings)); $this->assertEquals(1, count($metadatas['CmsGroups']->associationMappings));
$this->assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings); $this->assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);

View File

@ -55,6 +55,29 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Poweruser', $a2->getUser()->type); $this->assertEquals('Poweruser', $a2->getUser()->type);
} }
/**
* @group DDC-1386
*/
public function testGetPartialReferenceWithDefaultValueNotEvalutedInFlush()
{
$user = new DefaultValueUser;
$user->name = 'romanb';
$user->type = 'Normaluser';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->getPartialReference('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
$this->assertTrue($this->_em->getUnitOfWork()->isReadOnly($user));
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->find('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
$this->assertEquals('Normaluser', $user->type);
}
} }

View File

@ -38,12 +38,19 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user2->status = 'dev'; $user2->status = 'dev';
$this->_em->persist($user2); $this->_em->persist($user2);
$user3 = new CmsUser;
$user3->name = 'Benjamin';
$user3->username = 'beberlei';
$user3->status = null;
$this->_em->persist($user3);
$this->_em->flush(); $this->_em->flush();
$user1Id = $user->getId(); $user1Id = $user->getId();
unset($user); unset($user);
unset($user2); unset($user2);
unset($user3);
$this->_em->clear(); $this->_em->clear();
@ -189,7 +196,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findAll(); $users = $repos->findAll();
$this->assertEquals(2, count($users)); $this->assertEquals(3, count($users));
} }
public function testFindByAlias() public function testFindByAlias()
@ -202,7 +209,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$repos = $this->_em->getRepository('CMS:CmsUser'); $repos = $this->_em->getRepository('CMS:CmsUser');
$users = $repos->findAll(); $users = $repos->findAll();
$this->assertEquals(2, count($users)); $this->assertEquals(3, count($users));
} }
/** /**
@ -284,10 +291,11 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFindMagicCallByNullValue() public function testFindMagicCallByNullValue()
{ {
$this->loadFixture(); $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$this->setExpectedException('Doctrine\ORM\ORMException');
$users = $repos->findByStatus(null); $users = $repos->findByStatus(null);
$this->assertEquals(1, count($users));
} }
/** /**
@ -389,7 +397,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
/** /**
* @group DDC-1087 * @group DDC-1087
*/ */
public function testIsNullCriteria() public function testIsNullCriteriaDoesNotGenerateAParameter()
{ {
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findBy(array('status' => null, 'username' => 'romanb')); $users = $repos->findBy(array('status' => null, 'username' => 'romanb'));
@ -399,6 +407,16 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(array('romanb'), $params); $this->assertEquals(array('romanb'), $params);
} }
public function testIsNullCriteria()
{
$this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findBy(array('status' => null));
$this->assertEquals(1, count($users));
}
/** /**
* @group DDC-1094 * @group DDC-1094
*/ */
@ -411,7 +429,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$users1 = $repos->findBy(array(), null, 1, 0); $users1 = $repos->findBy(array(), null, 1, 0);
$users2 = $repos->findBy(array(), null, 1, 1); $users2 = $repos->findBy(array(), null, 1, 1);
$this->assertEquals(2, count($repos->findBy(array()))); $this->assertEquals(3, count($repos->findBy(array())));
$this->assertEquals(1, count($users1)); $this->assertEquals(1, count($users1));
$this->assertEquals(1, count($users2)); $this->assertEquals(1, count($users2));
$this->assertNotSame($users1[0], $users2[0]); $this->assertNotSame($users1[0], $users2[0]);
@ -428,10 +446,10 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$usersAsc = $repos->findBy(array(), array("username" => "ASC")); $usersAsc = $repos->findBy(array(), array("username" => "ASC"));
$usersDesc = $repos->findBy(array(), array("username" => "DESC")); $usersDesc = $repos->findBy(array(), array("username" => "DESC"));
$this->assertEquals(2, count($usersAsc), "Pre-condition: only two users in fixture"); $this->assertEquals(3, count($usersAsc), "Pre-condition: only three users in fixture");
$this->assertEquals(2, count($usersDesc), "Pre-condition: only two users in fixture"); $this->assertEquals(3, count($usersDesc), "Pre-condition: only three users in fixture");
$this->assertSame($usersAsc[0], $usersDesc[1]); $this->assertSame($usersAsc[0], $usersDesc[2]);
$this->assertSame($usersAsc[1], $usersDesc[0]); $this->assertSame($usersAsc[2], $usersDesc[0]);
} }

View File

@ -78,7 +78,7 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
$reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id); $reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id);
$this->assertFalse($reference->postLoadCallbackInvoked); $this->assertFalse($reference->postLoadCallbackInvoked);
$reference->getId(); // trigger proxy load $reference->getValue(); // trigger proxy load
$this->assertTrue($reference->postLoadCallbackInvoked); $this->assertTrue($reference->postLoadCallbackInvoked);
} }
@ -210,6 +210,10 @@ class LifecycleCallbackTestEntity
return $this->id; return $this->id;
} }
public function getValue() {
return $this->value;
}
/** @PrePersist */ /** @PrePersist */
public function doStuffOnPrePersist() { public function doStuffOnPrePersist() {
$this->prePersistCallbackInvoked = true; $this->prePersistCallbackInvoked = true;

View File

@ -0,0 +1,95 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* PostFlushEventTest
*
* @author Daniel Freudenberger <df@rebuy.de>
*/
class PostFlushEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var PostFlushListener
*/
private $listener;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new PostFlushListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::postFlush, $this->listener);
}
public function testListenerShouldBeNotified()
{
$this->_em->persist($this->createNewValidUser());
$this->_em->flush();
$this->assertTrue($this->listener->wasNotified);
}
public function testListenerShouldNotBeNotifiedWhenFlushThrowsException()
{
$user = new CmsUser();
$user->username = 'dfreudenberger';
$this->_em->persist($user);
$exceptionRaised = false;
try {
$this->_em->flush();
} catch (\Exception $ex) {
$exceptionRaised = true;
}
$this->assertTrue($exceptionRaised);
$this->assertFalse($this->listener->wasNotified);
}
public function testListenerShouldReceiveEntityManagerThroughArgs()
{
$this->_em->persist($this->createNewValidUser());
$this->_em->flush();
$receivedEm = $this->listener->receivedArgs->getEntityManager();
$this->assertSame($this->_em, $receivedEm);
}
/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'dfreudenberger';
$user->name = 'Daniel Freudenberger';
return $user;
}
}
class PostFlushListener
{
/**
* @var bool
*/
public $wasNotified = false;
/**
* @var PostFlushEventArgs
*/
public $receivedArgs;
/**
* @param PostFlushEventArgs $args
*/
public function postFlush(PostFlushEventArgs $args)
{
$this->wasNotified = true;
$this->receivedArgs = $args;
}
}

View File

@ -104,7 +104,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$cache = $this->getMock('Doctrine\Common\Cache\ArrayCache', array('doFetch', 'doSave')); $cache = $this->getMock('Doctrine\Common\Cache\ArrayCache', array('doFetch', 'doSave', 'doGetStats'));
$cache->expects($this->at(0)) $cache->expects($this->at(0))
->method('doFetch') ->method('doFetch')
->with($this->isType('string')) ->with($this->isType('string'))
@ -135,7 +135,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
->will($this->returnValue($sqlExecMock)); ->will($this->returnValue($sqlExecMock));
$cache = $this->getMock('Doctrine\Common\Cache\CacheProvider', $cache = $this->getMock('Doctrine\Common\Cache\CacheProvider',
array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush')); array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush', 'doGetStats'));
$cache->expects($this->once()) $cache->expects($this->once())
->method('doFetch') ->method('doFetch')
->with($this->isType('string')) ->with($this->isType('string'))

View File

@ -533,4 +533,34 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(2, count($users)); $this->assertEquals(2, count($users));
} }
public function testQueryWithHiddenAsSelectExpression()
{
$userA = new CmsUser;
$userA->name = 'Benjamin';
$userA->username = 'beberlei';
$userA->status = 'developer';
$this->_em->persist($userA);
$userB = new CmsUser;
$userB->name = 'Roman';
$userB->username = 'romanb';
$userB->status = 'developer';
$this->_em->persist($userB);
$userC = new CmsUser;
$userC->name = 'Jonathan';
$userC->username = 'jwage';
$userC->status = 'developer';
$this->_em->persist($userC);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("SELECT u, (SELECT COUNT(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2) AS HIDDEN total FROM Doctrine\Tests\Models\CMS\CmsUser u");
$users = $query->execute();
$this->assertEquals(3, count($users));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
}
} }

View File

@ -147,4 +147,28 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup()."); $this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup().");
} }
public function testDoNotInitializeProxyOnGettingTheIdentifier()
{
$id = $this->createProduct();
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals($id, $entity->getId());
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier()
{
$id = $this->createProduct();
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals('Doctrine Cookbook', $entity->getName());
$this->assertTrue($entity->__isInitialized__, "Getting something other than the identifier initializes the proxy.");
}
} }

View File

@ -27,15 +27,16 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em); $tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes); $sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]);
$this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]); $this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]);
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]); $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]); $this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]); $this->assertEquals("ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]); $this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]); $this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id)", $sql[7]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[8]);
$this->assertEquals(8, count($sql)); $this->assertEquals(9, count($sql));
} }
public function testGetCreateSchemaSql2() public function testGetCreateSchemaSql2()

View File

@ -52,6 +52,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush(); $this->_em->flush();
$this->_em->clear(); $this->_em->clear();
// force proxy load, getId() doesn't work anymore
$user->getName();
$userId = $user->getId(); $userId = $user->getId();
$this->_em->clear(); $this->_em->clear();
@ -60,6 +62,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
$user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId); $user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
// force proxy load, getId() doesn't work anymore
$user->getName();
$this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type"); $this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type");
} }
} }

View File

@ -0,0 +1,214 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use DateTime;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1135
*/
class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135Phone'),
));
$this->loadFixture();
} catch(\Exception $e) {
}
}
public function testDql()
{
$dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals(sizeof($result['foo@foo.com']->phones), 3);
$this->assertEquals(sizeof($result['bar@bar.com']->phones), 3);
$this->assertEquals(sizeof($result['foobar@foobar.com']->phones), 3);
$this->assertArrayHasKey(1, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(2, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(3, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(4, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(5, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(6, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(7, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
}
public function testTicket()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.id');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id', $dql);
}
public function testIndexByUnique()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.email', $dql);
}
public function testIndexWithJoin()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u','p')
->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email')
->join('u.phones', 'p', null, null, 'p.id');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals(sizeof($result['foo@foo.com']->phones), 3);
$this->assertEquals(sizeof($result['bar@bar.com']->phones), 3);
$this->assertEquals(sizeof($result['foobar@foobar.com']->phones), 3);
$this->assertArrayHasKey(1, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(2, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(3, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(4, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(5, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(6, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(7, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
$this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);
}
private function loadFixture()
{
$p1 = array('11 xxxx-xxxx','11 yyyy-yyyy','11 zzzz-zzzz');
$p2 = array('22 xxxx-xxxx','22 yyyy-yyyy','22 zzzz-zzzz');
$p3 = array('33 xxxx-xxxx','33 yyyy-yyyy','33 zzzz-zzzz');
$u1 = new DDC1135User("foo@foo.com", "Foo",$p1);
$u2 = new DDC1135User("bar@bar.com", "Bar",$p2);
$u3 = new DDC1135User("foobar@foobar.com", "Foo Bar",$p3);
$this->_em->persist($u1);
$this->_em->persist($u2);
$this->_em->persist($u3);
$this->_em->flush();
$this->_em->clear();
}
}
/**
* @Entity
*/
class DDC1135User
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
public $id;
/**
* @Column(type="string", unique=true)
*/
public $email;
/**
* @Column(type="string")
*/
public $name;
/**
* @OneToMany(targetEntity="DDC1135Phone", mappedBy="user", cascade={"persist", "remove"})
*/
public $phones;
public function __construct($email, $name, array $numbers = array())
{
$this->name = $name;
$this->email = $email;
$this->phones = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($numbers as $number) {
$this->phones->add(new DDC1135Phone($this,$number));
}
}
}
/**
* @Entity
*/
class DDC1135Phone
{
/**
* @Id
* @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Column(name="number", type="string", nullable = false)
*/
public $number;
/**
* @ManyToOne(targetEntity="DDC1135User", inversedBy="phones")
* @JoinColumn(name="user_id", referencedColumnName="id", nullable = false)
*/
public $user;
public function __construct($user, $number)
{
$this->user = $user;
$this->number = $number;
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1383
*/
class DDC1383Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383AbstractEntity'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383Entity'),
));
} catch(\Exception $ignored) {}
}
public function testFailingCase()
{
$parent = new DDC1383Entity();
$child = new DDC1383Entity();
$child->setReference($parent);
$this->_em->persist($parent);
$this->_em->persist($child);
$id = $child->getId();
$this->_em->flush();
$this->_em->clear();
// Try merging the parent entity
$child = $this->_em->merge($child);
$parent = $child->getReference();
// Parent is not instance of the abstract class
self::assertTrue($parent instanceof DDC1383AbstractEntity,
"Entity class is " . get_class($parent) . ', "DDC1383AbstractEntity" was expected');
// Parent is NOT instance of entity
self::assertTrue($parent instanceof DDC1383Entity,
"Entity class is " . get_class($parent) . ', "DDC1383Entity" was expected');
}
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="integer")
* @DiscriminatorMap({1 = "DDC1383Entity"})
*/
abstract class DDC1383AbstractEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $id;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
/**
* @Entity
*/
class DDC1383Entity extends DDC1383AbstractEntity
{
/**
* @ManyToOne(targetEntity="DDC1383AbstractEntity")
*/
protected $reference;
public function getReference()
{
return $this->reference;
}
public function setReference(DDC1383AbstractEntity $reference)
{
$this->reference = $reference;
}
}

View File

@ -0,0 +1,127 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1392
*/
class DDC1392Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$file = new DDC1392File;
$picture = new DDC1392Picture;
$picture->setFile($file);
$em = $this->_em;
$em->persist($picture);
$em->flush();
$em->clear();
$fileId = $file->getFileId();
$pictureId = $picture->getPictureId();
$this->assertTrue($fileId > 0);
$picture = $em->find(__NAMESPACE__ . '\DDC1392Picture', $pictureId);
$this->assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), "Lazy Proxy should be marked MANAGED.");
$file = $picture->getFile();
// With this activated there will be no problem
//$file->__load();
$picture->setFile(null);
$em->clear();
$em->merge($file);
$em->flush();
$q = $this->_em->createQuery("SELECT COUNT(e) FROM " . __NAMESPACE__ . '\DDC1392File e');
$result = $q->getSingleScalarResult();
self::assertEquals(1, $result);
}
}
/**
* @Entity
*/
class DDC1392Picture
{
/**
* @Column(name="picture_id", type="integer")
* @Id @GeneratedValue
*/
private $pictureId;
/**
* @ManyToOne(targetEntity="DDC1392File", cascade={"persist", "remove"})
* @JoinColumn(name="file_id", referencedColumnName="file_id")
*/
private $file;
/**
* Get pictureId
*/
public function getPictureId()
{
return $this->pictureId;
}
/**
* Set file
*/
public function setFile($value = null)
{
$this->file = $value;
}
/**
* Get file
*/
public function getFile()
{
return $this->file;
}
}
/**
* @Entity
*/
class DDC1392File
{
/**
* @Column(name="file_id", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $fileId;
/**
* Get fileId
*/
public function getFileId()
{
return $this->fileId;
}
}

View File

@ -31,8 +31,8 @@ class DDC381Test extends \Doctrine\Tests\OrmFunctionalTestCase
$entity = $this->_em->getReference('Doctrine\Tests\ORM\Functional\Ticket\DDC381Entity', $persistedId); $entity = $this->_em->getReference('Doctrine\Tests\ORM\Functional\Ticket\DDC381Entity', $persistedId);
// explicitly load proxy // explicitly load proxy (getId() does not trigger reload of proxy)
$id = $entity->getId(); $id = $entity->getOtherMethod();
$data = serialize($entity); $data = serialize($entity);
$entity = unserialize($data); $entity = unserialize($data);
@ -55,4 +55,9 @@ class DDC381Entity
{ {
return $this->id; return $this->id;
} }
public function getOtherMethod()
{
}
} }

View File

@ -37,6 +37,10 @@ class TypeTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0.1515, $decimal->highScale); $this->assertEquals(0.1515, $decimal->highScale);
} }
/**
* @group DDC-1394
* @return void
*/
public function testBoolean() public function testBoolean()
{ {
$bool = new BooleanModel(); $bool = new BooleanModel();
@ -46,7 +50,7 @@ class TypeTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush(); $this->_em->flush();
$this->_em->clear(); $this->_em->clear();
$dql = "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b"; $dql = "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true";
$bool = $this->_em->createQuery($dql)->getSingleResult(); $bool = $this->_em->createQuery($dql)->getSingleResult();
$this->assertTrue($bool->booleanField); $this->assertTrue($bool->booleanField);
@ -56,7 +60,7 @@ class TypeTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush(); $this->_em->flush();
$this->_em->clear(); $this->_em->clear();
$dql = "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b"; $dql = "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false";
$bool = $this->_em->createQuery($dql)->getSingleResult(); $bool = $this->_em->createQuery($dql)->getSingleResult();
$this->assertFalse($bool->booleanField); $this->assertFalse($bool->booleanField);

View File

@ -763,4 +763,58 @@ class ArrayHydratorTest extends HydrationTestCase
$this->assertEquals(1, count($result)); $this->assertEquals(1, count($result));
} }
/**
* @group DDC-1358
*/
public function testMissingIdForRootEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(4, count($result), "Should hydrate four results.");
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertEquals(array('id' => 1, 'status' => 'developer'), $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertEquals(array('id' => 2, 'status' => 'developer'), $result[2][0]);
$this->assertNull($result[3][0]);
}
} }

View File

@ -1008,4 +1008,165 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals(4, count($result[1]->groups)); $this->assertEquals(4, count($result[1]->groups));
$this->assertEquals(2, count($result[1]->phonenumbers)); $this->assertEquals(2, count($result[1]->phonenumbers));
} }
/**
* @group DDC-1358
*/
public function testMissingIdForRootEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(4, count($result), "Should hydrate four results.");
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][0]);
$this->assertNull($result[3][0]);
}
/**
* @group DDC-1358
* @return void
*/
public function testMissingIdForCollectionValuedChildEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
'u',
'phonenumbers'
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
'p__phonenumber' => '91'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
'p__phonenumber' => null
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertEquals(1, $result[0][0]->phonenumbers->count());
$this->assertEquals(1, $result[1][0]->phonenumbers->count());
}
/**
* @group DDC-1358
*/
public function testMissingIdForSingleValuedChildEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsAddress',
'a',
'u',
'address'
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addFieldResult('a', 'a__id', 'id');
$rsm->addFieldResult('a', 'a__city', 'city');
$rsm->addMetaResult('a', 'user_id', 'user_id');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'a__id' => 1,
'a__city' => 'Berlin',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'BENJAMIN',
'a__id' => null,
'a__city' => null,
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][0]->address);
$this->assertNull($result[1][0]->address);
}
} }

View File

@ -114,7 +114,7 @@ class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase
*/ */
public function testUnmappedEntityInHierachy() public function testUnmappedEntityInHierachy()
{ {
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException', "Entity 'Doctrine\Tests\ORM\Mapping\HierachyBEntity' has to be part of the descriminator map of 'Doctrine\Tests\ORM\Mapping\HierachyBase' to be properly mapped in the inheritance hierachy. Alternatively you can make 'Doctrine\Tests\ORM\Mapping\HierachyBEntity' an abstract class to avoid this exception from occuring."); $this->setExpectedException('Doctrine\ORM\Mapping\MappingException', "Entity 'Doctrine\Tests\ORM\Mapping\HierachyBEntity' has to be part of the discriminator map of 'Doctrine\Tests\ORM\Mapping\HierachyBase' to be properly mapped in the inheritance hierachy. Alternatively you can make 'Doctrine\Tests\ORM\Mapping\HierachyBEntity' an abstract class to avoid this exception from occuring.");
$class = $this->_factory->getMetadataFor(__NAMESPACE__ . '\\HierachyE'); $class = $this->_factory->getMetadataFor(__NAMESPACE__ . '\\HierachyE');
} }

View File

@ -0,0 +1,113 @@
<?php
/*
* 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\Tests\ORM\Mapping\Symfony;
/**
* @group DDC-1418
*/
abstract class AbstractDriverTest extends \PHPUnit_Framework_TestCase
{
public function testFindMappingFile()
{
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\EntityFoo' => 'foo',
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
touch($filename = $this->dir.'/Foo'.$this->getFileExtension());
$this->assertEquals($filename, $this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo')));
}
public function testFindMappingFileInSubnamespace()
{
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
touch($filename = $this->dir.'/Foo.Bar'.$this->getFileExtension());
$this->assertEquals($filename, $this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo\Bar')));
}
public function testFindMappingFileNamespacedFoundFileNotFound()
{
$this->setExpectedException(
'Doctrine\ORM\Mapping\MappingException',
"No mapping file found named '".$this->dir."/Foo".$this->getFileExtension()."' for class 'MyNamespace\MySubnamespace\Entity\Foo'."
);
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
$this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo'));
}
public function testFindMappingNamespaceNotFound()
{
$this->setExpectedException(
'Doctrine\ORM\Mapping\MappingException',
"No mapping file found named 'Foo".$this->getFileExtension()."' for class 'MyOtherNamespace\MySubnamespace\Entity\Foo'."
);
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
$this->invoke($driver, '_findMappingFile', array('MyOtherNamespace\MySubnamespace\Entity\Foo'));
}
protected function setUp()
{
$this->dir = sys_get_temp_dir().'/abstract_driver_test';
@mkdir($this->dir, 0777, true);
}
protected function tearDown()
{
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->dir), \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $path) {
if ($path->isDir()) {
@rmdir($path);
} else {
@unlink($path);
}
}
@rmdir($this->dir);
}
abstract protected function getFileExtension();
abstract protected function getDriver(array $paths = array());
private function setField($obj, $field, $value)
{
$ref = new \ReflectionProperty($obj, $field);
$ref->setAccessible(true);
$ref->setValue($obj, $value);
}
private function invoke($obj, $method, array $args = array()) {
$ref = new \ReflectionMethod($obj, $method);
$ref->setAccessible(true);
return $ref->invokeArgs($obj, $args);
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* 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\Tests\ORM\Mapping\Symfony;
use \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver;
/**
* @group DDC-1418
*/
class XmlDriverTest extends AbstractDriverTest
{
protected function getFileExtension()
{
return '.orm.xml';
}
protected function getDriver(array $paths = array())
{
$driver = new SimplifiedXmlDriver(array_flip($paths));
return $driver;
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* 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\Tests\ORM\Mapping\Symfony;
use \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver;
/**
* @group DDC-1418
*/
class YamlDriverTest extends AbstractDriverTest
{
protected function getFileExtension()
{
return '.orm.yml';
}
protected function getDriver(array $paths = array())
{
$driver = new SimplifiedYamlDriver(array_flip($paths));
return $driver;
}
}

View File

@ -73,12 +73,13 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$persister = $this->_getMockPersister(); $persister = $this->_getMockPersister();
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister); $this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
$proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier); $proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier);
$persister->expects($this->atLeastOnce()) $persister->expects($this->atLeastOnce())
->method('load') ->method('load')
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass)) ->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
->will($this->returnValue(new \stdClass())); // fake return of entity instance ->will($this->returnValue(new \stdClass())); // fake return of entity instance
$proxy->getId();
$proxy->getDescription(); $proxy->getDescription();
$proxy->getProduct();
} }
public function testReferenceProxyRespectsMethodsParametersTypeHinting() public function testReferenceProxyRespectsMethodsParametersTypeHinting()

View File

@ -729,12 +729,12 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertSqlGeneration( $this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true", "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = 'true'" "SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = true"
); );
$this->assertSqlGeneration( $this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false", "SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = 'false'" "SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = false"
); );
$this->_em->getConnection()->setDatabasePlatform($oldPlat); $this->_em->getConnection()->setDatabasePlatform($oldPlat);
@ -1095,6 +1095,162 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"Doctrine\ORM\Query\QueryException" "Doctrine\ORM\Query\QueryException"
); );
} }
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInRootClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInRootClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p',
'SELECT c0_.id AS id0, c0_.name AS name1, c0_.discr AS discr2 FROM company_persons c0_',
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInChildClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c2_.car_id AS car_id6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInChildClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInLeafClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7, c2_.car_id AS car_id8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeJoinInLeafClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT m FROM Doctrine\Tests\Models\Company\CompanyManager m',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.discr AS discr6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInRootClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6, c0_.salesPerson_id AS salesPerson_id7 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInRootClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_contracts c0_ WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInChildClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.hoursWorked AS hoursWorked2, c0_.pricePerHour AS pricePerHour3, c0_.maxPrice AS maxPrice4, c0_.discr AS discr5, c0_.salesPerson_id AS salesPerson_id6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInChildClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT fc FROM Doctrine\Tests\Models\Company\CompanyFlexContract fc',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.hoursWorked AS hoursWorked2, c0_.pricePerHour AS pricePerHour3, c0_.maxPrice AS maxPrice4, c0_.discr AS discr5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexible', 'flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInLeafClassWithDisabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.hoursWorked AS hoursWorked2, c0_.pricePerHour AS pricePerHour3, c0_.maxPrice AS maxPrice4, c0_.discr AS discr5, c0_.salesPerson_id AS salesPerson_id6 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1389
*/
public function testInheritanceTypeSingleTableInLeafClassWithEnabledForcePartialLoad()
{
$this->assertSqlGeneration(
'SELECT fuc FROM Doctrine\Tests\Models\Company\CompanyFlexUltraContract fuc',
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.hoursWorked AS hoursWorked2, c0_.pricePerHour AS pricePerHour3, c0_.maxPrice AS maxPrice4, c0_.discr AS discr5 FROM company_contracts c0_ WHERE c0_.discr IN ('flexultra')",
array(Query::HINT_FORCE_PARTIAL_LOAD => true)
);
}
/**
* @group DDC-1161
*/
public function testSelfReferenceWithOneToOneDoesNotDuplicateAlias()
{
$this->assertSqlGeneration(
'SELECT p, pp FROM Doctrine\Tests\Models\Company\CompanyPerson p JOIN p.spouse pp',
"SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c3_.id AS id7, c3_.name AS name8, c4_.title AS title9, c4_.car_id AS car_id10, c5_.salary AS salary11, c5_.department AS department12, c5_.startDate AS startDate13, c0_.discr AS discr14, c0_.spouse_id AS spouse_id15, c3_.discr AS discr16, c3_.spouse_id AS spouse_id17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id",
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
} }

View File

@ -21,6 +21,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->_tmpDir = \sys_get_temp_dir(); $this->_tmpDir = \sys_get_temp_dir();
\mkdir($this->_tmpDir . \DIRECTORY_SEPARATOR . $this->_namespace); \mkdir($this->_tmpDir . \DIRECTORY_SEPARATOR . $this->_namespace);
$this->_generator = new EntityGenerator(); $this->_generator = new EntityGenerator();
$this->_generator->setAnnotationPrefix("");
$this->_generator->setGenerateAnnotations(true); $this->_generator->setGenerateAnnotations(true);
$this->_generator->setGenerateStubMethods(true); $this->_generator->setGenerateStubMethods(true);
$this->_generator->setRegenerateEntityIfExists(false); $this->_generator->setRegenerateEntityIfExists(false);
@ -179,14 +180,15 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
public function testLoadPrefixedMetadata() public function testLoadPrefixedMetadata()
{ {
$this->_generator->setAnnotationPrefix('orm:'); $this->_generator->setAnnotationPrefix('ORM\\');
$metadata = $this->generateBookEntityFixture(); $metadata = $this->generateBookEntityFixture();
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, array());
$book = $this->newInstance($metadata); $book = $this->newInstance($metadata);
$cm = new \Doctrine\ORM\Mapping\ClassMetadata($metadata->name); $cm = new \Doctrine\ORM\Mapping\ClassMetadata($metadata->name);
$driver = $this->createAnnotationDriver(array(), 'orm');
$driver->loadMetadataForClass($cm->name, $cm); $driver->loadMetadataForClass($cm->name, $cm);
$this->assertEquals($cm->columnNames, $metadata->columnNames); $this->assertEquals($cm->columnNames, $metadata->columnNames);

View File

@ -98,6 +98,8 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
public function testExportDirectoryAndFilesAreCreated() public function testExportDirectoryAndFilesAreCreated()
{ {
$this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType());
$type = $this->_getType(); $type = $this->_getType();
$metadataDriver = $this->_createMetadataDriver($type, __DIR__ . '/' . $type); $metadataDriver = $this->_createMetadataDriver($type, __DIR__ . '/' . $type);
$em = $this->_createEntityManager($metadataDriver); $em = $this->_createEntityManager($metadataDriver);
@ -113,6 +115,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
$exporter = $cme->getExporter($type, __DIR__ . '/export/' . $type); $exporter = $cme->getExporter($type, __DIR__ . '/export/' . $type);
if ($type === 'annotation') { if ($type === 'annotation') {
$entityGenerator = new EntityGenerator(); $entityGenerator = new EntityGenerator();
$entityGenerator->setAnnotationPrefix("");
$exporter->setEntityGenerator($entityGenerator); $exporter->setEntityGenerator($entityGenerator);
} }
$this->_extension = $exporter->getExtension(); $this->_extension = $exporter->getExtension();
@ -139,6 +142,8 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
$cmf = $this->_createClassMetadataFactory($em, $type); $cmf = $this->_createClassMetadataFactory($em, $type);
$metadata = $cmf->getAllMetadata(); $metadata = $cmf->getAllMetadata();
$this->assertEquals(1, count($metadata));
$class = current($metadata); $class = current($metadata);
$this->assertEquals('Doctrine\Tests\ORM\Tools\Export\ExportedUser', $class->name); $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\ExportedUser', $class->name);
@ -322,8 +327,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
public function __destruct() public function __destruct()
{ {
$type = $this->_getType(); # $this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType());
$this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType());
} }
protected function _deleteDirectory($path) protected function _deleteDirectory($path)

View File

@ -11,6 +11,7 @@ abstract class OrmTestCase extends DoctrineTestCase
{ {
/** The metadata cache that is shared between all ORM tests (except functional tests). */ /** The metadata cache that is shared between all ORM tests (except functional tests). */
private static $_metadataCacheImpl = null; private static $_metadataCacheImpl = null;
/** The query cache that is shared between all ORM tests (except functional tests). */ /** The query cache that is shared between all ORM tests (except functional tests). */
private static $_queryCacheImpl = null; private static $_queryCacheImpl = null;
@ -66,19 +67,18 @@ abstract class OrmTestCase extends DoctrineTestCase
*/ */
protected function _getTestEntityManager($conn = null, $conf = null, $eventManager = null, $withSharedMetadata = true) protected function _getTestEntityManager($conn = null, $conf = null, $eventManager = null, $withSharedMetadata = true)
{ {
$metadataCache = $withSharedMetadata
? self::getSharedMetadataCacheImpl()
: new \Doctrine\Common\Cache\ArrayCache;
$config = new \Doctrine\ORM\Configuration(); $config = new \Doctrine\ORM\Configuration();
if($withSharedMetadata) {
$config->setMetadataCacheImpl(self::getSharedMetadataCacheImpl());
} else {
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache);
}
$config->setMetadataCacheImpl($metadataCache);
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver()); $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
$config->setQueryCacheImpl(self::getSharedQueryCacheImpl()); $config->setQueryCacheImpl(self::getSharedQueryCacheImpl());
$config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Doctrine\Tests\Proxies'); $config->setProxyNamespace('Doctrine\Tests\Proxies');
$eventManager = new \Doctrine\Common\EventManager();
if ($conn === null) { if ($conn === null) {
$conn = array( $conn = array(
'driverClass' => 'Doctrine\Tests\Mocks\DriverMock', 'driverClass' => 'Doctrine\Tests\Mocks\DriverMock',
@ -87,9 +87,11 @@ abstract class OrmTestCase extends DoctrineTestCase
'password' => 'wayne' 'password' => 'wayne'
); );
} }
if (is_array($conn)) { if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager); $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
} }
return \Doctrine\Tests\Mocks\EntityManagerMock::create($conn, $config, $eventManager); return \Doctrine\Tests\Mocks\EntityManagerMock::create($conn, $config, $eventManager);
} }
@ -98,6 +100,7 @@ abstract class OrmTestCase extends DoctrineTestCase
if (self::$_metadataCacheImpl === null) { if (self::$_metadataCacheImpl === null) {
self::$_metadataCacheImpl = new \Doctrine\Common\Cache\ArrayCache; self::$_metadataCacheImpl = new \Doctrine\Common\Cache\ArrayCache;
} }
return self::$_metadataCacheImpl; return self::$_metadataCacheImpl;
} }
@ -106,6 +109,7 @@ abstract class OrmTestCase extends DoctrineTestCase
if (self::$_queryCacheImpl === null) { if (self::$_queryCacheImpl === null) {
self::$_queryCacheImpl = new \Doctrine\Common\Cache\ArrayCache; self::$_queryCacheImpl = new \Doctrine\Common\Cache\ArrayCache;
} }
return self::$_queryCacheImpl; return self::$_queryCacheImpl;
} }
} }