Compare commits

..

87 Commits

Author SHA1 Message Date
Benjamin Eberlei 4e9438ba60 Release 2.2.2 2012-04-13 10:02:34 +02:00
Benjamin Eberlei 73483bf003 Bump Versions 2012-04-13 10:01:15 +02:00
Benjamin Eberlei 245c30a1a0 Merge branch 'DDC-1534' into 2.2 2012-04-07 10:43:49 +02:00
Benjamin Eberlei 15dc64f2f9 [DDC-1534] YamlDriver wrongly used "inversedBy" inside join table condition although its independent. 2012-04-07 10:43:21 +02:00
Benjamin Eberlei 66951f3c47 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-04-07 10:35:59 +02:00
Benjamin Eberlei 5f21f177e9 Merge branch 'DDC-1771' into 2.2 2012-04-07 10:31:02 +02:00
Benjamin Eberlei 7af877d17e [DDC-1771] Abstract classes cannot be proxies and should be skipped in complete generation. 2012-04-07 10:30:32 +02:00
Benjamin Eberlei f628a7f609 Merge pull request #327 from Netpositive/2.2
2.2 addDiscriminatorMapClass fix
2012-04-07 00:44:16 -07:00
Benjamin Eberlei 64c222e4d3 Merge branch 'DDC-1766' into 2.2 2012-04-07 09:12:17 +02:00
Benjamin Eberlei 13db27609c [DDC-1766] Rewrite getHydrationCacheId() to use existing processParameterValue() method. Other code style changes. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei f039902a44 [DDC-1766] More cleanups 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 59d0c02aa9 [DDC-1766] Cleaned up code. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 63d1d847ac [DDC-1766] Explain details of Hydration cache, introduce AbstractQuery#setResultCacheProfile method 2012-04-07 09:11:18 +02:00
Benjamin Eberlei ce4aacaaee [DDC-1766] Add usage of default result cache driver, add more docs. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei dadcc7e9fc [DDC-1766] Add test with explicit cache key. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei cc9b96e259 [DDC-1766] Remove some testcode 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 006412bad9 [DDC-1766] Rename closure 2012-04-07 09:11:18 +02:00
Benjamin Eberlei ffbaaece93 [DDC-1766] Initial implementation of hydration cache. 2012-04-07 09:11:18 +02:00
Somfai Mátyás c3fa29f298 Fixing a bug when calling setDiscriminatorMap from multiple sources (ie: from Events::loadClassMetadata and annotation). 2012-04-04 14:47:42 +02:00
Benjamin Eberlei aa82a75726 Merge branch 'DDC-1705' into 2.2 2012-04-01 12:51:53 +02:00
Benjamin Eberlei fba85d481f [DDC-1705] Fix notice 2012-04-01 12:51:35 +02:00
Benjamin Eberlei 5bdfad9e0a Merge branch 'DDC-1648' into 2.2 2012-03-14 21:40:19 +01:00
rivaros d69a79c723 Convention fix 2012-03-14 21:39:45 +01:00
Rivaros f51409b627 convention fixes #2 2012-03-14 21:39:45 +01:00
Rivaros 8c8deb9c9b Convention fixes 2012-03-14 21:39:45 +01:00
Rivaros 79c8f42483 Primary Keys as Foreign Keys - reverse engineering 2012-03-14 21:39:45 +01:00
Benjamin Eberlei 04ae8f2c64 Merge branch 'DDC-1692' into 2.2 2012-03-14 21:10:44 +01:00
Benjamin Eberlei ecb495d293 [DBAL-1692] Throw exception if table has no primary key instead of fatal error. 2012-03-14 21:10:09 +01:00
Benjamin Eberlei 1418173870 Merge branch 'DDC-1683' into 2.2 2012-03-14 20:50:27 +01:00
Benjamin Eberlei 26a6b69993 [DDC-1683] Fix bug with booleans not handled by Expr#literal() in query builder. 2012-03-14 20:49:51 +01:00
Benjamin Eberlei fa5ee57faf Merge branch 'DDC-1698' into 2.2 2012-03-14 20:05:55 +01:00
Benjamin Eberlei 2a2f010788 [DDC-1698] Add autoloader especially for the non PSR-0 Proxy class names. This is necessary when you want to deserialize your proxy classes from the session. 2012-03-14 20:03:34 +01:00
Benjamin Eberlei e958085559 Merge branch 'DDC-1695' into 2.2 2012-03-11 23:31:33 +01:00
Benjamin Eberlei 98ba0c1ab2 [DDC-1695] Fix bug in SQL Walker array hydration with escaped fields. 2012-03-11 23:31:01 +01:00
Benjamin Eberlei b574be5e3b Merge branch 'DDC-1693' into 2.2 2012-03-11 22:30:05 +01:00
Benjamin Eberlei 79ad9068e1 [DDC-1693] Fix fatal errors in DQL when using Optimistic or None lock modes. Added tests. 2012-03-11 22:29:38 +01:00
Benjamin Eberlei af403d52fb Bump dev version to 2.2.2 2012-03-03 22:26:27 +01:00
Benjamin Eberlei 78fa740b94 Release 2.2.1 2012-03-03 22:26:27 +01:00
Benjamin Eberlei 715fdd7559 Merge branch 'DDC-1668' into 2.2 2012-03-03 22:25:36 +01:00
Benjamin Eberlei 884ef42075 [DDC-1668] Fix problem with the is_int fowards compatibility check. Its not really necesssary anymore, we should remove this code in the future. 2012-03-03 22:25:11 +01:00
Benjamin Eberlei 5d3a626c2b Merge branch 'DDC-1678' into 2.2 2012-03-03 22:17:07 +01:00
Francisco Facioni 587dda90d3 UnitTest for ManyToMany update notification 2012-03-03 22:16:44 +01:00
Francisco Facioni 21ae0d2a45 When using a ManyToMany relationship no listener is notified about any change to the owning entity.
What I'm doing with this patch is marking the entity for update when there is a modification in the ManyToMany relationship so the listeners are notified about it.

The main reason for this is for hooking up services like Solr or other indexers to update the entities even for ManyToMany relationships.
2012-03-03 22:16:44 +01:00
Benjamin Eberlei f4dc533127 Bump Common to 2.2.1 2012-03-03 22:08:36 +01:00
Benjamin Eberlei c9d1b3a428 Merge branch 'DDC-1652' into 2.2 2012-03-03 21:11:15 +01:00
Benjamin Eberlei 9170e1b810 [DDC-1652] Fix SqlWalker to include foreign key identifiers in SQL SELECT statement no matter what the meta column setting is suggesting. 2012-03-03 21:10:33 +01:00
Benjamin Eberlei f1d2a79a87 Merge branch 'DDC-1673' into 2.2 2012-03-03 19:44:27 +01:00
Guilherme Blanco dd2f3967cd [DDC-1673] Fixed unused in ProxyFactory. 2012-03-03 19:26:27 +01:00
Benjamin Eberlei 4100a6a7a3 Merge branch 'DDC-1667' into 2.2 2012-03-03 19:11:05 +01:00
Guilherme Blanco bfb590459e [DDC-1667] Removed implicit obligation to define an Index and UniqueConstraint name. It is optional, but Annotations Driver was broken if not defined. 2012-03-03 19:10:06 +01:00
Sergio Moya 191520439b No unique join column fields for Single Table inheritance type. 2012-02-20 15:56:11 +01:00
Benjamin Eberlei 31709b4c1a Merge branch 'DDC-1649' into 2.2 2012-02-20 15:40:07 +01:00
Benjamin Eberlei af6de10c5b [DDC-1649] Fix notice by last commit. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei f4a78e13ee [DDC-1649] Add additional check for not allowed mapping of dependent association keys. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei bd37b83f4f Merge branch 'DDC-1654' into 2.2 2012-02-20 10:34:27 +01:00
Benjamin Eberlei 19969e2d4b [DDC-1654] Add support for orphanRemoval on ManyToMany associations. This only makes sense when ManyToMany is used as uni-directional OneToMany association with join table. The join column has a unique constraint on it to enforce this on the DB level, but we dont validate that this actually happens. Foreign Key constraints help prevent issues and notify developers early if they use it wrong. 2012-02-20 10:33:43 +01:00
Benjamin Eberlei 573a7f81e1 Merge branch 'DDC-1659' into 2.2 2012-02-20 09:37:59 +01:00
Benjamin Eberlei 2460b3f115 [DDC-1659] Remove read only marker when clearing entities. 2012-02-20 09:37:11 +01:00
Benjamin Michotte 5f2fa7c08a Add fluent code for relations 2012-02-20 09:24:42 +01:00
Asmir Mustafic a04df0622d nullable assoc 2012-02-20 00:32:30 +01:00
Benjamin Eberlei b7e80decf3 Merge branch 'DDC-1651' into 2.2 2012-02-18 16:10:57 +01:00
Benjamin Eberlei 56d35f7932 [DDC-1651] Convert entities as parameters early in setParameter() to avoid them being part of result cache strings, which causes non-uniqueness. 2012-02-18 16:08:43 +01:00
Benjamin Eberlei 2116518096 Merge branch 'DDC-1643' into 2.2 2012-02-18 00:43:51 +01:00
Benjamin Eberlei 647bd2b2f2 [DDC-1643] Fix bugs when cloning PersistentCollection and re-using it. 2012-02-18 00:43:19 +01:00
Benjamin Eberlei dadc85a650 Merge branch 'DDC-1655' into 2.2 2012-02-17 23:28:23 +01:00
Benjamin Eberlei 835943bd8d [DDC-1655][DDC-1650][DDC-1556] Fix issues with @postLoad Callback being not fired, or fired multiple times. 2012-02-17 23:27:35 +01:00
Benjamin Eberlei 39ff164c7e DDC-1641 - Fix test producing failure when skipped. 2012-02-10 21:38:42 +01:00
Benjamin Eberlei 5cdbc8ea6d Merge branch 'DDC-1634' into 2.2 2012-02-05 11:15:29 +01:00
Miha Vrhovnik 34ccde978a Proxy not initialized when parent has get<IDENTIFIER> function. Fixes DDC-1625 2012-02-05 11:14:55 +01:00
Benjamin Eberlei 5675e8cc8c Bump dev version to 2.2.1 2012-01-29 17:04:24 +01:00
Benjamin Eberlei d679154fba Release 2.2.0 2012-01-29 17:04:23 +01:00
Benjamin Eberlei 7cf4337dc1 Bump and bind dependencies of ORM 2.2 2012-01-29 15:22:06 +01:00
Benjamin Eberlei 41cf2ce4b1 Fix test for non-mysql like datetimes. 2012-01-29 15:01:36 +01:00
Benjamin Eberlei 13b8d05def Bump dependencies to release versions. 2012-01-29 13:12:25 +01:00
Benjamin Eberlei 209d098672 Fix Oracle Tests 2012-01-29 13:09:51 +01:00
Benjamin Eberlei 53600484cd Merge branch 'DDC-1526' into 2.2 2012-01-28 12:26:54 +01:00
Benjamin Eberlei ac25d62a24 [DDC-1526] Collections are not marked as initialized when they are fetch joined but dont contain any results. This only occurs when using LEFT JOINs on the assocations and causes another query to be fired when the empty collection is accessed again. 2012-01-28 12:26:25 +01:00
Benjamin Eberlei f7936bda70 Merge branch 'DDC-1617' into 2.2 2012-01-28 11:17:08 +01:00
Benjamin Eberlei 78899cedac [DDC-1617] Implement support for Generating Unique Constraints/Indexes in @Table annotation of EntityGenerator. 2012-01-28 11:16:58 +01:00
Benjamin Eberlei 73fef2867f Merge remote-tracking branch 'origin/2.2' into 2.2 2012-01-25 23:29:44 +01:00
armetiz c883ff2644 Update lib/Doctrine/ORM/Tools/SchemaTool.php 2012-01-25 22:06:37 +01:00
Benjamin Eberlei 7595025f26 Merge branch 'DDC-1619' into 2.2 2012-01-25 10:26:42 +01:00
Benjamin Eberlei 8fb6f986dd [DDC-1619] Add QueryBuilder#distinct 2012-01-25 10:25:38 +01:00
Benjamin Eberlei 6dbb368995 Merge branch 'DDC-1618' into 2.2 2012-01-25 00:02:44 +01:00
Thomas Rabaix c39ad9250a Add SqlWalker::HINT_DISTINCT constant 2012-01-25 00:02:16 +01:00
Thomas Rabaix 8adc267d87 Fix DDC-1618 - add more check before throwing an iterateWithFetchJoinNotAllowed exception 2012-01-25 00:02:16 +01:00
Benjamin Eberlei 2a8dd72b33 Bump dev version to 2.2.0 2012-01-22 17:54:32 +01:00
62 changed files with 1783 additions and 176 deletions
+2 -2
View File
@@ -92,8 +92,8 @@
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" maximum_version="2.2.99" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" maximum_version="2.2.99" />
<package name="Console" channel="pear.symfony.com" minimum_version="2.0.0" />
<package name="Yaml" channel="pear.symfony.com" minimum_version="2.0.0" />
</dependencies>
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "doctrine/orm",
"type": "library","version":"2.2.0-RC1",
"type": "library","version":"2.2.2",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
@@ -14,8 +14,8 @@
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": ">=2.2-dev,<2.3-dev",
"doctrine/dbal": ">=2.2-dev,<2.3-dev"
"doctrine/common": ">=2.2.0,<2.2.99",
"doctrine/dbal": ">=2.2.1,<2.2.99"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }
+1
View File
@@ -350,6 +350,7 @@
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
+171 -3
View File
@@ -20,8 +20,9 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Cache\QueryCacheProfile,
Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
Doctrine\ORM\Internal\Hydration\CacheHydrator;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -29,7 +30,6 @@ use Doctrine\DBAL\Types\Type,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
@@ -101,6 +101,11 @@ abstract class AbstractQuery
*/
protected $_expireResultCache = false;
/**
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
*/
protected $_hydrationCacheProfile;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
@@ -208,6 +213,7 @@ abstract class AbstractQuery
{
$key = trim($key, ':');
$value = $this->processParameterValue($value);
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
@@ -218,6 +224,53 @@ abstract class AbstractQuery
return $this;
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return $value;
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
return $this->convertObjectParameterToScalarValue($value);
default:
return $value;
}
}
protected function convertObjectParameterToScalarValue($value)
{
$class = $this->_em->getClassMetadata(get_class($value));
if ($class->isIdentifierComposite) {
throw new \InvalidArgumentException("Binding an entity with a composite primary key to a query is not supported. You should split the parameter into the explicit fields and bind them seperately.");
}
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
$values = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$values = $class->getIdentifierValues($value);
}
$value = $values[$class->getSingleIdentifierFieldName()];
if (!$value) {
throw new \InvalidArgumentException("Binding entities to query parameters only allowed for entities that have an identifier.");
}
return $value;
}
/**
* Sets a collection of query parameters.
*
@@ -247,6 +300,68 @@ abstract class AbstractQuery
return $this;
}
/**
* Set a cache profile for hydration caching.
*
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* Important: Hydration caching does NOT register entities in the
* UnitOfWork when retrieved from the cache. Never use result cached
* entities for requests that also flush the EntityManager. If you want
* some form of caching with UnitOfWork registration you should use
* {@see AbstractQuery::setResultCacheProfile()}.
*
* @example
* $lifetime = 100;
* $resultKey = "abc";
* $query->setHydrationCacheProfile(new QueryCacheProfile());
* $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
* @return \Doctrine\ORM\AbstractQuery
*/
public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
{
if ( ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_hydrationCacheProfile = $profile;
return $this;
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function getHydrationCacheProfile()
{
return $this->_hydrationCacheProfile;
}
/**
* Set a cache profile for the result cache.
*
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
* @return \Doctrine\ORM\AbstractQuery
*/
public function setResultCacheProfile(QueryCacheProfile $profile = null)
{
if ( ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_queryCacheProfile = $profile;
return $this;
}
/**
* Defines a cache driver to be used for caching result sets and implictly enables caching.
*
@@ -592,15 +707,68 @@ abstract class AbstractQuery
$this->setParameters($params);
}
$setCacheEntry = function() {};
if ($this->_hydrationCacheProfile !== null) {
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
$queryCacheProfile = $this->getHydrationCacheProfile();
$cache = $queryCacheProfile->getResultCacheDriver();
$result = $cache->fetch($cacheKey);
if (isset($result[$realCacheKey])) {
return $result[$realCacheKey];
}
if ( ! $result) {
$result = array();
}
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
$result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
};
}
$stmt = $this->_doExecute();
if (is_numeric($stmt)) {
$setCacheEntry($stmt);
return $stmt;
}
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$setCacheEntry($data);
return $data;
}
/**
* 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.
*
* @return array ($key, $hash)
*/
protected function getHydrationCacheId()
{
$params = $this->getParameters();
foreach ($params AS $key => $value) {
$params[$key] = $this->processParameterValue($value);
}
$sql = $this->getSQL();
$queryCacheProfile = $this->getHydrationCacheProfile();
$hints = $this->getHints();
$hints['hydrationMode'] = $this->getHydrationMode();
ksort($hints);
return $queryCacheProfile->generateCacheKeys($sql, $params, $hints);
}
/**
+22
View File
@@ -230,6 +230,28 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['queryCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for the hydration cache (SQL cache).
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getHydrationCacheImpl()
{
return isset($this->_attributes['hydrationCacheImpl'])
? $this->_attributes['hydrationCacheImpl']
: null;
}
/**
* Sets the cache driver implementation that is used for the hydration cache (SQL cache).
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setHydrationCacheImpl(Cache $cacheImpl)
{
$this->_attributes['hydrationCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for metadata caching.
*
@@ -233,7 +233,7 @@ abstract class AbstractHydrator
if (isset($cache[$key]['isScalar'])) {
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;
@@ -286,4 +286,4 @@ class ArrayHydrator extends AbstractHydrator
return $this->_ce[$className];
}
}
}
@@ -234,20 +234,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_hints['fetchAlias'] = $dqlAlias;
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->_ce[$className]->lifecycleCallbacks[Events::postLoad])) {
$this->_ce[$className]->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
return $entity;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
@@ -359,7 +346,6 @@ class ObjectHydrator extends AbstractHydrator
continue;
}
$parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
$oid = spl_object_hash($parentObject);
$relationField = $this->_rsm->relationMap[$dqlAlias];
@@ -368,6 +354,7 @@ class ObjectHydrator extends AbstractHydrator
// Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
$reflFieldValue = $reflField->getValue($parentObject);
// PATH A: Collection-valued association
if (isset($nonemptyComponents[$dqlAlias])) {
$collKey = $oid . $relationField;
@@ -408,9 +395,12 @@ class ObjectHydrator extends AbstractHydrator
// Update result pointer
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
}
} else if ( ! $reflField->getValue($parentObject)) {
} else if ( ! $reflFieldValue) {
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
} else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
$reflFieldValue->setInitialized(true);
}
} else {
// PATH B: Single-valued association
$reflFieldValue = $reflField->getValue($parentObject);
@@ -130,17 +130,6 @@ class SimpleObjectHydrator extends AbstractHydrator
$uow = $this->_em->getUnitOfWork();
$entity = $uow->createEntity($entityName, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->class->lifecycleCallbacks[Events::postLoad])) {
$this->class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
$result[] = $entity;
}
@@ -191,4 +180,4 @@ class SimpleObjectHydrator extends AbstractHydrator
);
}
}
}
}
@@ -1105,7 +1105,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
if ( ($mapping['type'] & (self::MANY_TO_ONE|self::MANY_TO_MANY)) > 0 &&
if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
isset($mapping['orphanRemoval']) &&
$mapping['orphanRemoval'] == true) {
@@ -1207,9 +1207,11 @@ class ClassMetadataInfo implements ClassMetadata
$uniqueContraintColumns = array();
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE) {
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
if (count($mapping['joinColumns']) == 1) {
$joinColumn['unique'] = true;
if (! isset($mapping['id']) || ! $mapping['id']) {
$joinColumn['unique'] = true;
}
} else {
$uniqueContraintColumns[] = $joinColumn['name'];
}
@@ -1336,6 +1338,8 @@ class ClassMetadataInfo implements ClassMetadata
}
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
@@ -1989,7 +1993,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name)) {
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
$this->subClasses[] = $className;
}
}
@@ -142,8 +142,7 @@ class AnnotationDriver implements Driver
$classAnnotations = $this->_reader->getClassAnnotations($class);
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
$classAnnotations[get_class($annot)] = $annot;
}
@@ -176,17 +175,25 @@ class AnnotationDriver implements Driver
if ($tableAnnot->indexes !== null) {
foreach ($tableAnnot->indexes as $indexAnnot) {
$primaryTable['indexes'][$indexAnnot->name] = array(
'columns' => $indexAnnot->columns
);
$index = array('columns' => $indexAnnot->columns);
if ( ! empty($indexAnnot->name)) {
$primaryTable['indexes'][$indexAnnot->name] = $index;
} else {
$primaryTable['indexes'][] = $index;
}
}
}
if ($tableAnnot->uniqueConstraints !== null) {
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraint) {
$primaryTable['uniqueConstraints'][$uniqueConstraint->name] = array(
'columns' => $uniqueConstraint->columns
);
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
$uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns);
if ( ! empty($uniqueConstraintAnnot->name)) {
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
} else {
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
}
}
}
@@ -407,6 +414,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
@@ -424,8 +432,7 @@ class AnnotationDriver implements Driver
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->_reader->getMethodAnnotations($method);
// Compatibility with Doctrine Common 3.x
if ($annotations && is_int(key($annotations))) {
if ($annotations && is_numeric(key($annotations))) {
foreach ($annotations as $annot) {
$annotations[get_class($annot)] = $annot;
}
@@ -480,8 +487,7 @@ class AnnotationDriver implements Driver
{
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
if ($annot instanceof \Doctrine\ORM\Mapping\Entity) {
return false;
@@ -130,6 +130,13 @@ class DatabaseDriver implements Driver
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
@@ -185,12 +192,13 @@ class DatabaseDriver implements Driver
$fieldMappings = array();
foreach ($columns as $column) {
$fieldMapping = array();
if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
} else if (in_array($column->getName(), $allForeignKeyColumns)) {
if (in_array($column->getName(), $allForeignKeyColumns)) {
continue;
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
}
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
@@ -291,13 +299,23 @@ class DatabaseDriver implements Driver
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
$associationMapping['id'] = true;
}
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$metadata->mapManyToOne($associationMapping);
//Here we need to check if $cols are the same as $primaryKeyColums
if (!array_diff($cols,$primaryKeyColumns)) {
$metadata->mapOneToOne($associationMapping);
} else {
$metadata->mapManyToOne($associationMapping);
}
}
}
@@ -397,6 +397,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']);
}
if (isset($manyToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphan-removal'];
}
if (isset($manyToManyElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$manyToManyElement['mapped-by'];
} else if (isset($manyToManyElement->{'join-table'})) {
@@ -402,9 +402,6 @@ class YamlDriver extends AbstractFileDriver
if (isset($manyToManyElement['mappedBy'])) {
$mapping['mappedBy'] = $manyToManyElement['mappedBy'];
} else if (isset($manyToManyElement['joinTable'])) {
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
$joinTableElement = $manyToManyElement['joinTable'];
$joinTable = array(
@@ -434,6 +431,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['joinTable'] = $joinTable;
}
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
if (isset($manyToManyElement['cascade'])) {
$mapping['cascade'] = $manyToManyElement['cascade'];
}
@@ -446,6 +447,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
$metadata->mapManyToMany($mapping);
}
}
+2
View File
@@ -35,6 +35,8 @@ final class ManyToMany implements Annotation
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}
+27 -3
View File
@@ -305,6 +305,7 @@ final class PersistentCollection implements Collection
if ($this->association !== null &&
$this->association['isOwningSide'] &&
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
$this->owner &&
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
@@ -387,7 +388,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
@@ -425,7 +426,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] === ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -630,7 +631,7 @@ final class PersistentCollection implements Collection
$uow = $this->em->getUnitOfWork();
if ($this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
if ($this->association['type'] & ClassMetadata::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();
@@ -759,4 +760,27 @@ final class PersistentCollection implements Collection
return $this->coll->slice($offset, $length);
}
/**
* Cleanup internal state of cloned persistent collection.
*
* The following problems have to be prevented:
* 1. Added entities are added to old PC
* 2. New collection is not dirty, if reused on other entity nothing
* changes.
* 3. Snapshot leads to invalid diffs being generated.
* 4. Lazy loading grabs entities from old owner object.
* 5. New collection is connected to old owner and leads to duplicate keys.
*/
public function __clone()
{
$this->initialize();
$this->owner = null;
if (is_object($this->coll)) {
$this->coll = clone $this->coll;
}
$this->snapshot = array();
$this->changed();
}
}
@@ -204,4 +204,4 @@ abstract class AbstractCollectionPersister
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
}
@@ -698,16 +698,6 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
}
/**
@@ -1153,6 +1143,7 @@ class BasicEntityPersister
$placeholder = '?';
if (isset($this->_columnTypes[$column]) &&
isset($this->_class->fieldNames[$column]) &&
isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) {
$type = Type::getType($this->_columnTypes[$column]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
+78
View File
@@ -0,0 +1,78 @@
<?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\Proxy;
/**
* Special Autoloader for Proxy classes because them not being PSR-0 compatible.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class Autoloader
{
/**
* Resolve proxy class name to a filename based on the following pattern.
*
* 1. Remove Proxy namespace from class name
* 2. Remove namespace seperators from remaining class name.
* 3. Return PHP filename from proxy-dir with the result from 2.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param string $className
* @return string
*/
static public function resolveFile($proxyDir, $proxyNamespace, $className)
{
if (0 !== strpos($className, $proxyNamespace)) {
throw ProxyException::notProxyClass($className, $proxyNamespace);
}
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1));
return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php';
}
/**
* Register and return autoloader callback for the given proxy dir and
* namespace.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param Closure $notFoundCallback Invoked when the proxy file is not found.
* @return Closure
*/
static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null)
{
$proxyNamespace = ltrim($proxyNamespace, "\\");
$autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) {
if (0 === strpos($className, $proxyNamespace)) {
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
if ($notFoundCallback && ! file_exists($file)) {
$notFoundCallback($proxyDir, $proxyNamespace, $className);
}
require $file;
}
};
spl_autoload_register($autoloader);
return $autoloader;
}
}
+9 -1
View File
@@ -40,4 +40,12 @@ class ProxyException extends \Doctrine\ORM\ORMException {
return new self("You must configure a proxy namespace. See docs for details");
}
}
public static function notProxyClass($className, $proxyNamespace)
{
return new self(sprintf(
"The class %s is not part of the proxy namespace %s",
$className, $proxyNamespace
));
}
}
+18 -6
View File
@@ -106,11 +106,16 @@ class ProxyFactory
* Generate the Proxy file name
*
* @param string $className
* @param string $baseDir Optional base directory for proxy file name generation.
* If not specified, the directory configured on the Configuration of the
* EntityManager will be used by this factory.
* @return string
*/
private function getProxyFileName($className)
private function getProxyFileName($className, $baseDir = null)
{
return $this->_proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
$proxyDir = $baseDir ?: $this->_proxyDir;
return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
}
/**
@@ -120,20 +125,27 @@ class ProxyFactory
* @param string $toDir The target directory of the proxy classes. If not specified, the
* directory configured on the Configuration of the EntityManager used
* by this factory is used.
* @return int Number of generated proxies.
*/
public function generateProxyClasses(array $classes, $toDir = null)
{
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
$num = 0;
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass) {
if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) {
continue;
}
$proxyFileName = $this->getProxyFileName($class->name);
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
$num++;
}
return $num;
}
/**
@@ -275,7 +287,7 @@ class ProxyFactory
);
if ($cheapCheck) {
$code = file($class->reflClass->getFileName());
$code = file($method->getDeclaringClass()->getFileName());
$code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1)));
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
+2 -34
View File
@@ -282,7 +282,8 @@ final class Query extends AbstractQuery
}
$sqlPositions = $paramMappings[$key];
$value = array_values($this->processParameterValue($value));
// optimized multi value sql positions away for now, they are not allowed in DQL anyways.
$value = array($value);
$countValue = count($value);
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
@@ -305,39 +306,6 @@ final class Query extends AbstractQuery
return array($sqlParams, $types);
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
// TODO: What about Entities that have composite primary key?
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return array($value);
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
}
$class = $this->_em->getClassMetadata(get_class($value));
return array_values($class->getIdentifierValues($value));
default:
return array($value);
}
}
/**
* Defines a cache driver to be used for caching queries.
*
+2
View File
@@ -562,6 +562,8 @@ class Expr
{
if (is_numeric($literal) && !is_string($literal)) {
return (string) $literal;
} else if (is_bool($literal)) {
return $literal ? "true" : "false";
} else {
return "'" . str_replace("'", "''", $literal) . "'";
}
+31 -8
View File
@@ -39,6 +39,11 @@ use Doctrine\DBAL\LockMode,
*/
class SqlWalker implements TreeWalker
{
/**
* @var string
*/
const HINT_DISTINCT = 'doctrine.distinct';
/**
* @var ResultSetMapping
*/
@@ -435,13 +440,15 @@ class SqlWalker implements TreeWalker
$sql .= ' ' . $this->_platform->getWriteLockSQL();
break;
case LockMode::PESSIMISTIC_OPTIMISTIC:
case LockMode::OPTIMISTIC:
foreach ($this->_selectedClasses AS $selectedClass) {
if ( ! $class->isVersioned) {
if ( ! $selectedClass['class']->isVersioned) {
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
break;
case LockMode::NONE:
break;
default:
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
@@ -590,6 +597,10 @@ class SqlWalker implements TreeWalker
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
$this->_query->setHint(self::HINT_DISTINCT, true);
}
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
||
@@ -627,11 +638,17 @@ class SqlWalker implements TreeWalker
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) continue;
if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
continue;
}
// Add foreign key columns of class and also parent classes
foreach ($class->associationMappings as $assoc) {
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
continue;
} else if ( !$addMetaColumns && !isset($assoc['id'])) {
continue;
}
$owningClass = (isset($assoc['inherited'])) ? $this->_em->getClassMetadata($assoc['inherited']) : $class;
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
@@ -645,6 +662,11 @@ class SqlWalker implements TreeWalker
}
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) {
continue;
}
// Add foreign key columns of subclasses
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
@@ -811,9 +833,10 @@ class SqlWalker implements TreeWalker
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $relation['type'] & ClassMetadata::TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) {
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
}
}
if ($joinVarDecl->indexBy) {
@@ -1080,7 +1103,7 @@ class SqlWalker implements TreeWalker
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
$col = $sqlTableAlias . '.' . $columnName;
+25 -3
View File
@@ -50,6 +50,7 @@ class QueryBuilder
* @var array The array of DQL parts collected.
*/
private $_dqlParts = array(
'distinct' => false,
'select' => array(),
'from' => array(),
'join' => array(),
@@ -346,7 +347,7 @@ class QueryBuilder
* ->setParameters(array(
* 'user_id1' => 1,
* 'user_id2' => 2
* ));
));
* </code>
*
* @param array $params The query parameters to set.
@@ -503,6 +504,25 @@ class QueryBuilder
return $this->add('select', new Expr\Select($selects), false);
}
/**
* Add a DISTINCT flag to this query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->distinct()
* ->from('User', 'u');
* </code>
*
* @param bool
* @return QueryBuilder
*/
public function distinct($flag = true)
{
$this->_dqlParts['distinct'] = (bool) $flag;
return $this;
}
/**
* Adds an item that is to be returned in the query result.
*
@@ -981,7 +1001,9 @@ class QueryBuilder
private function _getDQLForSelect()
{
$dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$dql = 'SELECT'
. ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '')
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$fromParts = $this->getDQLPart('from');
$joinParts = $this->getDQLPart('join');
@@ -1090,4 +1112,4 @@ class QueryBuilder
}
}
}
}
}
@@ -128,10 +128,12 @@ public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
* <description>
*
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>[] = $<variableName>;
<spaces>return $this;
}';
private static $_lifecycleCallbackMethodTemplate =
@@ -583,9 +585,32 @@ public function <methodName>()
$table[] = 'name="' . $metadata->table['name'] . '"';
}
if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
$constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
$table[] = 'uniqueConstraints={' . $constraints . '}';
}
if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
$constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']);
$table[] = 'indexes={' . $constraints . '}';
}
return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
}
private function _generateTableConstraints($constraintName, $constraints)
{
$annotations = array();
foreach ($constraints as $name => $constraint) {
$columns = array();
foreach ($constraint['columns'] as $column) {
$columns[] = '"' . $column . '"';
}
$annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
}
return implode(', ', $annotations);
}
private function _generateInheritanceAnnotation($metadata)
{
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
@@ -658,6 +683,9 @@ public function <methodName>()
private function _isAssociationIsNullable($associationMapping)
{
if (isset($associationMapping['id']) && $associationMapping['id']) {
return false;
}
if (isset($associationMapping['joinColumns'])) {
$joinColumns = $associationMapping['joinColumns'];
} else {
@@ -834,6 +862,14 @@ public function <methodName>()
if ($this->_generateAnnotations) {
$lines[] = $this->_spaces . ' *';
if (isset($associationMapping['id']) && $associationMapping['id']) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
}
}
$type = null;
switch ($associationMapping['type']) {
+1 -1
View File
@@ -231,7 +231,7 @@ class SchemaTool
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] AS $indexName => $indexData) {
$table->addUniqueIndex($indexData['columns'], $indexName);
$table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
@@ -106,6 +106,11 @@ class SchemaValidator
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) {
$ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " .
"the target entity '". $targetMetadata->name . "' also maps an association as identifier.";
}
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
+39
View File
@@ -584,6 +584,23 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->associationMappings[$propName];
// Persistent collection was exchanged with the "originally"
// created one. This can only mean it was cloned and replaced
// on another entity.
if ($actualValue instanceof PersistentCollection) {
$owner = $actualValue->getOwner();
if ($owner === null) { // cloned
$actualValue->setOwner($entity, $assoc);
} else if ($owner !== $entity) { // no clone, we have to fix
if (!$actualValue->isInitialized()) {
$actualValue->initialize(); // we have to do this otherwise the cols share state
}
$newValue = clone $actualValue;
$newValue->setOwner($entity, $assoc);
$class->reflFields[$propName]->setValue($entity, $newValue);
}
}
if ($orgValue instanceof PersistentCollection) {
// A PersistentCollection was de-referenced, so delete it.
$coid = spl_object_hash($orgValue);
@@ -620,6 +637,15 @@ class UnitOfWork implements PropertyChangedListener
foreach ($class->associationMappings as $field => $assoc) {
if (($val = $class->reflFields[$field]->getValue($entity)) !== null) {
$this->computeAssociationChanges($assoc, $val);
if (!isset($this->entityChangeSets[$oid]) &&
$assoc['isOwningSide'] &&
$assoc['type'] == ClassMetadata::MANY_TO_MANY &&
$val instanceof PersistentCollection &&
$val->isDirty()) {
$this->entityChangeSets[$oid] = array();
$this->originalEntityData[$oid] = $actualData;
$this->entityUpdates[$oid] = $entity;
}
}
}
}
@@ -1376,6 +1402,7 @@ class UnitOfWork implements PropertyChangedListener
if (isset($this->identityMap[$className][$idHash])) {
unset($this->identityMap[$className][$idHash]);
unset($this->readOnlyObjects[$oid]);
//$this->entityStates[$oid] = self::STATE_DETACHED;
@@ -2189,6 +2216,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionDeletions =
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
@@ -2498,6 +2526,17 @@ class UnitOfWork implements PropertyChangedListener
}
}
if ($overrideLocalValues) {
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
if ($this->evm->hasListeners(Events::postLoad)) {
$this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
}
}
return $entity;
}
+1 -1
View File
@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.2.0-RC1';
const VERSION = '2.2.2';
/**
* Compares a Doctrine version with the current one.
@@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Navigation\NavCountry;
use Doctrine\Tests\Models\Navigation\NavPointOfInterest;
use Doctrine\Tests\Models\Navigation\NavTour;
use Doctrine\Tests\Models\Navigation\NavPhotos;
require_once __DIR__ . '/../../TestInit.php';
@@ -51,6 +52,25 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Brandenburger Tor', $poi->getName());
}
/**
* @group DDC-1651
*/
public function testSetParameterCompositeKeyObject()
{
$this->putGermanysBrandenburderTor();
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$photo = new NavPhotos($poi, "asdf");
$this->_em->persist($photo);
$this->_em->flush();
$this->_em->clear();
$dql = 'SELECT t FROM Doctrine\Tests\Models\Navigation\NavPhotos t WHERE t.poi = ?1';
$this->setExpectedException('Doctrine\ORM\Query\QueryException', 'A single-valued association path expression to an entity with a composite primary key is not supported.');
$sql = $this->_em->createQuery($dql)->getSQL();
}
public function testManyToManyCompositeRelation()
{
$this->putGermanysBrandenburderTor();
@@ -98,4 +118,4 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->setExpectedException('Doctrine\ORM\ORMException', 'The identifier long is missing for a query of Doctrine\Tests\Models\Navigation\NavPointOfInterest');
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('key1' => 100));
}
}
}
@@ -0,0 +1,86 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\Tests\Models\Cms\CmsUser;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\Common\Cache\ArrayCache;
/**
* @group DDC-1766
*/
class HydrationCacheTest extends OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user = new CmsUser;
$user->name = "Benjamin";
$user->username = "beberlei";
$user->status = 'active';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
}
public function testHydrationCache()
{
$cache = new ArrayCache();
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u";
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getResult();
$c = $this->getCurrentQueryCount();
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getResult();
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getArrayResult();
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration is part of cache key.");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getArrayResult();
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
->getArrayResult();
$this->assertTrue($cache->contains('cachekey'), 'Explicit cache key');
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
->getArrayResult();
$this->assertEquals($c + 2, $this->getCurrentQueryCount(), "Hydration now cached");
}
public function testHydrationParametersSerialization()
{
$cache = new ArrayCache();
$user = new CmsUser();
$user->id = 1;
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1";
$query = $this->_em->createQuery($dql)
->setParameter(1, $user)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
$query->getResult();
$c = $this->getCurrentQueryCount();
$query->getResult();
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
}
}
@@ -9,6 +9,9 @@ use Doctrine\Tests\Models\CMS\CmsArticle,
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group locking
*/
class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
protected function setUp() {
$this->useModelSet('cms');
@@ -139,7 +142,6 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
/**
* @group DDC-178
* @group locking
*/
public function testLockPessimisticRead() {
$readLockSql = $this->_em->getConnection()->getDatabasePlatform()->getReadLockSql();
@@ -166,4 +168,17 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$query = array_pop( $this->_sqlLoggerStack->queries );
$this->assertContains($readLockSql, $query['sql']);
}
}
/**
* @group DDC-1693
*/
public function testLockOptimisticNonVersionedThrowsExceptionInDQL()
{
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'";
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock on an entity failed.');
$sql = $this->_em->createQuery($dql)->setHint(
\Doctrine\ORM\Query::HINT_LOCK_MODE, \Doctrine\DBAL\LockMode::OPTIMISTIC
)->getSQL();
}
}
@@ -0,0 +1,76 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* ManyToManyEventTest
*
* @author Francisco Facioni <fran6co@gmail.com>
*/
class ManyToManyEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var PostUpdateListener
*/
private $listener;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new PostUpdateListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::postUpdate, $this->listener);
}
public function testListenerShouldBeNotifiedOnlyWhenUpdating()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->assertFalse($this->listener->wasNotified);
$group = new CmsGroup();
$group->name = "admins";
$user->addGroup($group);
$this->_em->persist($user);
$this->_em->flush();
$this->assertTrue($this->listener->wasNotified);
}
/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'fran6co';
$user->name = 'Francisco Facioni';
$group = new CmsGroup();
$group->name = "users";
$user->addGroup($group);
return $user;
}
}
class PostUpdateListener
{
/**
* @var bool
*/
public $wasNotified = false;
/**
* @param $args
*/
public function postUpdate($args)
{
$this->wasNotified = true;
}
}
@@ -127,14 +127,14 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
$train = $this->_em->find(get_class($train), $train->id);
$this->assertEquals(
$this->assertSQLEquals(
"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(
$this->assertSQLEquals(
"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']
);
@@ -155,13 +155,13 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
// The last query is the eager loading of the owner of the train
$this->assertEquals(
$this->assertSQLEquals(
"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']
);
// The one before is the fetching of the waggon and train
$this->assertEquals(
$this->assertSQLEquals(
"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 - 1]['sql']
);
@@ -176,7 +176,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
$waggon = $this->_em->find(get_class($owner), $owner->id);
$this->assertEquals(
$this->assertSQLEquals(
"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']
);
@@ -599,4 +599,24 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(3, count($users));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
}
}
/**
* @group DDC-1651
*/
public function testSetParameterBindingSingleIdentifierObjectConverted()
{
$userC = new CmsUser;
$userC->name = 'Jonathan';
$userC->username = 'jwage';
$userC->status = 'developer';
$this->_em->persist($userC);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1");
$q->setParameter(1, $userC);
$this->assertEquals($userC->id, $q->getParameter(1));
}
}
@@ -15,9 +15,12 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
} catch(\Exception $e) {
}
}
public function testReadOnlyEntityNeverChangeTracked()
@@ -36,6 +39,36 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->numericValue);
}
/**
* @group DDC-1659
*/
public function testClearReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear();
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
/**
* @group DDC-1659
*/
public function testClearEntitiesReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear(get_class($readOnly));
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
}
/**
@@ -58,4 +91,4 @@ class ReadOnlyEntity
$this->name = $name;
$this->numericValue = $number;
}
}
}
@@ -6,6 +6,7 @@ use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Proxy\ProxyClassGenerator;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
use Doctrine\Tests\Models\Company\CompanyAuction;
require_once __DIR__ . '/../../TestInit.php';
@@ -39,6 +40,18 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
return $product->getId();
}
public function createAuction()
{
$event = new CompanyAuction();
$event->setData('Doctrine Cookbook');
$this->_em->persist($event);
$this->_em->flush();
$this->_em->clear();
return $event->getId();
}
public function testLazyLoadsFieldValuesFromDatabase()
{
$id = $this->createProduct();
@@ -161,6 +174,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
/**
* @group DDC-1625
*/
public function testDoNotInitializeProxyOnGettingTheIdentifier_DDC_1625()
{
$id = $this->createAuction();
/* @var $entity Doctrine\Tests\Models\Company\CompanyAuction */
$entity = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyAuction' , $id);
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals($id, $entity->getId());
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy when extending.");
}
public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType()
{
$product = new ECommerceProduct();
@@ -433,4 +433,32 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($this->article1->id(), $refRep->source()->id());
$this->assertEquals($this->article2->id(), $refRep->target()->id());
}
/**
* @group DDC-1652
*/
public function testArrayHydrationWithCompositeKey()
{
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$before = count($this->_em->createQuery($dql)->getResult());
$this->article1 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id());
$this->article2 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article2->id());
$this->reference = new DDC117Reference($this->article2, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article1, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article2, $this->article2, "Test-Description");
$this->_em->persist($this->reference);
$this->_em->flush();
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$data = $this->_em->createQuery($dql)->getArrayResult();
$this->assertEquals($before + 3, count($data));
}
}
@@ -36,7 +36,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
->getQuery();
$this->assertEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -68,7 +68,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -97,7 +97,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -294,4 +294,4 @@ class DDC1430OrderProduct
$this->value = $value;
}
}
}
@@ -57,6 +57,8 @@ class DDC1514Test extends \Doctrine\Tests\OrmFunctionalTestCase
$dql = "SELECT a, b, ba, c FROM " . __NAMESPACE__ . "\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c";
$results = $this->_em->createQuery($dql)->getResult();
$this->assertInternalType('array', $results);
$this->assertTrue(count($results) >= 1, "At least one result expected in array");
$this->assertEquals($c->title, $results[1]->entityC->title);
}
}
@@ -0,0 +1,67 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1526
*/
class DDC1526Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1526Menu'),
));
}
public function testIssue()
{
$parents = array();
for ($i = 0; $i < 9; $i++) {
$entity = new DDC1526Menu;
if (isset ($parents[($i % 3)])) {
$entity->parent = $parents[($i%3)];
}
$this->_em->persist($entity);
$parents[$i] = $entity;
}
$this->_em->flush();
$this->_em->clear();
$dql = "SELECT m, c
FROM " . __NAMESPACE__ . "\DDC1526Menu m
LEFT JOIN m.children c";
$menus = $this->_em->createQuery($dql)->getResult();
// All Children collection now have to be initiailzed
foreach ($menus as $menu) {
$this->assertTrue($menu->children->isInitialized());
}
}
}
/**
* @Entity
*/
class DDC1526Menu
{
/**
* @Column(type="integer")
* @Id
* @GeneratedValue
*/
public $id;
/**
* @ManyToOne(targetEntity="DDC1526Menu", inversedBy="children")
*/
public $parent;
/**
* @OneToMany(targetEntity="DDC1526Menu", mappedBy="parent")
*/
public $children;
}
@@ -35,14 +35,14 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase
$entity1 = $repository->find($e1->id);
// DDC-1596
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
$entities = $entity1->getEntities()->getValues();
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.type FROM base t0 INNER JOIN entity1_entity2 ON t0.id = entity1_entity2.item WHERE entity1_entity2.parent = ? AND t0.type IN ('Entity2')",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
@@ -52,7 +52,7 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase
$entity1 = $repository->find($e1->id);
$entities = $entity1->getEntities()->count();
$this->assertEquals(
$this->assertSQLEquals(
"SELECT COUNT(*) FROM entity1_entity2 t WHERE parent = ?",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
@@ -108,4 +108,4 @@ class DDC1595InheritedEntity1 extends DDC1595BaseInheritance
*/
class DDC1595InheritedEntity2 extends DDC1595BaseInheritance
{
}
}
@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
/**
* @group DDC-1643
*/
class DDC1643Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $user1;
private $user2;
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user1 = new CmsUser();
$user1->username = "beberlei";
$user1->name = "Benjamin";
$user1->status = "active";
$group1 = new CmsGroup();
$group1->name = "test";
$group2 = new CmsGroup();
$group2->name = "test";
$user1->addGroup($group1);
$user1->addGroup($group2);
$user2 = new CmsUser();
$user2->username = "romanb";
$user2->name = "Roman";
$user2->status = "active";
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($group1);
$this->_em->persist($group2);
$this->_em->flush();
$this->_em->clear();
$this->user1 = $this->_em->find(get_class($user1), $user1->id);
$this->user2 = $this->_em->find(get_class($user1), $user2->id);
}
public function testClonePersistentCollectionAndReuse()
{
$user1 = $this->user1;
$user1->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$this->assertEquals(2, count($user1->groups));
}
public function testClonePersistentCollectionAndShare()
{
$user1 = $this->user1;
$user2 = $this->user2;
$user2->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(2, count($user1->groups));
$this->assertEquals(2, count($user2->groups));
}
public function testCloneThenDirtyPersistentCollection()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = clone $user1->groups;
$user2->groups->add($group3);
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(2, count($user1->groups));
}
public function testNotCloneAndPassAroundFlush()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = $user1->groups;
$user2->groups->add($group3);
$this->assertEQuals(1, count($user1->groups->getInsertDiff()));
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(3, count($user1->groups));
}
}
@@ -0,0 +1,103 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1654
*/
class DDC1654Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->setUpEntitySchema(array(
__NAMESPACE__ . '\\DDC1654Post',
__NAMESPACE__ . '\\DDC1654Comment',
));
}
public function testManyToManyRemoveFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->remove(0);
$post->comments->remove(1);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyRemoveElementFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->removeElement($post->comments[0]);
$post->comments->removeElement($post->comments[1]);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyClearCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->clear();
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
}
/**
* @Entity
*/
class DDC1654Post
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
/**
* @ManyToMany(targetEntity="DDC1654Comment", orphanRemoval=true,
* cascade={"persist"})
*/
public $comments = array();
}
/**
* @Entity
*/
class DDC1654Comment
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
}
@@ -0,0 +1,144 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1655
* @group DDC-1640
* @group DDC-1556
*/
class DDC1655Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Bar'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Baz'),
));
} catch(\Exception $e) {
}
}
public function testPostLoadOneToManyInheritance()
{
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Foo');
$this->assertEquals(array("postLoad" => array("postLoad")), $cm->lifecycleCallbacks);
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Bar');
$this->assertEquals(array("postLoad" => array("postLoad", "postSubLoaded")), $cm->lifecycleCallbacks);
$baz = new DDC1655Baz();
$foo = new DDC1655Foo();
$foo->baz = $baz;
$bar = new DDC1655Bar();
$bar->baz = $baz;
$this->_em->persist($foo);
$this->_em->persist($bar);
$this->_em->persist($baz);
$this->_em->flush();
$this->_em->clear();
$baz = $this->_em->find(get_class($baz), $baz->id);
foreach ($baz->foos as $foo) {
$this->assertEquals(1, $foo->loaded, "should have loaded callback counter incremented for " . get_class($foo));
}
}
/**
* Check that post load is not executed several times when the entity
* is rehydrated again although its already known.
*/
public function testPostLoadInheritanceChild()
{
$bar = new DDC1655Bar();
$this->_em->persist($bar);
$this->_em->flush();
$this->_em->clear();
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$dql = "SELECT b FROM " . __NAMESPACE__ . "\DDC1655Bar b WHERE b.id = ?1";
$bar = $this->_em->createQuery($dql)->setParameter(1, $bar->id)->getSingleResult();
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$this->_em->refresh($bar);
$this->assertEquals(2, $bar->loaded);
$this->assertEquals(2, $bar->subLoaded);
}
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({
* "foo" = "DDC1655Foo",
* "bar" = "DDC1655Bar"
* })
* @HasLifecycleCallbacks
*/
class DDC1655Foo
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
public $loaded = 0;
/**
* @ManyToOne(targetEntity="DDC1655Baz", inversedBy="foos")
*/
public $baz;
/**
* @PostLoad
*/
public function postLoad()
{
$this->loaded++;
}
}
/**
* @Entity
* @HasLifecycleCallbacks
*/
class DDC1655Bar extends DDC1655Foo
{
public $subLoaded;
/**
* @PostLoad
*/
public function postSubLoaded()
{
$this->subLoaded++;
}
}
/**
* @Entity
*/
class DDC1655Baz
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/**
* @OneToMany(targetEntity="DDC1655Foo", mappedBy="baz")
*/
public $foos = array();
}
@@ -0,0 +1,158 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1695
*/
class DDC1695Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function testIssue()
{
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != "sqlite") {
$this->markTestSkipped("Only with sqlite");
}
$dql = "SELECT n.smallText, n.publishDate FROM " . __NAMESPACE__ . "\\DDC1695News n";
$sql = $this->_em->createQuery($dql)->getSQL();
$this->assertEquals(
'SELECT d0_."SmallText" AS SmallText0, d0_."PublishDate" AS PublishDate1 FROM "DDC1695News" d0_',
$sql
);
}
}
/**
* @Table(name="`DDC1695News`")
* @Entity
*/
class DDC1695News
{
/**
* @var integer $idNews
*
* @Column(name="`IdNews`", type="integer", nullable=false)
* @Id
* @GeneratedValue
*/
private $idNews;
/**
* @var bigint $iduser
*
* @Column(name="`IdUser`", type="bigint", nullable=false)
*/
private $idUser;
/**
* @var integer $idLanguage
*
* @Column(name="`IdLanguage`", type="integer", nullable=false)
*/
private $idLanguage;
/**
* @var integer $idCondition
*
* @Column(name="`IdCondition`", type="integer", nullable=true)
*/
private $idCondition;
/**
* @var integer $idHealthProvider
*
* @Column(name="`IdHealthProvider`", type="integer", nullable=true)
*/
private $idHealthProvider;
/**
* @var integer $idSpeciality
*
* @Column(name="`IdSpeciality`", type="integer", nullable=true)
*/
private $idSpeciality;
/**
* @var integer $idMedicineType
*
* @Column(name="`IdMedicineType`", type="integer", nullable=true)
*/
private $idMedicineType;
/**
* @var integer $idTreatment
*
* @Column(name="`IdTreatment`", type="integer", nullable=true)
*/
private $idTreatment;
/**
* @var string $title
*
* @Column(name="`Title`", type="string", nullable=true)
*/
private $title;
/**
* @var string $smallText
*
* @Column(name="`SmallText`", type="string", nullable=true)
*/
private $smallText;
/**
* @var string $longText
*
* @Column(name="`LongText`", type="string", nullable=true)
*/
private $longText;
/**
* @var datetimetz $publishDate
*
* @Column(name="`PublishDate`", type="datetimetz", nullable=true)
*/
private $publishDate;
/**
* @var tsvector $idxNews
*
* @Column(name="`IdxNews`", type="tsvector", nullable=true)
*/
private $idxNews;
/**
* @var boolean $highlight
*
* @Column(name="`Highlight`", type="boolean", nullable=false)
*/
private $highlight;
/**
* @var integer $order
*
* @Column(name="`Order`", type="integer", nullable=false)
*/
private $order;
/**
* @var boolean $deleted
*
* @Column(name="`Deleted`", type="boolean", nullable=false)
*/
private $deleted;
/**
* @var boolean $active
*
* @Column(name="`Active`", type="boolean", nullable=false)
*/
private $active;
/**
* @var boolean $updateToHighlighted
*
* @Column(name="`UpdateToHighlighted`", type="boolean", nullable=true)
*/
private $updateToHighlighted;
}
@@ -40,13 +40,13 @@ class DDC657Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertCount(2,$result);
$this->assertEquals('11:11:11', $result[0]['time']);
$this->assertEquals('2010-01-01', $result[0]['date']);
$this->assertEquals('2010-01-01 11:11:11', $result[0]['datetime']);
$this->assertContains('11:11:11', $result[0]['time']);
$this->assertContains('2010-01-01', $result[0]['date']);
$this->assertContains('2010-01-01 11:11:11', $result[0]['datetime']);
$this->assertEquals('12:12:12', $result[1]['time']);
$this->assertEquals('2010-02-02', $result[1]['date']);
$this->assertEquals('2010-02-02 12:12:12', $result[1]['datetime']);
$this->assertContains('12:12:12', $result[1]['time']);
$this->assertContains('2010-02-02', $result[1]['date']);
$this->assertContains('2010-02-02 12:12:12', $result[1]['datetime']);
}
public function testaTicketEntityArrayResult()
@@ -115,4 +115,4 @@ class DDC657Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
}
}
}
@@ -377,6 +377,7 @@ class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
array(
'user_id' => 'id',
),
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}
@@ -0,0 +1,62 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM\Proxy;
use Doctrine\Tests\OrmTestCase;
use Doctrine\ORM\Proxy\Autoloader;
/**
* @group DDC-1698
*/
class AutoloaderTest extends OrmTestCase
{
static public function dataResolveFile()
{
return array(
array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp/__CG__OtherRealClass.php'),
);
}
/**
* @dataProvider dataResolveFile
*/
public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile)
{
$actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
$this->assertEquals($expectedProxyFile, $actualProxyFile);
}
public function testAutoload()
{
if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) {
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
$autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) {
file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ");
});
$this->assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true));
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
}
@@ -155,6 +155,20 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals(1, substr_count($classCode, 'function __sleep'));
}
/**
* @group DDC-1771
*/
public function testSkipAbstractClassesOnGeneration()
{
$cm = new \Doctrine\ORM\Mapping\ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertNotNull($cm->reflClass);
$num = $this->_proxyFactory->generateProxyClasses(array($cm));
$this->assertEquals(0, $num, "No proxies generated.");
}
public function testNoConfigDir_ThrowsException()
{
$this->setExpectedException('Doctrine\ORM\Proxy\ProxyException');
@@ -183,3 +197,8 @@ class SleepClass
return array('id');
}
}
abstract class AbstractClass
{
}
+10 -1
View File
@@ -336,4 +336,13 @@ class ExprTest extends \Doctrine\Tests\OrmTestCase
$orExpr = $this->_expr->orx();
$orExpr->add($this->_expr->quot(5, 2));
}
}
/**
* @group DDC-1683
*/
public function testBooleanLiteral()
{
$this->assertEquals('true', $this->_expr->literal(true));
$this->assertEquals('false', $this->_expr->literal(false));
}
}
@@ -101,4 +101,28 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
$q->useResultCache(true);
$this->assertSame($this->_em->getConfiguration()->getResultCacheImpl(), $q->getQueryCacheProfile()->getResultCacheDriver());
}
/**
* @expectedException Doctrine\ORM\Query\QueryException
**/
public function testIterateWithNoDistinctAndWrongSelectClause()
{
$q = $this->_em->createQuery("select u, a from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a");
$q->iterate();
}
/**
* @expectedException Doctrine\ORM\Query\QueryException
**/
public function testIterateWithNoDistinctAndWithValidSelectClause()
{
$q = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a");
$q->iterate();
}
public function testIterateWithDistinct()
{
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a");
$q->iterate();
}
}
@@ -893,6 +893,20 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
/**
* @group DDC-1693
* @group locking
*/
public function testLockModeNoneQueryHint()
{
$this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'",
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ".
"FROM cms_users c0_ WHERE c0_.username = 'gblanco'",
array(Query::HINT_LOCK_MODE => \Doctrine\DBAL\LockMode::NONE)
);
}
/**
* @group DDC-430
*/
@@ -1059,7 +1073,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
"SELECT t, s, l FROM Doctrine\Tests\Models\DDC117\DDC117Link l INNER JOIN l.target t INNER JOIN l.source s",
"SELECT d0_.article_id AS article_id0, d0_.title AS title1, d1_.article_id AS article_id2, d1_.title AS title3 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id"
"SELECT d0_.article_id AS article_id0, d0_.title AS title1, d1_.article_id AS article_id2, d1_.title AS title3, d2_.source_id AS source_id4, d2_.target_id AS target_id5 FROM DDC117Link d2_ INNER JOIN DDC117Article d0_ ON d2_.target_id = d0_.article_id INNER JOIN DDC117Article d1_ ON d2_.source_id = d1_.article_id"
);
}
+14 -1
View File
@@ -732,4 +732,17 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL());
}
}
/**
* @group DDC-1619
*/
public function testAddDistinct()
{
$qb = $this->_em->createQueryBuilder()
->select('u')
->distinct()
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$this->assertEquals('SELECT DISTINCT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $qb->getDQL());
}
}
@@ -47,6 +47,8 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
$metadata->customRepositoryClassName = $this->_namespace . '\EntityGeneratorBookRepository';
$metadata->table['name'] = 'book';
$metadata->table['uniqueConstraints']['name_uniq'] = array('columns' => array('name'));
$metadata->table['indexes']['status_idx'] = array('columns' => array('status'));
$metadata->mapField(array('fieldName' => 'name', 'type' => 'string'));
$metadata->mapField(array('fieldName' => 'status', 'type' => 'string', 'default' => 'published'));
$metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
@@ -109,6 +109,33 @@ class SchemaValidatorTest extends \Doctrine\Tests\OrmTestCase
$ce
);
}
/**
* @group DDC-1587
*/
public function testValidOneToOneAsIdentifierSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1587ValidEntity2');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1587ValidEntity1');
$ce = $this->validator->validateClass($class1);
$this->assertEquals(array(), $ce);
}
/**
* @group DDC-1649
*/
public function testInvalidTripleAssociationAsKeyMapping()
{
$classThree = $this->em->getClassMetadata(__NAMESPACE__ . '\DDC1649Three');
$ce = $this->validator->validateClass($classThree);
$this->assertEquals(Array(
"Cannot map association 'Doctrine\Tests\ORM\Tools\DDC1649Three#two as identifier, because the target entity 'Doctrine\Tests\ORM\Tools\DDC1649Two' also maps an association as identifier.",
"The referenced column name 'id' has to be a primary key column on the target entity class 'Doctrine\Tests\ORM\Tools\DDC1649Two'."
), $ce);
}
}
/**
@@ -154,3 +181,87 @@ class InvalidEntity2
*/
protected $assoc;
}
/**
* @Entity(repositoryClass="Entity\Repository\Agent")
* @Table(name="agent")
*/
class DDC1587ValidEntity1
{
/**
* @var int
*
* @Id @GeneratedValue
* @Column(name="pk", type="integer")
*/
private $pk;
/**
* @var string
*
* @Column(name="name", type="string", length=32)
*/
private $name;
/**
* @var Identifier
*
* @OneToOne(targetEntity="DDC1587ValidEntity2", cascade={"all"}, mappedBy="agent")
* @JoinColumn(name="pk", referencedColumnName="pk_agent")
*/
private $identifier;
}
/**
* @Entity
* @Table
*/
class DDC1587ValidEntity2
{
/**
* @var DDC1587ValidEntity1
*
* @Id
* @OneToOne(targetEntity="DDC1587ValidEntity1", inversedBy="identifier")
* @JoinColumn(name="pk_agent", referencedColumnName="pk", nullable=false)
*/
private $agent;
/**
* @var string
*
* @Column(name="num", type="string", length=16, nullable=true)
*/
private $num;
}
/**
* @Entity
*/
class DDC1649One
{
/**
* @Id @Column @GeneratedValue
*/
public $id;
}
/**
* @Entity
*/
class DDC1649Two
{
/** @Id @ManyToOne(targetEntity="DDC1649One")@JoinColumn(name="id", referencedColumnName="id") */
public $one;
}
/**
* @Entity
*/
class DDC1649Three
{
/** @Id @ManyToOne(targetEntity="DDC1649Two") @JoinColumn(name="id",
* referencedColumnName="id") */
private $two;
}
+16 -12
View File
@@ -22,6 +22,21 @@ class SetupTest extends \Doctrine\Tests\OrmTestCase
$this->originalIncludePath = get_include_path();
}
public function tearDown()
{
if ( ! $this->originalIncludePath) {
return;
}
set_include_path($this->originalIncludePath);
$loaders = spl_autoload_functions();
for ($i = 0; $i < count($loaders); $i++) {
if ($i > $this->originalAutoloaderCount+1) {
spl_autoload_unregister($loaders[$i]);
}
}
}
public function testGitAutoload()
{
Setup::registerAutoloadGit(__DIR__ . "/../../../../../");
@@ -92,15 +107,4 @@ class SetupTest extends \Doctrine\Tests\OrmTestCase
$this->assertSame($cache, $config->getMetadataCacheImpl());
$this->assertSame($cache, $config->getQueryCacheImpl());
}
public function tearDown()
{
set_include_path($this->originalIncludePath);
$loaders = spl_autoload_functions();
for ($i = 0; $i < count($loaders); $i++) {
if ($i > $this->originalAutoloaderCount+1) {
spl_autoload_unregister($loaders[$i]);
}
}
}
}
}
@@ -38,6 +38,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
/** Whether the database schema has already been created. */
protected static $_tablesCreated = array();
/**
* Array of entity class name to their tables that were created.
* @var array
*/
protected static $_entityTablesCreated = array();
/** List of model sets and their classes. */
protected static $_modelSets = array(
'cms' => array(
@@ -235,6 +241,25 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$this->_em->clear();
}
protected function setUpEntitySchema(array $classNames)
{
if ($this->_em === null) {
throw new \RuntimeException("EntityManager not set, you have to call parent::setUp() before invoking this method.");
}
$classes = array();
foreach ($classNames as $className) {
if ( ! isset(static::$_entityTablesCreated[$className])) {
static::$_entityTablesCreated[$className] = true;
$classes[] = $this->_em->getClassMetadata($className);
}
}
if ($classes) {
$this->_schemaTool->createSchema($classes);
}
}
/**
* Creates a connection to the test database, if there is none yet, and
* creates the necessary tables.
+5
View File
@@ -114,4 +114,9 @@ abstract class OrmTestCase extends DoctrineTestCase
return self::$_queryCacheImpl;
}
public function assertSQLEquals($expectedSQL, $actualSQL, $failureMessage = null)
{
return $this->assertEquals(strtolower($expectedSQL), strtolower($actualSQL), $failureMessage);
}
}