Merge origin/master into DDC-1384
This commit is contained in:
commit
64d405f7dd
@ -1,3 +1,17 @@
|
||||
# ResultCache implementation rewritten
|
||||
|
||||
The result cache is completly rewritten and now works on the database result level, not inside the ORM AbstractQuery
|
||||
anymore. This means that for result cached queries the hydration will now always be performed again, regardless of
|
||||
the hydration mode. Affected areas are:
|
||||
|
||||
1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork
|
||||
leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.
|
||||
2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.
|
||||
|
||||
The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now
|
||||
deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile`
|
||||
instance with access to result cache driver, lifetime and cache key.
|
||||
|
||||
# EntityManager#getPartialReference() creates read-only entity
|
||||
|
||||
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they
|
||||
|
@ -20,7 +20,8 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Query\QueryException;
|
||||
Doctrine\ORM\Query\QueryException,
|
||||
Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
@ -91,34 +92,15 @@ abstract class AbstractQuery
|
||||
protected $_hydrationMode = self::HYDRATE_OBJECT;
|
||||
|
||||
/**
|
||||
* The locally set cache driver used for caching result sets of this query.
|
||||
*
|
||||
* @var CacheDriver
|
||||
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||
*/
|
||||
protected $_resultCacheDriver;
|
||||
|
||||
/**
|
||||
* Boolean flag for whether or not to cache the results of this query.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_useResultCache;
|
||||
|
||||
/**
|
||||
* @var string The id to store the result cache entry under.
|
||||
*/
|
||||
protected $_resultCacheId;
|
||||
protected $_queryCacheProfile;
|
||||
|
||||
/**
|
||||
* @var boolean Boolean value that indicates whether or not expire the result cache.
|
||||
*/
|
||||
protected $_expireResultCache = false;
|
||||
|
||||
/**
|
||||
* @var int Result Cache lifetime.
|
||||
*/
|
||||
protected $_resultCacheTTL;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
||||
*
|
||||
@ -260,7 +242,7 @@ abstract class AbstractQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cache driver to be used for caching result sets.
|
||||
* Defines a cache driver to be used for caching result sets and implictly enables caching.
|
||||
*
|
||||
* @param Doctrine\Common\Cache\Cache $driver Cache driver
|
||||
* @return Doctrine\ORM\AbstractQuery
|
||||
@ -270,9 +252,10 @@ abstract class AbstractQuery
|
||||
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
|
||||
throw ORMException::invalidResultCacheDriver();
|
||||
}
|
||||
$this->_resultCacheDriver = $resultCacheDriver;
|
||||
if ($resultCacheDriver) {
|
||||
$this->_useResultCache = true;
|
||||
if ($this->_queryCacheProfile) {
|
||||
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
|
||||
} else {
|
||||
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@ -280,12 +263,13 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Returns the cache driver used for caching result sets.
|
||||
*
|
||||
* @deprecated
|
||||
* @return Doctrine\Common\Cache\Cache Cache driver
|
||||
*/
|
||||
public function getResultCacheDriver()
|
||||
{
|
||||
if ($this->_resultCacheDriver) {
|
||||
return $this->_resultCacheDriver;
|
||||
if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
|
||||
return $this->_queryCacheProfile->getResultCacheDriver();
|
||||
} else {
|
||||
return $this->_em->getConfiguration()->getResultCacheImpl();
|
||||
}
|
||||
@ -296,18 +280,17 @@ abstract class AbstractQuery
|
||||
* how long and which ID to use for the cache entry.
|
||||
*
|
||||
* @param boolean $bool
|
||||
* @param integer $timeToLive
|
||||
* @param integer $lifetime
|
||||
* @param string $resultCacheId
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
|
||||
public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
|
||||
{
|
||||
$this->_useResultCache = $bool;
|
||||
if ($timeToLive) {
|
||||
$this->setResultCacheLifetime($timeToLive);
|
||||
}
|
||||
if ($resultCacheId) {
|
||||
$this->_resultCacheId = $resultCacheId;
|
||||
if ($bool) {
|
||||
$this->setResultCacheLifetime($lifetime);
|
||||
$this->setResultCacheId($resultCacheId);
|
||||
} else {
|
||||
$this->_queryCacheProfile = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@ -315,27 +298,33 @@ abstract class AbstractQuery
|
||||
/**
|
||||
* Defines how long the result cache will be active before expire.
|
||||
*
|
||||
* @param integer $timeToLive How long the cache entry is valid.
|
||||
* @param integer $lifetime How long the cache entry is valid.
|
||||
* @return Doctrine\ORM\AbstractQuery This query instance.
|
||||
*/
|
||||
public function setResultCacheLifetime($timeToLive)
|
||||
public function setResultCacheLifetime($lifetime)
|
||||
{
|
||||
if ($timeToLive !== null) {
|
||||
$timeToLive = (int) $timeToLive;
|
||||
if ($lifetime === null) {
|
||||
$lifetime = 0;
|
||||
} else {
|
||||
$lifetime = (int)$lifetime;
|
||||
}
|
||||
if ($this->_queryCacheProfile) {
|
||||
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
|
||||
} else {
|
||||
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
|
||||
}
|
||||
|
||||
$this->_resultCacheTTL = $timeToLive;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the lifetime of resultset cache.
|
||||
*
|
||||
* @deprecated
|
||||
* @return integer
|
||||
*/
|
||||
public function getResultCacheLifetime()
|
||||
{
|
||||
return $this->_resultCacheTTL;
|
||||
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -360,6 +349,14 @@ abstract class AbstractQuery
|
||||
return $this->_expireResultCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryCacheProfile
|
||||
*/
|
||||
public function getQueryCacheProfile()
|
||||
{
|
||||
return $this->_queryCacheProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default fetch mode of an association for this query.
|
||||
*
|
||||
@ -548,7 +545,7 @@ abstract class AbstractQuery
|
||||
*
|
||||
* @param array $params The query parameters.
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = null)
|
||||
{
|
||||
@ -584,28 +581,6 @@ abstract class AbstractQuery
|
||||
$this->setParameters($params);
|
||||
}
|
||||
|
||||
// Check result cache
|
||||
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
|
||||
list($key, $hash) = $this->getResultCacheId();
|
||||
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
|
||||
|
||||
if ($cached === false || !isset($cached[$key])) {
|
||||
// Cache miss.
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
// Cache hit.
|
||||
return $cached[$key];
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->_doExecute();
|
||||
|
||||
if (is_numeric($stmt)) {
|
||||
@ -627,43 +602,23 @@ abstract class AbstractQuery
|
||||
*/
|
||||
public function setResultCacheId($id)
|
||||
{
|
||||
$this->_resultCacheId = $id;
|
||||
if ($this->_queryCacheProfile) {
|
||||
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
|
||||
} else {
|
||||
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result cache id to use to store the result set cache entry.
|
||||
* Will return the configured id if it exists otherwise a hash will be
|
||||
* automatically generated for you.
|
||||
* Get the result cache id to use to store the result set cache entry if set.
|
||||
*
|
||||
* @return array ($key, $hash)
|
||||
* @deprecated
|
||||
* @return string
|
||||
*/
|
||||
protected function getResultCacheId()
|
||||
public function getResultCacheId()
|
||||
{
|
||||
if ($this->_resultCacheId) {
|
||||
return array($this->_resultCacheId, $this->_resultCacheId);
|
||||
} else {
|
||||
$params = $this->_params;
|
||||
foreach ($params AS $key => $value) {
|
||||
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
|
||||
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
} else {
|
||||
$class = $this->_em->getClassMetadata(get_class($value));
|
||||
$idValues = $class->getIdentifierValues($value);
|
||||
}
|
||||
$params[$key] = $idValues;
|
||||
} else {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $this->getSql();
|
||||
ksort($this->_hints);
|
||||
$key = implode(";", (array)$sql) . var_export($params, true) .
|
||||
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
|
||||
return array($key, md5($key));
|
||||
}
|
||||
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
$this->_attributes['metadataDriverImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @return \Doctrine\Common\Cache\Cache
|
||||
*/
|
||||
public function getResultCacheImpl()
|
||||
{
|
||||
return isset($this->_attributes['resultCacheImpl']) ?
|
||||
$this->_attributes['resultCacheImpl'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache driver implementation that is used for query result caching.
|
||||
*
|
||||
* @param \Doctrine\Common\Cache\Cache $cacheImpl
|
||||
*/
|
||||
public function setResultCacheImpl(Cache $cacheImpl)
|
||||
{
|
||||
$this->_attributes['resultCacheImpl'] = $cacheImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache driver implementation that is used for the query cache (SQL cache).
|
||||
*
|
||||
|
@ -325,13 +325,17 @@ class EntityManager implements ObjectManager
|
||||
* This effectively synchronizes the in-memory state of managed objects with the
|
||||
* 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
|
||||
* makes use of optimistic locking fails.
|
||||
*/
|
||||
public function flush()
|
||||
public function flush($entity = null)
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
$this->unitOfWork->commit();
|
||||
$this->unitOfWork->commit($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,13 +19,15 @@
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use \Doctrine\Common\EventSubscriber;
|
||||
use \LogicException;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Delegate events only for certain entities they are registered for.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @since 2.2
|
||||
*/
|
||||
class EntityEventDelegator implements EventSubscriber
|
||||
@ -54,17 +56,23 @@ class EntityEventDelegator implements EventSubscriber
|
||||
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.");
|
||||
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);
|
||||
$entities = array_flip((array) $entities);
|
||||
|
||||
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));
|
||||
$this->listeners[$event][$hash] = array(
|
||||
'listener' => $listener,
|
||||
'entities' => $entities
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +81,7 @@ class EntityEventDelegator implements EventSubscriber
|
||||
* interested in and added as a listener for these events.
|
||||
*
|
||||
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
|
||||
* @param array $entities
|
||||
*/
|
||||
public function addEventSubscriber(EventSubscriber $subscriber, $entities)
|
||||
{
|
||||
@ -87,24 +96,27 @@ class EntityEventDelegator implements EventSubscriber
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
$this->frozen = true;
|
||||
|
||||
return array_keys($this->listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate the event to an appropriate listener
|
||||
*
|
||||
* @param $eventName
|
||||
* @param $event
|
||||
* @param string $eventName
|
||||
* @param array $args
|
||||
* @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])) {
|
||||
|
||||
if ( ! isset($listenerData['entities'][$class])) continue;
|
||||
|
||||
$listenerData['listener']->$eventName($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,42 +19,59 @@
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
|
||||
* of entities.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class LifecycleEventArgs extends \Doctrine\Common\EventArgs
|
||||
class LifecycleEventArgs extends EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
private $_entity;
|
||||
private $entity;
|
||||
|
||||
public function __construct($entity, $em)
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param object $entity
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct($entity, EntityManager $em)
|
||||
{
|
||||
$this->_entity = $entity;
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->_entity;
|
||||
$this->entity = $entity;
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
* Retireve associated Entity.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
return $this->em;
|
||||
}
|
||||
}
|
@ -1,9 +1,25 @@
|
||||
<?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\EventArgs;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
@ -16,18 +32,20 @@ use Doctrine\ORM\EntityManager;
|
||||
class LoadClassMetadataEventArgs extends EventArgs
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
* @var Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
private $classMetadata;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @param ClassMetadataInfo $classMetadata
|
||||
* @param EntityManager $em
|
||||
* Constructor.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
|
||||
{
|
||||
@ -36,7 +54,9 @@ class LoadClassMetadataEventArgs extends EventArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadataInfo
|
||||
* Retrieve associated ClassMetadata.
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadataInfo
|
||||
*/
|
||||
public function getClassMetadata()
|
||||
{
|
||||
@ -44,7 +64,9 @@ class LoadClassMetadataEventArgs extends EventArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
|
@ -23,16 +23,15 @@ namespace Doctrine\ORM\Event;
|
||||
* Provides event arguments for the onClear event.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityManager
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
@ -42,7 +41,10 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
private $entityClass;
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\EntityManager $em
|
||||
* Constructor.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
* @param string $entityClass Optional entity class
|
||||
*/
|
||||
public function __construct($em, $entityClass = null)
|
||||
{
|
||||
@ -51,7 +53,9 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Doctrine\ORM\EntityManager
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
@ -75,6 +79,6 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
*/
|
||||
public function clearsAllEntities()
|
||||
{
|
||||
return $this->entityClass === null;
|
||||
return ($this->entityClass === null);
|
||||
}
|
||||
}
|
@ -21,37 +21,45 @@
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Provides event arguments for the preFlush event.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class OnFlushEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
* @var Doctirne\ORM\EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
private $em;
|
||||
|
||||
//private $_entitiesToPersist = array();
|
||||
//private $_entitiesToRemove = array();
|
||||
//private $entitiesToPersist = array();
|
||||
//private $entitiesToRemove = array();
|
||||
|
||||
public function __construct($em)
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->_em;
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
/*
|
||||
|
61
lib/Doctrine/ORM/Event/PostFlushEventArgs.php
Normal file
61
lib/Doctrine/ORM/Event/PostFlushEventArgs.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?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.org
|
||||
* @since 2.0
|
||||
* @author Daniel Freudenberger <df@rebuy.de>
|
||||
*/
|
||||
class PostFlushEventArgs extends EventArgs
|
||||
{
|
||||
/**
|
||||
* @var Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve associated EntityManager.
|
||||
*
|
||||
* @return Doctrine\ORM\EntityManager
|
||||
*/
|
||||
public function getEntityManager()
|
||||
{
|
||||
return $this->em;
|
||||
}
|
||||
}
|
@ -1,4 +1,23 @@
|
||||
<?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;
|
||||
|
||||
@ -8,6 +27,7 @@ use Doctrine\Common\EventArgs,
|
||||
/**
|
||||
* Class that holds event arguments for a preInsert/preUpdate event.
|
||||
*
|
||||
* @author Guilherme Blanco <guilehrmeblanco@hotmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
@ -17,33 +37,40 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_entityChangeSet;
|
||||
private $entityChangeSet;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param EntityManager $em
|
||||
* @param Doctrine\ORM\EntityManager $em
|
||||
* @param array $changeSet
|
||||
*/
|
||||
public function __construct($entity, $em, array &$changeSet)
|
||||
public function __construct($entity, EntityManager $em, array &$changeSet)
|
||||
{
|
||||
parent::__construct($entity, $em);
|
||||
$this->_entityChangeSet = &$changeSet;
|
||||
}
|
||||
|
||||
public function getEntityChangeSet()
|
||||
{
|
||||
return $this->_entityChangeSet;
|
||||
$this->entityChangeSet = &$changeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field has a changeset?
|
||||
* Retrieve entity changeset.
|
||||
*
|
||||
* @return bool
|
||||
* @return array
|
||||
*/
|
||||
public function getEntityChangeSet()
|
||||
{
|
||||
return $this->entityChangeSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if field has a changeset.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasChangedField($field)
|
||||
{
|
||||
return isset($this->_entityChangeSet[$field]);
|
||||
return isset($this->entityChangeSet[$field]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,9 +81,9 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
*/
|
||||
public function getOldValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
$this->assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][0];
|
||||
return $this->entityChangeSet[$field][0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,9 +94,9 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
*/
|
||||
public function getNewValue($field)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
$this->assertValidField($field);
|
||||
|
||||
return $this->_entityChangeSet[$field][1];
|
||||
return $this->entityChangeSet[$field][1];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,18 +107,24 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
*/
|
||||
public function setNewValue($field, $value)
|
||||
{
|
||||
$this->_assertValidField($field);
|
||||
$this->assertValidField($field);
|
||||
|
||||
$this->_entityChangeSet[$field][1] = $value;
|
||||
$this->entityChangeSet[$field][1] = $value;
|
||||
}
|
||||
|
||||
private function _assertValidField($field)
|
||||
/**
|
||||
* Assert the field exists in changeset.
|
||||
*
|
||||
* @param string $field
|
||||
*/
|
||||
private function assertValidField($field)
|
||||
{
|
||||
if (!isset($this->_entityChangeSet[$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Field '".$field."' is not a valid field of the entity ".
|
||||
"'".get_class($this->getEntity())."' in PreUpdateEventArgs."
|
||||
);
|
||||
if ( ! isset($this->entityChangeSet[$field])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.',
|
||||
$field,
|
||||
get_class($this->getEntity())
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,17 @@ final class Events
|
||||
*/
|
||||
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,
|
||||
* after all references to entities have been removed from the unit of work.
|
||||
|
@ -369,10 +369,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
|
||||
}
|
||||
} else if ( ! $reflField->getValue($parentObject)) {
|
||||
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
|
||||
$coll->setOwner($parentObject, $relation);
|
||||
$reflField->setValue($parentObject, $coll);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
|
||||
}
|
||||
} else {
|
||||
// PATH B: Single-valued association
|
||||
|
@ -494,6 +494,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
*/
|
||||
public function isTransient($class)
|
||||
{
|
||||
if ( ! $this->initialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->driver->isTransient($class);
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
@ -1111,23 +1111,23 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getIdentifierColumnNames()
|
||||
{
|
||||
if ($this->isIdentifierComposite) {
|
||||
$columnNames = array();
|
||||
foreach ($this->identifier as $idField) {
|
||||
if (isset($this->associationMappings[$idField])) {
|
||||
// no composite pk as fk entity assumption:
|
||||
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
|
||||
} else {
|
||||
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
|
||||
|
||||
foreach ($this->identifier as $idProperty) {
|
||||
if (isset($this->fieldMappings[$idProperty])) {
|
||||
$columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Association defined as Id field
|
||||
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
|
||||
$assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
|
||||
|
||||
$columnNames = array_merge($columnNames, $assocColumnNames);
|
||||
}
|
||||
|
||||
return $columnNames;
|
||||
} else if(isset($this->fieldMappings[$this->identifier[0]])) {
|
||||
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
|
||||
} else {
|
||||
// no composite pk as fk entity assumption:
|
||||
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1904,6 +1904,42 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return array
|
||||
*/
|
||||
public function getQuotedIdentifierColumnNames($platform)
|
||||
{
|
||||
$quotedColumnNames = array();
|
||||
|
||||
foreach ($this->identifier as $idProperty) {
|
||||
if (isset($this->fieldMappings[$idProperty])) {
|
||||
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
|
||||
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
|
||||
: $this->fieldMappings[$idProperty]['columnName'];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Association defined as Id field
|
||||
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
|
||||
$assocQuotedColumnNames = array_map(
|
||||
function ($joinColumn) {
|
||||
return isset($joinColumn['quoted'])
|
||||
? $platform->quoteIdentifier($joinColumn['name'])
|
||||
: $joinColumn['name'];
|
||||
},
|
||||
$joinColumns
|
||||
);
|
||||
|
||||
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
|
||||
}
|
||||
|
||||
return $quotedColumnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (possibly quoted) column name of a mapped field for safe use
|
||||
* in an SQL statement.
|
||||
@ -1914,7 +1950,9 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getQuotedColumnName($field, $platform)
|
||||
{
|
||||
return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName'];
|
||||
return isset($this->fieldMappings[$field]['quoted'])
|
||||
? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
|
||||
: $this->fieldMappings[$field]['columnName'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,7 +288,7 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
|
||||
{
|
||||
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 avoid this exception from occuring."
|
||||
);
|
||||
|
@ -57,17 +57,17 @@ final class NativeQuery extends AbstractQuery
|
||||
*/
|
||||
protected function _doExecute()
|
||||
{
|
||||
$stmt = $this->_em->getConnection()->prepare($this->_sql);
|
||||
$params = $this->_params;
|
||||
foreach ($params as $key => $value) {
|
||||
if (isset($this->_paramTypes[$key])) {
|
||||
$stmt->bindValue($key, $value, $this->_paramTypes[$key]);
|
||||
} else {
|
||||
$stmt->bindValue($key, $value);
|
||||
$types = $this->_paramTypes;
|
||||
if ($params) {
|
||||
if (is_int(key($params))) {
|
||||
ksort($params);
|
||||
ksort($types);
|
||||
$params = array_values($params);
|
||||
$types = array_values($types);
|
||||
}
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt;
|
||||
return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
|
||||
}
|
||||
}
|
@ -1491,7 +1491,7 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$sql = 'SELECT 1 '
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name)
|
||||
. $this->getLockTablesSql()
|
||||
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
|
||||
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
@ -230,10 +230,12 @@ class ProxyFactory
|
||||
*/
|
||||
private function isShortIdentifierGetter($method, $class)
|
||||
{
|
||||
$identifier = lcfirst(substr($method->getName(), 3));
|
||||
return (
|
||||
$method->getNumberOfParameters() == 0 &&
|
||||
substr($method->getName(), 0, 3) == "get" &&
|
||||
in_array(lcfirst(substr($method->getName(), 3)), $class->identifier, true) &&
|
||||
in_array($identifier, $class->identifier, true) &&
|
||||
$class->hasField($identifier) &&
|
||||
(($method->getEndLine() - $method->getStartLine()) <= 4)
|
||||
);
|
||||
}
|
||||
|
@ -232,6 +232,9 @@ final class Query extends AbstractQuery
|
||||
protected function _doExecute()
|
||||
{
|
||||
$executor = $this->_parse()->getSqlExecutor();
|
||||
if ($this->_queryCacheProfile) {
|
||||
$executor->setQueryCacheProfile($this->_queryCacheProfile);
|
||||
}
|
||||
|
||||
// Prepare parameters
|
||||
$paramMappings = $this->_parserResult->getParameterMappings();
|
||||
@ -523,7 +526,7 @@ final class Query extends AbstractQuery
|
||||
*
|
||||
* @param array $params The query parameters.
|
||||
* @param integer $hydrationMode The hydration mode to use.
|
||||
* @return IterableResult
|
||||
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
|
||||
*/
|
||||
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
|
||||
{
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -22,6 +20,7 @@
|
||||
namespace Doctrine\ORM\Query\Exec;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
|
||||
/**
|
||||
* Base class for SQL statement executors.
|
||||
@ -34,8 +33,16 @@ use Doctrine\DBAL\Connection;
|
||||
*/
|
||||
abstract class AbstractSqlExecutor
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_sqlStatements;
|
||||
|
||||
/**
|
||||
* @var QueryCacheProfile
|
||||
*/
|
||||
protected $queryCacheProfile;
|
||||
|
||||
/**
|
||||
* Gets the SQL statements that are executed by the executor.
|
||||
*
|
||||
@ -46,11 +53,17 @@ abstract class AbstractSqlExecutor
|
||||
return $this->_sqlStatements;
|
||||
}
|
||||
|
||||
public function setQueryCacheProfile(QueryCacheProfile $qcp)
|
||||
{
|
||||
$this->queryCacheProfile = $qcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all sql statements.
|
||||
*
|
||||
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
|
||||
* @param array $params The parameters.
|
||||
* @param array $types The parameter types.
|
||||
* @return Doctrine\DBAL\Driver\Statement
|
||||
*/
|
||||
abstract public function execute(Connection $conn, array $params, array $types);
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -32,7 +30,6 @@ use Doctrine\DBAL\Connection,
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link http://www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
*/
|
||||
class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
{
|
||||
@ -102,11 +99,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all SQL statements.
|
||||
*
|
||||
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
|
||||
* @param array $params The parameters.
|
||||
* @override
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function execute(Connection $conn, array $params, array $types)
|
||||
{
|
||||
|
@ -141,11 +141,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all SQL statements.
|
||||
*
|
||||
* @param Connection $conn The database connection that is used to execute the queries.
|
||||
* @param array $params The parameters.
|
||||
* @override
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function execute(Connection $conn, array $params, array $types)
|
||||
{
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @version $Revision$
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
*/
|
||||
@ -41,8 +38,11 @@ class SingleSelectExecutor extends AbstractSqlExecutor
|
||||
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function execute(Connection $conn, array $params, array $types)
|
||||
{
|
||||
return $conn->executeQuery($this->_sqlStatements, $params, $types);
|
||||
return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @version $Revision$
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
|
||||
@ -46,6 +43,9 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function execute(Connection $conn, array $params, array $types)
|
||||
{
|
||||
return $conn->executeUpdate($this->_sqlStatements, $params, $types);
|
||||
|
@ -40,10 +40,12 @@ class Expr
|
||||
*
|
||||
* [php]
|
||||
* // (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
|
||||
* at least one defined when converting to string.
|
||||
* @param Doctrine\ORM\Query\Expr\Comparison |
|
||||
* 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
|
||||
*/
|
||||
public function andX($x = null)
|
||||
|
@ -157,33 +157,25 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function getExecutor($AST)
|
||||
{
|
||||
$isDeleteStatement = $AST instanceof AST\DeleteStatement;
|
||||
$isUpdateStatement = $AST instanceof AST\UpdateStatement;
|
||||
switch (true) {
|
||||
case ($AST instanceof AST\DeleteStatement):
|
||||
$primaryClass = $this->_em->getClassMetadata($AST->deleteClause->abstractSchemaName);
|
||||
|
||||
if ($isDeleteStatement) {
|
||||
$primaryClass = $this->_em->getClassMetadata(
|
||||
$AST->deleteClause->abstractSchemaName
|
||||
);
|
||||
return ($primaryClass->isInheritanceTypeJoined())
|
||||
? new Exec\MultiTableDeleteExecutor($AST, $this)
|
||||
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new Exec\MultiTableDeleteExecutor($AST, $this);
|
||||
} else {
|
||||
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
}
|
||||
} else if ($isUpdateStatement) {
|
||||
$primaryClass = $this->_em->getClassMetadata(
|
||||
$AST->updateClause->abstractSchemaName
|
||||
);
|
||||
case ($AST instanceof AST\UpdateStatement):
|
||||
$primaryClass = $this->_em->getClassMetadata($AST->updateClause->abstractSchemaName);
|
||||
|
||||
if ($primaryClass->isInheritanceTypeJoined()) {
|
||||
return new Exec\MultiTableUpdateExecutor($AST, $this);
|
||||
} else {
|
||||
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
}
|
||||
}
|
||||
return ($primaryClass->isInheritanceTypeJoined())
|
||||
? new Exec\MultiTableUpdateExecutor($AST, $this)
|
||||
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
|
||||
|
||||
default:
|
||||
return new Exec\SingleSelectExecutor($AST, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique, short SQL table alias.
|
||||
@ -260,10 +252,9 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($class->identifier as $idField) {
|
||||
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
|
||||
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
|
||||
}
|
||||
}
|
||||
@ -276,10 +267,9 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($class->identifier as $idField) {
|
||||
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
|
||||
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
|
||||
}
|
||||
}
|
||||
@ -1277,83 +1267,77 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkSimpleSelectExpression($simpleSelectExpression)
|
||||
{
|
||||
$sql = '';
|
||||
$expr = $simpleSelectExpression->expression;
|
||||
$sql = ' ';
|
||||
|
||||
if ($expr instanceof AST\PathExpression) {
|
||||
switch (true) {
|
||||
case ($expr instanceof AST\PathExpression):
|
||||
$sql .= $this->walkPathExpression($expr);
|
||||
} else if ($expr instanceof AST\AggregateExpression) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\AggregateExpression):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
|
||||
|
||||
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
|
||||
} else if ($expr instanceof AST\Subselect) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\Subselect):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
|
||||
|
||||
$columnAlias = 'sclr' . $this->_aliasCounter++;
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias('sclr');
|
||||
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else if ($expr instanceof AST\Functions\FunctionNode) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\Functions\FunctionNode):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias('sclr');
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
|
||||
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else if (
|
||||
$expr instanceof AST\SimpleArithmeticExpression ||
|
||||
$expr instanceof AST\ArithmeticTerm ||
|
||||
$expr instanceof AST\ArithmeticFactor ||
|
||||
$expr instanceof AST\ArithmeticPrimary
|
||||
) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\SimpleArithmeticExpression):
|
||||
case ($expr instanceof AST\ArithmeticTerm):
|
||||
case ($expr instanceof AST\ArithmeticFactor):
|
||||
case ($expr instanceof AST\ArithmeticPrimary):
|
||||
case ($expr instanceof AST\Literal):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias('sclr');
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
|
||||
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else if (
|
||||
$expr instanceof AST\NullIfExpression ||
|
||||
$expr instanceof AST\CoalesceExpression ||
|
||||
$expr instanceof AST\GeneralCaseExpression ||
|
||||
$expr instanceof AST\SimpleCaseExpression
|
||||
) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\NullIfExpression):
|
||||
case ($expr instanceof AST\CoalesceExpression):
|
||||
case ($expr instanceof AST\GeneralCaseExpression):
|
||||
case ($expr instanceof AST\SimpleCaseExpression):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias('sclr');
|
||||
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
|
||||
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else {
|
||||
// IdentificationVariable
|
||||
|
||||
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
|
||||
break;
|
||||
|
||||
default: // IdentificationVariable
|
||||
$class = $this->_queryComponents[$expr]['metadata'];
|
||||
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
|
||||
$first = true;
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($class->identifier as $identifier) {
|
||||
if ($first) $first = false; else $sql .= ', ';
|
||||
$sql .= $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform);
|
||||
}
|
||||
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
|
||||
$sqlParts[] = $tableAlias . '.' . $columnName;
|
||||
}
|
||||
|
||||
return ' ' . $sql;
|
||||
$sql .= implode(', ', $sqlParts);
|
||||
break;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1376,25 +1360,24 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkGroupByClause($groupByClause)
|
||||
{
|
||||
$sql = '';
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($groupByClause->groupByItems AS $groupByItem) {
|
||||
if (is_string($groupByItem)) {
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
if ( ! is_string($groupByItem)) {
|
||||
$sqlParts[] = $this->walkGroupByItem($groupByItem);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
|
||||
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
|
||||
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
|
||||
$sql .= $this->walkGroupByItem($groupByItem);
|
||||
}
|
||||
} else {
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
}
|
||||
$sql .= $this->walkGroupByItem($groupByItem);
|
||||
|
||||
$sqlParts[] = $this->walkGroupByItem($groupByItem);
|
||||
}
|
||||
}
|
||||
return ' GROUP BY ' . $sql;
|
||||
|
||||
return ' GROUP BY ' . implode(', ', $sqlParts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1416,12 +1399,11 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkDeleteClause(AST\DeleteClause $deleteClause)
|
||||
{
|
||||
$sql = 'DELETE FROM ';
|
||||
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
|
||||
$tableName = $class->getTableName();
|
||||
$sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
|
||||
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
|
||||
|
||||
return $sql;
|
||||
@ -1435,17 +1417,14 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkUpdateClause($updateClause)
|
||||
{
|
||||
$sql = 'UPDATE ';
|
||||
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
|
||||
$tableName = $class->getTableName();
|
||||
$sql = 'UPDATE ' . $class->getQuotedTableName($this->_platform);
|
||||
|
||||
$this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
|
||||
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
|
||||
|
||||
$sql .= ' SET ' . implode(
|
||||
', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)
|
||||
);
|
||||
$sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems));
|
||||
|
||||
return $sql;
|
||||
}
|
||||
@ -1462,15 +1441,20 @@ class SqlWalker implements TreeWalker
|
||||
$this->_useSqlTableAliases = false;
|
||||
|
||||
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
|
||||
|
||||
$newValue = $updateItem->newValue;
|
||||
|
||||
if ($newValue === null) {
|
||||
$sql .= 'NULL';
|
||||
} else if ($newValue instanceof AST\Node) {
|
||||
switch (true) {
|
||||
case ($newValue instanceof AST\Node):
|
||||
$sql .= $newValue->dispatch($this);
|
||||
} else {
|
||||
break;
|
||||
|
||||
case ($newValue === null):
|
||||
$sql .= 'NULL';
|
||||
break;
|
||||
|
||||
default:
|
||||
$sql .= $this->_conn->quote($newValue);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->_useSqlTableAliases = $useTableAliasesBefore;
|
||||
@ -1492,7 +1476,9 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($condSql) {
|
||||
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
|
||||
} else if ($discrSql) {
|
||||
}
|
||||
|
||||
if ($discrSql) {
|
||||
return ' WHERE ' . $discrSql;
|
||||
}
|
||||
|
||||
@ -1509,11 +1495,11 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
return ( ! ($condExpr instanceof AST\ConditionalExpression))
|
||||
? $this->walkConditionalTerm($condExpr)
|
||||
: implode(
|
||||
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
|
||||
);
|
||||
if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
|
||||
return $this->walkConditionalTerm($condExpr);
|
||||
}
|
||||
|
||||
return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1526,11 +1512,11 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalTerm
|
||||
// if only one ConditionalFactor is defined
|
||||
return ( ! ($condTerm instanceof AST\ConditionalTerm))
|
||||
? $this->walkConditionalFactor($condTerm)
|
||||
: implode(
|
||||
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
|
||||
);
|
||||
if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
|
||||
return $this->walkConditionalFactor($condTerm);
|
||||
}
|
||||
|
||||
return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1558,7 +1544,9 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
if ($primary->isSimpleConditionalExpression()) {
|
||||
return $primary->simpleConditionalExpression->dispatch($this);
|
||||
} else if ($primary->isConditionalExpression()) {
|
||||
}
|
||||
|
||||
if ($primary->isConditionalExpression()) {
|
||||
$condExpr = $primary->conditionalExpression;
|
||||
|
||||
return '(' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
@ -1629,12 +1617,11 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' AND ';
|
||||
$first = true;
|
||||
|
||||
foreach ($targetClass->identifier as $idField) {
|
||||
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
$sql .= $targetTableAlias . '.'
|
||||
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
|
||||
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
|
||||
}
|
||||
} else { // many-to-many
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
@ -1679,12 +1666,11 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' AND ';
|
||||
$first = true;
|
||||
|
||||
foreach ($targetClass->identifier as $idField) {
|
||||
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
$sql .= $targetTableAlias . '.'
|
||||
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
|
||||
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1740,11 +1726,9 @@ class SqlWalker implements TreeWalker
|
||||
$sql = $this->walkPathExpression($inExpr->pathExpression)
|
||||
. ($inExpr->not ? ' NOT' : '') . ' IN (';
|
||||
|
||||
if ($inExpr->subselect) {
|
||||
$sql .= $this->walkSubselect($inExpr->subselect);
|
||||
} else {
|
||||
$sql .= implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
|
||||
}
|
||||
$sql .= ($inExpr->subselect)
|
||||
? $this->walkSubselect($inExpr->subselect)
|
||||
: implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
|
||||
|
||||
$sql .= ')';
|
||||
|
||||
@ -1819,9 +1803,9 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkInParameter($inParam)
|
||||
{
|
||||
return $inParam instanceof AST\InputParameter ?
|
||||
$this->walkInputParameter($inParam) :
|
||||
$this->walkLiteral($inParam);
|
||||
return $inParam instanceof AST\InputParameter
|
||||
? $this->walkInputParameter($inParam)
|
||||
: $this->walkLiteral($inParam);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1914,23 +1898,19 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkComparisonExpression($compExpr)
|
||||
{
|
||||
$sql = '';
|
||||
$leftExpr = $compExpr->leftExpression;
|
||||
$rightExpr = $compExpr->rightExpression;
|
||||
$sql = '';
|
||||
|
||||
if ($leftExpr instanceof AST\Node) {
|
||||
$sql .= $leftExpr->dispatch($this);
|
||||
} else {
|
||||
$sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
|
||||
}
|
||||
$sql .= ($leftExpr instanceof AST\Node)
|
||||
? $leftExpr->dispatch($this)
|
||||
: (is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr));
|
||||
|
||||
$sql .= ' ' . $compExpr->operator . ' ';
|
||||
|
||||
if ($rightExpr instanceof AST\Node) {
|
||||
$sql .= $rightExpr->dispatch($this);
|
||||
} else {
|
||||
$sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
|
||||
}
|
||||
$sql .= ($rightExpr instanceof AST\Node)
|
||||
? $rightExpr->dispatch($this)
|
||||
: (is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr));
|
||||
|
||||
return $sql;
|
||||
}
|
||||
@ -1969,11 +1949,11 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
|
||||
{
|
||||
return ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression))
|
||||
? $this->walkArithmeticTerm($simpleArithmeticExpr)
|
||||
: implode(
|
||||
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
|
||||
);
|
||||
if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
|
||||
return $this->walkArithmeticTerm($simpleArithmeticExpr);
|
||||
}
|
||||
|
||||
return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1985,22 +1965,18 @@ class SqlWalker implements TreeWalker
|
||||
public function walkArithmeticTerm($term)
|
||||
{
|
||||
if (is_string($term)) {
|
||||
if (isset($this->_queryComponents[$term])) {
|
||||
$columnName = $this->_queryComponents[$term]['token']['value'];
|
||||
|
||||
return $this->_scalarResultAliasMap[$columnName];
|
||||
}
|
||||
|
||||
return $term;
|
||||
return (isset($this->_queryComponents[$term]))
|
||||
? $this->_scalarResultAliasMap[$this->_queryComponents[$term]['token']['value']]
|
||||
: $term;
|
||||
}
|
||||
|
||||
// Phase 2 AST optimization: Skip processment of ArithmeticTerm
|
||||
// if only one ArithmeticFactor is defined
|
||||
return ( ! ($term instanceof AST\ArithmeticTerm))
|
||||
? $this->walkArithmeticFactor($term)
|
||||
: implode(
|
||||
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
|
||||
);
|
||||
if ( ! ($term instanceof AST\ArithmeticTerm)) {
|
||||
return $this->walkArithmeticFactor($term);
|
||||
}
|
||||
|
||||
return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2017,10 +1993,13 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
// Phase 2 AST optimization: Skip processment of ArithmeticFactor
|
||||
// if only one ArithmeticPrimary is defined
|
||||
return ( ! ($factor instanceof AST\ArithmeticFactor))
|
||||
? $this->walkArithmeticPrimary($factor)
|
||||
: ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''))
|
||||
. $this->walkArithmeticPrimary($factor->arithmeticPrimary);
|
||||
if ( ! ($factor instanceof AST\ArithmeticFactor)) {
|
||||
return $this->walkArithmeticPrimary($factor);
|
||||
}
|
||||
|
||||
$sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
|
||||
|
||||
return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2033,7 +2012,9 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
if ($primary instanceof AST\SimpleArithmeticExpression) {
|
||||
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
|
||||
} else if ($primary instanceof AST\Node) {
|
||||
}
|
||||
|
||||
if ($primary instanceof AST\Node) {
|
||||
return $primary->dispatch($this);
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,9 @@ class XmlExporter extends AbstractExporter
|
||||
if (isset($field['columnName'])) {
|
||||
$idXml->addAttribute('column', $field['columnName']);
|
||||
}
|
||||
if (isset($field['associationKey']) && $field['associationKey']) {
|
||||
$idXml->addAttribute('association-key', 'true');
|
||||
}
|
||||
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
|
||||
$generatorXml = $idXml->addChild('generator');
|
||||
$generatorXml->addAttribute('strategy', $idGeneratorType);
|
||||
|
@ -254,11 +254,17 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* 4) All collection updates
|
||||
* 5) All entity deletions
|
||||
*
|
||||
* @param object $entity
|
||||
* @return void
|
||||
*/
|
||||
public function commit()
|
||||
public function commit($entity = null)
|
||||
{
|
||||
// Compute changes done since last commit.
|
||||
if ($entity === null) {
|
||||
$this->computeChangeSets();
|
||||
} else {
|
||||
$this->computeSingleEntityChangeSet($entity);
|
||||
}
|
||||
|
||||
if ( ! ($this->entityInsertions ||
|
||||
$this->entityDeletions ||
|
||||
@ -334,6 +340,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$coll->takeSnapshot();
|
||||
}
|
||||
|
||||
// Raise postFlush
|
||||
if ($this->evm->hasListeners(Events::postFlush)) {
|
||||
$this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em));
|
||||
}
|
||||
|
||||
// Clear up
|
||||
$this->entityInsertions =
|
||||
$this->entityUpdates =
|
||||
@ -347,6 +358,61 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$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.
|
||||
*/
|
||||
@ -526,10 +592,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
public function computeChangeSets()
|
||||
{
|
||||
// Compute changes for INSERTed entities first. This must always happen.
|
||||
foreach ($this->entityInsertions as $entity) {
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
$this->computeChangeSet($class, $entity);
|
||||
}
|
||||
$this->computeScheduleInsertsChangeSets();
|
||||
|
||||
// Compute changes for other MANAGED entities. Change tracking policies take effect here.
|
||||
foreach ($this->identityMap as $className => $entities) {
|
||||
@ -1991,6 +2054,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||
|
||||
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) &&
|
||||
! $this->eagerLoadingEntities[$class->rootEntityName]) {
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName]);
|
||||
}
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
||||
foreach ($class->associationMappings as $field => $assoc) {
|
||||
|
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
@ -1 +1 @@
|
||||
Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2
|
||||
Subproject commit b2fd909b4b5476df01744c9d34c7a23723a687b6
|
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
@ -1 +1 @@
|
||||
Subproject commit f91395b6f469b5076f52fefd64574c443b076485
|
||||
Subproject commit 537de7ea6a34edbcc40bc6ca92e0a3f816b59330
|
@ -9,6 +9,7 @@ class DDC117Article
|
||||
{
|
||||
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @Column */
|
||||
private $title;
|
||||
|
||||
|
@ -1030,4 +1030,173 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers 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->assertEquals(1, count($metadatas['CmsGroups']->associationMappings));
|
||||
$this->assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);
|
||||
|
@ -20,7 +20,9 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
if ($this->_em) {
|
||||
$this->_em->getConfiguration()->setEntityNamespaces(array());
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
|
95
tests/Doctrine/Tests/ORM/Functional/PostFlushEventTest.php
Normal file
95
tests/Doctrine/Tests/ORM/Functional/PostFlushEventTest.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$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))
|
||||
->method('doFetch')
|
||||
->with($this->isType('string'))
|
||||
@ -135,7 +135,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->will($this->returnValue($sqlExecMock));
|
||||
|
||||
$cache = $this->getMock('Doctrine\Common\Cache\CacheProvider',
|
||||
array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush'));
|
||||
array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush', 'doGetStats'));
|
||||
$cache->expects($this->once())
|
||||
->method('doFetch')
|
||||
->with($this->isType('string'))
|
||||
|
@ -90,10 +90,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testUseResultCache()
|
||||
{
|
||||
$cache = new \Doctrine\Common\Cache\ArrayCache();
|
||||
$this->_em->getConfiguration()->setResultCacheImpl($cache);
|
||||
|
||||
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
|
||||
$query->useResultCache(true);
|
||||
$query->setResultCacheDriver($cache);
|
||||
$query->setResultCacheId('testing_result_cache_id');
|
||||
$users = $query->getResult();
|
||||
|
||||
@ -108,11 +108,11 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testUseResultCacheParams()
|
||||
{
|
||||
$cache = new \Doctrine\Common\Cache\ArrayCache();
|
||||
$this->_em->getConfiguration()->setResultCacheImpl($cache);
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1');
|
||||
$query->setParameter(1, 1);
|
||||
$query->setResultCacheDriver($cache);
|
||||
$query->useResultCache(true);
|
||||
$query->getResult();
|
||||
|
||||
@ -149,10 +149,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <type> $query
|
||||
* @param string $query
|
||||
* @depends testNativeQueryResultCaching
|
||||
*/
|
||||
public function testResultCacheDependsOnQueryHints($query)
|
||||
public function testResultCacheNotDependsOnQueryHints($query)
|
||||
{
|
||||
$cache = $query->getResultCacheDriver();
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
@ -160,7 +160,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$query->setHint('foo', 'bar');
|
||||
$query->getResult();
|
||||
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
$this->assertEquals($cacheCount, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,7 +182,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
* @param <type> $query
|
||||
* @depends testNativeQueryResultCaching
|
||||
*/
|
||||
public function testResultCacheDependsOnHydrationMode($query)
|
||||
public function testResultCacheNotDependsOnHydrationMode($query)
|
||||
{
|
||||
$cache = $query->getResultCacheDriver();
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
@ -190,7 +190,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
|
||||
$query->getArrayResult();
|
||||
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
$this->assertEquals($cacheCount, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,15 +27,16 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$tool = new SchemaTool($this->_em);
|
||||
$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_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_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_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(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_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]);
|
||||
$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 ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id)", $sql[5]);
|
||||
$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_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()
|
||||
|
69
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php
Normal file
69
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1454
|
||||
*/
|
||||
class DDC1454Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454File'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454Picture'),
|
||||
));
|
||||
} catch (\Exception $ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testFailingCase()
|
||||
{
|
||||
$pic = new DDC1454Picture();
|
||||
$this->_em->getUnitOfWork()->getEntityState($pic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1454Picture extends DDC1454File
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("JOINED")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"picture" = "DDC1454Picture"})
|
||||
*/
|
||||
class DDC1454File
|
||||
{
|
||||
/**
|
||||
* @Column(name="file_id", type="integer")
|
||||
* @Id
|
||||
*/
|
||||
public $fileId;
|
||||
|
||||
public function __construct() {
|
||||
$this->fileId = rand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fileId
|
||||
*/
|
||||
public function getFileId() {
|
||||
return $this->fileId;
|
||||
}
|
||||
|
||||
}
|
@ -114,7 +114,7 @@ class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
@ -604,7 +604,24 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
. ' WHERE EXISTS ('
|
||||
. 'SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id'
|
||||
. ')'
|
||||
);
|
||||
}
|
||||
|
||||
public function testExistsExpressionWithSimpleSelectReturningScalar()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
// DQL
|
||||
// The result of this query consists of all employees whose spouses are also employees.
|
||||
'SELECT DISTINCT emp FROM Doctrine\Tests\Models\CMS\CmsEmployee emp
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp
|
||||
WHERE spouseEmp = emp.spouse)',
|
||||
// SQL
|
||||
'SELECT DISTINCT c0_.id AS id0, c0_.name AS name1 FROM cms_employees c0_'
|
||||
. ' WHERE EXISTS ('
|
||||
. 'SELECT 1 AS sclr2 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id'
|
||||
. ')'
|
||||
);
|
||||
}
|
||||
|
||||
@ -1274,6 +1291,16 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
'SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @group DDC-1435
|
||||
*/
|
||||
public function testForeignKeyAsPrimaryKeySubselect()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT s FROM Doctrine\Tests\Models\DDC117\DDC117Article s WHERE EXISTS (SELECT r FROM Doctrine\Tests\Models\DDC117\DDC117Reference r WHERE r.source = s)",
|
||||
"SELECT d0_.article_id AS article_id0, d0_.title AS title1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,10 +70,16 @@ class TestUtil
|
||||
} else {
|
||||
$sm = $realConn->getSchemaManager();
|
||||
|
||||
$tableNames = $sm->listTableNames();
|
||||
/* @var $schema Schema */
|
||||
$schema = $sm->createSchema();
|
||||
$stmts = $schema->toDropSql($realConn->getDatabasePlatform());
|
||||
|
||||
foreach ($tableNames AS $tableName) {
|
||||
$sm->dropTable($tableName);
|
||||
foreach ($stmts AS $stmt) {
|
||||
try {
|
||||
$realConn->exec($stmt);
|
||||
} catch(\Exception $e) {
|
||||
// TODO: Now is this a real good idea?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user