Compare commits

...

38 Commits

Author SHA1 Message Date
Marco Pivetta
fc19c3b53d Release 2.4.4 2014-07-11 05:05:53 +02:00
Marco Pivetta
5c5abb6771 Merge branch 'hotfix/DDC-3208-backport-DDC-3160' into 2.4 2014-07-11 04:49:42 +02:00
Marco Pivetta
42226dadd1 DDC-3208 - hotfix for DDC-3160 backported to 2.4.x 2014-07-11 04:49:30 +02:00
Benjamin Eberlei
84f8ef5ca4 Bump version to 2.4.4 2014-06-10 13:49:09 +02:00
Benjamin Eberlei
8a13376d42 Release 2.4.3 2014-06-10 13:49:08 +02:00
Marco Pivetta
8b5632cb65 The proxy factory always expects non-null identifier values 2014-06-10 13:48:35 +02:00
Benjamin Eberlei
859465b691 Fix wrong version 2014-06-03 21:43:21 +02:00
Benjamin Eberlei
530c01b5e3 [DDC-3120] Fix bug with unserialize bc break in PHP 5.4.29 and PHP 5.5.13 2014-06-03 17:36:31 +02:00
Marco Pivetta
63c5758070 Merge pull request #784 from eventhorizonpl/fix_docs
fix documentation warnings p1
Conflicts:
	docs/en/reference/advanced-configuration.rst
2014-04-21 05:07:57 +00:00
Benjamin Eberlei
2a9a53ae9d Merge branch 'DDC-3076' into 2.4 2014-04-17 00:03:39 +02:00
Frank Liepert
0081319712 [DDC-3076] Add/Improve tests 2014-04-17 00:03:11 +02:00
Frank Liepert
78ceda7ecf [DDC-3076] Fix ObjectHydrator 2014-04-17 00:03:11 +02:00
Frank Liepert
1f4810e370 [DDC-3076] Add test 2014-04-17 00:03:11 +02:00
Frank
376a3ac3b6 Fix: handle invalid discriminator value 2014-04-17 00:03:11 +02:00
Frank
ab87dd6325 Add: invalidDiscriminatorValue method 2014-04-17 00:03:11 +02:00
Guilherme Blanco
2ae245db30 Fixes DDC-2984. Made DDC-742 more resilient to recurring failures. 2014-04-16 23:41:53 +02:00
Benjamin Eberlei
9d36e855c0 Disable SQLServer platform related test, because of a wrong merge into 2.4 2014-03-23 16:38:05 +01:00
Benjamin Eberlei
f7c87cddd8 Merge branch 'DDC-2997' into 2.4 2014-03-23 15:38:07 +01:00
Alexandru Patranescu
1566b8a057 allow passing EntityManagerInterface when creating a HelperSet 2014-03-23 15:37:07 +01:00
Benjamin Eberlei
333fa1090a Merge branch 'DDC-3018' into 2.4 2014-03-23 15:17:56 +01:00
Benjamin Eberlei
0262b083bb [DDC-3018] Fix string literals in new operator. 2014-03-23 15:17:47 +01:00
Benjamin Eberlei
60c9c27c08 Merge branch 'DDC-2996' into 2.4 2014-03-23 13:19:43 +01:00
Benjamin Eberlei
8b75e3563a [DDC-2996] Fix bug in UnitOfWork#recomputeSingleEntityChangeSet
When calling UnitOfWork#recomputeSingleEntityChangeSet on an entity
that didn't have a changeset before, the computation was ignored.
This method however is suggested to be used in "onFlush" and "preFlush"
events in the documentation.

Also fix a bug where recomputeSingleEntityChangeSet was used
before calculating a real changeset for an object.
2014-03-23 13:19:22 +01:00
Benjamin Eberlei
cbf16f1cf8 Merge branch 'DDC-3033' into 2.4 2014-03-23 12:41:23 +01:00
Benjamin Eberlei
b84c828ea1 [DDC-3033] Clarify restrictions in events. 2014-03-23 12:40:56 +01:00
Benjamin Eberlei
55b7e4cff2 [DDC-3033] Fix bug in UnitOfWork#recomputeSingleEntityChangeSet.
The fix for DDC-2624 had a side effect on recomputation of
changesets in preUpdate events. The method wasn't adjusted
to the changes in its sister method computeChangeSet() and
had wrong assumptions about the computation.

Especially:
1. Collections have to be skipped
2. Comparison was changed to strict equality only.
2014-03-23 12:38:51 +01:00
Thomas Lallement
e38af55100 Update DDC3033Test.php 2014-03-23 12:38:51 +01:00
Thomas Lallement
7338c2d1f8 Update DDC3033Test.php 2014-03-23 12:38:51 +01:00
Thomas Lallement
0ff3cdf150 Failing Test (since commit 53a5a48aed)
Hi,

It seems to be a regression since the commit 53a5a48aed

Doctrine\ORM\PersistentCollection can be populated in $changeSet if you set a PreUpdate and PostUpdate event.

Original issue: http://www.doctrine-project.org/jira/browse/DDC-3033
2014-03-23 12:38:51 +01:00
Benjamin Eberlei
24c5c54c1e Merge branch 'DDC-3041' into 2.4 2014-03-23 10:18:36 +01:00
Menno Holtkamp
6bb367f488 Added test to ensure boolean metadata is properly exported/serialized to XML 2014-03-23 10:16:43 +01:00
Menno Holtkamp
213cc5c695 Use boolean values for 'unique' attribute
As defined in: https://github.com/doctrine/doctrine2/blob/master/doctrine-mapping.xsd#L294

Same as 'nullable' attribute. 

It was being exported as a "1" for TRUE and "0" for false
2014-03-23 10:16:43 +01:00
Benjamin Eberlei
a949e87ca8 Merge branch 'DDC-1985' into 2.4 2014-02-09 15:45:36 +01:00
Benjamin Eberlei
f18d0e093b [DDC-1985] Fix ordering preservation in SQL limit subquery output walker. 2014-02-09 15:45:28 +01:00
Benjamin Eberlei
0e139055f9 Merge branch 'DDC-2624' into 2.4 2014-02-09 14:31:15 +01:00
Benjamin Eberlei
b9c6659b70 Fix tests in 2.4 branch 2014-02-09 14:29:45 +01:00
Benjamin Eberlei
5c06121d94 [DDC-2624] Fix bug when persistent collection is cloned and used in a new entity. 2014-02-09 14:27:54 +01:00
Benjamin Eberlei
5bfa56aee0 Bump version to 2.4.3 2014-02-08 17:35:11 +01:00
30 changed files with 767 additions and 79 deletions

View File

@@ -14,7 +14,7 @@ Doctrine ORM don't panic. You can get help from different sources:
- There is a :doc:`FAQ <reference/faq>` with answers to frequent questions.
- The `Doctrine Mailing List <http://groups.google.com/group/doctrine-user>`_
- Internet Relay Chat (IRC) in `#doctrine on Freenode <irc://irc.freenode.net/doctrine>`_
- Internet Relay Chat (IRC) in #doctrine on Freenode
- Report a bug on `JIRA <http://www.doctrine-project.org/jira>`_.
- On `Twitter <https://twitter.com/search/%23doctrine2>`_ with ``#doctrine2``
- On `StackOverflow <http://stackoverflow.com/questions/tagged/doctrine2>`_
@@ -120,3 +120,4 @@ Cookbook
:doc:`MySQL Enums <cookbook/mysql-enums>`
:doc:`Advanced Field Value Conversion <cookbook/advanced-field-value-conversion-using-custom-mapping-types>`
.. include:: toc.rst

View File

@@ -106,7 +106,7 @@ Redis
In order to use the Redis cache driver you must have it compiled
and enabled in your php.ini. You can read about what is Redis
`from here <http://redis.io/>`_. Also check
`here <https://github.com/nicolasff/phpredis/>`_ for how you can use
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
and install Redis PHP extension.
Below is a simple example of how you could use the Redis cache

View File

@@ -100,7 +100,7 @@ Doctrine ships with a number of command line tools that are very helpful
during development. You can call this command from the Composer binary
directory:
.. code-block::
.. code-block:: sh
$ php vendor/bin/doctrine

View File

@@ -207,10 +207,13 @@ listeners:
- Lifecycle Callbacks are methods on the entity classes that are
called when the event is triggered. They receives some kind of ``EventArgs``.
called when the event is triggered. As of v2.4 they receive some kind
of ``EventArgs`` instance.
- Lifecycle Event Listeners and Subscribers are classes with specific callback
methods that receives some kind of ``EventArgs`` instance which
give access to the entity, EntityManager or other relevant data.
methods that receives some kind of ``EventArgs`` instance.
The EventArgs instance received by the listener gives access to the entity,
EntityManager and other relevant data.
.. note::
@@ -224,10 +227,11 @@ listeners:
Lifecycle Callbacks
-------------------
A lifecycle event is a regular event with the additional feature of
providing a mechanism to register direct callbacks inside the
corresponding entity classes that are executed when the lifecycle
event occurs.
Lifecycle Callbacks are defined on an entity class. They allow you to
trigger callbacks whenever an instance of that entity class experiences
a relevant lifecycle event. More than one callback can be defined for each
lifecycle event. Lifecycle Callbacks are best used for simple operations
specific to a particular entity class's lifecycle.
.. code-block:: php
@@ -277,8 +281,9 @@ event occurs.
}
}
Note that when using annotations you have to apply the
@HasLifecycleCallbacks marker annotation on the entity class.
Note that the methods set as lifecycle callbacks need to be public and,
when using these annotations, you have to apply the
``@HasLifecycleCallbacks`` marker annotation on the entity class.
If you want to register lifecycle callbacks from YAML or XML you
can do it with the following.
@@ -295,6 +300,10 @@ can do it with the following.
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
postPersist: [ doStuffOnPostPersist ]
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
are triggering on and the value is the method (or methods) to call. The allowed
event types are the ones listed in the previous Lifecycle Events section.
XML would look something like this:
.. code-block:: xml
@@ -317,9 +326,14 @@ XML would look something like this:
</doctrine-mapping>
You just need to make sure a public ``doStuffOnPrePersist()`` and
``doStuffOnPostPersist()`` method is defined on your ``User``
model.
In XML the ``type`` of the lifecycle-callback entry is the event that you
are triggering on and the ``method`` is the method to call. The allowed event
types are the ones listed in the previous Lifecycle Events section.
When using YAML or XML you need to remember to create public methods to match the
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
defined on your ``User`` model.
.. code-block:: php
@@ -375,8 +389,10 @@ Listening and subscribing to Lifecycle Events
Lifecycle event listeners are much more powerful than the simple
lifecycle callbacks that are defined on the entity classes. They
allow to implement re-usable behaviors between different entity
classes, yet require much more detailed knowledge about the inner
sit at a level above the entities and allow you to implement re-usable
behaviors across different entity classes.
Note that they require much more detailed knowledge about the inner
workings of the EntityManager and UnitOfWork. Please read the
*Implementing Event Listeners* section carefully if you are trying
to write your own listener.
@@ -476,8 +492,8 @@ data and lost updates/persists/removes.
For the described events that are also lifecycle callback events
the restrictions apply as well, with the additional restriction
that you do not have access to the EntityManager or UnitOfWork APIs
inside these events.
that (prior to version 2.4) you do not have access to the
EntityManager or UnitOfWork APIs inside these events.
prePersist
~~~~~~~~~~
@@ -501,11 +517,9 @@ The following restrictions apply to ``prePersist``:
- If you are using a PrePersist Identity Generator such as
sequences the ID value will *NOT* be available within any
PrePersist events.
- Doctrine will not recognize changes made to relations in a pre
persist event called by "reachability" through a cascade persist
unless you use the internal ``UnitOfWork`` API. We do not recommend
such operations in the persistence by reachability context, so do
this at your own risk and possibly supported by unit-tests.
- Doctrine will not recognize changes made to relations in a prePersist
event. This includes modifications to
collections such as additions, removals or replacement.
preRemove
~~~~~~~~~
@@ -699,7 +713,8 @@ Restrictions for this event:
recognized by the flush operation anymore.
- Changes to fields of the passed entities are not recognized by
the flush operation anymore, use the computed change-set passed to
the event to modify primitive field values.
the event to modify primitive field values, e.g. use
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
- Any calls to ``EntityManager#persist()`` or
``EntityManager#remove()``, even in combination with the UnitOfWork
API are strongly discouraged and don't work as expected outside the
@@ -769,9 +784,10 @@ An ``Entity Listener`` could be any class, by default it should be a class with
- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
- An entity listener method receives two arguments, the entity instance and the lifecycle event.
- A callback method could be defined by naming convention or specifying a method mapping.
- When the listener mapping is not given the parser will lookup for methods that match with the naming convention.
- When the listener mapping is given the parser won't lookup for any naming convention.
- The callback method can be defined by naming convention or specifying a method mapping.
- When a listener mapping is not given the parser will use the naming convention to look for a matching method,
e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event.
- When a listener mapping is given the parser will not look for any methods using the naming convention.
.. code-block:: php

View File

@@ -143,7 +143,7 @@ See the documentation chapter on :doc:`inheritance mapping <inheritance-mapping>
the details.
Why does Doctrine not create proxy objects for my inheritance hierarchy?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you set a many-to-one or one-to-one association target-entity to any parent class of
an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of.

View File

@@ -8,12 +8,14 @@ Tutorials
:maxdepth: 1
tutorials/getting-started
tutorials/getting-started-database
tutorials/getting-started-models
tutorials/working-with-indexed-associations
tutorials/extra-lazy-associations
tutorials/composite-primary-keys
tutorials/ordered-associations
tutorials/in-ten-quick-steps
tutorials/override-field-association-mappings-in-subclasses
tutorials/pagination.rst
Reference Guide
---------------
@@ -22,9 +24,9 @@ Reference Guide
:maxdepth: 1
:numbered:
reference/introduction
reference/architecture
reference/configuration
reference/installation
reference/configuration.rst
reference/faq
reference/basic-mapping
reference/association-mapping
@@ -51,9 +53,9 @@ Reference Guide
reference/metadata-drivers
reference/best-practices
reference/limitations-and-known-issues
tutorials/pagination.rst
reference/filters.rst
reference/namingstrategy.rst
reference/advanced-configuration.rst
Cookbook
@@ -63,6 +65,7 @@ Cookbook
:maxdepth: 1
cookbook/aggregate-fields
cookbook/custom-mapping-types
cookbook/decorator-pattern
cookbook/dql-custom-walkers
cookbook/dql-user-defined-functions
@@ -70,6 +73,7 @@ Cookbook
cookbook/implementing-the-notify-changetracking-policy
cookbook/implementing-wakeup-or-clone
cookbook/integrating-with-codeigniter
cookbook/resolve-target-entity-listener
cookbook/sql-table-prefixes
cookbook/strategy-cookbook-introduction
cookbook/validation-of-entities

View File

@@ -88,4 +88,18 @@ class HydrationException extends \Doctrine\ORM\ORMException
$discrColumnName, $entityName, $dqlAlias
));
}
/**
* @param string $discrValue
* @param array $discrMap
*
* @return HydrationException
*/
public static function invalidDiscriminatorValue($discrValue, $discrMap)
{
return new self(sprintf(
'The discriminator value "%s" is invalid. It must be one of "%s".',
$discrValue, implode('", "', $discrMap)
));
}
}

View File

@@ -266,7 +266,13 @@ class ObjectHydrator extends AbstractHydrator
throw HydrationException::emptyDiscriminatorValue($dqlAlias);
}
$className = $this->ce[$className]->discriminatorMap[$data[$discrColumn]];
$discrMap = $this->ce[$className]->discriminatorMap;
if ( ! isset($discrMap[$data[$discrColumn]])) {
throw HydrationException::invalidDiscriminatorValue($data[$discrColumn], array_keys($discrMap));
}
$className = $discrMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}

View File

@@ -98,7 +98,13 @@ class SimpleObjectHydrator extends AbstractHydrator
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
}
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
$discrMap = $this->class->discriminatorMap;
if ( ! isset($discrMap[$sqlResult[$discrColumnName]])) {
throw HydrationException::invalidDiscriminatorValue($sqlResult[$discrColumnName], array_keys($discrMap));
}
$entityName = $discrMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
}

View File

@@ -866,7 +866,11 @@ class ClassMetadataInfo implements ClassMetadata
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
if (PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513) {
$this->_prototype = $this->reflClass->newInstanceWithoutConstructor();
} else {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
}
return clone $this->_prototype;

View File

@@ -1497,6 +1497,7 @@ class SqlWalker implements TreeWalker
break;
}
$fieldType = 'string';
switch (true) {
case ($e instanceof AST\PathExpression):
$fieldName = $e->field;
@@ -1517,10 +1518,6 @@ class SqlWalker implements TreeWalker
break;
}
break;
default:
$fieldType = 'string';
break;
}
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;

View File

@@ -22,7 +22,7 @@ namespace Doctrine\ORM\Tools\Console;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\ORM\Version;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
@@ -35,10 +35,10 @@ class ConsoleRunner
/**
* Create a Symfony Console HelperSet
*
* @param EntityManager $entityManager
* @param EntityManagerInterface $entityManager
* @return HelperSet
*/
public static function createHelperSet(EntityManager $entityManager)
public static function createHelperSet(EntityManagerInterface $entityManager)
{
return new HelperSet(array(
'db' => new ConnectionHelper($entityManager->getConnection()),

View File

@@ -193,7 +193,7 @@ class XmlExporter extends AbstractExporter
}
if (isset($field['unique']) && $field['unique']) {
$fieldXml->addAttribute('unique', $field['unique']);
$fieldXml->addAttribute('unique', $field['unique'] ? 'true' : 'false');
}
if (isset($field['options'])) {

View File

@@ -100,9 +100,9 @@ class LimitSubqueryOutputWalker extends SqlWalker
$hiddens[$idx] = $expr->hiddenAliasResultVariable;
$expr->hiddenAliasResultVariable = false;
}
$innerSql = parent::walkSelectStatement($AST);
// Restore hiddens
foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
$expr->hiddenAliasResultVariable = $hiddens[$idx];
@@ -160,11 +160,8 @@ class LimitSubqueryOutputWalker extends SqlWalker
$sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result',
implode(', ', $sqlIdentifier), $innerSql);
if ($this->platform instanceof PostgreSqlPlatform ||
$this->platform instanceof OraclePlatform) {
//http://www.doctrine-project.org/jira/browse/DDC-1958
$this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql);
}
// http://www.doctrine-project.org/jira/browse/DDC-1958
$sql = $this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql);
// Apply the limit and offset.
$sql = $this->platform->modifyLimitQuery(
@@ -181,7 +178,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
return $sql;
}
/**
* Generates new SQL for Postgresql or Oracle if necessary.
*
@@ -192,7 +189,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
*
* @return void
*/
public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql)
public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, $sql)
{
// For every order by, find out the SQL alias by inspecting the ResultSetMapping.
$sqlOrderColumns = array();
@@ -215,11 +212,6 @@ class LimitSubqueryOutputWalker extends SqlWalker
$sqlOrderColumns = array_diff($sqlOrderColumns, $sqlIdentifier);
}
// We don't need orderBy in inner query.
// However at least on 5.4.6 I'm getting a segmentation fault and thus we don't clear it for now.
/*$AST->orderByClause = null;
$innerSql = parent::walkSelectStatement($AST);*/
if (count($orderBy)) {
$sql = sprintf(
'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s',
@@ -228,5 +220,7 @@ class LimitSubqueryOutputWalker extends SqlWalker
implode(', ', $orderBy)
);
}
return $sql;
}
}

View File

@@ -541,7 +541,15 @@ class UnitOfWork implements PropertyChangedListener
foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity);
if ($class->isCollectionValuedAssociation($name) && $value !== null && ! ($value instanceof PersistentCollection)) {
if ($class->isCollectionValuedAssociation($name) && $value !== null) {
if ($value instanceof PersistentCollection) {
if ($value->getOwner() === $entity) {
continue;
}
$value = new ArrayCollection($value->getValues());
}
// If $value is not a Collection then use an ArrayCollection.
if ( ! $value instanceof Collection) {
$value = new ArrayCollection($value);
@@ -894,20 +902,24 @@ class UnitOfWork implements PropertyChangedListener
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) {
if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
&& ($name !== $class->versionField)
&& ! $class->isCollectionValuedAssociation($name)) {
$actualData[$name] = $refProp->getValue($entity);
}
}
if ( ! isset($this->originalEntityData[$oid])) {
throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
}
$originalData = $this->originalEntityData[$oid];
$changeSet = array();
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (is_object($orgValue) && $orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
} else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
if ($orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
}
@@ -915,8 +927,10 @@ class UnitOfWork implements PropertyChangedListener
if ($changeSet) {
if (isset($this->entityChangeSets[$oid])) {
$this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
} else if ( ! isset($this->entityInsertions[$oid])) {
$this->entityChangeSets[$oid] = $changeSet;
$this->entityUpdates[$oid] = $entity;
}
$this->originalEntityData[$oid] = $actualData;
}
}
@@ -2479,16 +2493,16 @@ class UnitOfWork implements PropertyChangedListener
? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]
: $data[$fieldName];
}
$idHash = implode(' ', $id);
} else {
$idHash = isset($class->associationMappings[$class->identifier[0]])
$id = isset($class->associationMappings[$class->identifier[0]])
? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]
: $data[$class->identifier[0]];
$id = array($class->identifier[0] => $idHash);
$id = array($class->identifier[0] => $id);
}
$idHash = implode(' ', $id);
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
$entity = $this->identityMap[$class->rootEntityName][$idHash];
$oid = spl_object_hash($entity);

View File

@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.4.2';
const VERSION = '2.4.4';
/**
* Compares a Doctrine version with the current one.

View File

@@ -137,6 +137,11 @@ class ECommerceProduct
}
}
public function setCategories($categories)
{
$this->categories = $categories;
}
public function getCategories()
{
return $this->categories;
@@ -166,6 +171,9 @@ class ECommerceProduct
public function __clone()
{
$this->isCloned = true;
if ($this->categories) {
$this->categories = clone $this->categories;
}
}
/**

View File

@@ -48,8 +48,9 @@ class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testPersistUpdate()
{
// Considering case (a)
$proxy = $this->_em->getProxyFactory()->getProxy('Doctrine\Tests\Models\CMS\CmsUser', array('id' => null));
$proxy = $this->_em->getProxyFactory()->getProxy('Doctrine\Tests\Models\CMS\CmsUser', array('id' => 123));
$proxy->__isInitialized__ = true;
$proxy->id = null;
$proxy->username = 'ocra';
$proxy->name = 'Marco';
$this->_em->persist($proxy);

View File

@@ -12,6 +12,12 @@ use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
*/
class DDC2074Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('ecommerce');
parent::setUp();
}
public function testShouldNotScheduleDeletionOnClonedInstances()
{
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
@@ -26,4 +32,30 @@ class DDC2074Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0, count($uow->getScheduledCollectionDeletions()));
}
}
public function testSavingClonedPersistentCollection()
{
$product = new ECommerceProduct();
$category = new ECommerceCategory();
$category->setName('foo');
$product->addCategory($category);
$this->_em->persist($product);
$this->_em->persist($category);
$this->_em->flush();
$newProduct = clone $product;
$this->_em->persist($newProduct);
$this->_em->flush();
$this->_em->clear();
$product1 = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $product->getId());
$product2 = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $newProduct->getId());
$this->assertCount(1, $product1->getCategories());
$this->assertCount(1, $product2->getCategories());
$this->assertSame($product1->getCategories()->get(0), $product2->getCategories()->get(0));
}
}

View File

@@ -114,6 +114,5 @@ class DDC2931User
public function __wakeup()
{
echo 'foo';
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\Type;
/**
* @group DDC-2984
*/
class DDC2984Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
if ( ! Type::hasType('ddc2984_domain_user_id')) {
Type::addType(
'ddc2984_domain_user_id',
__NAMESPACE__ . '\DDC2984UserIdCustomDbalType'
);
}
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2984User'),
));
} catch (\Exception $e) {
// no action needed - schema seems to be already in place
}
}
public function testIssue()
{
$user = new DDC2984User(new DDC2984DomainUserId('unique_id_within_a_vo'));
$user->applyName('Alex');
$this->_em->persist($user);
$this->_em->flush($user);
$repository = $this->_em->getRepository(__NAMESPACE__ . "\DDC2984User");
$sameUser = $repository->find(new DDC2984DomainUserId('unique_id_within_a_vo'));
//Until know, everything works as expected
$this->assertTrue($user->sameIdentityAs($sameUser));
$this->_em->clear();
//After clearing the identity map, the UnitOfWork produces the warning described in DDC-2984
$equalUser = $repository->find(new DDC2984DomainUserId('unique_id_within_a_vo'));
$this->assertNotSame($user, $equalUser);
$this->assertTrue($user->sameIdentityAs($equalUser));
}
}
/** @Entity @Table(name="users") */
class DDC2984User
{
/**
* @Id @Column(type="ddc2984_domain_user_id")
* @GeneratedValue(strategy="NONE")
*
* @var DDC2984DomainUserId
*/
private $userId;
/** @Column(type="string", length=50) */
private $name;
public function __construct(DDC2984DomainUserId $aUserId)
{
$this->userId = $aUserId;
}
/**
* @return DDC2984DomainUserId
*/
public function userId()
{
return $this->userId;
}
/**
* @return string
*/
public function name()
{
return $this->name;
}
/**
* @param string $name
*/
public function applyName($name)
{
$this->name = $name;
}
/**
* @param DDC2984User $other
* @return bool
*/
public function sameIdentityAs(DDC2984User $other)
{
return $this->userId()->sameValueAs($other->userId());
}
}
/**
* DDC2984DomainUserId ValueObject
*
* @author Alexander Miertsch <kontakt@codeliner.ws>
*/
class DDC2984DomainUserId
{
/**
* @var string
*/
private $userIdString;
/**
* @param string $aUserIdString
*/
public function __construct($aUserIdString)
{
$this->userIdString = $aUserIdString;
}
/**
* @return string
*/
public function toString()
{
return $this->userIdString;
}
/**
* @return string
*/
public function __toString()
{
return $this->toString();
}
/**
* @param DDC2984DomainUserId $other
* @return bool
*/
public function sameValueAs(DDC2984DomainUserId $other)
{
return $this->toString() === $other->toString();
}
}
/**
* Class DDC2984UserIdCustomDbalType
*
* @author Alexander Miertsch <kontakt@codeliner.ws>
*/
class DDC2984UserIdCustomDbalType extends StringType
{
public function getName()
{
return 'ddc2984_domain_user_id';
}
/**
* {@inheritDoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return ! empty($value)
? new DDC2984DomainUserId($value)
: null;
}
/**
* {@inheritDoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (empty($value)) {
return null;
}
if (is_string($value)) {
return $value;
}
if ( ! $value instanceof DDC2984DomainUserId) {
throw ConversionException::conversionFailed($value, $this->getName());
}
return $value->toString();
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* @group DDC-2996
*/
class DDC2996Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function testIssue()
{
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2996User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2996UserPreference'),
));
$pref = new DDC2996UserPreference();
$pref->user = new DDC2996User();
$pref->value = "foo";
$this->_em->persist($pref);
$this->_em->persist($pref->user);
$this->_em->flush();
$pref->value = "bar";
$this->_em->flush();
$this->assertEquals(1, $pref->user->counter);
$this->_em->clear();
$pref = $this->_em->find(__NAMESPACE__ . '\\DDC2996UserPreference', $pref->id);
$this->assertEquals(1, $pref->user->counter);
}
}
/**
* @Entity
*/
class DDC2996User
{
/**
* @Id @GeneratedValue @Column(type="integer")
*/
public $id;
/**
* @Column(type="integer")
*/
public $counter = 0;
}
/**
* @Entity @HasLifecycleCallbacks
*/
class DDC2996UserPreference
{
/**
* @Id @GeneratedValue @Column(type="integer")
*/
public $id;
/**
* @Column(type="string")
*/
public $value;
/**
* @ManyToOne(targetEntity="DDC2996User")
*/
public $user;
/**
* @PreFlush
*/
public function preFlush($event)
{
$em = $event->getEntityManager();
$uow = $em->getUnitOfWork();
if ($uow->getOriginalEntityData($this->user)) {
$this->user->counter++;
$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($this->user)),
$this->user
);
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* @group DDC-3033
*/
class DDC3033Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function testIssue()
{
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC3033User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC3033Product'),
));
$user = new DDC3033User();
$user->name = "Test User";
$this->_em->persist($user);
$user2 = new DDC3033User();
$user2->name = "Test User 2";
$this->_em->persist($user2);
$product = new DDC3033Product();
$product->title = "Test product";
$product->buyers[] = $user;
$this->_em->persist($product);
$this->_em->flush();
$product->title = "Test Change title";
$product->buyers[] = $user2;
$this->_em->persist($product);
$this->_em->flush();
$expect = array(
'title' => array(
0 => 'Test product',
1 => 'Test Change title',
),
);
$this->assertEquals($expect, $product->changeSet);
}
}
/**
* @Table
* @Entity @HasLifecycleCallbacks
*/
class DDC3033Product
{
public $changeSet = array();
/**
* @var integer $id
*
* @Column(name="id", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @var string $title
*
* @Column(name="title", type="string", length=255)
*/
public $title;
/**
* @ManyToMany(targetEntity="DDC3033User")
* @JoinTable(
* name="user_purchases_3033",
* joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
public $buyers;
/**
* Default constructor
*/
public function __construct()
{
$this->buyers = new ArrayCollection();
}
/**
* @PreUpdate
*/
public function preUpdate(LifecycleEventArgs $eventArgs)
{
}
/**
* @PostUpdate
*/
public function postUpdate(LifecycleEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
$entity = $eventArgs->getEntity();
$classMetadata = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($classMetadata, $entity);
$this->changeSet = $uow->getEntityChangeSet($entity);
}
}
/**
* @Table
* @Entity @HasLifecycleCallbacks
*/
class DDC3033User
{
/**
* @var integer
*
* @Column(name="id", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @var string
*
* @Column(name="title", type="string", length=255)
*/
public $name;
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* FlushEventTest
*
* @author robo
*/
class DDC3160Test extends OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
}
/**
* @group DDC-3160
*/
public function testNoUpdateOnInsert()
{
$listener = new DDC3160OnFlushListener();
$this->_em->getEventManager()->addEventListener(Events::onFlush, $listener);
$user = new CmsUser;
$user->username = 'romanb';
$user->name = 'Roman';
$user->status = 'Dev';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->refresh($user);
$this->assertEquals('romanc', $user->username);
$this->assertEquals(1, $listener->inserts);
$this->assertEquals(0, $listener->updates);
}
}
class DDC3160OnFlushListener
{
public $inserts = 0;
public $updates = 0;
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
$this->inserts++;
if ($entity instanceof CmsUser) {
$entity->username = 'romanc';
$cm = $em->getClassMetadata(get_class($entity));
$uow->recomputeSingleEntityChangeSet($cm, $entity);
}
}
foreach ($uow->getScheduledEntityUpdates() as $entity) {
$this->updates++;
}
}
}

View File

@@ -78,7 +78,7 @@ class DDC742Test extends \Doctrine\Tests\OrmFunctionalTestCase
/**
* @Entity
* @Table(name="users")
* @Table(name="ddc742_users")
*/
class DDC742User
{
@@ -111,7 +111,7 @@ class DDC742User
/**
* @Entity
* @Table(name="comments")
* @Table(name="ddc742_comments")
*/
class DDC742Comment
{

View File

@@ -1928,4 +1928,34 @@ class ObjectHydratorTest extends HydrationTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$hydrator->hydrateAll($stmt, $rsm);
}
/**
* @group DDC-3076
*
* @expectedException \Doctrine\ORM\Internal\Hydration\HydrationException
* @expectedExceptionMessage The discriminator value "subworker" is invalid. It must be one of "person", "manager", "employee".
*/
public function testInvalidDiscriminatorValueException()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\Company\CompanyPerson', 'p');
$rsm->addFieldResult('p', 'p__id', 'id');
$rsm->addFieldResult('p', 'p__name', 'name');
$rsm->addMetaResult('p', 'discr', 'discr');
$rsm->setDiscriminatorColumn('p', 'discr');
$resultSet = array(
array(
'p__id' => '1',
'p__name' => 'Fabio B. Silva',
'discr' => 'subworker'
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$hydrator->hydrateAll($stmt, $rsm);
}
}

View File

@@ -58,4 +58,34 @@ class SimpleObjectHydratorTest extends HydrationTestCase
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals($result[0], $expectedEntity);
}
/**
* @group DDC-3076
*
* @expectedException \Doctrine\ORM\Internal\Hydration\HydrationException
* @expectedExceptionMessage The discriminator value "subworker" is invalid. It must be one of "person", "manager", "employee".
*/
public function testInvalidDiscriminatorValueException()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\Company\CompanyPerson', 'p');
$rsm->addFieldResult('p', 'p__id', 'id');
$rsm->addFieldResult('p', 'p__name', 'name');
$rsm->addMetaResult('p', 'discr', 'discr');
$rsm->setDiscriminatorColumn('p', 'discr');
$resultSet = array(
array(
'p__id' => '1',
'p__name' => 'Fabio B. Silva',
'discr' => 'subworker'
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator($this->_em);
$hydrator->hydrateAll($stmt, $rsm);
}
}

View File

@@ -592,12 +592,20 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
}
/**
* @gorup DDC-1858
* @group DDC-1858
*/
public function testHavingSupportIsNullExpression()
{
$this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL");
}
/**
* @group DDC-3018
*/
public function testNewLiteralExpression()
{
$this->assertValidDQL("SELECT new " . __NAMESPACE__ . "\\DummyStruct(u.id, 'foo', 1, true) FROM Doctrine\Tests\Models\CMS\CmsUser u");
}
}
/** @Entity */
@@ -617,3 +625,10 @@ class DQLKeywordsModelGroup
/** @Column */
private $from;
}
class DummyStruct
{
public function __construct($id, $arg1, $arg2, $arg3)
{
}
}

View File

@@ -1990,15 +1990,15 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"SELECT c0_.id || c0_.name || c0_.status AS sclr0 FROM cms_users c0_ WHERE c0_.id = ?"
);
$connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\SQLServerPlatform());
/*$connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\SQLServerPlatform());
$this->assertSqlGeneration(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1",
"SELECT c0_.id AS id0 FROM cms_users c0_ WITH (NOLOCK) WHERE (c0_.name + c0_.status + 's') = ?"
"SELECT c0_.id AS id0 FROM cms_users c0_ WHERE (c0_.name + c0_.status + 's') = ?"
);
$this->assertSqlGeneration(
"SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1",
"SELECT (c0_.id + c0_.name + c0_.status) AS sclr0 FROM cms_users c0_ WITH (NOLOCK) WHERE c0_.id = ?"
);
"SELECT (c0_.id + c0_.name + c0_.status) AS sclr0 FROM cms_users c0_ WHERE c0_.id = ?"
);*/
$connMock->setDatabasePlatform($orgPlatform);
}

View File

@@ -209,6 +209,27 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
return $class;
}
/**
* @depends testExportDirectoryAndFilesAreCreated
*/
public function testFieldsAreProperlySerialized()
{
$type = $this->_getType();
if ($type == 'xml') {
$xml = simplexml_load_file(__DIR__ . '/export/'.$type.'/Doctrine.Tests.ORM.Tools.Export.ExportedUser.dcm.xml');
$xml->registerXPathNamespace("d", "http://doctrine-project.org/schemas/orm/doctrine-mapping");
$nodes = $xml->xpath("/d:doctrine-mapping/d:entity/d:field[@name='name' and @type='string' and @nullable='true']");
$this->assertEquals(1, count($nodes));
$nodes = $xml->xpath("/d:doctrine-mapping/d:entity/d:field[@name='name' and @type='string' and @unique='true']");
$this->assertEquals(1, count($nodes));
}
else {
$this->markTestSkipped('Test available only for '.$type.' driver');
}
}
/**
* @depends testFieldsAreExported
* @param ClassMetadataInfo $class