Compare commits

..

107 Commits

Author SHA1 Message Date
Marco Pivetta c0d3cdbdfb Release 2.4.5 2014-09-22 23:58:51 +02:00
Marco Pivetta a50ae2c898 Merge pull request #1142 from TwoWholeWorms/2.4
func_get_args() call order fix for HHVM bug
2014-09-22 23:43:46 +02:00
Benjamin Nolan dbbe7a4be5 Fix for HHVM 2014-09-22 20:00:15 +01:00
Marco Pivetta 406e177062 Bump version to 2.4.5 2014-07-11 05:13:19 +02:00
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 https://github.com/doctrine/doctrine2/commit/53a5a48aed7d87aa1533c0bcbd72e41b686527d8

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
Benjamin Eberlei 0363a5548d Release 2.4.2 2014-02-08 17:35:09 +01:00
Benjamin Eberlei 3764e49e6c Merge branch 'DDC-2895' into 2.4 2014-02-08 16:01:57 +01:00
Geoffrey Wagner 6ee20204a5 Fix some code standard things 2014-02-08 16:01:41 +01:00
Geoffrey Wagner d9b0c87ded Fix some code standard things 2014-02-08 16:01:41 +01:00
Geoffrey Wagner 8594e5c4da Add a test
addLifecycleCallback now only allows a callback once so we do not hook them twice
2014-02-08 16:01:41 +01:00
Geoffrey Wagner 5f821f3b98 Fix Lifecycle Callbacks
Remove a bit of code that breaks lifecycle callbacks of parent MappedSuperclasses
2014-02-08 16:01:41 +01:00
Benjamin Eberlei b566525099 Merge branch 'DDC-2931' into 2.4 2014-02-08 15:53:12 +01:00
Marco Pivetta 215c4a03e1 DDC-2931 - Removing previous broken fix for DDC-2931 - hardened 2014-02-08 15:52:46 +01:00
Marco Pivetta b3ccd6466b DDC-2931 - Safe comparison between proxies and entities when refreshing objects 2014-02-08 15:52:46 +01:00
Marco Pivetta b596bbb29f DDC-2931 - adding test that verifies that fetch-joined entities get refreshed with hints 2014-02-08 15:52:46 +01:00
Marco Pivetta c204e6c6a1 DDC-2931 - removing old comments 2014-02-08 15:52:46 +01:00
Marco Pivetta 0bc94589e1 DDC-2931 - Removing refresh hints when fetching association data in hydrators 2014-02-08 15:52:45 +01:00
Marco Pivetta f37856829f DDC-2931 - Detailed explanation 2014-02-08 15:52:45 +01:00
Marco Pivetta 157c793810 DDC-2931 - cleaning up code formatting/simplifying test case 2014-02-08 15:52:45 +01:00
root 72d838a804 [DDC-2931] testcase to reproduce Jira 2931 2014-02-08 15:52:45 +01:00
Benjamin Eberlei 58f8dc5d4c Update UPGRADE.md notes with BC mention. 2014-02-08 15:42:09 +01:00
Benjamin Eberlei 7d3ecd9481 Merge branch 'DDC-2947' into 2.4 2014-02-08 15:31:56 +01:00
Tim Lieberman 1bb55703a7 Make SchemaTool and SchemaValidator use EntityManagerInterface instead of EntityManager 2014-02-08 15:31:08 +01:00
Tim Lieberman 56cbcec13d Substitute EntityManagerInterface for EntityManager in Console EntityManagerHelper 2014-02-08 15:31:07 +01:00
Tim Lieberman 837c19bfc0 Console EntityManagerHelper now accepts EntityManagerInterface as constructor argument, instead of insisting on an EntityManager 2014-02-08 15:31:07 +01:00
Benjamin Eberlei 7b8f09ee4a Merge branch 'DDC-2700' into 2.4 2014-01-02 23:51:07 +01:00
Benjamin Eberlei 488a4dc78a [DDC-2700] Add test and fix CS. 2014-01-02 23:50:37 +01:00
Alex Pogodin 1364b6acc6 Identifier can be empty for MappedSuperclasses
When MappedSuperclass is inspected without identifier column been assigned, always return false. Solves "Undefined offset" notice.
2014-01-02 23:50:37 +01:00
Benjamin Eberlei 3dbe181762 Merge branch 'DDC-2732' into 2.4 2014-01-02 23:34:44 +01:00
Benjamin Eberlei a3acaab65c [DDC-2732] Add tests for XML id options fix. 2014-01-02 23:34:17 +01:00
Eduardo f183d25a33 Options not respected for ID Fields in XML Mapping Driver (XSD update)
XSD update.

The same bug of the yaml driver: see http://www.doctrine-project.org/jira/browse/DDC-2661
2014-01-02 23:34:17 +01:00
Eduardo 7c8350094e Options not respected for ID Fields in XML Mapping Driver
Same bug of the YAML driver, see: http://www.doctrine-project.org/jira/browse/DDC-2661
2014-01-02 23:34:17 +01:00
Benjamin Eberlei c613410ba6 Merge branch 'DDC-2764' into 2.4 2014-01-02 23:16:56 +01:00
Sander Marechal 6bb7581dd7 Add rootAlias to Criteria where clauses 2014-01-02 23:16:35 +01:00
Sander Marechal ab71dab7d1 Set rootAlias outside loop 2014-01-02 23:15:31 +01:00
Sander Marechal 2c114756bd [DDC-2764] Prefix criteria orderBy with rootAlias 2014-01-02 23:15:31 +01:00
Benjamin Eberlei 45496f040d Merge branch 'DDC-2775' into 2.4 2014-01-02 23:11:16 +01:00
Benjamin Eberlei b40866c624 [DDC-2775] cleanup test. 2014-01-02 23:11:07 +01:00
Matthieu Napoli a89cc7abea Inlined the model for the DCC2775 test case inside the test class 2014-01-02 23:07:53 +01:00
Matthieu Napoli 5ac111e5f8 Cleaned up tests for DDC-2775 2014-01-02 23:07:53 +01:00
Matthieu Napoli c5f66e6e7f Fixed tests failing in pgsql because of used of a reserved keyword 2014-01-02 23:07:53 +01:00
Matthieu Napoli b59f495875 Fixed tests for pgsql: was using reserved keyword as table name 2014-01-02 23:07:53 +01:00
Matthieu Napoli 3829b9c28b [DDC-2775] Bugfix 2014-01-02 23:07:53 +01:00
Matthieu Napoli 65bcdbf4c7 [DDC-2775] Tests reproducing DDC-2775 2014-01-02 23:07:53 +01:00
Benjamin Eberlei 95d000e51b Merge branch 'DDC-2692' into 2.4 2014-01-02 22:17:20 +01:00
Stefan Kleff 3657df3b01 Listener class prefix 2014-01-02 22:16:59 +01:00
Stefan Kleff 1661ffae9a removed unused use statements, fixed typo and group tag 2014-01-02 22:16:59 +01:00
Stefan Kleff b424a5cf14 Added unit test 2014-01-02 22:16:59 +01:00
Stefan Kleff 2767a4eec4 Multiple invokation of listeners on PreFlush event
Only lifecycle callbacks and entity listeners should be triggered here. The preFlush listener event is already triggered at the beginning of commit()
2014-01-02 22:16:59 +01:00
Benjamin Eberlei 9486867279 Merge branch 'DDC-2645' into 2.4 2013-12-15 23:34:57 +01:00
Pouyan Savoli 6f2bb08972 [DDC-2645] Apply patch to fix issue 2013-12-15 23:34:34 +01:00
Aaron Muylaert da2d3b406e Create failing test for DDC-2645.
Merge not dealing correctly with composite primary keys.
2013-12-15 23:34:34 +01:00
Benjamin Eberlei c4b7d3fbea Bump version to 2.4.2 2013-11-12 13:40:15 +01:00
Benjamin Eberlei 84373d05a4 Release 2.4.1 2013-11-12 13:40:13 +01:00
Benjamin Eberlei e82e7147fa Merge branch 'DDC-2715' into 2.4 2013-10-29 09:25:13 +01:00
jan brunnert e23ed2250d Removed unnecessary is_object() check 2013-10-29 09:24:52 +01:00
jan brunnert 192bb6fd21 When the OptimisticLockingException is generated with the static function lockFailedVersionMismatch and the passed parameters are DateTime instances, the exception could not be thrown because the DateTime object is not implicitly converted to a string. 2013-10-29 09:24:52 +01:00
Benjamin Eberlei 0f3679f034 Merge branch 'DDC-2759' into 2.4 2013-10-26 11:17:34 +02:00
Benjamin Eberlei 1d2cd82706 [DDC-2759] Fix regression in ArrayHydrator introduced in DDC-1884 at SHA c7b4c9bf0f 2013-10-26 11:16:53 +02:00
Chris Collins b983d86612 Added a failing test case for DDC-2759. 2013-10-26 11:16:53 +02:00
Benjamin Eberlei b11f01643c Merge branch 'DDC-2668' into 2.4 2013-09-26 23:24:14 +02:00
Fabio B. Silva b58fb8f5d4 [DDC-2668] Fix trim leading zero string 2013-09-26 23:23:49 +02:00
Benjamin Eberlei 925a22b71d Merge branch 'DDC-2608' into 2.4 2013-09-08 16:01:38 +02:00
Benjamin Eberlei 0f0d8abd67 [DDC-2608][DDC-2662] Fix SequenceGenerator requiring "sequenceName" and now throw exception. Fix a bug in quoting the sequenceName. 2013-09-08 16:00:14 +02:00
Benjamin Eberlei 470c15ce05 Merge branch 'DDC-2660' into 2.4 2013-09-08 14:39:54 +02:00
Benjamin Eberlei 3cc5fc0252 [DDC-2660] Fix error with NativeSQL, ResultSetMappingBuilder and Associations as Primary Key. 2013-09-08 14:39:25 +02:00
Benjamin Eberlei fd0657089a Merge branch 'DDC-2661' into 2.4 2013-09-08 10:38:03 +02:00
Benjamin Eberlei de3b237292 [DDC-2661] Fix bug in YamlDriver not passing options from id to mapField() 2013-09-08 10:37:42 +02:00
Benjamin Eberlei 1221cc3a2a More excludes 2013-09-07 18:27:20 +02:00
Benjamin Eberlei 9efbc1fa71 Bump version to 2.4.1 2013-09-07 18:19:57 +02:00
60 changed files with 1870 additions and 176 deletions
+6
View File
@@ -44,6 +44,12 @@ Now parenthesis are considered, the previous DQL will generate:
# Upgrade to 2.3
## Auto Discriminator Map breaks userland implementations with Listener
The new feature to detect discriminator maps automatically when none
are provided breaks userland implementations doing this with a
listener in ``loadClassMetadata`` event.
## EntityManager#find() not calls EntityRepository#find() anymore
Previous to 2.3, calling ``EntityManager#find()`` would be delegated to
+1 -1
View File
@@ -35,6 +35,6 @@
}
},
"archive": {
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor"]
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"]
}
}
+2 -1
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
+1 -1
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
+1 -1
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
+41 -25
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
+1 -1
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.
+8 -4
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
+1
View File
@@ -350,6 +350,7 @@
<xs:element name="generator" type="orm:generator" minOccurs="0" />
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
<xs:element name="custom-id-generator" type="orm:custom-id-generator" minOccurs="0" maxOccurs="1" />
<xs:element name="options" type="orm:options" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
@@ -190,17 +190,14 @@ class ArrayHydrator extends AbstractHydrator
( ! isset($baseElement[$relationAlias]))
) {
$baseElement[$relationAlias] = null;
} else if (
( ! isset($baseElement[$relationAlias])) ||
( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]))
) {
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = $data;
}
}
$coll =& $baseElement[$relationAlias];
if ($coll !== null) {
if (is_array($coll)) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
} else {
@@ -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)
));
}
}
@@ -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]);
}
@@ -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]);
}
+20 -3
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;
@@ -1031,9 +1035,14 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function isIdentifier($fieldName)
{
if ( ! $this->identifier) {
return false;
}
if ( ! $this->isIdentifierComposite) {
return $fieldName === $this->identifier[0];
}
return in_array($fieldName, $this->identifier);
}
@@ -2483,6 +2492,10 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function addLifecycleCallback($callback, $event)
{
if(isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
return;
}
$this->lifecycleCallbacks[$event][] = $callback;
}
@@ -2790,8 +2803,12 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function setSequenceGeneratorDefinition(array $definition)
{
if (isset($definition['name']) && $definition['name'] == '`') {
$definition['name'] = trim($definition['name'], '`');
if ( ! isset($definition['sequenceName'])) {
throw MappingException::missingSequenceName($this->name);
}
if ($definition['sequenceName'][0] == '`') {
$definition['sequenceName'] = trim($definition['sequenceName'], '`');
$definition['quoted'] = true;
}
@@ -450,12 +450,9 @@ class AnnotationDriver extends AbstractAnnotationDriver
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
/* @var $method \ReflectionMethod */
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
// filter for the declaring class only, callbacks from parents will already be registered.
if ($method->getDeclaringClass()->name !== $class->name) {
continue;
}
foreach ($this->getMethodCallbacks($method) as $value) {
$metadata->addLifecycleCallback($value[0], $value[1]);
}
}
@@ -278,6 +278,10 @@ class XmlDriver extends FileDriver
$mapping['columnDefinition'] = (string)$idElement['column-definition'];
}
if (isset($idElement->options)) {
$mapping['options'] = $this->_parseOptions($idElement->options->children());
}
$metadata->mapField($mapping);
if (isset($idElement->generator)) {
@@ -264,6 +264,10 @@ class YamlDriver extends FileDriver
$mapping['columnDefinition'] = $idElement['columnDefinition'];
}
if (isset($idElement['options'])) {
$mapping['options'] = $idElement['options'];
}
$metadata->mapField($mapping);
if (isset($idElement['generator'])) {
@@ -757,4 +757,16 @@ class MappingException extends \Doctrine\ORM\ORMException
$cascades
));
}
/**
* @param string $className
*
* @return MappingException
*/
public static function missingSequenceName($className)
{
return new self(
sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className)
);
}
}
@@ -73,6 +73,8 @@ class OptimisticLockException extends ORMException
*/
public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion)
{
$expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion;
$actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion;
return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity);
}
@@ -20,6 +20,8 @@
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
@@ -36,71 +38,60 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
class TrimFunction extends FunctionNode
{
/**
* @var bool
* @var boolean
*/
public $leading;
/**
* @var bool
* @var boolean
*/
public $trailing;
/**
* @var bool
* @var boolean
*/
public $both;
/**
* @var bool
* @var boolean
*/
public $trimChar = false;
/**
* @var \Doctrine\ORM\Query\AST\Node
*/
public $stringPrimary;
/**
* @override
* {@inheritdoc}
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
public function getSql(SqlWalker $sqlWalker)
{
$pos = AbstractPlatform::TRIM_UNSPECIFIED;
if ($this->leading) {
$pos = AbstractPlatform::TRIM_LEADING;
} else if ($this->trailing) {
$pos = AbstractPlatform::TRIM_TRAILING;
} else if ($this->both) {
$pos = AbstractPlatform::TRIM_BOTH;
}
$stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary);
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
$trimMode = $this->getTrimMode();
$trimChar = ($this->trimChar !== false)
? $sqlWalker->getConnection()->quote($this->trimChar)
: false;
return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression(
$sqlWalker->walkStringPrimary($this->stringPrimary),
$pos,
($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false
);
return $platform->getTrimExpression($stringPrimary, $trimMode, $trimChar);
}
/**
* @override
* {@inheritdoc}
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
public function parse(Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
if (strcasecmp('leading', $lexer->lookahead['value']) === 0) {
$parser->match(Lexer::T_LEADING);
$this->leading = true;
} else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) {
$parser->match(Lexer::T_TRAILING);
$this->trailing = true;
} else if (strcasecmp('both', $lexer->lookahead['value']) === 0) {
$parser->match(Lexer::T_BOTH);
$this->both = true;
}
$this->parseTrimMode($parser);
if ($lexer->isNextToken(Lexer::T_STRING)) {
$parser->match(Lexer::T_STRING);
$this->trimChar = $lexer->token['value'];
}
@@ -112,4 +103,61 @@ class TrimFunction extends FunctionNode
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
/**
* @param \Doctrine\ORM\Query\Parser $parser
*
* @return integer
*/
private function getTrimMode()
{
if ($this->leading) {
return AbstractPlatform::TRIM_LEADING;
}
if ($this->trailing) {
return AbstractPlatform::TRIM_TRAILING;
}
if ($this->both) {
return AbstractPlatform::TRIM_BOTH;
}
return AbstractPlatform::TRIM_UNSPECIFIED;
}
/**
* @param \Doctrine\ORM\Query\Parser $parser
*
* @return void
*/
private function parseTrimMode(Parser $parser)
{
$lexer = $parser->getLexer();
$value = $lexer->lookahead['value'];
if (strcasecmp('leading', $value) === 0) {
$parser->match(Lexer::T_LEADING);
$this->leading = true;
return;
}
if (strcasecmp('trailing', $value) === 0) {
$parser->match(Lexer::T_TRAILING);
$this->trailing = true;
return;
}
if (strcasecmp('both', $value) === 0) {
$parser->match(Lexer::T_BOTH);
$this->both = true;
return;
}
}
}
@@ -47,6 +47,11 @@ class QueryExpressionVisitor extends ExpressionVisitor
Comparison::LTE => Expr\Comparison::LTE
);
/**
* @var string
*/
private $rootAlias;
/**
* @var Expr
*/
@@ -58,10 +63,13 @@ class QueryExpressionVisitor extends ExpressionVisitor
private $parameters = array();
/**
* Constructor with internal initialization.
* Constructor
*
* @param string $rootAlias
*/
public function __construct()
public function __construct($rootAlias)
{
$this->rootAlias = $rootAlias;
$this->expr = new Expr();
}
@@ -133,33 +141,33 @@ class QueryExpressionVisitor extends ExpressionVisitor
switch ($comparison->getOperator()) {
case Comparison::IN:
$this->parameters[] = $parameter;
return $this->expr->in($comparison->getField(), $placeholder);
return $this->expr->in($this->rootAlias . '.' . $comparison->getField(), $placeholder);
case Comparison::NIN:
$this->parameters[] = $parameter;
return $this->expr->notIn($comparison->getField(), $placeholder);
return $this->expr->notIn($this->rootAlias . '.' . $comparison->getField(), $placeholder);
case Comparison::EQ:
case Comparison::IS:
if ($this->walkValue($comparison->getValue()) === null) {
return $this->expr->isNull($comparison->getField());
return $this->expr->isNull($this->rootAlias . '.' . $comparison->getField());
}
$this->parameters[] = $parameter;
return $this->expr->eq($comparison->getField(), $placeholder);
return $this->expr->eq($this->rootAlias . '.' . $comparison->getField(), $placeholder);
case Comparison::NEQ:
if ($this->walkValue($comparison->getValue()) === null) {
return $this->expr->isNotNull($comparison->getField());
return $this->expr->isNotNull($this->rootAlias . '.' . $comparison->getField());
}
$this->parameters[] = $parameter;
return $this->expr->neq($comparison->getField(), $placeholder);
return $this->expr->neq($this->rootAlias . '.' . $comparison->getField(), $placeholder);
default:
$operator = self::convertComparisonOperator($comparison->getOperator());
if ($operator) {
$this->parameters[] = $parameter;
return new Expr\Comparison(
$comparison->getField(),
$this->rootAlias . '.' . $comparison->getField(),
$operator,
$placeholder
);
@@ -168,7 +168,12 @@ class ResultSetMappingBuilder extends ResultSetMapping
throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $columnAlias, $columnName);
$this->addMetaResult(
$alias,
$columnAlias,
$columnName,
(isset($associationMapping['id']) && $associationMapping['id'] === true)
);
}
}
}
+1 -4
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;
+7 -6
View File
@@ -894,8 +894,8 @@ class QueryBuilder
*/
public function andWhere($where)
{
$where = $this->getDQLPart('where');
$args = func_get_args();
$where = $this->getDQLPart('where');
if ($where instanceof Expr\Andx) {
$where->addMultiple($args);
@@ -927,8 +927,8 @@ class QueryBuilder
*/
public function orWhere($where)
{
$where = $this->getDqlPart('where');
$args = func_get_args();
$where = $this->getDqlPart('where');
if ($where instanceof Expr\Orx) {
$where->addMultiple($args);
@@ -1007,8 +1007,8 @@ class QueryBuilder
*/
public function andHaving($having)
{
$having = $this->getDqlPart('having');
$args = func_get_args();
$having = $this->getDqlPart('having');
if ($having instanceof Expr\Andx) {
$having->addMultiple($args);
@@ -1030,8 +1030,8 @@ class QueryBuilder
*/
public function orHaving($having)
{
$having = $this->getDqlPart('having');
$args = func_get_args();
$having = $this->getDqlPart('having');
if ($having instanceof Expr\Orx) {
$having->addMultiple($args);
@@ -1087,7 +1087,8 @@ class QueryBuilder
*/
public function addCriteria(Criteria $criteria)
{
$visitor = new QueryExpressionVisitor();
$rootAlias = $this->getRootAlias();
$visitor = new QueryExpressionVisitor($rootAlias);
if ($whereExpression = $criteria->getWhereExpression()) {
$this->andWhere($visitor->dispatch($whereExpression));
@@ -1098,7 +1099,7 @@ class QueryBuilder
if ($criteria->getOrderings()) {
foreach ($criteria->getOrderings() as $sort => $order) {
$this->addOrderBy($sort, $order);
$this->addOrderBy($rootAlias . '.' . $sort, $order);
}
}
@@ -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()),
@@ -19,8 +19,9 @@
namespace Doctrine\ORM\Tools\Console\Helper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Helper\Helper;
use Doctrine\ORM\EntityManager;
/**
* Doctrine CLI Connection Helper.
@@ -35,18 +36,18 @@ use Doctrine\ORM\EntityManager;
class EntityManagerHelper extends Helper
{
/**
* Doctrine ORM EntityManager.
* Doctrine ORM EntityManagerInterface.
*
* @var EntityManager
* @var EntityManagerInterface
*/
protected $_em;
/**
* Constructor.
*
* @param \Doctrine\ORM\EntityManager $em
* @param EntityManagerInterface $em
*/
public function __construct(EntityManager $em)
public function __construct(EntityManagerInterface $em)
{
$this->_em = $em;
}
@@ -54,7 +55,7 @@ class EntityManagerHelper extends Helper
/**
* Retrieves Doctrine ORM EntityManager.
*
* @return EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -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'])) {
@@ -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;
}
}
+4 -4
View File
@@ -26,7 +26,7 @@ use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Internal\CommitOrderCalculator;
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
@@ -47,7 +47,7 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
class SchemaTool
{
/**
* @var \Doctrine\ORM\EntityManager
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $em;
@@ -67,9 +67,9 @@ class SchemaTool
* Initializes a new SchemaTool instance that uses the connection of the
* provided EntityManager.
*
* @param \Doctrine\ORM\EntityManager $em
* @param \Doctrine\ORM\EntityManagerInterface $em
*/
public function __construct(EntityManager $em)
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->platform = $em->getConnection()->getDatabasePlatform();
+4 -4
View File
@@ -19,7 +19,7 @@
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\DBAL\Types\Type;
@@ -37,14 +37,14 @@ use Doctrine\DBAL\Types\Type;
class SchemaValidator
{
/**
* @var EntityManager
* @var EntityManagerInterface
*/
private $em;
/**
* @param EntityManager $em
* @param EntityManagerInterface $em
*/
public function __construct(EntityManager $em)
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
+71 -16
View File
@@ -530,7 +530,7 @@ class UnitOfWork implements PropertyChangedListener
$class = $this->em->getClassMetadata(get_class($entity));
}
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush);
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER;
if ($invoke !== ListenersInvoker::INVOKE_NONE) {
$this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke);
@@ -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;
}
}
@@ -1736,6 +1750,8 @@ class UnitOfWork implements PropertyChangedListener
$associatedId = $this->getEntityIdentifier($idValue);
$flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]];
} else {
$flatId[$idField] = $idValue;
}
}
@@ -2231,6 +2247,8 @@ class UnitOfWork implements PropertyChangedListener
function ($assoc) { return $assoc['isCascadeRemove']; }
);
$entitiesToCascade = array();
foreach ($associationMappings as $assoc) {
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
$entity->__load();
@@ -2243,18 +2261,22 @@ class UnitOfWork implements PropertyChangedListener
case (is_array($relatedEntities)):
// If its a PersistentCollection initialization is intended! No unwrap!
foreach ($relatedEntities as $relatedEntity) {
$this->doRemove($relatedEntity, $visited);
$entitiesToCascade[] = $relatedEntity;
}
break;
case ($relatedEntities !== null):
$this->doRemove($relatedEntities, $visited);
$entitiesToCascade[] = $relatedEntities;
break;
default:
// Do nothing
}
}
foreach ($entitiesToCascade as $relatedEntity) {
$this->doRemove($relatedEntity, $visited);
}
}
/**
@@ -2471,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);
@@ -2490,14 +2512,14 @@ class UnitOfWork implements PropertyChangedListener
&& isset($hints[Query::HINT_REFRESH_ENTITY])
&& ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
&& $unmanagedProxy instanceof Proxy
&& (($unmanagedProxyClass = $this->em->getClassMetadata(get_class($unmanagedProxy))) === $class)
&& $this->isIdentifierEquals($unmanagedProxy, $entity)
) {
// DDC-1238 - we have a managed instance, but it isn't the provided one.
// Therefore we clear its identifier. Also, we must re-fetch metadata since the
// refreshed object may be anything
foreach ($unmanagedProxyClass->identifier as $fieldName) {
$unmanagedProxyClass->reflFields[$fieldName]->setValue($unmanagedProxy, null);
foreach ($class->identifier as $fieldName) {
$class->reflFields[$fieldName]->setValue($unmanagedProxy, null);
}
return $unmanagedProxy;
@@ -3206,4 +3228,37 @@ class UnitOfWork implements PropertyChangedListener
$this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
}
}
/**
* Verifies if two given entities actually are the same based on identifier comparison
*
* @param object $entity1
* @param object $entity2
*
* @return bool
*/
private function isIdentifierEquals($entity1, $entity2)
{
if ($entity1 === $entity2) {
return true;
}
$class = $this->em->getClassMetadata(get_class($entity1));
if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
return false;
}
$oid1 = spl_object_hash($entity1);
$oid2 = spl_object_hash($entity2);
$id1 = isset($this->entityIdentifiers[$oid1])
? $this->entityIdentifiers[$oid1]
: $this->flattenIdentifier($class, $class->getIdentifierValues($entity1));
$id2 = isset($this->entityIdentifiers[$oid2])
? $this->entityIdentifiers[$oid2]
: $this->flattenIdentifier($class, $class->getIdentifierValues($entity2));
return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.4.0';
const VERSION = '2.4.5';
/**
* Compares a Doctrine version with the current one.
@@ -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;
}
}
/**
@@ -7,6 +7,8 @@ use Doctrine\ORM\OptimisticLockException;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\Tests\TestUtil;
use Doctrine\DBAL\LockMode;
use DateTime;
require_once __DIR__ . '/../../../TestInit.php';
@@ -181,13 +183,44 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', array(date($format, strtotime($test->version->format($format)) + 3600), $test->id));
// Try and update the record and it should throw an exception
$caughtException = null;
$test->name = 'Testing again';
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
$caughtException = $e;
}
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
$this->assertSame($test, $caughtException->getEntity());
}
/**
* @depends testOptimisticTimestampSetsDefaultValue
*/
public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
$q->setParameter('id', $entity->id);
$test = $q->getSingleResult();
$this->assertInstanceOf('DateTime', $test->version);
// Try to lock the record with an older timestamp and it should throw an exception
$caughtException = null;
try {
$expectedVersionExpired = DateTime::createFromFormat('U', $test->version->getTimestamp()-3600);
$this->_em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired);
} catch (OptimisticLockException $e) {
$caughtException = $e;
}
$this->assertNotNull($caughtException, "No OptimisticLockingException was thrown");
$this->assertSame($test, $caughtException->getEntity());
}
}
/**
@@ -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);
@@ -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));
}
}
@@ -0,0 +1,55 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-2645
*/
class DDC2645Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function testIssue()
{
$bar = new DDC2645Bar;
$bar->id = 123;
$foo = new DDC2645Foo(1, $bar, 'Foo');
$foo2 = new DDC2645Foo(1, $bar, 'Bar');
$this->_em->persist($bar);
$this->_em->persist($foo);
$foo3 = $this->_em->merge($foo2);
$this->assertSame($foo, $foo3);
$this->assertEquals('Bar', $foo->name);
}
}
/** @Entity */
class DDC2645Foo
{
/** @Id @Column(type="integer") */
private $id;
/** @Id @ManyToOne(targetEntity="DDC2645Bar") */
private $bar;
/** @Column */
public $name;
public function __construct($id, $bar, $name)
{
$this->id = $id;
$this->bar = $bar;
$this->name = $name;
}
}
/** @Entity */
class DDC2645Bar
{
/** @Id @Column(type="integer") @GeneratedValue(strategy="NONE") */
public $id;
}
@@ -0,0 +1,122 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @group
*/
class DDC2660Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Product'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Customer'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder')
));
} catch(\Exception $e) {
return;
}
for ($i = 0; $i < 5; $i++) {
$product = new DDC2660Product();
$customer = new DDC2660Customer();
$order = new DDC2660CustomerOrder($product, $customer, 'name' . $i);
$this->_em->persist($product);
$this->_em->persist($customer);
$this->_em->flush();
$this->_em->persist($order);
$this->_em->flush();
}
$this->_em->clear();
}
public function testIssueWithExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
public function testIssueWithoutExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
}
/**
* @Entity @Table(name="ddc_2660_product")
*/
class DDC2660Product
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer") */
class DDC2660Customer
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer_order") */
class DDC2660CustomerOrder
{
/**
* @Id @ManyToOne(targetEntity="DDC2660Product")
*/
public $product;
/**
* @Id @ManyToOne(targetEntity="DDC2660Customer")
*/
public $customer;
/**
* @Column(type="string")
*/
public $name;
public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name)
{
$this->product = $product;
$this->customer = $customer;
$this->name = $name;
}
}
@@ -0,0 +1,63 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PreFlushEventArgs;
/**
* @group DDC-2692
*/
class DDC2692Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2692Foo'),
));
} catch(\Exception $e) {
return;
}
$this->_em->clear();
}
public function testIsListenerCalledOnlyOnceOnPreFlush()
{
$listener = $this->getMock('Doctrine\Tests\ORM\Functional\Ticket\DDC2692Listener', array('preFlush'));
$listener->expects($this->once())->method('preFlush');
$this->_em->getEventManager()->addEventSubscriber($listener);
$this->_em->persist(new DDC2692Foo);
$this->_em->persist(new DDC2692Foo);
$this->_em->flush();
$this->_em->clear();
}
}
/**
* @Entity @Table(name="ddc_2692_foo")
*/
class DDC2692Foo
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
class DDC2692Listener implements EventSubscriber {
public function getSubscribedEvents() {
return array(\Doctrine\ORM\Events::preFlush);
}
public function preFlush(PreFlushEventArgs $args) {
}
}
@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-2759
*/
class DDC2759Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'),
));
} catch(\Exception $e) {
return;
}
$qualification = new DDC2759Qualification();
$qualificationMetadata = new DDC2759QualificationMetadata($qualification);
$category1 = new DDC2759Category();
$category2 = new DDC2759Category();
$metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1);
$metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2);
$this->_em->persist($qualification);
$this->_em->persist($qualificationMetadata);
$this->_em->persist($category1);
$this->_em->persist($category2);
$this->_em->persist($metadataCategory1);
$this->_em->persist($metadataCategory2);
$this->_em->flush();
$this->_em->clear();
}
public function testCorrectNumberOfAssociationsIsReturned()
{
$repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification');
$builder = $repository->createQueryBuilder('q')
->select('q, qm, qmc')
->innerJoin('q.metadata', 'qm')
->innerJoin('qm.metadataCategories', 'qmc');
$result = $builder->getQuery()
->getArrayResult();
$this->assertCount(2, $result[0]['metadata']['metadataCategories']);
}
}
/** @Entity @Table(name="ddc_2759_qualification") */
class DDC2759Qualification
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */
public $metadata;
}
/** @Entity @Table(name="ddc_2759_category") */
class DDC2759Category
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */
public $metadataCategories;
}
/** @Entity @Table(name="ddc_2759_qualification_metadata") */
class DDC2759QualificationMetadata
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */
public $content;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */
protected $metadataCategories;
public function __construct(DDC2759Qualification $content)
{
$this->content = $content;
}
}
/** @Entity @Table(name="ddc_2759_metadata_category") */
class DDC2759MetadataCategory
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */
public $metadata;
/** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */
public $category;
public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category)
{
$this->metadata = $metadata;
$this->category = $category;
}
}
@@ -0,0 +1,146 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* Functional tests for cascade remove with class table inheritance.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class DDC2775Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->setUpEntitySchema(array(
'Doctrine\Tests\ORM\Functional\Ticket\User',
'Doctrine\Tests\ORM\Functional\Ticket\Role',
'Doctrine\Tests\ORM\Functional\Ticket\AdminRole',
'Doctrine\Tests\ORM\Functional\Ticket\Authorization',
));
}
/**
* @group DDC-2775
*/
public function testIssueCascadeRemove()
{
$user = new User();
$role = new AdminRole();
$user->addRole($role);
$authorization = new Authorization();
$user->addAuthorization($authorization);
$role->addAuthorization($authorization);
$this->_em->persist($user);
$this->_em->flush();
// Need to clear so that associations are lazy-loaded
$this->_em->clear();
$user = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\User', $user->id);
$this->_em->remove($user);
$this->_em->flush();
// With the bug, the second flush throws an error because the cascade remove didn't work correctly
$this->_em->flush();
}
}
/**
* @Entity @Table(name="ddc2775_role")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="role_type", type="string")
* @DiscriminatorMap({"admin"="AdminRole"})
*/
abstract class Role
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
public $id;
/**
* @ManyToOne(targetEntity="User", inversedBy="roles")
*/
public $user;
/**
* @OneToMany(targetEntity="Authorization", mappedBy="role", cascade={"all"}, orphanRemoval=true)
*/
public $authorizations;
public function addAuthorization(Authorization $authorization)
{
$this->authorizations[] = $authorization;
$authorization->role = $this;
}
}
/** @Entity @Table(name="ddc2775_admin_role") */
class AdminRole extends Role
{
}
/**
* @Entity @Table(name="ddc2775_authorizations")
*/
class Authorization
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
public $id;
/**
* @ManyToOne(targetEntity="User", inversedBy="authorizations")
*/
public $user;
/**
* @ManyToOne(targetEntity="Role", inversedBy="authorizations")
*/
public $role;
}
/**
* @Entity @Table(name="ddc2775_users")
*/
class User
{
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @OneToMany(targetEntity="Role", mappedBy="user", cascade={"all"}, orphanRemoval=true)
*/
public $roles;
/**
* @OneToMany(targetEntity="Authorization", mappedBy="user", cascade={"all"}, orphanRemoval=true)
*/
public $authorizations;
public function addRole(Role $role)
{
$this->roles[] = $role;
$role->user = $this;
}
public function addAuthorization(Authorization $authorization)
{
$this->authorizations[] = $authorization;
$authorization->user = $this;
}
}
@@ -0,0 +1,112 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* Class DDC2895Test
* @package Doctrine\Tests\ORM\Functional\Ticket
* @author http://github.com/gwagner
*/
class DDC2895Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2895'),
));
} catch(\Exception $e) {
}
}
public function testPostLoadOneToManyInheritance()
{
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC2895');
$this->assertEquals(
array(
"prePersist" => array("setLastModifiedPreUpdate"),
"preUpdate" => array("setLastModifiedPreUpdate"),
),
$cm->lifecycleCallbacks
);
$ddc2895 = new DDC2895();
$this->_em->persist($ddc2895);
$this->_em->flush();
$this->_em->clear();
/** @var DDC2895 $ddc2895 */
$ddc2895 = $this->_em->find(get_class($ddc2895), $ddc2895->id);
$this->assertNotNull($ddc2895->getLastModified());
}
}
/**
* @MappedSuperclass
* @HasLifecycleCallbacks
*/
abstract class AbstractDDC2895
{
/**
* @Column(name="last_modified", type="datetimetz", nullable=false)
* @var \DateTime
*/
protected $lastModified;
/**
* @PrePersist
* @PreUpdate
*/
public function setLastModifiedPreUpdate()
{
$this->setLastModified(new \DateTime());
}
/**
* @param \DateTime $lastModified
*/
public function setLastModified( $lastModified )
{
$this->lastModified = $lastModified;
}
/**
* @return \DateTime
*/
public function getLastModified()
{
return $this->lastModified;
}
}
/**
* @Entity
* @HasLifecycleCallbacks
*/
class DDC2895 extends AbstractDDC2895
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/**
* @param mixed $id
*/
public function setId( $id )
{
$this->id = $id;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Query;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-2931
*/
class DDC2931Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2931User'),
));
} catch (\Exception $e) {
// no action needed - schema seems to be already in place
}
}
public function testIssue()
{
$first = new DDC2931User();
$second = new DDC2931User();
$third = new DDC2931User();
$second->parent = $first;
$third->parent = $second;
$this->_em->persist($first);
$this->_em->persist($second);
$this->_em->persist($third);
$this->_em->flush();
$this->_em->clear();
$second = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC2931User', $second->id);
$this->assertSame(2, $second->getRank());
}
public function testFetchJoinedEntitiesCanBeRefreshed()
{
$first = new DDC2931User();
$second = new DDC2931User();
$third = new DDC2931User();
$second->parent = $first;
$third->parent = $second;
$first->value = 1;
$second->value = 2;
$third->value = 3;
$this->_em->persist($first);
$this->_em->persist($second);
$this->_em->persist($third);
$this->_em->flush();
$first->value = 4;
$second->value = 5;
$third->value = 6;
$refreshedSecond = $this
->_em
->createQuery(
'SELECT e, p, c FROM '
. __NAMESPACE__ . '\\DDC2931User e LEFT JOIN e.parent p LEFT JOIN e.child c WHERE e = :id'
)
->setParameter('id', $second)
->setHint(Query::HINT_REFRESH, true)
->getResult();
$this->assertCount(1, $refreshedSecond);
$this->assertSame(1, $first->value);
$this->assertSame(2, $second->value);
$this->assertSame(3, $third->value);
}
}
/** @Entity */
class DDC2931User
{
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
public $id;
/** @OneToOne(targetEntity="DDC2931User", inversedBy="child") */
public $parent;
/** @OneToOne(targetEntity="DDC2931User", mappedBy="parent") */
public $child;
/** @Column(type="integer") */
public $value = 0;
/**
* Return Rank recursively
* My rank is 1 + rank of my parent
* @return integer
*/
public function getRank()
{
return 1 + ($this->parent ? $this->parent->getRank() : 0);
}
public function __wakeup()
{
}
}
@@ -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();
}
}
@@ -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
);
}
}
}
@@ -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;
}
@@ -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++;
}
}
}
@@ -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
{
@@ -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);
}
}
@@ -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);
}
}
@@ -187,12 +187,32 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($class->fieldMappings['name']['nullable']);
$this->assertTrue($class->fieldMappings['name']['unique']);
return $class;
}
/**
* @depends testEntityTableNameAndInheritance
* @param ClassMetadata $class
*/
public function testFieldOptions($class)
{
$expected = array('foo' => 'bar', 'baz' => array('key' => 'val'));
$this->assertEquals($expected, $class->fieldMappings['name']['options']);
return $class;
}
/**
* @depends testEntityTableNameAndInheritance
* @param ClassMetadata $class
*/
public function testIdFieldOptions($class)
{
$this->assertEquals(array('foo' => 'bar'), $class->fieldMappings['id']['options']);
return $class;
}
/**
* @depends testFieldMappings
* @param ClassMetadata $class
@@ -890,7 +910,7 @@ class User
{
/**
* @Id
* @Column(type="integer")
* @Column(type="integer", options={"foo": "bar"})
* @generatedValue(strategy="AUTO")
* @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100)
**/
@@ -971,6 +991,7 @@ class User
'fieldName' => 'id',
'type' => 'integer',
'columnName' => 'id',
'options' => array('foo' => 'bar'),
));
$metadata->mapField(array(
'fieldName' => 'name',
@@ -1066,6 +1066,50 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals(array('customtypeparent_source' => 'id'), $cm->associationMappings['friendsWithMe']['relationToSourceKeyColumns']);
$this->assertEquals(array('customtypeparent_target' => 'id'), $cm->associationMappings['friendsWithMe']['relationToTargetKeyColumns']);
}
/**
* @group DDC-2608
*/
public function testSetSequenceGeneratorThrowsExceptionWhenSequenceNameIsMissing()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
$cm->setSequenceGeneratorDefinition(array());
}
/**
* @group DDC-2662
*/
public function testQuotedSequenceName()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setSequenceGeneratorDefinition(array('sequenceName' => '`foo`'));
$this->assertEquals(array('sequenceName' => 'foo', 'quoted' => true), $cm->sequenceGeneratorDefinition);
}
/**
* @group DDC-2700
*/
public function testIsIdentifierMappedSuperClass()
{
$class = new ClassMetadata(__NAMESPACE__ . '\\DDC2700MappedSuperClass');
$this->assertFalse($class->isIdentifier('foo'));
}
}
/**
* @MappedSuperclass
*/
class DDC2700MappedSuperClass
{
/** @Column */
private $foo;
}
class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
@@ -1092,4 +1136,4 @@ class MyPrefixNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
{
return strtolower($this->classToTableName($className)) . '_' . $propertyName;
}
}
}
@@ -19,6 +19,7 @@ $metadata->mapField(array(
'fieldName' => 'id',
'type' => 'integer',
'columnName' => 'id',
'options' => array('foo' => 'bar'),
));
$metadata->mapField(array(
'fieldName' => 'name',
@@ -35,6 +35,9 @@
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
<sequence-generator sequence-name="tablename_seq" allocation-size="100" initial-value="1" />
<options>
<option name="foo">bar</option>
</options>
</id>
<field name="name" column="name" type="string" length="50" nullable="true" unique="true">
@@ -16,6 +16,8 @@ Doctrine\Tests\ORM\Mapping\User:
sequenceName: tablename_seq
allocationSize: 100
initialValue: 1
options:
foo: bar
fields:
name:
type: string
@@ -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)
{
}
}
@@ -47,7 +47,7 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase
*/
protected function setUp()
{
$this->visitor = new QueryExpressionVisitor();
$this->visitor = new QueryExpressionVisitor('o');
}
/**
@@ -71,22 +71,22 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase
$qb = new QueryBuilder();
return array(
array($cb->eq('field', 'value'), $qb->eq('field', ':field'), new Parameter('field', 'value')),
array($cb->neq('field', 'value'), $qb->neq('field', ':field'), new Parameter('field', 'value')),
array($cb->eq('field', null), $qb->isNull('field')),
array($cb->neq('field', null), $qb->isNotNull('field')),
array($cb->isNull('field'), $qb->isNull('field')),
array($cb->eq('field', 'value'), $qb->eq('o.field', ':field'), new Parameter('field', 'value')),
array($cb->neq('field', 'value'), $qb->neq('o.field', ':field'), new Parameter('field', 'value')),
array($cb->eq('field', null), $qb->isNull('o.field')),
array($cb->neq('field', null), $qb->isNotNull('o.field')),
array($cb->isNull('field'), $qb->isNull('o.field')),
array($cb->gt('field', 'value'), $qb->gt('field', ':field'), new Parameter('field', 'value')),
array($cb->gte('field', 'value'), $qb->gte('field', ':field'), new Parameter('field', 'value')),
array($cb->lt('field', 'value'), $qb->lt('field', ':field'), new Parameter('field', 'value')),
array($cb->lte('field', 'value'), $qb->lte('field', ':field'), new Parameter('field', 'value')),
array($cb->gt('field', 'value'), $qb->gt('o.field', ':field'), new Parameter('field', 'value')),
array($cb->gte('field', 'value'), $qb->gte('o.field', ':field'), new Parameter('field', 'value')),
array($cb->lt('field', 'value'), $qb->lt('o.field', ':field'), new Parameter('field', 'value')),
array($cb->lte('field', 'value'), $qb->lte('o.field', ':field'), new Parameter('field', 'value')),
array($cb->in('field', array('value')), $qb->in('field', ':field'), new Parameter('field', array('value'))),
array($cb->notIn('field', array('value')), $qb->notIn('field', ':field'), new Parameter('field', array('value'))),
array($cb->in('field', array('value')), $qb->in('o.field', ':field'), new Parameter('field', array('value'))),
array($cb->notIn('field', array('value')), $qb->notIn('o.field', ':field'), new Parameter('field', array('value'))),
// Test parameter conversion
array($cb->eq('object.field', 'value'), $qb->eq('object.field', ':object_field'), new Parameter('object_field', 'value')),
array($cb->eq('object.field', 'value'), $qb->eq('o.object.field', ':object_field'), new Parameter('object_field', 'value')),
);
}
@@ -394,6 +394,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
/**
* @group DDC-2668
*/
public function testSupportsTrimLeadingZeroString()
{
$this->assertSqlGeneration(
"SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(TRAILING '0' FROM u.name) != ''",
"SELECT c0_.name AS name0 FROM cms_users c0_ WHERE TRIM(TRAILING '0' FROM c0_.name) <> ''"
);
}
// Ticket 894
public function testSupportsBetweenClauseWithPositionalParameters()
{
@@ -1979,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);
}
+16 -3
View File
@@ -402,30 +402,39 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
public function testAddCriteriaWhere()
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$criteria = new Criteria();
$criteria->where($criteria->expr()->eq('field', 'value'));
$qb->addCriteria($criteria);
$this->assertEquals('field = :field', (string) $qb->getDQLPart('where'));
$this->assertEquals('u.field = :field', (string) $qb->getDQLPart('where'));
$this->assertNotNull($qb->getParameter('field'));
}
public function testAddCriteriaOrder()
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$criteria = new Criteria();
$criteria->orderBy(array('field' => Criteria::DESC));
$qb->addCriteria($criteria);
$this->assertCount(1, $orderBy = $qb->getDQLPart('orderBy'));
$this->assertEquals('field DESC', (string) $orderBy[0]);
$this->assertEquals('u.field DESC', (string) $orderBy[0]);
}
public function testAddCriteriaLimit()
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$criteria = new Criteria();
$criteria->setFirstResult(2);
$criteria->setMaxResults(10);
@@ -439,7 +448,11 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
public function testAddCriteriaUndefinedLimit()
{
$qb = $this->_em->createQueryBuilder();
$qb->setFirstResult(2)->setMaxResults(10);
$qb->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->setFirstResult(2)
->setMaxResults(10);
$criteria = new Criteria();
$qb->addCriteria($criteria);
@@ -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