Merge remote-tracking branch 'origin/master' into DDC-551
Conflicts: lib/Doctrine/ORM/Persisters/BasicEntityPersister.php lib/Doctrine/ORM/Query.php
This commit is contained in:
commit
be48821e86
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,3 +7,6 @@ download/
|
||||
lib/api/
|
||||
lib/Doctrine/Common
|
||||
lib/Doctrine/DBAL
|
||||
/.settings/
|
||||
.buildpath
|
||||
.project
|
||||
|
19
.travis.yml
Normal file
19
.travis.yml
Normal file
@ -0,0 +1,19 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
env:
|
||||
- DB=mysql
|
||||
- DB=pgsql
|
||||
- DB=sqlite
|
||||
|
||||
before_script:
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi"
|
||||
- git submodule update --init
|
||||
|
||||
script: phpunit --configuration tests/travis/$DB.travis.xml
|
@ -1,14 +1,18 @@
|
||||
# Doctrine 2 ORM
|
||||
|
||||
Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2)
|
||||
2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
|
||||
|
||||
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
|
||||
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
|
||||
without requiring unnecessary code duplication.
|
||||
|
||||
More resources:
|
||||
## More resources:
|
||||
|
||||
* [Website](http://www.doctrine-project.org)
|
||||
* [Documentation](http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en)
|
||||
* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC)
|
||||
* [Downloads](http://github.com/doctrine/doctrine2/downloads)
|
||||
|
||||
|
@ -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
|
||||
|
@ -16,5 +16,8 @@
|
||||
"ext-pdo": "*",
|
||||
"doctrine/common": "master-dev",
|
||||
"doctrine/dbal": "master-dev"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Doctrine\\ORM": "lib/" }
|
||||
}
|
||||
}
|
||||
|
@ -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).
|
||||
*
|
||||
|
@ -207,6 +207,7 @@ class EntityManager implements ObjectManager
|
||||
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
|
||||
*
|
||||
* @param Closure $func The function to execute transactionally.
|
||||
* @return mixed Returns the non-empty value returned from the closure or true instead
|
||||
*/
|
||||
public function transactional(Closure $func)
|
||||
{
|
||||
@ -333,13 +334,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,6 +225,14 @@ class EntityRepository implements ObjectRepository
|
||||
return $this->_entityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->getEntityName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
53
lib/Doctrine/ORM/Event/PreFlushEventArgs.php
Normal file
53
lib/Doctrine/ORM/Event/PreFlushEventArgs.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Provides event arguments for the preFlush event.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Roman Borschel <roman@code-factory.de>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class PreFlushEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
public function __construct($em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 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())
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,13 @@ final class Events
|
||||
*/
|
||||
const loadClassMetadata = 'loadClassMetadata';
|
||||
|
||||
/**
|
||||
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* but before any changes to managed entites have been calculated. This event is
|
||||
* always raised right after EntityManager#flush() call.
|
||||
*/
|
||||
const preFlush = 'preFlush';
|
||||
|
||||
/**
|
||||
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
|
||||
* after any changes to managed entities have been determined but before any
|
||||
@ -120,6 +127,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.
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\ORMException;
|
||||
|
||||
/**
|
||||
@ -43,44 +44,27 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
$class = $em->getClassMetadata(get_class($entity));
|
||||
$identifier = array();
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idFields = $class->getIdentifierFieldNames();
|
||||
$identifier = array();
|
||||
|
||||
foreach ($idFields as $idField) {
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
if (isset($value)) {
|
||||
|
||||
if ( ! isset($value)) {
|
||||
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
|
||||
}
|
||||
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) {
|
||||
throw ORMException::entityMissingForeignAssignedId($entity, $value);
|
||||
}
|
||||
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
} else {
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$idField = $class->identifier[0];
|
||||
$value = $class->reflFields[$idField]->getValue($entity);
|
||||
if (isset($value)) {
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
|
||||
throw ORMException::entityMissingForeignAssignedId($entity, $value);
|
||||
$value = current($em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
}
|
||||
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
} else {
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
|
||||
}
|
||||
}
|
||||
|
||||
return $identifier;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class IdentityGenerator extends AbstractIdGenerator
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId($this->_seqName);
|
||||
return (int)$em->getConnection()->lastInsertId($this->_seqName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,9 +60,11 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
$this->_nextValue = $conn->fetchColumn($sql);
|
||||
|
||||
$this->_nextValue = (int)$conn->fetchColumn($sql);
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
}
|
||||
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
|
||||
@ -97,6 +99,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$array = unserialize($serialized);
|
||||
|
||||
$this->_sequenceName = $array['sequenceName'];
|
||||
$this->_allocationSize = $array['allocationSize'];
|
||||
}
|
||||
|
@ -50,11 +50,12 @@ class TableGenerator extends AbstractIdGenerator
|
||||
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
|
||||
// Allocate new values
|
||||
$conn = $em->getConnection();
|
||||
if ($conn->getTransactionNestingLevel() == 0) {
|
||||
|
||||
if ($conn->getTransactionNestingLevel() === 0) {
|
||||
// use select for update
|
||||
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
|
||||
$currentLevel = $conn->fetchColumn($sql);
|
||||
|
||||
if ($currentLevel != null) {
|
||||
$this->_nextValue = $currentLevel;
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
@ -74,6 +75,7 @@ class TableGenerator extends AbstractIdGenerator
|
||||
// or do we want to work with table locks exclusively?
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_nextValue++;
|
||||
}
|
||||
}
|
@ -22,7 +22,8 @@ namespace Doctrine\ORM\Internal\Hydration;
|
||||
use PDO,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\EntityManager;
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
@ -31,6 +32,7 @@ use PDO,
|
||||
* @since 2.0
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
*/
|
||||
abstract class AbstractHydrator
|
||||
{
|
||||
@ -72,6 +74,7 @@ abstract class AbstractHydrator
|
||||
*
|
||||
* @param object $stmt
|
||||
* @param object $resultSetMapping
|
||||
*
|
||||
* @return IterableResult
|
||||
*/
|
||||
public function iterate($stmt, $resultSetMapping, array $hints = array())
|
||||
@ -79,7 +82,9 @@ abstract class AbstractHydrator
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
|
||||
$this->prepare();
|
||||
|
||||
return new IterableResult($this);
|
||||
}
|
||||
|
||||
@ -95,9 +100,13 @@ abstract class AbstractHydrator
|
||||
$this->_stmt = $stmt;
|
||||
$this->_rsm = $resultSetMapping;
|
||||
$this->_hints = $hints;
|
||||
$this->_prepare();
|
||||
$result = $this->_hydrateAll();
|
||||
$this->_cleanup();
|
||||
|
||||
$this->prepare();
|
||||
|
||||
$result = $this->hydrateAllData();
|
||||
|
||||
$this->cleanup();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -110,12 +119,17 @@ abstract class AbstractHydrator
|
||||
public function hydrateRow()
|
||||
{
|
||||
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ( ! $row) {
|
||||
$this->_cleanup();
|
||||
$this->cleanup();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$this->_hydrateRow($row, $this->_cache, $result);
|
||||
|
||||
$this->hydrateRowData($row, $this->_cache, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -123,16 +137,17 @@ abstract class AbstractHydrator
|
||||
* Excutes one-time preparation tasks, once each time hydration is started
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _prepare()
|
||||
protected function prepare()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
|
||||
* through {@link hydrateAll} or {@link iterate()}.
|
||||
*/
|
||||
protected function _cleanup()
|
||||
protected function cleanup()
|
||||
{
|
||||
$this->_rsm = null;
|
||||
|
||||
$this->_stmt->closeCursor();
|
||||
$this->_stmt = null;
|
||||
}
|
||||
@ -146,23 +161,24 @@ abstract class AbstractHydrator
|
||||
* @param array $cache The cache to use.
|
||||
* @param mixed $result The result to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
protected function hydrateRowData(array $data, array &$cache, array &$result)
|
||||
{
|
||||
throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
|
||||
throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates all rows from the current statement instance at once.
|
||||
*/
|
||||
abstract protected function _hydrateAll();
|
||||
abstract protected function hydrateAllData();
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
*
|
||||
* Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
|
||||
* Puts the elements of a result row into a new array, grouped by the class
|
||||
* Puts the elements of a result row into a new array, grouped by the dql alias
|
||||
* they belong to. The column names in the result set are mapped to their
|
||||
* field names during this procedure as well as any necessary conversions on
|
||||
* the values applied.
|
||||
* the values applied. Scalar values are kept in a specfic key 'scalars'.
|
||||
*
|
||||
* @param array $data SQL Result Row
|
||||
* @param array &$cache Cache for column to field result information
|
||||
@ -172,40 +188,51 @@ abstract class AbstractHydrator
|
||||
* @return array An array with all the fields (name => value) of the data row,
|
||||
* grouped by their component alias.
|
||||
*/
|
||||
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
|
||||
protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
switch (true) {
|
||||
// NOTE: Most of the times it's a field mapping, so keep it first!!!
|
||||
case (isset($this->_rsm->fieldMappings[$key])):
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
|
||||
case (isset($this->_rsm->scalarMappings[$key])):
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
break;
|
||||
|
||||
case (isset($this->_rsm->metaMappings[$key])):
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$fieldName = $this->_rsm->metaMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]);
|
||||
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
|
||||
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
|
||||
break;
|
||||
|
||||
default:
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -219,10 +246,11 @@ abstract class AbstractHydrator
|
||||
if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// in an inheritance hierachy the same field could be defined several times.
|
||||
// in an inheritance hierarchy the same field could be defined several times.
|
||||
// We overwrite this value so long we dont have a non-null value, that value we keep.
|
||||
// Per definition it cannot be that a field is defined several times and has several values.
|
||||
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
|
||||
@ -241,6 +269,7 @@ abstract class AbstractHydrator
|
||||
|
||||
/**
|
||||
* Processes a row of the result set.
|
||||
*
|
||||
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
|
||||
* simply converts column names to field names and properly converts the
|
||||
* values according to their types. The resulting row has the same number
|
||||
@ -248,52 +277,77 @@ abstract class AbstractHydrator
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $cache
|
||||
*
|
||||
* @return array The processed row.
|
||||
*/
|
||||
protected function _gatherScalarRowData(&$data, &$cache)
|
||||
protected function gatherScalarRowData(&$data, &$cache)
|
||||
{
|
||||
$rowData = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Parse each column name only once. Cache the results.
|
||||
if ( ! isset($cache[$key])) {
|
||||
if (isset($this->_rsm->scalarMappings[$key])) {
|
||||
switch (true) {
|
||||
// NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!!
|
||||
case (isset($this->_rsm->scalarMappings[$key])):
|
||||
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
|
||||
$cache[$key]['isScalar'] = true;
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
break;
|
||||
|
||||
case (isset($this->_rsm->fieldMappings[$key])):
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
|
||||
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else if (!isset($this->_rsm->metaMappings[$key])) {
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
|
||||
case (isset($this->_rsm->metaMappings[$key])):
|
||||
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
|
||||
$cache[$key]['isMetaColumn'] = true;
|
||||
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
break;
|
||||
|
||||
default:
|
||||
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$fieldName = $cache[$key]['fieldName'];
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
switch (true) {
|
||||
case (isset($cache[$key]['isScalar'])):
|
||||
$rowData[$fieldName] = $value;
|
||||
} else if (isset($cache[$key]['isMetaColumn'])) {
|
||||
break;
|
||||
|
||||
case (isset($cache[$key]['isMetaColumn'])):
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
|
||||
break;
|
||||
|
||||
default:
|
||||
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
|
||||
} else {
|
||||
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
protected function registerManaged($class, $entity, $data)
|
||||
/**
|
||||
* Register entity as managed in UnitOfWork.
|
||||
*
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $class
|
||||
* @param object $entity
|
||||
* @param array $data
|
||||
*
|
||||
* @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow
|
||||
*/
|
||||
protected function registerManaged(ClassMetadata $class, $entity, array $data)
|
||||
{
|
||||
if ($class->isIdentifierComposite) {
|
||||
$id = array();
|
||||
@ -311,6 +365,7 @@ abstract class AbstractHydrator
|
||||
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,14 @@ use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
|
||||
* The ArrayHydrator produces a nested array "graph" that is often (not always)
|
||||
* interchangeable with the corresponding object graph for read-only access.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 1.0
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
*
|
||||
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
|
||||
* Example: SELECT u AS user FROM User u
|
||||
* The result should contains an array where each array index is an array: array('user' => [User object])
|
||||
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
|
||||
*/
|
||||
class ArrayHydrator extends AbstractHydrator
|
||||
{
|
||||
@ -38,14 +44,17 @@ class ArrayHydrator extends AbstractHydrator
|
||||
private $_idTemplate = array();
|
||||
private $_resultCounter = 0;
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
|
||||
$this->_identifierMap = array();
|
||||
$this->_resultPointers = array();
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_resultPointers[$dqlAlias] = array();
|
||||
@ -53,30 +62,37 @@ class ArrayHydrator extends AbstractHydrator
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateAllData()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($data, $cache, $result);
|
||||
$this->hydrateRowData($data, $cache, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateRowData(array $row, array &$cache, array &$result)
|
||||
{
|
||||
// 1) Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
|
||||
unset($rowData['scalars']);
|
||||
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
@ -111,11 +127,12 @@ class ArrayHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
|
||||
$relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
|
||||
$oneToOne = false;
|
||||
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
@ -128,20 +145,21 @@ class ArrayHydrator extends AbstractHydrator
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
$element = $data;
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$baseElement[$relationAlias][$element[$field]] = $element;
|
||||
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
|
||||
} else {
|
||||
$baseElement[$relationAlias][] = $element;
|
||||
}
|
||||
|
||||
end($baseElement[$relationAlias]);
|
||||
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
|
||||
key($baseElement[$relationAlias]);
|
||||
|
||||
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]);
|
||||
}
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
} else {
|
||||
$oneToOne = true;
|
||||
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = null;
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
@ -159,14 +177,16 @@ class ArrayHydrator extends AbstractHydrator
|
||||
// It's a root result element
|
||||
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root
|
||||
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
|
||||
|
||||
// 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);
|
||||
$result[] = array($entityKey => null);
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
continue;
|
||||
}
|
||||
@ -174,26 +194,23 @@ class ArrayHydrator extends AbstractHydrator
|
||||
// Check for an existing element
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $rowData[$dqlAlias];
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($entityKey => $element);
|
||||
}
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element[$field] => $element);
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$element[$field]] = $element;
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($element);
|
||||
++$this->_resultCounter;
|
||||
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||
$result[$resultKey] = $element;
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter;
|
||||
$result[] = $element;
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
}
|
||||
end($result);
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
|
||||
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
|
||||
} else {
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$resultKey = $index;
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] =& $result[$index];
|
||||
++$this->_resultCounter;
|
||||
@ -205,8 +222,17 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
if ( ! isset($resultKey) ) {
|
||||
// this only ever happens when no object is fetched (scalar result only)
|
||||
if (isset($this->_rsm->indexByMap['scalars'])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter - 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
$result[$resultKey][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,28 +250,45 @@ class ArrayHydrator extends AbstractHydrator
|
||||
{
|
||||
if ($coll === null) {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
|
||||
return;
|
||||
}
|
||||
if ($index !== false) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||
return;
|
||||
} else {
|
||||
if ($coll) {
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
} else {
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function _getClassMetadata($className)
|
||||
if ($index !== false) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $coll) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve ClassMetadata associated to entity class name.
|
||||
*
|
||||
* @param string $className
|
||||
*
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
*/
|
||||
private function getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
}
|
@ -14,4 +14,13 @@ class HydrationException extends \Doctrine\ORM\ORMException
|
||||
return new self("The parent object of entity result with alias '$alias' was not found."
|
||||
. " The parent alias is '$parentAlias'.");
|
||||
}
|
||||
|
||||
public static function emptyDiscriminatorValue($dqlAlias)
|
||||
{
|
||||
return new self("The DQL alias '" . $dqlAlias . "' contains an entity ".
|
||||
"of an inheritance hierachy with an empty discriminator value. This means " .
|
||||
"that the database contains inconsistent data with an empty " .
|
||||
"discriminator value in a table row."
|
||||
);
|
||||
}
|
||||
}
|
@ -29,9 +29,16 @@ use PDO,
|
||||
/**
|
||||
* The ObjectHydrator constructs an object graph out of an SQL result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
*
|
||||
* @internal Highly performance-sensitive code.
|
||||
*
|
||||
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
|
||||
* Example: SELECT u AS user FROM User u
|
||||
* The result should contains an array where each array index is an array: array('user' => [User object])
|
||||
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
|
||||
*/
|
||||
class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
@ -53,12 +60,14 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
protected function prepare()
|
||||
{
|
||||
$this->_identifierMap =
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
|
||||
$this->_resultCounter = 0;
|
||||
|
||||
if ( ! isset($this->_hints['deferEagerLoad'])) {
|
||||
$this->_hints['deferEagerLoad'] = true;
|
||||
}
|
||||
@ -66,15 +75,17 @@ class ObjectHydrator extends AbstractHydrator
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
$class = $this->_em->getClassMetadata($className);
|
||||
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $class;
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
|
||||
// Remember which associations are "fetch joined", so that we know where to inject
|
||||
// collection stubs or proxies and where not.
|
||||
if (isset($this->_rsm->relationMap[$dqlAlias])) {
|
||||
if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
|
||||
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
|
||||
}
|
||||
@ -82,30 +93,30 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
|
||||
$sourceClass = $this->_getClassMetadata($sourceClassName);
|
||||
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
|
||||
if ($sourceClass->subClasses) {
|
||||
foreach ($sourceClass->subClasses as $sourceSubclassName) {
|
||||
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
|
||||
|
||||
$this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true;
|
||||
|
||||
if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
|
||||
|
||||
// Mark any non-collection opposite sides as fetched, too.
|
||||
if ($assoc['mappedBy']) {
|
||||
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
|
||||
} else {
|
||||
$this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle fetch-joined owning side bi-directional one-to-one associations
|
||||
if ($assoc['inversedBy']) {
|
||||
$class = $this->_ce[$className];
|
||||
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
|
||||
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
|
||||
if ($class->subClasses) {
|
||||
foreach ($class->subClasses as $targetSubclassName) {
|
||||
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,11 +124,12 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _cleanup()
|
||||
protected function cleanup()
|
||||
{
|
||||
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
|
||||
|
||||
parent::_cleanup();
|
||||
parent::cleanup();
|
||||
|
||||
$this->_identifierMap =
|
||||
$this->_initializedCollections =
|
||||
$this->_existingCollections =
|
||||
@ -131,13 +143,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _hydrateAll()
|
||||
protected function hydrateAllData()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
$this->hydrateRowData($row, $cache, $result);
|
||||
}
|
||||
|
||||
// Take snapshots from all newly initialized collections
|
||||
@ -152,35 +164,40 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* Initializes a related collection.
|
||||
*
|
||||
* @param object $entity The entity to which the collection belongs.
|
||||
* @param ClassMetadata $class
|
||||
* @param string $name The name of the field on the entity that holds the collection.
|
||||
* @param string $parentDqlAlias Alias of the parent fetch joining this collection.
|
||||
*/
|
||||
private function _initRelatedCollection($entity, $class, $fieldName)
|
||||
private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
|
||||
$value = $class->reflFields[$fieldName]->getValue($entity);
|
||||
|
||||
if ($value === null) {
|
||||
$value = new ArrayCollection;
|
||||
}
|
||||
|
||||
if ( ! $value instanceof PersistentCollection) {
|
||||
$value = new PersistentCollection(
|
||||
$this->_em,
|
||||
$this->_ce[$relation['targetEntity']],
|
||||
$value
|
||||
$this->_em, $this->_ce[$relation['targetEntity']], $value
|
||||
);
|
||||
$value->setOwner($entity, $relation);
|
||||
|
||||
$class->reflFields[$fieldName]->setValue($entity, $value);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
|
||||
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
|
||||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
|
||||
! $value->isInitialized()) {
|
||||
} else if (
|
||||
isset($this->_hints[Query::HINT_REFRESH]) ||
|
||||
isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
|
||||
! $value->isInitialized()
|
||||
) {
|
||||
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
|
||||
$value->setDirty(false);
|
||||
$value->setInitialized(true);
|
||||
$value->unwrap()->clear();
|
||||
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
} else {
|
||||
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
|
||||
@ -200,17 +217,24 @@ class ObjectHydrator extends AbstractHydrator
|
||||
private function _getEntity(array $data, $dqlAlias)
|
||||
{
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
|
||||
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
|
||||
$discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
|
||||
|
||||
if ($data[$discrColumn] === "") {
|
||||
throw HydrationException::emptyDiscriminatorValue($dqlAlias);
|
||||
}
|
||||
|
||||
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
|
||||
|
||||
unset($data[$discrColumn]);
|
||||
}
|
||||
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
|
||||
$class = $this->_ce[$className];
|
||||
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
$this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
}
|
||||
|
||||
$this->_hints['fetchAlias'] = $dqlAlias;
|
||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
}
|
||||
|
||||
@ -218,6 +242,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
|
||||
$class = $this->_ce[$className];
|
||||
|
||||
/* @var $class ClassMetadata */
|
||||
if ($class->isIdentifierComposite) {
|
||||
$idHash = '';
|
||||
@ -249,6 +274,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
|
||||
return $this->_ce[$className];
|
||||
}
|
||||
|
||||
@ -273,18 +299,20 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param array $cache The cache to use.
|
||||
* @param array $result The result array to fill.
|
||||
*/
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
protected function hydrateRowData(array $row, array &$cache, array &$result)
|
||||
{
|
||||
// Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
// Split the row data into chunks of class data.
|
||||
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
|
||||
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
|
||||
|
||||
// Extract scalar values. They're appended at the end.
|
||||
if (isset($rowData['scalars'])) {
|
||||
$scalars = $rowData['scalars'];
|
||||
|
||||
unset($rowData['scalars']);
|
||||
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
@ -311,7 +339,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
|
||||
$parentObject = $first[key($first)];
|
||||
} else if (isset($this->_resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->_resultPointers[$parentAlias];
|
||||
} else {
|
||||
@ -333,7 +361,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if (isset($this->_initializedCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initializedCollections[$collKey];
|
||||
} else if ( ! isset($this->_existingCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
|
||||
}
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
|
||||
@ -352,8 +380,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
$indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||
$reflFieldValue->hydrateSet($indexValue, $element);
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
|
||||
} else {
|
||||
@ -369,10 +396,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, $parentAlias);
|
||||
}
|
||||
} else {
|
||||
// PATH B: Single-valued association
|
||||
@ -383,6 +407,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$reflField->setValue($parentObject, $element);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
|
||||
$targetClass = $this->_ce[$relation['targetEntity']];
|
||||
|
||||
if ($relation['isOwningSide']) {
|
||||
//TODO: Just check hints['fetched'] here?
|
||||
// If there is an inverse mapping on the target class its bidirectional
|
||||
@ -413,14 +438,16 @@ class ObjectHydrator extends AbstractHydrator
|
||||
} else {
|
||||
// PATH C: Its a root result element
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
|
||||
|
||||
// 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);
|
||||
$result[] = array($entityKey => null);
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
continue;
|
||||
}
|
||||
@ -428,35 +455,31 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// check for existing result from the iterations before
|
||||
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($key => $element);
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
} else {
|
||||
$result[$key] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
|
||||
$element = array($entityKey => $element);
|
||||
}
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateSet($key, $element);
|
||||
$this->_hints['collection']->hydrateSet($resultKey, $element);
|
||||
}
|
||||
|
||||
$result[$resultKey] = $element;
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array(0 => $element);
|
||||
}
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateAdd($element);
|
||||
}
|
||||
|
||||
$result[] = $element;
|
||||
}
|
||||
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
|
||||
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
|
||||
@ -464,6 +487,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// Update result pointer
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$this->_resultPointers[$dqlAlias] = $result[$index];
|
||||
$resultKey = $index;
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] = $result[$index];
|
||||
++$this->_resultCounter;
|
||||
@ -474,8 +498,16 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
// Append scalar values to mixed result sets
|
||||
if (isset($scalars)) {
|
||||
if ( ! isset($resultKey) ) {
|
||||
if (isset($this->_rsm->indexByMap['scalars'])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter - 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($scalars as $name => $value) {
|
||||
$result[$this->_resultCounter - 1][$name] = $value;
|
||||
$result[$resultKey][$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,25 +26,32 @@ use Doctrine\DBAL\Connection;
|
||||
* The created result is almost the same as a regular SQL result set, except
|
||||
* that column names are mapped to field names and data type conversions take place.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class ScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateAllData()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
$this->hydrateRowData($data, $cache, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
protected function _hydrateRow(array $data, array &$cache, array &$result)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateRowData(array $data, array &$cache, array &$result)
|
||||
{
|
||||
$result[] = $this->_gatherScalarRowData($data, $cache);
|
||||
$result[] = $this->gatherScalarRowData($data, $cache);
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use \PDO;
|
||||
@ -32,15 +31,21 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $declaringClasses = array();
|
||||
|
||||
protected function _hydrateAll()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateAllData()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
$this->hydrateRowData($row, $cache, $result);
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->triggerEagerLoads();
|
||||
@ -48,79 +53,73 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function _prepare()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
if (count($this->_rsm->aliasMap) == 1) {
|
||||
if (count($this->_rsm->aliasMap) !== 1) {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.");
|
||||
}
|
||||
|
||||
if ($this->_rsm->scalarMappings) {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
|
||||
}
|
||||
|
||||
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
|
||||
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
|
||||
// We only need to add declaring classes if we have inheritance.
|
||||
if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->_rsm->declaringClasses AS $column => $class) {
|
||||
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
|
||||
}
|
||||
if ($this->_rsm->scalarMappings) {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateRowData(array $sqlResult, array &$cache, array &$result)
|
||||
{
|
||||
$data = array();
|
||||
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
|
||||
if (!isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
|
||||
$cache[$column]['field'] = true;
|
||||
} else {
|
||||
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$column]['field'])) {
|
||||
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
$data[$cache[$column]['name']] = $value;
|
||||
}
|
||||
$entityName = $this->class->name;
|
||||
} else {
|
||||
$data = array();
|
||||
|
||||
// We need to find the correct entity class name if we have inheritance in resultset
|
||||
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
|
||||
|
||||
if ($sqlResult[$discrColumnName] === '') {
|
||||
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
|
||||
}
|
||||
|
||||
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
|
||||
|
||||
unset($sqlResult[$discrColumnName]);
|
||||
}
|
||||
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
// Hydrate column information if not yet present
|
||||
if ( ! isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$field = $this->_rsm->fieldMappings[$column];
|
||||
$class = $this->declaringClasses[$column];
|
||||
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
|
||||
$cache[$column]['name'] = $field;
|
||||
$cache[$column]['class'] = $class;
|
||||
}
|
||||
} else if (isset($this->_rsm->relationMap[$column])) {
|
||||
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
|
||||
$cache[$column]['name'] = $field;
|
||||
}
|
||||
} else {
|
||||
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
|
||||
}
|
||||
if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($cache[$column]['class'])) {
|
||||
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
$cache[$column] = $info;
|
||||
}
|
||||
|
||||
// the second and part is to prevent overwrites in case of multiple
|
||||
// inheritance classes using the same property name (See AbstractHydrator)
|
||||
// Convert field to a valid PHP value
|
||||
if (isset($cache[$column]['field'])) {
|
||||
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
|
||||
$value = $type->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
|
||||
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
|
||||
if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) {
|
||||
$data[$cache[$column]['name']] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
|
||||
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
@ -128,4 +127,52 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
|
||||
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve column information form ResultSetMapping.
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param string $column
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function hydrateColumnInfo($entityName, $column)
|
||||
{
|
||||
switch (true) {
|
||||
case (isset($this->_rsm->fieldMappings[$column])):
|
||||
$class = isset($this->declaringClasses[$column])
|
||||
? $this->declaringClasses[$column]
|
||||
: $this->class;
|
||||
|
||||
// If class is not part of the inheritance, ignore
|
||||
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array(
|
||||
'class' => $class,
|
||||
'name' => $this->_rsm->fieldMappings[$column],
|
||||
'field' => true,
|
||||
);
|
||||
|
||||
case (isset($this->_rsm->relationMap[$column])):
|
||||
$class = isset($this->_rsm->relationMap[$column])
|
||||
? $this->_rsm->relationMap[$column]
|
||||
: $this->class;
|
||||
|
||||
// If class is not self referencing, ignore
|
||||
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Decide what to do with associations. It seems original code is incomplete.
|
||||
// One solution is to load the association, but it might require extra efforts.
|
||||
return array('name' => $column);
|
||||
|
||||
default:
|
||||
return array(
|
||||
'name' => $this->_rsm->metaMappings[$column]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,30 +19,37 @@
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Connection,
|
||||
Doctrine\ORM\NoResultException,
|
||||
Doctrine\ORM\NonUniqueResultException;
|
||||
|
||||
/**
|
||||
* Hydrator that hydrates a single scalar value from the result set.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class SingleScalarHydrator extends AbstractHydrator
|
||||
{
|
||||
/** @override */
|
||||
protected function _hydrateAll()
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hydrateAllData()
|
||||
{
|
||||
$cache = array();
|
||||
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$num = count($result);
|
||||
$data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$numRows = count($data);
|
||||
|
||||
if ($num == 0) {
|
||||
throw new \Doctrine\ORM\NoResultException;
|
||||
} else if ($num > 1 || count($result[key($result)]) > 1) {
|
||||
throw new \Doctrine\ORM\NonUniqueResultException;
|
||||
if ($numRows === 0) {
|
||||
throw new NoResultException();
|
||||
}
|
||||
|
||||
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
|
||||
if ($numRows > 1 || count($data[key($data)]) > 1) {
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
|
||||
$cache = array();
|
||||
$result = $this->gatherScalarRowData($data[key($data)], $cache);
|
||||
|
||||
return array_shift($result);
|
||||
}
|
||||
|
@ -313,6 +313,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$class->containsForeignIdentifier = true;
|
||||
}
|
||||
|
||||
if ($parent && !empty ($parent->namedQueries)) {
|
||||
$this->addInheritedNamedQueries($class, $parent);
|
||||
}
|
||||
|
||||
$class->setParentClasses($visited);
|
||||
|
||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||
@ -429,6 +433,25 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inherited named queries to the subclass mapping.
|
||||
*
|
||||
* @since 2.2
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
|
||||
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
|
||||
*/
|
||||
private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->namedQueries as $name => $query) {
|
||||
if (!isset ($subClass->namedQueries[$name])) {
|
||||
$subClass->addNamedQuery(array(
|
||||
'name' => $query['name'],
|
||||
'query' => $query['query']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||
* most appropriate for the targeted database platform.
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
@ -136,10 +137,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Identifies a many-to-one association.
|
||||
*/
|
||||
const MANY_TO_ONE = 2;
|
||||
/**
|
||||
* Combined bitmask for to-one (single-valued) associations.
|
||||
*/
|
||||
const TO_ONE = 3;
|
||||
/**
|
||||
* Identifies a one-to-many association.
|
||||
*/
|
||||
@ -148,6 +145,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
* Identifies a many-to-many association.
|
||||
*/
|
||||
const MANY_TO_MANY = 8;
|
||||
/**
|
||||
* Combined bitmask for to-one (single-valued) associations.
|
||||
*/
|
||||
const TO_ONE = 3;
|
||||
/**
|
||||
* Combined bitmask for to-many (collection-valued) associations.
|
||||
*/
|
||||
@ -318,7 +319,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
|
||||
@ -685,7 +686,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
if ( ! isset($this->namedQueries[$queryName])) {
|
||||
throw MappingException::queryNotFound($this->name, $queryName);
|
||||
}
|
||||
return $this->namedQueries[$queryName];
|
||||
return $this->namedQueries[$queryName]['dql'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -746,6 +747,14 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->isIdentifierComposite = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
|
||||
if (isset($mapping['id']) && $mapping['id'] === true) {
|
||||
throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
|
||||
}
|
||||
|
||||
$mapping['requireSQLConversion'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -783,6 +792,13 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
|
||||
}
|
||||
|
||||
if ( ($mapping['type'] & (self::MANY_TO_ONE|self::MANY_TO_MANY)) > 0 &&
|
||||
isset($mapping['orphanRemoval']) &&
|
||||
$mapping['orphanRemoval'] == true) {
|
||||
|
||||
throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
|
||||
}
|
||||
|
||||
// Complete id mapping
|
||||
if (isset($mapping['id']) && $mapping['id'] === true) {
|
||||
if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
|
||||
@ -1111,23 +1127,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']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1448,8 +1464,15 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
if (isset($this->namedQueries[$queryMapping['name']])) {
|
||||
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
|
||||
}
|
||||
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
|
||||
$this->namedQueries[$queryMapping['name']] = $query;
|
||||
|
||||
$name = $queryMapping['name'];
|
||||
$query = $queryMapping['query'];
|
||||
$dql = str_replace('__CLASS__', $this->name, $query);
|
||||
$this->namedQueries[$name] = array(
|
||||
'name' => $name,
|
||||
'query' => $query,
|
||||
'dql' => $dql
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1504,14 +1527,16 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Stores the association mapping.
|
||||
*
|
||||
* @param AssociationMapping $assocMapping
|
||||
* @param array $assocMapping
|
||||
*/
|
||||
protected function _storeAssociationMapping(array $assocMapping)
|
||||
{
|
||||
$sourceFieldName = $assocMapping['fieldName'];
|
||||
|
||||
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
|
||||
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
|
||||
}
|
||||
|
||||
$this->associationMappings[$sourceFieldName] = $assocMapping;
|
||||
}
|
||||
|
||||
@ -1904,6 +1929,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 +1975,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'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1939,4 +2002,22 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
{
|
||||
return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @return bool
|
||||
*/
|
||||
public function isAssociationInverseSide($fieldName)
|
||||
{
|
||||
return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @return string
|
||||
*/
|
||||
public function getAssociationMappedByTargetField($fieldName)
|
||||
{
|
||||
return $this->associationMappings[$fieldName]['mappedBy'];
|
||||
}
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ class AnnotationDriver implements Driver
|
||||
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
|
||||
$mapping['cascade'] = $oneToOneAnnot->cascade;
|
||||
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch);
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
|
||||
$metadata->mapOneToOne($mapping);
|
||||
} else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
|
||||
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
|
||||
@ -339,7 +339,7 @@ class AnnotationDriver implements Driver
|
||||
$mapping['cascade'] = $oneToManyAnnot->cascade;
|
||||
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
|
||||
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
@ -355,7 +355,7 @@ class AnnotationDriver implements Driver
|
||||
$mapping['cascade'] = $manyToOneAnnot->cascade;
|
||||
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
|
||||
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch);
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
|
||||
$metadata->mapManyToOne($mapping);
|
||||
} else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
|
||||
$joinTable = array();
|
||||
@ -395,7 +395,7 @@ class AnnotationDriver implements Driver
|
||||
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
|
||||
$mapping['cascade'] = $manyToManyAnnot->cascade;
|
||||
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
|
||||
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
|
||||
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
|
||||
|
||||
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
|
||||
$mapping['orderBy'] = $orderByAnnot->value;
|
||||
@ -446,6 +446,10 @@ class AnnotationDriver implements Driver
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
|
||||
}
|
||||
|
||||
if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) {
|
||||
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,6 +540,22 @@ class AnnotationDriver implements Driver
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the fetch mode.
|
||||
*
|
||||
* @param string $className The class name
|
||||
* @param string $fetchMode The fetch mode
|
||||
* @return integer The fetch mode as defined in ClassMetadata
|
||||
* @throws MappingException If the fetch mode is not valid
|
||||
*/
|
||||
private function getFetchMode($className, $fetchMode)
|
||||
{
|
||||
if(!defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
|
||||
throw MappingException::invalidFetchMode($className, $fetchMode);
|
||||
}
|
||||
|
||||
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
|
||||
}
|
||||
/**
|
||||
* Factory method for the Annotation Driver
|
||||
*
|
||||
|
@ -387,3 +387,9 @@ final class PostRemove implements Annotation {}
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostLoad implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreFlush implements Annotation {}
|
||||
|
@ -166,9 +166,12 @@ class XmlDriver extends AbstractFileDriver
|
||||
foreach ($xmlRoot->field as $fieldMapping) {
|
||||
$mapping = array(
|
||||
'fieldName' => (string)$fieldMapping['name'],
|
||||
'type' => (string)$fieldMapping['type']
|
||||
);
|
||||
|
||||
if (isset($fieldMapping['type'])) {
|
||||
$mapping['type'] = (string)$fieldMapping['type'];
|
||||
}
|
||||
|
||||
if (isset($fieldMapping['column'])) {
|
||||
$mapping['columnName'] = (string)$fieldMapping['column'];
|
||||
}
|
||||
@ -219,10 +222,13 @@ class XmlDriver extends AbstractFileDriver
|
||||
|
||||
$mapping = array(
|
||||
'id' => true,
|
||||
'fieldName' => (string)$idElement['name'],
|
||||
'type' => (string)$idElement['type']
|
||||
'fieldName' => (string)$idElement['name']
|
||||
);
|
||||
|
||||
if (isset($idElement['type'])) {
|
||||
$mapping['type'] = (string)$idElement['type'];
|
||||
}
|
||||
|
||||
if (isset($idElement['column'])) {
|
||||
$mapping['columnName'] = (string)$idElement['column'];
|
||||
}
|
||||
@ -327,6 +333,8 @@ class XmlDriver extends AbstractFileDriver
|
||||
|
||||
if (isset($oneToManyElement['index-by'])) {
|
||||
$mapping['indexBy'] = (string)$oneToManyElement['index-by'];
|
||||
} else if (isset($oneToManyElement->{'index-by'})) {
|
||||
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
|
||||
}
|
||||
|
||||
$metadata->mapOneToMany($mapping);
|
||||
@ -369,10 +377,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
|
||||
}
|
||||
|
||||
if (isset($manyToOneElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToOneElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
$metadata->mapManyToOne($mapping);
|
||||
}
|
||||
}
|
||||
@ -420,10 +424,6 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement->{'orphan-removal'})) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToManyElement->{'orphan-removal'};
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement->{'order-by'})) {
|
||||
$orderBy = array();
|
||||
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} AS $orderByField) {
|
||||
@ -432,8 +432,10 @@ class XmlDriver extends AbstractFileDriver
|
||||
$mapping['orderBy'] = $orderBy;
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement->{'index-by'})) {
|
||||
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
|
||||
if (isset($manyToManyElement['index-by'])) {
|
||||
$mapping['indexBy'] = (string)$manyToManyElement['index-by'];
|
||||
} else if (isset($manyToManyElement->{'index-by'})) {
|
||||
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
|
||||
}
|
||||
|
||||
$metadata->mapManyToMany($mapping);
|
||||
|
@ -165,16 +165,15 @@ class YamlDriver extends AbstractFileDriver
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($idElement['type'])) {
|
||||
throw MappingException::propertyTypeIsRequired($className, $name);
|
||||
}
|
||||
|
||||
$mapping = array(
|
||||
'id' => true,
|
||||
'fieldName' => $name,
|
||||
'type' => $idElement['type']
|
||||
'fieldName' => $name
|
||||
);
|
||||
|
||||
if (isset($idElement['type'])) {
|
||||
$mapping['type'] = $idElement['type'];
|
||||
}
|
||||
|
||||
if (isset($idElement['column'])) {
|
||||
$mapping['columnName'] = $idElement['column'];
|
||||
}
|
||||
@ -201,19 +200,21 @@ class YamlDriver extends AbstractFileDriver
|
||||
// Evaluate fields
|
||||
if (isset($element['fields'])) {
|
||||
foreach ($element['fields'] as $name => $fieldMapping) {
|
||||
if (!isset($fieldMapping['type'])) {
|
||||
throw MappingException::propertyTypeIsRequired($className, $name);
|
||||
}
|
||||
|
||||
$mapping = array(
|
||||
'fieldName' => $name
|
||||
);
|
||||
|
||||
if (isset($fieldMapping['type'])) {
|
||||
$e = explode('(', $fieldMapping['type']);
|
||||
$fieldMapping['type'] = $e[0];
|
||||
$mapping['type'] = $fieldMapping['type'];
|
||||
|
||||
if (isset($e[1])) {
|
||||
$fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1);
|
||||
}
|
||||
$mapping = array(
|
||||
'fieldName' => $name,
|
||||
'type' => $fieldMapping['type']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($fieldMapping['id'])) {
|
||||
$mapping['id'] = true;
|
||||
if (isset($fieldMapping['generator']['strategy'])) {
|
||||
@ -378,10 +379,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $manyToOneElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($manyToOneElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToOneElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
$metadata->mapManyToOne($mapping);
|
||||
}
|
||||
}
|
||||
@ -437,10 +434,6 @@ class YamlDriver extends AbstractFileDriver
|
||||
$mapping['cascade'] = $manyToManyElement['cascade'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orphanRemoval'])) {
|
||||
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
|
||||
}
|
||||
|
||||
if (isset($manyToManyElement['orderBy'])) {
|
||||
$mapping['orderBy'] = $manyToManyElement['orderBy'];
|
||||
}
|
||||
|
@ -226,6 +226,11 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
|
||||
}
|
||||
|
||||
public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type)
|
||||
{
|
||||
return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $columnName
|
||||
@ -270,6 +275,12 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
"part of the identifier in '$className#$field'.");
|
||||
}
|
||||
|
||||
public static function illegalOrphanRemoval($className, $field)
|
||||
{
|
||||
return new self("Orphan removal is only allowed on one-to-one and one-to-many ".
|
||||
"associations, but " . $className."#" .$field . " is not.");
|
||||
}
|
||||
|
||||
public static function illegalInverseIdentifierAssocation($className, $field)
|
||||
{
|
||||
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
|
||||
@ -288,7 +299,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."
|
||||
);
|
||||
@ -298,4 +309,9 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
{
|
||||
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
|
||||
}
|
||||
|
||||
public static function invalidFetchMode($className, $annotation)
|
||||
{
|
||||
return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -59,6 +59,15 @@ class ORMException extends Exception
|
||||
return new self("Unrecognized field: $field");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $field
|
||||
*/
|
||||
public static function invalidOrientation($className, $field)
|
||||
{
|
||||
return new self("Invalid order by orientation specified for " . $className . "#" . $field);
|
||||
}
|
||||
|
||||
public static function invalidFlushMode($mode)
|
||||
{
|
||||
return new self("'$mode' is an invalid flush mode.");
|
||||
|
@ -468,7 +468,7 @@ final class PersistentCollection implements Collection
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->count($this) + $this->coll->count();
|
||||
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
@ -567,6 +567,9 @@ final class PersistentCollection implements Collection
|
||||
return;
|
||||
}
|
||||
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
||||
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
|
||||
// hence for event listeners we need the objects in memory.
|
||||
$this->initialize();
|
||||
foreach ($this->coll as $element) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
}
|
||||
@ -672,7 +675,10 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
if ( ! $this->initialized &&
|
||||
! $this->isDirty &&
|
||||
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->slice($this, $offset, $length);
|
||||
|
@ -62,17 +62,21 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
|
||||
|
||||
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
|
||||
$type = Type::getType($class->getTypeOfField($field));
|
||||
$sql = $type->convertToPHPValueSQL($sql, $this->_platform);
|
||||
}
|
||||
|
||||
return $sql . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
||||
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
|
||||
{
|
||||
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
|
||||
$columnAlias = $this->getSQLColumnAlias($joinColumnName);
|
||||
$this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName);
|
||||
|
||||
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
@ -229,6 +229,7 @@ class BasicEntityPersister
|
||||
|
||||
if (isset($insertData[$tableName])) {
|
||||
$paramIndex = 1;
|
||||
|
||||
foreach ($insertData[$tableName] as $column => $value) {
|
||||
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]);
|
||||
}
|
||||
@ -337,10 +338,19 @@ class BasicEntityPersister
|
||||
$set = $params = $types = array();
|
||||
|
||||
foreach ($updateData as $columnName => $value) {
|
||||
$set[] = (isset($this->_class->fieldNames[$columnName]))
|
||||
? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'
|
||||
: $columnName . ' = ?';
|
||||
$column = $columnName;
|
||||
$placeholder = '?';
|
||||
|
||||
if (isset($this->_class->fieldNames[$columnName])) {
|
||||
$column = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform);
|
||||
|
||||
if (isset($this->_class->fieldMappings[$this->_class->fieldNames[$columnName]]['requireSQLConversion'])) {
|
||||
$type = Type::getType($this->_columnTypes[$columnName]);
|
||||
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
|
||||
}
|
||||
}
|
||||
|
||||
$set[] = $column . ' = ' . $placeholder;
|
||||
$params[] = $value;
|
||||
$types[] = $this->_columnTypes[$columnName];
|
||||
}
|
||||
@ -627,13 +637,7 @@ class BasicEntityPersister
|
||||
$hints = array();
|
||||
|
||||
if ($isInverseSingleValued) {
|
||||
$hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true;
|
||||
|
||||
if ($targetClass->subClasses) {
|
||||
foreach ($targetClass->subClasses as $targetSubclassName) {
|
||||
$hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true;
|
||||
}
|
||||
}
|
||||
$hints['fetched']["r"][$assoc['inversedBy']] = true;
|
||||
}
|
||||
|
||||
/* cascade read-only status
|
||||
@ -919,7 +923,6 @@ class BasicEntityPersister
|
||||
* @param array $orderBy
|
||||
* @param string $baseTableAlias
|
||||
* @return string
|
||||
* @todo Rename: _getOrderBySQL
|
||||
*/
|
||||
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
|
||||
{
|
||||
@ -930,6 +933,11 @@ class BasicEntityPersister
|
||||
throw ORMException::unrecognizedField($fieldName);
|
||||
}
|
||||
|
||||
$orientation = strtoupper(trim($orientation));
|
||||
if ($orientation != 'ASC' && $orientation != 'DESC') {
|
||||
throw ORMException::invalidOrientation($this->_class->name, $fieldName);
|
||||
}
|
||||
|
||||
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
|
||||
$this->_getSQLTableAlias($this->_class->fieldMappings[$fieldName]['inherited'])
|
||||
: $baseTableAlias;
|
||||
@ -1008,22 +1016,18 @@ class BasicEntityPersister
|
||||
$columnList .= $assoc2ColumnSQL;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
|
||||
$first = true;
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
$this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']);
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
if ( ! $first) {
|
||||
$this->_selectJoinSql .= ' AND ';
|
||||
}
|
||||
|
||||
$tableAlias = $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias);
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
|
||||
. $tableAlias . '.' . $targetCol . ' ';
|
||||
|
||||
$first = false;
|
||||
}
|
||||
|
||||
@ -1035,6 +1039,7 @@ class BasicEntityPersister
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
|
||||
|
||||
$this->_selectJoinSql .= ' LEFT JOIN';
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON ';
|
||||
|
||||
@ -1044,7 +1049,7 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
|
||||
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
|
||||
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol;
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
@ -1074,8 +1079,7 @@ class BasicEntityPersister
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$resultColumnName = $this->getSQLColumnAlias($srcColumn);
|
||||
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
|
||||
. '.' . $srcColumn . ' AS ' . $resultColumnName;
|
||||
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
|
||||
@ -1139,7 +1143,19 @@ class BasicEntityPersister
|
||||
);
|
||||
} else {
|
||||
$columns = array_unique($columns);
|
||||
$values = array_fill(0, count($columns), '?');
|
||||
|
||||
$values = array();
|
||||
foreach ($columns AS $column) {
|
||||
$placeholder = '?';
|
||||
|
||||
if (isset($this->_columnTypes[$column]) &&
|
||||
isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) {
|
||||
$type = Type::getType($this->_columnTypes[$column]);
|
||||
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
|
||||
}
|
||||
|
||||
$values[] = $placeholder;
|
||||
}
|
||||
|
||||
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
|
||||
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
|
||||
@ -1178,6 +1194,7 @@ class BasicEntityPersister
|
||||
}
|
||||
} else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) {
|
||||
$columns[] = $this->_class->getQuotedColumnName($name, $this->_platform);
|
||||
$this->_columnTypes[$name] = $this->_class->fieldMappings[$name]['type'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1194,13 +1211,17 @@ class BasicEntityPersister
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
|
||||
. '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
$columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]);
|
||||
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
|
||||
|
||||
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
|
||||
$type = Type::getType($class->getTypeOfField($field));
|
||||
$sql = $type->convertToPHPValueSQL($sql, $this->_platform);
|
||||
}
|
||||
|
||||
return $sql . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
||||
@ -1259,10 +1280,10 @@ class BasicEntityPersister
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getLockTablesSql()
|
||||
protected function getLockTablesSql($alias = null)
|
||||
{
|
||||
return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($this->_class->name);
|
||||
. (null !== $alias ? $alias : $this->_getSQLTableAlias($this->_class->name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1283,12 +1304,19 @@ class BasicEntityPersister
|
||||
foreach ($criteria as $field => $value) {
|
||||
$conditionSql .= $conditionSql ? ' AND ' : '';
|
||||
|
||||
$placeholder = '?';
|
||||
|
||||
if (isset($this->_class->columnNames[$field])) {
|
||||
$className = (isset($this->_class->fieldMappings[$field]['inherited']))
|
||||
? $this->_class->fieldMappings[$field]['inherited']
|
||||
: $this->_class->name;
|
||||
|
||||
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform);
|
||||
|
||||
if (isset($this->_class->fieldMappings[$field]['requireSQLConversion'])) {
|
||||
$type = Type::getType($this->_class->getTypeOfField($field));
|
||||
$placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->_platform);
|
||||
}
|
||||
} else if (isset($this->_class->associationMappings[$field])) {
|
||||
if ( ! $this->_class->associationMappings[$field]['isOwningSide']) {
|
||||
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field);
|
||||
@ -1309,7 +1337,7 @@ class BasicEntityPersister
|
||||
throw ORMException::unrecognizedField($field);
|
||||
}
|
||||
|
||||
$conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?');
|
||||
$conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ' . $placeholder);
|
||||
}
|
||||
return $conditionSql;
|
||||
}
|
||||
@ -1509,7 +1537,7 @@ class BasicEntityPersister
|
||||
$alias = $this->_getSQLTableAlias($this->_class->name);
|
||||
|
||||
$sql = 'SELECT 1'
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $alias
|
||||
. $this->getLockTablesSql($alias)
|
||||
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
|
||||
|
||||
$filterSql = $this->generateFilterConditionSQL($this->_class, $alias);
|
||||
@ -1522,6 +1550,39 @@ class BasicEntityPersister
|
||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the appropriate join SQL for the given join column.
|
||||
*
|
||||
* @param array $joinColumns The join columns definition of an association.
|
||||
* @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
|
||||
*/
|
||||
protected function getJoinSQLForJoinColumns($joinColumns)
|
||||
{
|
||||
// if one of the join columns is nullable, return left join
|
||||
foreach($joinColumns as $joinColumn) {
|
||||
if(isset($joinColumn['nullable']) && $joinColumn['nullable']){
|
||||
return 'LEFT JOIN';
|
||||
}
|
||||
}
|
||||
|
||||
return 'INNER JOIN';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an SQL column alias for a column name.
|
||||
*
|
||||
* @param string $columnName
|
||||
* @return string
|
||||
*/
|
||||
public function getSQLColumnAlias($columnName)
|
||||
{
|
||||
// Trim the column alias to the maximum identifier length of the platform.
|
||||
// If the alias is to long, characters are cut off from the beginning.
|
||||
return $this->_platform->getSQLResultCasing(
|
||||
substr($columnName . $this->_sqlAliasCounter++, -$this->_platform->getMaxIdentifierLength())
|
||||
);
|
||||
}
|
||||
|
||||
private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)
|
||||
{
|
||||
$filterSql = '';
|
||||
|
@ -165,11 +165,13 @@ class ProxyFactory
|
||||
{
|
||||
$methods = '';
|
||||
|
||||
$methodNames = array();
|
||||
foreach ($class->reflClass->getMethods() as $method) {
|
||||
/* @var $method ReflectionMethod */
|
||||
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone"))) {
|
||||
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) {
|
||||
continue;
|
||||
}
|
||||
$methodNames[$method->getName()] = true;
|
||||
|
||||
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
|
||||
$methods .= "\n" . ' public function ';
|
||||
@ -210,8 +212,12 @@ class ProxyFactory
|
||||
$methods .= $parameterString . ')';
|
||||
$methods .= "\n" . ' {' . "\n";
|
||||
if ($this->isShortIdentifierGetter($method, $class)) {
|
||||
$identifier = lcfirst(substr($method->getName(), 3));
|
||||
|
||||
$cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : '';
|
||||
|
||||
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
|
||||
$methods .= ' return $this->_identifier["' . lcfirst(substr($method->getName(), 3)) . '"];' . "\n";
|
||||
$methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n";
|
||||
$methods .= ' }' . "\n";
|
||||
}
|
||||
$methods .= ' $this->__load();' . "\n";
|
||||
@ -237,6 +243,7 @@ class ProxyFactory
|
||||
in_array($identifier, $class->identifier, true) &&
|
||||
$class->hasField($identifier) &&
|
||||
(($method->getEndLine() - $method->getStartLine()) <= 4)
|
||||
&& in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string'))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,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();
|
||||
@ -282,9 +285,16 @@ final class Query extends AbstractQuery
|
||||
}
|
||||
}
|
||||
|
||||
if (count($sqlParams) != count($types)) {
|
||||
throw QueryException::parameterTypeMissmatch();
|
||||
}
|
||||
|
||||
if ($sqlParams) {
|
||||
ksort($sqlParams);
|
||||
$sqlParams = array_values($sqlParams);
|
||||
|
||||
ksort($types);
|
||||
$types = array_values($types);
|
||||
}
|
||||
|
||||
return array($sqlParams, $types);
|
||||
@ -526,7 +536,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)
|
||||
|
@ -59,7 +59,7 @@ class Composite extends Base
|
||||
}
|
||||
|
||||
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
|
||||
if (mb_stripos($queryPart, ' OR ') !== false || mb_stripos($queryPart, ' AND ') !== false) {
|
||||
if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) {
|
||||
return $this->_preSeparator . $queryPart . $this->_postSeparator;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -47,6 +47,11 @@ class QueryException extends \Doctrine\ORM\ORMException
|
||||
return new self('[Semantical Error] ' . $message);
|
||||
}
|
||||
|
||||
public static function invalidLockMode()
|
||||
{
|
||||
return new self('Invalid lock mode hint provided.');
|
||||
}
|
||||
|
||||
public static function invalidParameterType($expected, $received)
|
||||
{
|
||||
return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.');
|
||||
@ -72,6 +77,11 @@ class QueryException extends \Doctrine\ORM\ORMException
|
||||
return new self("Invalid parameter: token ".$key." is not defined in the query.");
|
||||
}
|
||||
|
||||
public static function parameterTypeMissmatch()
|
||||
{
|
||||
return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal.");
|
||||
}
|
||||
|
||||
public static function invalidPathExpression($pathExpr)
|
||||
{
|
||||
return new self(
|
||||
|
@ -36,87 +36,79 @@ namespace Doctrine\ORM\Query;
|
||||
class ResultSetMapping
|
||||
{
|
||||
/**
|
||||
* Whether the result is mixed (contains scalar values together with field values).
|
||||
*
|
||||
* @ignore
|
||||
* @var boolean
|
||||
* @var boolean Whether the result is mixed (contains scalar values together with field values).
|
||||
*/
|
||||
public $isMixed = false;
|
||||
|
||||
/**
|
||||
* Maps alias names to class names.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps alias names to class names.
|
||||
*/
|
||||
public $aliasMap = array();
|
||||
|
||||
/**
|
||||
* Maps alias names to related association field names.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps alias names to related association field names.
|
||||
*/
|
||||
public $relationMap = array();
|
||||
|
||||
/**
|
||||
* Maps alias names to parent alias names.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps alias names to parent alias names.
|
||||
*/
|
||||
public $parentAliasMap = array();
|
||||
|
||||
/**
|
||||
* Maps column names in the result set to field names for each class.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps column names in the result set to field names for each class.
|
||||
*/
|
||||
public $fieldMappings = array();
|
||||
|
||||
/**
|
||||
* Maps column names in the result set to the alias/field name to use in the mapped result.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps column names in the result set to the alias/field name to use in the mapped result.
|
||||
*/
|
||||
public $scalarMappings = array();
|
||||
|
||||
/**
|
||||
* Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps entities in the result set to the alias name to use in the mapped result.
|
||||
*/
|
||||
public $entityMappings = array();
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @var array Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
|
||||
*/
|
||||
public $metaMappings = array();
|
||||
|
||||
/**
|
||||
* Maps column names in the result set to the alias they belong to.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps column names in the result set to the alias they belong to.
|
||||
*/
|
||||
public $columnOwnerMap = array();
|
||||
|
||||
/**
|
||||
* List of columns in the result set that are used as discriminator columns.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array List of columns in the result set that are used as discriminator columns.
|
||||
*/
|
||||
public $discriminatorColumns = array();
|
||||
|
||||
/**
|
||||
* Maps alias names to field names that should be used for indexing.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Maps alias names to field names that should be used for indexing.
|
||||
*/
|
||||
public $indexByMap = array();
|
||||
|
||||
/**
|
||||
* Map from column names to class names that declare the field the column is mapped to.
|
||||
*
|
||||
* @ignore
|
||||
* @var array
|
||||
* @var array Map from column names to class names that declare the field the column is mapped to.
|
||||
*/
|
||||
public $declaringClasses = array();
|
||||
|
||||
/**
|
||||
* This is necessary to hydrate derivate foreign keys correctly.
|
||||
*
|
||||
* @var array
|
||||
* @var array This is necessary to hydrate derivate foreign keys correctly.
|
||||
*/
|
||||
public $isIdentifierColumn = array();
|
||||
|
||||
@ -126,11 +118,19 @@ class ResultSetMapping
|
||||
* @param string $class The class name of the entity.
|
||||
* @param string $alias The alias for the class. The alias must be unique among all entity
|
||||
* results or joined entity results within this ResultSetMapping.
|
||||
* @param string $resultAlias The result alias with which the entity result should be
|
||||
* placed in the result structure.
|
||||
*
|
||||
* @todo Rename: addRootEntity
|
||||
*/
|
||||
public function addEntityResult($class, $alias)
|
||||
public function addEntityResult($class, $alias, $resultAlias = null)
|
||||
{
|
||||
$this->aliasMap[$alias] = $class;
|
||||
$this->entityMappings[$alias] = $resultAlias;
|
||||
|
||||
if ($resultAlias !== null) {
|
||||
$this->isMixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,6 +141,7 @@ class ResultSetMapping
|
||||
* @param string $alias The alias of the entity result or joined entity result the discriminator
|
||||
* column should be used for.
|
||||
* @param string $discrColumn The name of the discriminator column in the SQL result set.
|
||||
*
|
||||
* @todo Rename: addDiscriminatorColumn
|
||||
*/
|
||||
public function setDiscriminatorColumn($alias, $discrColumn)
|
||||
@ -157,7 +158,51 @@ class ResultSetMapping
|
||||
*/
|
||||
public function addIndexBy($alias, $fieldName)
|
||||
{
|
||||
$this->indexByMap[$alias] = $fieldName;
|
||||
$found = false;
|
||||
|
||||
foreach ($this->fieldMappings AS $columnName => $columnFieldName) {
|
||||
if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue;
|
||||
|
||||
$this->addIndexByColumn($alias, $columnName);
|
||||
$found = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals
|
||||
if ( ! $found) {
|
||||
$message = sprintf(
|
||||
'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',
|
||||
$alias,
|
||||
$fieldName
|
||||
);
|
||||
|
||||
throw new \LogicException($message);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to index by a scalar result column name
|
||||
*
|
||||
* @param $resultColumnName
|
||||
* @return void
|
||||
*/
|
||||
public function addIndexByScalar($resultColumnName)
|
||||
{
|
||||
$this->indexByMap['scalars'] = $resultColumnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a column to use for indexing an entity or joined entity result by the given alias name.
|
||||
*
|
||||
* @param $alias
|
||||
* @param $resultColumnName
|
||||
* @return void
|
||||
*/
|
||||
public function addIndexByColumn($alias, $resultColumnName)
|
||||
{
|
||||
$this->indexByMap[$alias] = $resultColumnName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,6 +252,7 @@ class ResultSetMapping
|
||||
$this->columnOwnerMap[$columnName] = $alias;
|
||||
// field name => class name of declaring class
|
||||
$this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
|
||||
|
||||
if ( ! $this->isMixed && $this->scalarMappings) {
|
||||
$this->isMixed = true;
|
||||
}
|
||||
@ -238,6 +284,7 @@ class ResultSetMapping
|
||||
public function addScalarResult($columnName, $alias)
|
||||
{
|
||||
$this->scalarMappings[$columnName] = $alias;
|
||||
|
||||
if ( ! $this->isMixed && $this->fieldMappings) {
|
||||
$this->isMixed = true;
|
||||
}
|
||||
@ -397,6 +444,7 @@ class ResultSetMapping
|
||||
{
|
||||
$this->metaMappings[$columnName] = $fieldName;
|
||||
$this->columnOwnerMap[$columnName] = $alias;
|
||||
|
||||
if ($isIdentifierColumn) {
|
||||
$this->isIdentifierColumn[$alias][$columnName] = true;
|
||||
}
|
||||
|
@ -86,20 +86,22 @@ class ResultSetMappingBuilder extends ResultSetMapping
|
||||
if (isset($renamedColumns[$columnName])) {
|
||||
$columnName = $renamedColumns[$columnName];
|
||||
}
|
||||
$columnName = $platform->getSQLResultCasing($columnName);
|
||||
if (isset($this->fieldMappings[$columnName])) {
|
||||
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
|
||||
}
|
||||
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
|
||||
$this->addFieldResult($alias, $columnName, $propertyName);
|
||||
}
|
||||
foreach ($classMetadata->associationMappings AS $associationMapping) {
|
||||
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
foreach ($associationMapping['joinColumns'] AS $joinColumn) {
|
||||
$columnName = $joinColumn['name'];
|
||||
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
|
||||
$renamedColumnName = $platform->getSQLResultCasing($renamedColumnName);
|
||||
if (isset($this->metaMappings[$renamedColumnName])) {
|
||||
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
|
||||
}
|
||||
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName));
|
||||
$this->addMetaResult($alias, $renamedColumnName, $columnName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -313,7 +313,7 @@ class QueryBuilder
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = :user_id')
|
||||
* ->setParameter(':user_id', 1);
|
||||
* ->setParameter('user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
@ -344,8 +344,8 @@ class QueryBuilder
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = :user_id1 OR u.id = :user_id2')
|
||||
* ->setParameters(array(
|
||||
* ':user_id1' => 1,
|
||||
* ':user_id2' => 2
|
||||
* 'user_id1' => 1,
|
||||
* 'user_id2' => 2
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
@ -539,7 +539,7 @@ class QueryBuilder
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->delete('User', 'u')
|
||||
* ->where('u.id = :user_id');
|
||||
* ->setParameter(':user_id', 1);
|
||||
* ->setParameter('user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string $delete The class/type whose instances are subject to the deletion.
|
||||
|
@ -117,7 +117,7 @@ public function <methodName>()
|
||||
* @param <variableType>$<variableName>
|
||||
* @return <entity>
|
||||
*/
|
||||
public function <methodName>(<methodTypeHint>$<variableName>)
|
||||
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
|
||||
{
|
||||
<spaces>$this-><fieldName> = $<variableName>;
|
||||
<spaces>return $this;
|
||||
@ -406,7 +406,7 @@ public function <methodName>()
|
||||
}
|
||||
|
||||
if ($collections) {
|
||||
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate));
|
||||
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate));
|
||||
}
|
||||
|
||||
return '';
|
||||
@ -634,7 +634,8 @@ public function <methodName>()
|
||||
|
||||
foreach ($metadata->associationMappings as $associationMapping) {
|
||||
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
|
||||
$nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null;
|
||||
if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
|
||||
$methods[] = $code;
|
||||
}
|
||||
if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
|
||||
@ -653,6 +654,22 @@ public function <methodName>()
|
||||
return implode("\n\n", $methods);
|
||||
}
|
||||
|
||||
private function _isAssociationIsNullable($associationMapping)
|
||||
{
|
||||
if (isset($associationMapping['joinColumns'])) {
|
||||
$joinColumns = $associationMapping['joinColumns'];
|
||||
} else {
|
||||
//@todo thereis no way to retreive targetEntity metadata
|
||||
$joinColumns = array();
|
||||
}
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
|
||||
{
|
||||
if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
|
||||
@ -707,7 +724,7 @@ public function <methodName>()
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
|
||||
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
|
||||
{
|
||||
if ($type == "add") {
|
||||
$addMethod = explode("\\", $typeHint);
|
||||
@ -737,6 +754,7 @@ public function <methodName>()
|
||||
'<variableName>' => Inflector::camelize($fieldName),
|
||||
'<methodName>' => $methodName,
|
||||
'<fieldName>' => $fieldName,
|
||||
'<variableDefault>' => ($defaultValue !== null ) ? ('='.$defaultValue) : '',
|
||||
'<entity>' => $this->_getClassName($metadata)
|
||||
);
|
||||
|
||||
@ -805,7 +823,12 @@ public function <methodName>()
|
||||
{
|
||||
$lines = array();
|
||||
$lines[] = $this->_spaces . '/**';
|
||||
|
||||
if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
|
||||
$lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
|
||||
} else {
|
||||
$lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity'];
|
||||
}
|
||||
|
||||
if ($this->_generateAnnotations) {
|
||||
$lines[] = $this->_spaces . ' *';
|
||||
|
@ -279,6 +279,9 @@ class XmlExporter extends AbstractExporter
|
||||
if ($associationMapping['isCascadeDetach']) {
|
||||
$cascade[] = 'cascade-detach';
|
||||
}
|
||||
if (count($cascade) === 5) {
|
||||
$cascade = array('cascade-all');
|
||||
}
|
||||
if ($cascade) {
|
||||
$cascadeXml = $associationMappingXml->addChild('cascade');
|
||||
foreach ($cascade as $type) {
|
||||
|
@ -147,6 +147,9 @@ class YamlExporter extends AbstractExporter
|
||||
if ($associationMapping['isCascadeDetach']) {
|
||||
$cascade[] = 'detach';
|
||||
}
|
||||
if (count($cascade) === 5) {
|
||||
$cascade = array('all');
|
||||
}
|
||||
$associationMappingArray = array(
|
||||
'targetEntity' => $associationMapping['targetEntity'],
|
||||
'cascade' => $cascade,
|
||||
|
@ -67,7 +67,9 @@ class SchemaTool
|
||||
/**
|
||||
* Creates the database schema for the given array of ClassMetadata instances.
|
||||
*
|
||||
* @throws ToolsException
|
||||
* @param array $classes
|
||||
* @return void
|
||||
*/
|
||||
public function createSchema(array $classes)
|
||||
{
|
||||
@ -75,7 +77,11 @@ class SchemaTool
|
||||
$conn = $this->_em->getConnection();
|
||||
|
||||
foreach ($createSchemaSql as $sql) {
|
||||
try {
|
||||
$conn->executeQuery($sql);
|
||||
} catch(\Exception $e) {
|
||||
throw ToolsException::schemaToolFailure($sql, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +145,7 @@ class SchemaTool
|
||||
$this->_gatherRelationsSql($class, $table, $schema);
|
||||
|
||||
// Add the discriminator column
|
||||
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table);
|
||||
$this->addDiscriminatorColumnDefinition($class, $table);
|
||||
|
||||
// Aggregate all the information from all classes in the hierarchy
|
||||
foreach ($class->parentClasses as $parentClassName) {
|
||||
@ -171,7 +177,7 @@ class SchemaTool
|
||||
|
||||
// Add the discriminator column only to the root table
|
||||
if ($class->name == $class->rootEntityName) {
|
||||
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table);
|
||||
$this->addDiscriminatorColumnDefinition($class, $table);
|
||||
} else {
|
||||
// Add an ID FK column to child tables
|
||||
/* @var Doctrine\ORM\Mapping\ClassMetadata $class */
|
||||
@ -261,7 +267,7 @@ class SchemaTool
|
||||
* @return array The portable column definition of the discriminator column as required by
|
||||
* the DBAL.
|
||||
*/
|
||||
private function _getDiscriminatorColumnDefinition($class, $table)
|
||||
private function addDiscriminatorColumnDefinition($class, $table)
|
||||
{
|
||||
$discrColumn = $class->discriminatorColumn;
|
||||
|
||||
|
@ -50,7 +50,7 @@ class SchemaValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the internal consistency of mapping files.
|
||||
* Checks the internal consistency of all mapping files.
|
||||
*
|
||||
* There are several checks that can't be done at runtime or are too expensive, which can be verified
|
||||
* with this command. For example:
|
||||
@ -69,11 +69,29 @@ class SchemaValidator
|
||||
$classes = $cmf->getAllMetadata();
|
||||
|
||||
foreach ($classes AS $class) {
|
||||
if ($ce = $this->validateClass($class)) {
|
||||
$errors[$class->name] = $ce;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single class of the current
|
||||
*
|
||||
* @param ClassMetadataInfo $class
|
||||
* @return array
|
||||
*/
|
||||
public function validateClass(ClassMetadataInfo $class)
|
||||
{
|
||||
$ce = array();
|
||||
/* @var $class ClassMetadata */
|
||||
$cmf = $this->em->getMetadataFactory();
|
||||
|
||||
foreach ($class->associationMappings AS $fieldName => $assoc) {
|
||||
if (!$cmf->hasMetadataFor($assoc['targetEntity'])) {
|
||||
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
|
||||
return $ce;
|
||||
}
|
||||
|
||||
if ($assoc['mappedBy'] && $assoc['inversedBy']) {
|
||||
@ -139,59 +157,67 @@ class SchemaValidator
|
||||
}
|
||||
}
|
||||
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
|
||||
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
|
||||
if (!isset($targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
|
||||
if (!isset($targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
|
||||
$ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
|
||||
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
|
||||
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $targetClass->identifier)) {
|
||||
$fieldName = $targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $targetMetadata->identifier)) {
|
||||
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) {
|
||||
if (count($targetMetadata->getIdentifierColumnNames()) != 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 . "'";
|
||||
"have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " .
|
||||
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) .
|
||||
"' are missing.";
|
||||
}
|
||||
|
||||
if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) {
|
||||
if (count($class->getIdentifierColumnNames()) != 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 . "'";
|
||||
"have to contain to ALL identifier columns of the source entity '". $class->name . "', " .
|
||||
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) .
|
||||
"' are missing.";
|
||||
}
|
||||
|
||||
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
foreach ($assoc['joinColumns'] AS $joinColumn) {
|
||||
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
|
||||
if (!isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
|
||||
if (!isset($targetMetadata->fieldNames[$joinColumn['referencedColumnName']])) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
|
||||
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
|
||||
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $targetClass->fieldNames[$joinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $targetClass->identifier)) {
|
||||
$fieldName = $targetMetadata->fieldNames[$joinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $targetMetadata->identifier)) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($class->identifier) != count($assoc['joinColumns'])) {
|
||||
if (count($class->getIdentifierColumnNames()) != count($assoc['joinColumns'])) {
|
||||
$ids = array();
|
||||
foreach ($assoc['joinColumns'] AS $joinColumn) {
|
||||
$ids[] = $joinColumn['name'];
|
||||
}
|
||||
|
||||
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
|
||||
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
|
||||
"have to match to ALL identifier columns of the source entity '". $class->name . "', " .
|
||||
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), $ids)) .
|
||||
"' are missing.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
|
||||
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
|
||||
foreach ($assoc['orderBy'] AS $orderField => $orientation) {
|
||||
if (!$targetClass->hasField($orderField)) {
|
||||
if (!$targetMetadata->hasField($orderField)) {
|
||||
$ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
|
||||
$orderField . " that is not a field on the target entity " . $targetClass->name;
|
||||
$orderField . " that is not a field on the target entity " . $targetMetadata->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,12 +238,7 @@ class SchemaValidator
|
||||
}
|
||||
}
|
||||
|
||||
if ($ce) {
|
||||
$errors[$class->name] = $ce;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
return $ce;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,6 +251,6 @@ class SchemaValidator
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
|
||||
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
return (count($schemaTool->getUpdateSchemaSql($allMetadata, false)) == 0);
|
||||
return (count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,38 @@
|
||||
<?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\Tools;
|
||||
|
||||
use Doctrine\ORM\ORMException;
|
||||
|
||||
/**
|
||||
* Tools related Exceptions
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class ToolsException extends ORMException
|
||||
{
|
||||
public static function schemaToolFailure($sql, \Exception $e)
|
||||
{
|
||||
return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e);
|
||||
}
|
||||
|
||||
public static function couldNotMapDoctrine1Type($type)
|
||||
{
|
||||
return new self("Could not map doctrine 1 type '$type'!");
|
||||
|
File diff suppressed because it is too large
Load Diff
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
@ -1 +1 @@
|
||||
Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2
|
||||
Subproject commit 9c880cf9ae2c14102568520b5ee885b03bda93e4
|
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
@ -1 +1 @@
|
||||
Subproject commit f91395b6f469b5076f52fefd64574c443b076485
|
||||
Subproject commit 537de7ea6a34edbcc40bc6ca92e0a3f816b59330
|
34
tests/Doctrine/Tests/DbalTypes/NegativeToPositiveType.php
Normal file
34
tests/Doctrine/Tests/DbalTypes/NegativeToPositiveType.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\DbalTypes;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
class NegativeToPositiveType extends Type
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'negative_to_positive';
|
||||
}
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration);
|
||||
}
|
||||
|
||||
public function canRequireSQLConversion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
|
||||
{
|
||||
return 'ABS(' . $sqlExpr . ')';
|
||||
}
|
||||
|
||||
public function convertToPHPValueSQL($sqlExpr, $platform)
|
||||
{
|
||||
return '-(' . $sqlExpr . ')';
|
||||
}
|
||||
}
|
29
tests/Doctrine/Tests/DbalTypes/UpperCaseStringType.php
Normal file
29
tests/Doctrine/Tests/DbalTypes/UpperCaseStringType.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\DbalTypes;
|
||||
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
class UpperCaseStringType extends StringType
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'upper_case_string';
|
||||
}
|
||||
|
||||
public function canRequireSQLConversion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
|
||||
{
|
||||
return 'UPPER(' . $sqlExpr . ')';
|
||||
}
|
||||
|
||||
public function convertToPHPValueSQL($sqlExpr, $platform)
|
||||
{
|
||||
return 'LOWER(' . $sqlExpr . ')';
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ class ConnectionMock extends \Doctrine\DBAL\Connection
|
||||
private $_platformMock;
|
||||
private $_lastInsertId = 0;
|
||||
private $_inserts = array();
|
||||
private $_executeUpdates = array();
|
||||
|
||||
public function __construct(array $params, $driver, $config = null, $eventManager = null)
|
||||
{
|
||||
@ -30,11 +31,19 @@ class ConnectionMock extends \Doctrine\DBAL\Connection
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function insert($tableName, array $data)
|
||||
public function insert($tableName, array $data, array $types = array())
|
||||
{
|
||||
$this->_inserts[$tableName][] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function executeUpdate($query, array $params = array(), array $types = array())
|
||||
{
|
||||
$this->_executeUpdates[] = array('query' => $query, 'params' => $params, 'types' => $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@ -84,6 +93,11 @@ class ConnectionMock extends \Doctrine\DBAL\Connection
|
||||
return $this->_inserts;
|
||||
}
|
||||
|
||||
public function getExecuteUpdates()
|
||||
{
|
||||
return $this->_executeUpdates;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->_inserts = array();
|
||||
|
@ -87,4 +87,11 @@ class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform
|
||||
{
|
||||
|
||||
}
|
||||
/**
|
||||
* Gets the SQL Snippet used to declare a BLOB column type.
|
||||
*/
|
||||
public function getBlobTypeDeclarationSQL(array $field)
|
||||
{
|
||||
throw DBALException::notSupported(__METHOD__);
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace Doctrine\Tests\Mocks;
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class HydratorMockStatement implements \Doctrine\DBAL\Driver\Statement
|
||||
class HydratorMockStatement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement
|
||||
{
|
||||
private $_resultSet;
|
||||
|
||||
@ -98,4 +98,14 @@ class HydratorMockStatement implements \Doctrine\DBAL\Driver\Statement
|
||||
public function rowCount()
|
||||
{
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return $this->_resultSet;
|
||||
}
|
||||
|
||||
public function setFetchMode($fetchMode)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
21
tests/Doctrine/Tests/Models/CustomType/CustomTypeChild.php
Normal file
21
tests/Doctrine/Tests/Models/CustomType/CustomTypeChild.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CustomType;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="customtype_children")
|
||||
*/
|
||||
class CustomTypeChild
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="upper_case_string")
|
||||
*/
|
||||
public $lowerCaseString = 'foo';
|
||||
}
|
68
tests/Doctrine/Tests/Models/CustomType/CustomTypeParent.php
Normal file
68
tests/Doctrine/Tests/Models/CustomType/CustomTypeParent.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CustomType;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="customtype_parents")
|
||||
*/
|
||||
class CustomTypeParent
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="negative_to_positive", nullable=true)
|
||||
*/
|
||||
public $customInteger;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeChild", cascade={"persist", "remove"})
|
||||
*/
|
||||
public $child;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeParent", mappedBy="myFriends")
|
||||
*/
|
||||
private $friendsWithMe;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomTypeParent", inversedBy="friendsWithMe")
|
||||
* @JoinTable(
|
||||
* name="customtype_parent_friends",
|
||||
* joinColumns={@JoinColumn(name="customtypeparent_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="friend_customtypeparent_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $myFriends;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function addMyFriend(CustomTypeParent $friend)
|
||||
{
|
||||
$this->getMyFriends()->add($friend);
|
||||
$friend->addFriendWithMe($this);
|
||||
}
|
||||
|
||||
public function getMyFriends()
|
||||
{
|
||||
return $this->myFriends;
|
||||
}
|
||||
|
||||
public function addFriendWithMe(CustomTypeParent $friend)
|
||||
{
|
||||
$this->getFriendsWithMe()->add($friend);
|
||||
}
|
||||
|
||||
public function getFriendsWithMe()
|
||||
{
|
||||
return $this->friendsWithMe;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CustomType;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="customtype_uppercases")
|
||||
*/
|
||||
class CustomTypeUpperCase
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="upper_case_string")
|
||||
*/
|
||||
public $lowerCaseString;
|
||||
}
|
@ -9,6 +9,7 @@ class DDC117Article
|
||||
{
|
||||
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @Column */
|
||||
private $title;
|
||||
|
||||
|
@ -0,0 +1,76 @@
|
||||
<?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\Models\DDC1476;
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
*/
|
||||
class DDC1476EntityWithDefaultFieldType
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column()
|
||||
* @GeneratedValue("NONE")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @column() */
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
));
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'name',
|
||||
));
|
||||
|
||||
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadataInfo::GENERATOR_TYPE_NONE);
|
||||
}
|
||||
|
||||
}
|
@ -21,7 +21,7 @@ class LegacyUser
|
||||
*/
|
||||
public $_username;
|
||||
/**
|
||||
* @Column(type="string", length=255)
|
||||
* @Column(type="string", length=255, name="name")
|
||||
*/
|
||||
public $_name;
|
||||
/**
|
||||
|
@ -23,12 +23,12 @@ class LegacyUserReference
|
||||
private $_target;
|
||||
|
||||
/**
|
||||
* @column(type="string")
|
||||
* @column(type="string", name="description")
|
||||
*/
|
||||
private $_description;
|
||||
|
||||
/**
|
||||
* @column(type="datetime")
|
||||
* @column(type="datetime", name="created")
|
||||
*/
|
||||
private $_created;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -33,11 +31,13 @@ require_once __DIR__ . '/../../TestInit.php';
|
||||
* @link http://www.doctrine-project.org
|
||||
* @since 2.0
|
||||
*/
|
||||
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
protected function setUp() {
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
private $_em;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->_em = $this->_getTestEntityManager();
|
||||
}
|
||||
|
||||
public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -489,5 +491,15 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->getConfiguration()->setDefaultRepositoryClassName("Doctrine\Tests\Models\DDC753\DDC753InvalidRepository");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1500
|
||||
*/
|
||||
public function testInvalidOrientation()
|
||||
{
|
||||
$this->setExpectedException('Doctrine\ORM\ORMException', 'Invalid order by orientation specified for Doctrine\Tests\Models\CMS\CmsUser#username');
|
||||
|
||||
$repo = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$repo->findBy(array('status' => 'test'), array('username' => 'INVALID'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,6 +304,49 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1399
|
||||
*/
|
||||
public function testCountAfterAddThenFlush()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
|
||||
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$newGroup->name = "Test4";
|
||||
|
||||
$user->addGroup($newGroup);
|
||||
$this->_em->persist($newGroup);
|
||||
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
$this->assertEquals(4, count($user->groups));
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(4, count($user->groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1462
|
||||
*/
|
||||
public function testSliceOnDirtyCollection()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
/* @var $user CmsUser */
|
||||
|
||||
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$newGroup->name = "Test4";
|
||||
|
||||
$user->addGroup($newGroup);
|
||||
$this->_em->persist($newGroup);
|
||||
|
||||
$qc = $this->getCurrentQueryCount();
|
||||
$groups = $user->groups->slice(0, 10);
|
||||
|
||||
$this->assertEquals(4, count($groups));
|
||||
$this->assertEquals($qc + 1, $this->getCurrentQueryCount());
|
||||
}
|
||||
|
||||
private function loadFixture()
|
||||
{
|
||||
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
|
@ -43,6 +43,29 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals('changed from preUpdate callback!', $result[0]->value);
|
||||
}
|
||||
|
||||
public function testPreFlushCallbacksAreInvoked()
|
||||
{
|
||||
$entity = new LifecycleCallbackTestEntity;
|
||||
$entity->value = 'hello';
|
||||
$this->_em->persist($entity);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertTrue($entity->prePersistCallbackInvoked);
|
||||
$this->assertTrue($entity->preFlushCallbackInvoked);
|
||||
|
||||
$entity->preFlushCallbackInvoked = false;
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertTrue($entity->preFlushCallbackInvoked);
|
||||
|
||||
$entity->value = 'bye';
|
||||
$entity->preFlushCallbackInvoked = false;
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertTrue($entity->preFlushCallbackInvoked);
|
||||
}
|
||||
|
||||
public function testChangesDontGetLost()
|
||||
{
|
||||
$user = new LifecycleCallbackTestUser;
|
||||
@ -190,6 +213,8 @@ class LifecycleCallbackTestEntity
|
||||
public $postPersistCallbackInvoked = false;
|
||||
public $postLoadCallbackInvoked = false;
|
||||
|
||||
public $preFlushCallbackInvoked = false;
|
||||
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
@ -233,6 +258,11 @@ class LifecycleCallbackTestEntity
|
||||
public function doStuffOnPreUpdate() {
|
||||
$this->value = 'changed from preUpdate callback!';
|
||||
}
|
||||
|
||||
/** @PreFlush */
|
||||
public function doStuffOnPreFlush() {
|
||||
$this->preFlushCallbackInvoked = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,7 @@ class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$cleanFile = $this->_em->find(get_class($file), $file->getId());
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $cleanFile->getParent());
|
||||
$this->assertEquals($directory->getId(), $cleanFile->getParent()->getId());
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent());
|
||||
$this->assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId());
|
||||
|
@ -13,15 +13,14 @@ require_once __DIR__ . '/../../TestInit.php';
|
||||
*/
|
||||
class OneToManyOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected $userId;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testOrphanRemoval()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->status = 'dev';
|
||||
$user->username = 'romanb';
|
||||
@ -35,11 +34,13 @@ class OneToManyOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$userId = $user->getId();
|
||||
|
||||
$this->userId = $user->getId();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $userId);
|
||||
public function testOrphanRemoval()
|
||||
{
|
||||
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
|
||||
$this->_em->remove($userProxy);
|
||||
$this->_em->flush();
|
||||
@ -55,4 +56,20 @@ class OneToManyOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsPhonenumber should be removed by orphanRemoval');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1496
|
||||
*/
|
||||
public function testOrphanRemovalUnitializedCollection()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
|
||||
$user->phonenumbers->clear();
|
||||
$this->_em->flush();
|
||||
|
||||
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsPhonenumber should be removed by orphanRemoval');
|
||||
}
|
||||
}
|
@ -73,7 +73,14 @@ class OneToManyUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunction
|
||||
$this->_em->persist($routeA);
|
||||
$this->_em->persist($routeB);
|
||||
|
||||
$this->setExpectedException('Exception'); // depends on the underyling Database Driver
|
||||
$this->_em->flush(); // Exception
|
||||
$exceptionThrown = false;
|
||||
try {
|
||||
// exception depending on the underyling Database Driver
|
||||
$this->_em->flush();
|
||||
} catch(\Exception $e) {
|
||||
$exceptionThrown = true;
|
||||
}
|
||||
|
||||
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Train'),
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\TrainDriver'),
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\TrainOwner'),
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Waggon'),
|
||||
));
|
||||
} catch(\Exception $e) {}
|
||||
@ -26,7 +27,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testEagerLoadOneToOneOwningSide()
|
||||
{
|
||||
$train = new Train();
|
||||
$train = new Train(new TrainOwner("Alexander"));
|
||||
$driver = new TrainDriver("Benjamin");
|
||||
$waggon = new Waggon();
|
||||
|
||||
@ -48,7 +49,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testEagerLoadOneToOneNullOwningSide()
|
||||
{
|
||||
$train = new Train();
|
||||
$train = new Train(new TrainOwner("Alexander"));
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
@ -65,9 +66,8 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testEagerLoadOneToOneInverseSide()
|
||||
{
|
||||
$train = new Train();
|
||||
$driver = new TrainDriver("Benjamin");
|
||||
$train->setDriver($driver);
|
||||
$owner = new TrainOwner("Alexander");
|
||||
$train = new Train($owner);
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
@ -75,9 +75,9 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
|
||||
$driver = $this->_em->find(get_class($driver), $driver->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $driver->train);
|
||||
$this->assertNotNull($driver->train);
|
||||
$driver = $this->_em->find(get_class($owner), $owner->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $owner->train);
|
||||
$this->assertNotNull($owner->train);
|
||||
|
||||
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
|
||||
}
|
||||
@ -103,7 +103,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testEagerLoadManyToOne()
|
||||
{
|
||||
$train = new Train();
|
||||
$train = new Train(new TrainOwner("Alexander"));
|
||||
$waggon = new Waggon();
|
||||
$train->addWaggon($waggon);
|
||||
|
||||
@ -115,6 +115,59 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $waggon->train);
|
||||
$this->assertNotNull($waggon->train);
|
||||
}
|
||||
|
||||
public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides()
|
||||
{
|
||||
$train = new Train(new TrainOwner("Alexander"));
|
||||
$driver = new TrainDriver("Benjamin");
|
||||
$train->setDriver($driver);
|
||||
|
||||
$this->_em->persist($train);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$train = $this->_em->find(get_class($train), $train->id);
|
||||
$this->assertEquals(
|
||||
"SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5, t0.owner_id AS owner_id6, t7.id AS id8, t7.name AS name9 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id INNER JOIN TrainOwner t7 ON t0.owner_id = t7.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
|
||||
$this->_em->clear();
|
||||
$driver = $this->_em->find(get_class($driver), $driver->id);
|
||||
$this->assertEquals(
|
||||
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
}
|
||||
|
||||
public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSide()
|
||||
{
|
||||
$waggon = new Waggon();
|
||||
$this->_em->persist($waggon);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
|
||||
$this->assertEquals(
|
||||
"SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
}
|
||||
|
||||
public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningSide()
|
||||
{
|
||||
$owner = new TrainOwner('Alexander');
|
||||
$train = new Train($owner);
|
||||
$this->_em->persist($train);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$waggon = $this->_em->find(get_class($owner), $owner->id);
|
||||
$this->assertEquals(
|
||||
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,16 +183,23 @@ class Train
|
||||
/**
|
||||
* Owning side
|
||||
* @OneToOne(targetEntity="TrainDriver", inversedBy="train", fetch="EAGER", cascade={"persist"})
|
||||
* @JoinColumn(nullable=true)
|
||||
*/
|
||||
public $driver;
|
||||
/**
|
||||
* Owning side
|
||||
* @OneToOne(targetEntity="TrainOwner", inversedBy="train", fetch="EAGER", cascade={"persist"})
|
||||
*/
|
||||
public $owner;
|
||||
/**
|
||||
* @oneToMany(targetEntity="Waggon", mappedBy="train", cascade={"persist"})
|
||||
*/
|
||||
public $waggons;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(TrainOwner $owner)
|
||||
{
|
||||
$this->waggons = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->setOwner($owner);
|
||||
}
|
||||
|
||||
public function setDriver(TrainDriver $driver)
|
||||
@ -148,6 +208,12 @@ class Train
|
||||
$driver->setTrain($this);
|
||||
}
|
||||
|
||||
public function setOwner(TrainOwner $owner)
|
||||
{
|
||||
$this->owner = $owner;
|
||||
$owner->setTrain($this);
|
||||
}
|
||||
|
||||
public function addWaggon(Waggon $w)
|
||||
{
|
||||
$w->setTrain($this);
|
||||
@ -181,6 +247,32 @@ class TrainDriver
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class TrainOwner
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
/** @column(type="string") */
|
||||
public $name;
|
||||
/**
|
||||
* Inverse side
|
||||
* @OneToOne(targetEntity="Train", mappedBy="owner", fetch="EAGER")
|
||||
*/
|
||||
public $train;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setTrain(Train $t)
|
||||
{
|
||||
$this->train = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
|
105
tests/Doctrine/Tests/ORM/Functional/PersistentObjectTest.php
Normal file
105
tests/Doctrine/Tests/ORM/Functional/PersistentObjectTest.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Common\Persistence\PersistentObject;
|
||||
|
||||
/**
|
||||
* Test that Doctrine ORM correctly works with the ObjectManagerAware and PersistentObject
|
||||
* classes from Common.
|
||||
*
|
||||
* @group DDC-1448
|
||||
*/
|
||||
class PersistentObjectTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\PersistentEntity'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
PersistentObject::setObjectManager($this->_em);
|
||||
}
|
||||
|
||||
public function testPersist()
|
||||
{
|
||||
$entity = new PersistentEntity();
|
||||
$entity->setName("test");
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
public function testFind()
|
||||
{
|
||||
$entity = new PersistentEntity();
|
||||
$entity->setName("test");
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$entity = $this->_em->find(__NAMESPACE__ . '\PersistentEntity', $entity->getId());
|
||||
|
||||
$this->assertEquals('test', $entity->getName());
|
||||
$entity->setName('foobar');
|
||||
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
public function testGetReference()
|
||||
{
|
||||
$entity = new PersistentEntity();
|
||||
$entity->setName("test");
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$entity = $this->_em->getReference(__NAMESPACE__ . '\PersistentEntity', $entity->getId());
|
||||
|
||||
$this->assertEquals('test', $entity->getName());
|
||||
}
|
||||
|
||||
public function testSetAssociation()
|
||||
{
|
||||
$entity = new PersistentEntity();
|
||||
$entity->setName("test");
|
||||
$entity->setParent($entity);
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$entity = $this->_em->getReference(__NAMESPACE__ . '\PersistentEntity', $entity->getId());
|
||||
$this->assertSame($entity, $entity->getParent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class PersistentEntity extends PersistentObject
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer") @GeneratedValue
|
||||
* @var int
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="PersistentEntity")
|
||||
* @var PersistentEntity
|
||||
*/
|
||||
protected $parent;
|
||||
}
|
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'))
|
||||
|
@ -457,6 +457,42 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertNull($query->getOneOrNullResult(Query::HYDRATE_SCALAR));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DBAL-171
|
||||
*/
|
||||
public function testParameterOrder()
|
||||
{
|
||||
$user1 = new CmsUser;
|
||||
$user1->name = 'Benjamin';
|
||||
$user1->username = 'beberlei';
|
||||
$user1->status = 'developer';
|
||||
$this->_em->persist($user1);
|
||||
|
||||
$user2 = new CmsUser;
|
||||
$user2->name = 'Roman';
|
||||
$user2->username = 'romanb';
|
||||
$user2->status = 'developer';
|
||||
$this->_em->persist($user2);
|
||||
|
||||
$user3 = new CmsUser;
|
||||
$user3->name = 'Jonathan';
|
||||
$user3->username = 'jwage';
|
||||
$user3->status = 'developer';
|
||||
$this->_em->persist($user3);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :a AND u.id IN (:b)");
|
||||
$query->setParameters(array(
|
||||
'b' => array($user1->id, $user2->id, $user3->id),
|
||||
'a' => 'developer',
|
||||
));
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(3, count($result));
|
||||
}
|
||||
|
||||
public function testDqlWithAutoInferOfParameters()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
|
@ -27,14 +27,14 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$readOnly->name = "Test2";
|
||||
$readOnly->number = 4321;
|
||||
$readOnly->numericValue = 4321;
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$dbReadOnly = $this->_em->find('Doctrine\Tests\ORM\Functional\ReadOnlyEntity', $readOnly->id);
|
||||
$this->assertEquals("Test1", $dbReadOnly->name);
|
||||
$this->assertEquals(1234, $dbReadOnly->number);
|
||||
$this->assertEquals(1234, $dbReadOnly->numericValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,11 +51,11 @@ class ReadOnlyEntity
|
||||
/** @column(type="string") */
|
||||
public $name;
|
||||
/** @Column(type="integer") */
|
||||
public $number;
|
||||
public $numericValue;
|
||||
|
||||
public function __construct($name, $number)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->number = $number;
|
||||
$this->numericValue = $number;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Proxy\ProxyClassGenerator;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
@ -160,6 +161,29 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
|
||||
}
|
||||
|
||||
public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType()
|
||||
{
|
||||
$product = new ECommerceProduct();
|
||||
$product->setName('Doctrine Cookbook');
|
||||
|
||||
$shipping = new ECommerceShipping();
|
||||
$shipping->setDays(1);
|
||||
$product->setShipping($shipping);
|
||||
$this->_em->persist($product);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$id = $shipping->getId();
|
||||
|
||||
$product = $this->_em->getRepository('Doctrine\Tests\Models\ECommerce\ECommerceProduct')->find($product->getId());
|
||||
|
||||
$entity = $product->getShipping();
|
||||
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
|
||||
$this->assertEquals($id, $entity->getId());
|
||||
$this->assertSame($id, $entity->getId(), "Check that the id's are the same value, and type.");
|
||||
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
|
||||
}
|
||||
|
||||
public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier()
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
@ -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()
|
||||
|
@ -32,24 +32,28 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$tool = new SchemaTool($this->_em);
|
||||
$sql = $tool->getCreateSchemaSql($classes);
|
||||
$sqlCount = count($sql);
|
||||
|
||||
$this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", $sql[0]);
|
||||
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_ACAC157BA76ED395 ON cms_addresses (user_id)", $sql[1]);
|
||||
$this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[2]);
|
||||
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5F85E0677 ON cms_users (username)", $sql[3]);
|
||||
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[4]);
|
||||
$this->assertEquals("CREATE INDEX IDX_7EA9409AA76ED395 ON cms_users_groups (user_id)", $sql[5]);
|
||||
$this->assertEquals("CREATE INDEX IDX_7EA9409AFE54D947 ON cms_users_groups (group_id)", $sql[6]);
|
||||
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[7]);
|
||||
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", $sql[8]);
|
||||
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[9]);
|
||||
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[10]);
|
||||
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
|
||||
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
|
||||
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
|
||||
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
|
||||
$this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", array_shift($sql));
|
||||
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_ACAC157BA76ED395 ON cms_addresses (user_id)", array_shift($sql));
|
||||
$this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", array_shift($sql));
|
||||
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5F85E0677 ON cms_users (username)", array_shift($sql));
|
||||
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 ON cms_users (email_id)", array_shift($sql));
|
||||
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", array_shift($sql));
|
||||
$this->assertEquals("CREATE INDEX IDX_7EA9409AA76ED395 ON cms_users_groups (user_id)", array_shift($sql));
|
||||
$this->assertEquals("CREATE INDEX IDX_7EA9409AFE54D947 ON cms_users_groups (group_id)", array_shift($sql));
|
||||
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", array_shift($sql));
|
||||
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", array_shift($sql));
|
||||
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", array_shift($sql));
|
||||
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", array_shift($sql));
|
||||
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
|
||||
$this->assertEquals("ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
|
||||
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
|
||||
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
|
||||
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
|
||||
|
||||
$this->assertEquals(count($sql), 15);
|
||||
$this->assertEquals(array(), $sql, "SQL Array should be empty now.");
|
||||
$this->assertEquals(17, $sqlCount, "Total of 17 queries should be executed");
|
||||
}
|
||||
|
||||
public function testGetCreateSchemaSql2()
|
||||
@ -92,7 +96,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$tool = new SchemaTool($this->_em);
|
||||
$sql = $tool->getDropSchemaSQL($classes);
|
||||
|
||||
$this->assertEquals(13, count($sql));
|
||||
$this->assertEquals(14, count($sql));
|
||||
$dropSequenceSQLs = 0;
|
||||
foreach ($sql AS $stmt) {
|
||||
if (strpos($stmt, "DROP SEQUENCE") === 0) {
|
||||
|
@ -44,11 +44,10 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->setParameter('author', $user)
|
||||
->getResult();
|
||||
|
||||
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author AND a.text = :text";
|
||||
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author";
|
||||
$farticle = $this->_em->createQuery($dql)
|
||||
->setParameter('author', $user)
|
||||
->setParameter('topic', 'This is John Galt speaking!')
|
||||
->setParameter('text', 'Yadda Yadda!')
|
||||
->getSingleResult();
|
||||
|
||||
$this->assertSame($article, $farticle);
|
||||
@ -70,12 +69,11 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->persist($article);
|
||||
$this->_em->flush();
|
||||
|
||||
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3 AND a.text = ?4";
|
||||
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3";
|
||||
$farticle = $this->_em->createQuery($dql)
|
||||
->setParameter(1, 'This is John Galt speaking!')
|
||||
->setParameter(2, $user)
|
||||
->setParameter(3, $user)
|
||||
->setParameter(4, 'Yadda Yadda!')
|
||||
->getSingleResult();
|
||||
|
||||
$this->assertSame($article, $farticle);
|
||||
|
@ -27,8 +27,8 @@ class DDC1151Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals("CREATE TABLE \"Group\" (id INT NOT NULL, PRIMARY KEY(id))", $sql[4]);
|
||||
$this->assertEquals("CREATE SEQUENCE User_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[5]);
|
||||
$this->assertEquals("CREATE SEQUENCE Group_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[6]);
|
||||
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151user_id) REFERENCES \"User\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
|
||||
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
|
||||
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A3259AC5AD08A FOREIGN KEY (ddc1151user_id) REFERENCES \"User\" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
|
||||
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A32597357E0B1 FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,8 +209,15 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->article1->addTranslation('en', 'Bar');
|
||||
$this->article1->addTranslation('en', 'Baz');
|
||||
|
||||
$this->setExpectedException('Exception');
|
||||
$exceptionThrown = false;
|
||||
try {
|
||||
// exception depending on the underyling Database Driver
|
||||
$this->_em->flush();
|
||||
} catch(\Exception $e) {
|
||||
$exceptionThrown = true;
|
||||
}
|
||||
|
||||
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +106,7 @@ class DDC1209_3
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="datetime")
|
||||
* @Column(type="datetime", name="somedate")
|
||||
*/
|
||||
private $date;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user