mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a13376d42 | ||
|
|
8b5632cb65 | ||
|
|
859465b691 | ||
|
|
530c01b5e3 | ||
|
|
63c5758070 | ||
|
|
2a9a53ae9d | ||
|
|
0081319712 | ||
|
|
78ceda7ecf | ||
|
|
1f4810e370 | ||
|
|
376a3ac3b6 | ||
|
|
ab87dd6325 | ||
|
|
2ae245db30 | ||
|
|
9d36e855c0 | ||
|
|
f7c87cddd8 | ||
|
|
1566b8a057 | ||
|
|
333fa1090a | ||
|
|
0262b083bb | ||
|
|
60c9c27c08 | ||
|
|
8b75e3563a | ||
|
|
cbf16f1cf8 | ||
|
|
b84c828ea1 | ||
|
|
55b7e4cff2 | ||
|
|
e38af55100 | ||
|
|
7338c2d1f8 | ||
|
|
0ff3cdf150 | ||
|
|
24c5c54c1e | ||
|
|
6bb367f488 | ||
|
|
213cc5c695 | ||
|
|
a949e87ca8 | ||
|
|
f18d0e093b | ||
|
|
0e139055f9 | ||
|
|
b9c6659b70 | ||
|
|
5c06121d94 | ||
|
|
5bfa56aee0 | ||
|
|
0363a5548d | ||
|
|
3764e49e6c | ||
|
|
6ee20204a5 | ||
|
|
d9b0c87ded | ||
|
|
8594e5c4da | ||
|
|
5f821f3b98 | ||
|
|
b566525099 | ||
|
|
215c4a03e1 | ||
|
|
b3ccd6466b | ||
|
|
b596bbb29f | ||
|
|
c204e6c6a1 | ||
|
|
0bc94589e1 | ||
|
|
f37856829f | ||
|
|
157c793810 | ||
|
|
72d838a804 | ||
|
|
58f8dc5d4c | ||
|
|
7d3ecd9481 | ||
|
|
1bb55703a7 | ||
|
|
56cbcec13d | ||
|
|
837c19bfc0 | ||
|
|
7b8f09ee4a | ||
|
|
488a4dc78a | ||
|
|
1364b6acc6 | ||
|
|
3dbe181762 | ||
|
|
a3acaab65c | ||
|
|
f183d25a33 | ||
|
|
7c8350094e | ||
|
|
c613410ba6 | ||
|
|
6bb7581dd7 | ||
|
|
ab71dab7d1 | ||
|
|
2c114756bd | ||
|
|
45496f040d | ||
|
|
b40866c624 | ||
|
|
a89cc7abea | ||
|
|
5ac111e5f8 | ||
|
|
c5f66e6e7f | ||
|
|
b59f495875 | ||
|
|
3829b9c28b | ||
|
|
65bcdbf4c7 | ||
|
|
95d000e51b | ||
|
|
3657df3b01 | ||
|
|
1661ffae9a | ||
|
|
b424a5cf14 | ||
|
|
2767a4eec4 | ||
|
|
9486867279 | ||
|
|
6f2bb08972 | ||
|
|
da2d3b406e | ||
|
|
c4b7d3fbea | ||
|
|
84373d05a4 | ||
|
|
e82e7147fa | ||
|
|
e23ed2250d | ||
|
|
192bb6fd21 | ||
|
|
0f3679f034 | ||
|
|
1d2cd82706 | ||
|
|
b983d86612 | ||
|
|
b11f01643c | ||
|
|
b58fb8f5d4 | ||
|
|
925a22b71d | ||
|
|
0f0d8abd67 | ||
|
|
470c15ce05 | ||
|
|
3cc5fc0252 | ||
|
|
fd0657089a | ||
|
|
de3b237292 | ||
|
|
1221cc3a2a | ||
|
|
9efbc1fa71 | ||
|
|
57705e0d78 | ||
|
|
82bb6b78cd | ||
|
|
64c56b21aa | ||
|
|
b04e2e6364 | ||
|
|
a70f9b7f49 | ||
|
|
c88a7c1ffe | ||
|
|
c206728c96 | ||
|
|
e8d420c641 | ||
|
|
fdcab7eae8 | ||
|
|
45d7d5234f | ||
|
|
159ca79b81 | ||
|
|
2b148a27e0 | ||
|
|
0aef57f60c | ||
|
|
fef1e0286c | ||
|
|
4a38534150 | ||
|
|
1de22adb16 | ||
|
|
62b4160887 | ||
|
|
dbb7c4d2bf | ||
|
|
e8978ee365 | ||
|
|
c095b88804 | ||
|
|
efe4208ba6 | ||
|
|
453a56670d | ||
|
|
ec36e2c866 | ||
|
|
e250572cb4 | ||
|
|
758955e183 | ||
|
|
5b8d6a1486 | ||
|
|
3f1003fee9 | ||
|
|
7e241e89b8 | ||
|
|
67c1e1d2b1 | ||
|
|
261eacdbfc | ||
|
|
43df821691 | ||
|
|
11d09702da | ||
|
|
f9f14139cf | ||
|
|
39f4d46d36 | ||
|
|
1dae8d318f | ||
|
|
a361a7c1cb | ||
|
|
6a73608baf | ||
|
|
f9955152b2 | ||
|
|
5aad1df149 | ||
|
|
243832555b | ||
|
|
ae12fa6b5b | ||
|
|
edaf9b6813 | ||
|
|
b324a21abf | ||
|
|
ff34aaaa2c | ||
|
|
9767a814a6 | ||
|
|
e6007575e1 |
@@ -16,7 +16,7 @@ before_script:
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi"
|
||||
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi"
|
||||
- composer install --prefer-source --dev
|
||||
- composer install --prefer-dist --dev
|
||||
|
||||
script: phpunit --configuration tests/travis/$DB.travis.xml
|
||||
|
||||
|
||||
@@ -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,11 +1,3 @@
|
||||
# Project Name
|
||||
project.name=DoctrineORM
|
||||
|
||||
# Dependency minimum versions
|
||||
dependencies.common=2.2.0beta1
|
||||
dependencies.dbal=2.2.0beta1
|
||||
dependencies.sfconsole=2.0.0
|
||||
|
||||
# Version class and file
|
||||
project.version_class = Doctrine\ORM\Version
|
||||
project.version_class = Doctrine\\ORM\\Version
|
||||
project.version_file = lib/Doctrine/ORM/Version.php
|
||||
|
||||
199
build.xml
199
build.xml
@@ -1,114 +1,101 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="DoctrineORM" default="build" basedir=".">
|
||||
<taskdef classname="phing.tasks.ext.d51PearPkg2Task" name="d51pearpkg2" />
|
||||
<import file="${project.basedir}/lib/vendor/doctrine-build-common/packaging.xml" />
|
||||
|
||||
<property file="build.properties" />
|
||||
|
||||
<!--
|
||||
Fileset for artifacts shared across all distributed packages.
|
||||
-->
|
||||
<fileset id="shared-artifacts" dir=".">
|
||||
<include name="LICENSE"/>
|
||||
<include name="UPGRADE*" />
|
||||
<include name="doctrine-mapping.xsd" />
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Fileset for command line scripts
|
||||
-->
|
||||
<fileset id="bin-scripts" dir="./bin">
|
||||
<include name="doctrine"/>
|
||||
<include name="doctrine-pear.php"/>
|
||||
<include name="doctrine.bat"/>
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Fileset for the sources of the Doctrine Common dependency.
|
||||
-->
|
||||
<fileset id="common-sources" dir="./lib/vendor/doctrine-common/lib">
|
||||
<include name="Doctrine/Common/**"/>
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Fileset for the sources of the Doctrine DBAL dependency.
|
||||
-->
|
||||
<fileset id="dbal-sources" dir="./lib/vendor/doctrine-dbal/lib">
|
||||
<include name="Doctrine/DBAL/**"/>
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Fileset for the sources of the Doctrine ORM.
|
||||
-->
|
||||
<fileset id="orm-sources" dir="./lib">
|
||||
<include name="Doctrine/ORM/**"/>
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Fileset for source of the Symfony YAML and Console components.
|
||||
-->
|
||||
<fileset id="symfony-sources" dir="./lib/vendor">
|
||||
<include name="Symfony/Component/**"/>
|
||||
<exclude name="**/.git/**" />
|
||||
</fileset>
|
||||
|
||||
<!--
|
||||
Builds ORM package, preparing it for distribution.
|
||||
-->
|
||||
<target name="copy-files" depends="prepare">
|
||||
<copy todir="${build.dir}/${project.name}-${version}">
|
||||
<fileset refid="shared-artifacts"/>
|
||||
</copy>
|
||||
<copy todir="${build.dir}/${project.name}-${version}">
|
||||
<fileset refid="common-sources"/>
|
||||
<fileset refid="dbal-sources"/>
|
||||
<fileset refid="orm-sources"/>
|
||||
</copy>
|
||||
<copy todir="${build.dir}/${project.name}-${version}/Doctrine">
|
||||
<fileset refid="symfony-sources"/>
|
||||
</copy>
|
||||
<copy todir="${build.dir}/${project.name}-${version}/bin">
|
||||
<fileset refid="bin-scripts"/>
|
||||
</copy>
|
||||
<target name="php">
|
||||
<exec executable="which" outputproperty="php_executable">
|
||||
<arg value="php" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
Builds distributable PEAR packages.
|
||||
-->
|
||||
<target name="define-pear-package" depends="copy-files">
|
||||
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/${project.name}-${version}">
|
||||
<name>DoctrineORM</name>
|
||||
<summary>Doctrine Object Relational Mapper</summary>
|
||||
<channel>pear.doctrine-project.org</channel>
|
||||
<description>The Doctrine ORM package is the primary package containing the object relational mapper.</description>
|
||||
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
|
||||
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
|
||||
<lead user="romanb" name="Roman Borschel" email="roman@code-factory.org" />
|
||||
<lead user="beberlei" name="Benjamin Eberlei" email="kontakt@beberlei.de" />
|
||||
<license>LGPL</license>
|
||||
<version release="${pear.version}" api="${pear.version}" />
|
||||
<stability release="${pear.stability}" api="${pear.stability}" />
|
||||
<notes>-</notes>
|
||||
<dependencies>
|
||||
<php minimum_version="5.3.0" />
|
||||
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
|
||||
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
|
||||
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
|
||||
<package name="Console" channel="pear.symfony.com" minimum_version="2.0.0" />
|
||||
<package name="Yaml" channel="pear.symfony.com" minimum_version="2.0.0" />
|
||||
</dependencies>
|
||||
<dirroles key="bin">script</dirroles>
|
||||
<ignore>Doctrine/Common/</ignore>
|
||||
<ignore>Doctrine/DBAL/</ignore>
|
||||
<ignore>Symfony/Component/Yaml/</ignore>
|
||||
<ignore>Symfony/Component/Console/</ignore>
|
||||
<release>
|
||||
<install as="doctrine" name="bin/doctrine" />
|
||||
<install as="doctrine.php" name="bin/doctrine-pear.php" />
|
||||
<install as="doctrine.bat" name="bin/doctrine.bat" />
|
||||
</release>
|
||||
<replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" />
|
||||
<replacement path="bin/doctrine.bat" type="pear-config" from="@bin_dir@" to="bin_dir" />
|
||||
</d51pearpkg2>
|
||||
<target name="prepare">
|
||||
<mkdir dir="build" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="archive" />
|
||||
<arg value="--dir=build" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer" depends="php,composer-check,composer-download">
|
||||
<exec executable="${php_executable}">
|
||||
<arg value="build/composer.phar" />
|
||||
<arg value="install" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer-check" depends="prepare">
|
||||
<available file="build/composer.phar" property="composer.present"/>
|
||||
</target>
|
||||
|
||||
<target name="composer-download" unless="composer.present">
|
||||
<exec executable="wget">
|
||||
<arg value="-Obuild/composer.phar" />
|
||||
<arg value="http://getcomposer.org/composer.phar" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="make-release" depends="check-git-checkout-clean,prepare,php">
|
||||
<replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
|
||||
</exec>
|
||||
<exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
|
||||
<arg value="-r" />
|
||||
<arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
|
||||
if (count($parts) != 3) {
|
||||
throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
|
||||
}
|
||||
$parts[2]++;
|
||||
echo implode('.', $parts);
|
||||
" />
|
||||
</exec>
|
||||
|
||||
<git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
|
||||
<git-tag version="${doctrine.current_version}" />
|
||||
<replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
|
||||
<git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
|
||||
</target>
|
||||
|
||||
<target name="check-git-checkout-clean">
|
||||
<exec executable="git" failonerror="true">
|
||||
<arg value="diff-index" />
|
||||
<arg value="--quiet" />
|
||||
<arg value="HEAD" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<macrodef name="git-commit">
|
||||
<attribute name="file" default="NOT SET"/>
|
||||
<attribute name="message" default="NOT SET"/>
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="add" />
|
||||
<arg value="@{file}" />
|
||||
</exec>
|
||||
<exec executable="git">
|
||||
<arg value="commit" />
|
||||
<arg value="-m" />
|
||||
<arg value="@{message}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<macrodef name="git-tag">
|
||||
<attribute name="version" default="NOT SET" />
|
||||
|
||||
<sequential>
|
||||
<exec executable="git">
|
||||
<arg value="tag" />
|
||||
<arg value="-m" />
|
||||
<arg value="v@{version}" />
|
||||
<arg value="v@{version}" />
|
||||
</exec>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</project>
|
||||
|
||||
@@ -11,16 +11,15 @@
|
||||
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"ext-pdo": "*",
|
||||
"doctrine/collections": "~1.1",
|
||||
"doctrine/dbal": ">=2.4-beta,<2.5-dev",
|
||||
"symfony/console": "2.*"
|
||||
"doctrine/dbal": "~2.4",
|
||||
"symfony/console": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/yaml": "2.1",
|
||||
"symfony/yaml": "~2.1",
|
||||
"satooshi/php-coveralls": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -34,5 +33,8 @@
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4.x-dev"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -701,6 +701,18 @@ abstract class AbstractQuery
|
||||
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the query has a hint
|
||||
*
|
||||
* @param string $name The name of the hint
|
||||
*
|
||||
* @return bool False if the query does not have any hint
|
||||
*/
|
||||
public function hasHint($name)
|
||||
{
|
||||
return isset($this->_hints[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key value map of query hints that are currently set.
|
||||
*
|
||||
@@ -787,7 +799,7 @@ abstract class AbstractQuery
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll(
|
||||
$stmt, $this->_resultSetMapping, $this->_hints
|
||||
);
|
||||
|
||||
|
||||
@@ -99,13 +99,6 @@ use Doctrine\Common\Util\ClassUtils;
|
||||
*/
|
||||
private $eventManager;
|
||||
|
||||
/**
|
||||
* The maintained (cached) hydrators. One instance per type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $hydrators = array();
|
||||
|
||||
/**
|
||||
* The proxy factory used to create dynamic proxies.
|
||||
*
|
||||
@@ -840,17 +833,15 @@ use Doctrine\Common\Util\ClassUtils;
|
||||
* This method caches the hydrator instances which is used for all queries that don't
|
||||
* selectively iterate over the result.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $hydrationMode
|
||||
*
|
||||
* @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
|
||||
*/
|
||||
public function getHydrator($hydrationMode)
|
||||
{
|
||||
if ( ! isset($this->hydrators[$hydrationMode])) {
|
||||
$this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
return $this->hydrators[$hydrationMode];
|
||||
return $this->newHydrator($hydrationMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -146,6 +146,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
$baseElement =& $this->_resultPointers[$parent];
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -167,6 +168,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
$element = $data;
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
|
||||
} else {
|
||||
@@ -183,7 +185,10 @@ class ArrayHydrator extends AbstractHydrator
|
||||
} else {
|
||||
$oneToOne = true;
|
||||
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
|
||||
if (
|
||||
( ! isset($nonemptyComponents[$dqlAlias])) &&
|
||||
( ! isset($baseElement[$relationAlias]))
|
||||
) {
|
||||
$baseElement[$relationAlias] = null;
|
||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = $data;
|
||||
@@ -192,10 +197,9 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
$coll =& $baseElement[$relationAlias];
|
||||
|
||||
if ($coll !== null) {
|
||||
if (is_array($coll)) {
|
||||
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's a root result element
|
||||
|
||||
@@ -204,22 +208,21 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
// if this row has a NULL value for the root result id then make it a null result.
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array($entityKey => null);
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
$result[] = $this->_rsm->isMixed
|
||||
? array($entityKey => null)
|
||||
: null;
|
||||
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for an existing element
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $rowData[$dqlAlias];
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($entityKey => $element);
|
||||
}
|
||||
$element = $this->_rsm->isMixed
|
||||
? array($entityKey => $rowData[$dqlAlias])
|
||||
: $rowData[$dqlAlias];
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||
@@ -227,6 +230,7 @@ class ArrayHydrator extends AbstractHydrator
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter;
|
||||
$result[] = $element;
|
||||
|
||||
++$this->_resultCounter;
|
||||
}
|
||||
|
||||
@@ -234,11 +238,13 @@ class ArrayHydrator extends AbstractHydrator
|
||||
} else {
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$resultKey = $index;
|
||||
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] =& $result[$index];
|
||||
++$this->_resultCounter;
|
||||
}*/
|
||||
}
|
||||
|
||||
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
||||
}
|
||||
}
|
||||
@@ -247,11 +253,9 @@ class ArrayHydrator extends AbstractHydrator
|
||||
if (isset($scalars)) {
|
||||
if ( ! isset($resultKey) ) {
|
||||
// this only ever happens when no object is fetched (scalar result only)
|
||||
if (isset($this->_rsm->indexByMap['scalars'])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter - 1;
|
||||
}
|
||||
$resultKey = isset($this->_rsm->indexByMap['scalars'])
|
||||
? $row[$this->_rsm->indexByMap['scalars']]
|
||||
: $this->_resultCounter - 1;
|
||||
}
|
||||
|
||||
foreach ($scalars as $name => $value) {
|
||||
@@ -279,6 +283,12 @@ class ArrayHydrator extends AbstractHydrator
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($index !== false) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[$index];
|
||||
|
||||
@@ -289,12 +299,6 @@ class ArrayHydrator extends AbstractHydrator
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oneToOne) {
|
||||
$this->_resultPointers[$dqlAlias] =& $coll;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -757,6 +757,8 @@ final class PersistentCollection implements Collection, Selectable
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->key();
|
||||
}
|
||||
|
||||
@@ -765,6 +767,8 @@ final class PersistentCollection implements Collection, Selectable
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->current();
|
||||
}
|
||||
|
||||
@@ -773,6 +777,8 @@ final class PersistentCollection implements Collection, Selectable
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->next();
|
||||
}
|
||||
|
||||
|
||||
@@ -567,8 +567,8 @@ class BasicEntityPersister
|
||||
$tableName = $this->quoteStrategy->getTableName($class, $this->platform);
|
||||
$idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform);
|
||||
$id = array_combine($idColumns, $identifier);
|
||||
$types = array_map(function ($identifier) use ($class, $em) {
|
||||
|
||||
$types = array_map(function ($identifier) use ($class, $em) {
|
||||
if (isset($class->fieldMappings[$identifier])) {
|
||||
return $class->fieldMappings[$identifier]['type'];
|
||||
}
|
||||
@@ -580,7 +580,7 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) {
|
||||
$types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
|
||||
return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
|
||||
}
|
||||
|
||||
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
|
||||
|
||||
@@ -197,7 +197,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
foreach ($data as $columnName => $value) {
|
||||
if (!isset($id[$columnName])) {
|
||||
if (!is_array($id) || !isset($id[$columnName])) {
|
||||
$stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
$columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
|
||||
$referencedName = $joinColumn['referencedColumnName'];
|
||||
$conditions[] = $columnName . ' = ?';
|
||||
$conditions[] = 't.' . $columnName . ' = ?';
|
||||
$params[] = ($class->containsForeignIdentifier)
|
||||
? $id[$class->getFieldForColumn($referencedName)]
|
||||
: $id[$class->fieldNames[$referencedName]];
|
||||
@@ -361,12 +361,13 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$params = array();
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
$whereClauses[] = $joinTableColumn . ' = ?';
|
||||
$whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?';
|
||||
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
$params[] = ($targetClass->containsForeignIdentifier)
|
||||
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
||||
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -377,9 +378,12 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
}
|
||||
|
||||
if ($addFilters) {
|
||||
$quotedJoinTable .= ' t';
|
||||
|
||||
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
|
||||
|
||||
if ($filterSql) {
|
||||
$quotedJoinTable .= ' t ' . $joinTargetEntitySQL;
|
||||
$quotedJoinTable .= ' ' . $joinTargetEntitySQL;
|
||||
$whereClauses[] = $filterSql;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,20 @@ class RangeVariableDeclaration extends Node
|
||||
public $aliasIdentificationVariable;
|
||||
|
||||
/**
|
||||
* @param string $abstractSchemaName
|
||||
* @param string $aliasIdentificationVar
|
||||
* @var boolean
|
||||
*/
|
||||
public function __construct($abstractSchemaName, $aliasIdentificationVar)
|
||||
public $isRoot;
|
||||
|
||||
/**
|
||||
* @param string $abstractSchemaName
|
||||
* @param string $aliasIdentificationVar
|
||||
* @param boolean $isRoot
|
||||
*/
|
||||
public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true)
|
||||
{
|
||||
$this->abstractSchemaName = $abstractSchemaName;
|
||||
$this->abstractSchemaName = $abstractSchemaName;
|
||||
$this->aliasIdentificationVariable = $aliasIdentificationVar;
|
||||
$this->isRoot = $isRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1544,6 +1544,9 @@ class Parser
|
||||
public function IdentificationVariableDeclaration()
|
||||
{
|
||||
$rangeVariableDeclaration = $this->RangeVariableDeclaration();
|
||||
|
||||
$rangeVariableDeclaration->isRoot = true;
|
||||
|
||||
$indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
|
||||
$joins = array();
|
||||
|
||||
@@ -1622,15 +1625,19 @@ class Parser
|
||||
$this->match(Lexer::T_JOIN);
|
||||
|
||||
$next = $this->lexer->glimpse();
|
||||
$joinDeclaration = ($next['type'] === Lexer::T_DOT)
|
||||
? $this->JoinAssociationDeclaration()
|
||||
: $this->RangeVariableDeclaration();
|
||||
$joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration();
|
||||
$adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH);
|
||||
$join = new AST\Join($joinType, $joinDeclaration);
|
||||
|
||||
// Create AST node
|
||||
$join = new AST\Join($joinType, $joinDeclaration);
|
||||
// Describe non-root join declaration
|
||||
if ($joinDeclaration instanceof AST\RangeVariableDeclaration) {
|
||||
$joinDeclaration->isRoot = false;
|
||||
|
||||
$adhocConditions = true;
|
||||
}
|
||||
|
||||
// Check for ad-hoc Join conditions
|
||||
if ($this->lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) {
|
||||
if ($adhocConditions) {
|
||||
$this->match(Lexer::T_WITH);
|
||||
|
||||
$join->conditionalExpression = $this->ConditionalExpression();
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,7 +842,9 @@ class SqlWalker implements TreeWalker
|
||||
$class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
|
||||
$dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
|
||||
|
||||
$this->rootAliases[] = $dqlAlias;
|
||||
if ($rangeVariableDeclaration->isRoot) {
|
||||
$this->rootAliases[] = $dqlAlias;
|
||||
}
|
||||
|
||||
$sql = $this->quoteStrategy->getTableName($class,$this->platform) . ' '
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
@@ -858,13 +860,14 @@ class SqlWalker implements TreeWalker
|
||||
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param AST\JoinAssociationDeclaration $joinAssociationDeclaration
|
||||
* @param int $joinType
|
||||
* @param int $joinType
|
||||
* @param AST\ConditionalExpression $condExpr
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER)
|
||||
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null)
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
@@ -979,6 +982,13 @@ class SqlWalker implements TreeWalker
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle WITH clause
|
||||
if ($condExpr !== null) {
|
||||
// Phase 2 AST optimization: Skip processing of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
|
||||
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
|
||||
if ($targetClass->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
|
||||
@@ -1058,24 +1068,37 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
switch (true) {
|
||||
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
|
||||
$class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
|
||||
$condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER
|
||||
$class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
|
||||
$dqlAlias = $joinDeclaration->aliasIdentificationVariable;
|
||||
$tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
|
||||
$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
|
||||
? ' AND '
|
||||
: ' ON ';
|
||||
|
||||
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration)
|
||||
. $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
|
||||
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration);
|
||||
|
||||
$conditions = array($condition);
|
||||
|
||||
// Apply remaining inheritance restrictions
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias));
|
||||
|
||||
if ($discrSql) {
|
||||
$conditions[] = $discrSql;
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
$filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
|
||||
|
||||
if ($filterExpr) {
|
||||
$conditions[] = $filterExpr;
|
||||
}
|
||||
|
||||
$sql .= $condExprConjunction . implode(' AND ', $conditions);
|
||||
break;
|
||||
|
||||
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
|
||||
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
|
||||
|
||||
// Handle WITH clause
|
||||
if (($condExpr = $join->conditionalExpression) !== null) {
|
||||
// Phase 2 AST optimization: Skip processing of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1474,6 +1497,7 @@ class SqlWalker implements TreeWalker
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldType = 'string';
|
||||
switch (true) {
|
||||
case ($e instanceof AST\PathExpression):
|
||||
$fieldName = $e->field;
|
||||
@@ -1494,10 +1518,6 @@ class SqlWalker implements TreeWalker
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$fieldType = 'string';
|
||||
break;
|
||||
}
|
||||
|
||||
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -152,7 +152,7 @@ class EntityGenerator
|
||||
Type::SMALLINT => 'integer',
|
||||
Type::TEXT => 'string',
|
||||
Type::BLOB => 'string',
|
||||
Type::DECIMAL => 'float',
|
||||
Type::DECIMAL => 'string',
|
||||
Type::JSON_ARRAY => 'array',
|
||||
Type::SIMPLE_ARRAY => 'array',
|
||||
);
|
||||
@@ -911,7 +911,7 @@ public function __construct()
|
||||
protected function generateDiscriminatorColumnAnnotation($metadata)
|
||||
{
|
||||
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
|
||||
$discrColumn = $metadata->discriminatorValue;
|
||||
$discrColumn = $metadata->discriminatorColumn;
|
||||
$columnDefinition = 'name="' . $discrColumn['name']
|
||||
. '", type="' . $discrColumn['type']
|
||||
. '", length=' . $discrColumn['length'];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
26
lib/Doctrine/ORM/Tools/Pagination/Paginator.php
Normal file → Executable file
26
lib/Doctrine/ORM/Tools/Pagination/Paginator.php
Normal file → Executable file
@@ -121,7 +121,7 @@ class Paginator implements \Countable, \IteratorAggregate
|
||||
/* @var $countQuery Query */
|
||||
$countQuery = $this->cloneQuery($this->query);
|
||||
|
||||
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
|
||||
if ( ! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) {
|
||||
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class Paginator implements \Countable, \IteratorAggregate
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker');
|
||||
$countQuery->setResultSetMapping($rsm);
|
||||
} else {
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$this->appendTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker');
|
||||
}
|
||||
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
@@ -165,7 +165,7 @@ class Paginator implements \Countable, \IteratorAggregate
|
||||
if ($this->useOutputWalker($subQuery)) {
|
||||
$subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker');
|
||||
} else {
|
||||
$subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'));
|
||||
$this->appendTreeWalker($subQuery, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker');
|
||||
}
|
||||
|
||||
$subQuery->setFirstResult($offset)->setMaxResults($length);
|
||||
@@ -178,7 +178,7 @@ class Paginator implements \Countable, \IteratorAggregate
|
||||
return new \ArrayIterator(array());
|
||||
}
|
||||
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$this->appendTreeWalker($whereInQuery, 'Doctrine\ORM\Tools\Pagination\WhereInWalker');
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
|
||||
$whereInQuery->setFirstResult(null)->setMaxResults(null);
|
||||
$whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids);
|
||||
@@ -231,4 +231,22 @@ class Paginator implements \Countable, \IteratorAggregate
|
||||
|
||||
return $this->useOutputWalkers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a custom tree walker to the tree walkers hint.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param string $walkerClass
|
||||
*/
|
||||
private function appendTreeWalker(Query $query, $walkerClass)
|
||||
{
|
||||
$hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS);
|
||||
|
||||
if ($hints === false) {
|
||||
$hints = array();
|
||||
}
|
||||
|
||||
$hints[] = $walkerClass;
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -241,6 +241,11 @@ class SchemaValidator
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset($class->fieldMappings[$publicAttr->getName()]) &&
|
||||
! isset($class->associationMappings[$publicAttr->getName()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ".
|
||||
"or protected. Public fields may break lazy-loading.";
|
||||
}
|
||||
|
||||
@@ -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,30 +902,35 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if ($changeSet) {
|
||||
if (isset($this->entityChangeSets[$oid])) {
|
||||
$this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
|
||||
}
|
||||
$this->entityChangeSets[$oid] = (isset($this->entityChangeSets[$oid]))
|
||||
? array_merge($this->entityChangeSets[$oid], $changeSet)
|
||||
: $changeSet;
|
||||
|
||||
$this->originalEntityData[$oid] = $actualData;
|
||||
$this->entityUpdates[$oid] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1736,6 +1749,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$associatedId = $this->getEntityIdentifier($idValue);
|
||||
|
||||
$flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]];
|
||||
} else {
|
||||
$flatId[$idField] = $idValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,6 +2246,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 +2260,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,15 +2492,15 @@ 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];
|
||||
@@ -2490,14 +2511,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 +3227,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.4.0-DEV';
|
||||
const VERSION = '2.4.3';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
42
tests/Doctrine/Tests/Models/Taxi/Car.php
Normal file
42
tests/Doctrine/Tests/Models/Taxi/Car.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Taxi;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="taxi_car")
|
||||
*/
|
||||
class Car
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="string", length=25)
|
||||
* @GeneratedValue(strategy="NONE")
|
||||
*/
|
||||
private $brand;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=255);
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Ride", mappedBy="car")
|
||||
*/
|
||||
private $freeCarRides;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="PaidRide", mappedBy="car")
|
||||
*/
|
||||
private $carRides;
|
||||
|
||||
public function setBrand($brand)
|
||||
{
|
||||
$this->brand = $brand;
|
||||
}
|
||||
|
||||
public function setModel($model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
}
|
||||
37
tests/Doctrine/Tests/Models/Taxi/Driver.php
Normal file
37
tests/Doctrine/Tests/Models/Taxi/Driver.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Taxi;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="taxi_driver")
|
||||
*/
|
||||
class Driver
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=255);
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Ride", mappedBy="driver")
|
||||
*/
|
||||
private $freeDriverRides;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="PaidRide", mappedBy="driver")
|
||||
*/
|
||||
private $driverRides;
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
42
tests/Doctrine/Tests/Models/Taxi/PaidRide.php
Normal file
42
tests/Doctrine/Tests/Models/Taxi/PaidRide.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Taxi;
|
||||
|
||||
/**
|
||||
* Same as Ride but with an extra column that is not part of the composite primary key
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="taxi_paid_ride")
|
||||
*/
|
||||
class PaidRide
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Driver", inversedBy="driverRides")
|
||||
* @JoinColumn(name="driver_id", referencedColumnName="id")
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Car", inversedBy="carRides")
|
||||
* @JoinColumn(name="car", referencedColumnName="brand")
|
||||
*/
|
||||
private $car;
|
||||
|
||||
/**
|
||||
* @Column(type="decimal", precision=6, scale=2)
|
||||
*/
|
||||
private $fare;
|
||||
|
||||
public function __construct(Driver $driver, Car $car)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->car = $car;
|
||||
}
|
||||
|
||||
public function setFare($fare)
|
||||
{
|
||||
$this->fare = $fare;
|
||||
}
|
||||
}
|
||||
32
tests/Doctrine/Tests/Models/Taxi/Ride.php
Normal file
32
tests/Doctrine/Tests/Models/Taxi/Ride.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Taxi;
|
||||
|
||||
/**
|
||||
* Test model that contains only Id-columns
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="taxi_ride")
|
||||
*/
|
||||
class Ride
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Driver", inversedBy="freeDriverRides")
|
||||
* @JoinColumn(name="driver_id", referencedColumnName="id")
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="Car", inversedBy="freeCarRides")
|
||||
* @JoinColumn(name="car", referencedColumnName="brand")
|
||||
*/
|
||||
private $car;
|
||||
|
||||
public function __construct(Driver $driver, Car $car)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->car = $car;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -719,7 +719,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$selectClause = $rsm->generateSelectClause();
|
||||
|
||||
$this->assertEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause);
|
||||
$this->assertSQLEquals('u.id AS id, u.status AS status, u.username AS username, u.name AS name, u.email_id AS email_id', $selectClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -735,7 +735,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$selectClause = $rsm->generateSelectClause();
|
||||
|
||||
$this->assertEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause);
|
||||
$this->assertSQLEquals('u.id AS id1, u.status AS status, u.username AS username2, u.name AS name, u.email_id AS email_id', $selectClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,7 +748,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$selectClause = $rsm->generateSelectClause(array('u' => 'u1'));
|
||||
|
||||
$this->assertEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause);
|
||||
$this->assertSQLEquals('u1.id AS id, u1.status AS status, u1.username AS username, u1.name AS name, u1.email_id AS email_id', $selectClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,7 +761,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$selectClause = $rsm->generateSelectClause();
|
||||
|
||||
$this->assertEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause);
|
||||
$this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', $selectClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -772,6 +772,6 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
|
||||
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
|
||||
$this->assertEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string)$rsm);
|
||||
$this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string)$rsm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,14 +128,14 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->clear();
|
||||
|
||||
$train = $this->_em->find(get_class($train), $train->id);
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5, t0.owner_id AS owner_id6, t7.id AS id8, t7.name AS name9 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id INNER JOIN TrainOwner t7 ON t0.owner_id = t7.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
|
||||
$this->_em->clear();
|
||||
$driver = $this->_em->find(get_class($driver), $driver->id);
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
@@ -156,13 +156,13 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
|
||||
|
||||
// The last query is the eager loading of the owner of the train
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
|
||||
// The one before is the fetching of the waggon and train
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery - 1]['sql']
|
||||
);
|
||||
@@ -177,7 +177,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->clear();
|
||||
|
||||
$waggon = $this->_em->find(get_class($owner), $owner->id);
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id = ?",
|
||||
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
|
||||
);
|
||||
|
||||
@@ -139,6 +139,18 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertTrue($query->getParameters()->isEmpty());
|
||||
}
|
||||
|
||||
public function testQueryWalkerIsKept()
|
||||
{
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomPaginationTestTreeWalker'));
|
||||
|
||||
$paginator = new Paginator($query, true);
|
||||
$paginator->setUseOutputWalkers(false);
|
||||
$this->assertCount(1, $paginator->getIterator());
|
||||
$this->assertEquals(1, $paginator->count());
|
||||
}
|
||||
|
||||
public function populate()
|
||||
{
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
@@ -166,3 +178,22 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPaginationTestTreeWalker extends Query\TreeWalkerAdapter
|
||||
{
|
||||
public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
|
||||
{
|
||||
$condition = new Query\AST\ConditionalPrimary();
|
||||
|
||||
$path = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, 'u', 'name');
|
||||
$path->type = Query\AST\PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
$condition->simpleConditionalExpression = new Query\AST\ComparisonExpression(
|
||||
$path,
|
||||
'=',
|
||||
new Query\AST\Literal(Query\AST\Literal::STRING, 'Name1')
|
||||
);
|
||||
|
||||
$selectStatement->whereClause = new Query\AST\WhereClause($condition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -35,8 +35,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->orderBy('o.id')
|
||||
->getQuery();
|
||||
|
||||
$this->assertEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
$this->assertSQLEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
|
||||
|
||||
$result = $query->getResult();
|
||||
@@ -67,8 +67,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->getQuery();
|
||||
|
||||
|
||||
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
$this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
|
||||
$result = $query->getResult();
|
||||
|
||||
@@ -96,8 +96,8 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->getQuery();
|
||||
|
||||
|
||||
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
$this->assertSQLEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL());
|
||||
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
|
||||
|
||||
|
||||
$result = $query->getResult();
|
||||
@@ -294,4 +294,4 @@ class DDC1430OrderProduct
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$entity1 = $repository->find($e1->id);
|
||||
|
||||
// DDC-1596
|
||||
$this->assertEquals(
|
||||
$this->assertSQLEquals(
|
||||
"SELECT t0.id AS id1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')",
|
||||
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
|
||||
);
|
||||
@@ -52,8 +52,8 @@ class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$entity1 = $repository->find($e1->id);
|
||||
$entities = $entity1->getEntities()->count();
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT COUNT(*) FROM entity1_entity2 t WHERE parent = ?",
|
||||
$this->assertSQLEquals(
|
||||
"SELECT COUNT(*) FROM entity1_entity2 t WHERE t.parent = ?",
|
||||
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
|
||||
);
|
||||
}
|
||||
@@ -108,4 +108,4 @@ class DDC1595InheritedEntity1 extends DDC1595BaseInheritance
|
||||
*/
|
||||
class DDC1595InheritedEntity2 extends DDC1595BaseInheritance
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
158
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php
Normal file
158
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\Models\Taxi\Car,
|
||||
Doctrine\Tests\Models\Taxi\Driver,
|
||||
Doctrine\Tests\Models\Taxi\Ride,
|
||||
Doctrine\Tests\Models\Taxi\PaidRide;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1884
|
||||
* @author Sander Coolen <sander@jibber.nl>
|
||||
*/
|
||||
class DDC1884Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('taxi');
|
||||
parent::setUp();
|
||||
|
||||
list($bimmer, $crysler, $merc, $volvo) = $this->createCars('Doctrine\Tests\Models\Taxi\Car');
|
||||
list($john, $foo) = $this->createDrivers('Doctrine\Tests\Models\Taxi\Driver');
|
||||
$this->_em->flush();
|
||||
|
||||
$ride1 = new Ride($john, $bimmer);
|
||||
$ride2 = new Ride($john, $merc);
|
||||
$ride3 = new Ride($john, $volvo);
|
||||
$ride4 = new Ride($foo, $merc);
|
||||
|
||||
$this->_em->persist($ride1);
|
||||
$this->_em->persist($ride2);
|
||||
$this->_em->persist($ride3);
|
||||
$this->_em->persist($ride4);
|
||||
|
||||
$ride5 = new PaidRide($john, $bimmer);
|
||||
$ride5->setFare(10.50);
|
||||
|
||||
$ride6 = new PaidRide($john, $merc);
|
||||
$ride6->setFare(16.00);
|
||||
|
||||
$ride7 = new PaidRide($john, $volvo);
|
||||
$ride7->setFare(20.70);
|
||||
|
||||
$ride8 = new PaidRide($foo, $merc);
|
||||
$ride8->setFare(32.15);
|
||||
|
||||
$this->_em->persist($ride5);
|
||||
$this->_em->persist($ride6);
|
||||
$this->_em->persist($ride7);
|
||||
$this->_em->persist($ride8);
|
||||
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
private function createCars($class)
|
||||
{
|
||||
$bimmer = new $class;
|
||||
$bimmer->setBrand('BMW');
|
||||
$bimmer->setModel('7-Series');
|
||||
|
||||
$crysler = new $class;
|
||||
$crysler->setBrand('Crysler');
|
||||
$crysler->setModel('300');
|
||||
|
||||
$merc = new $class;
|
||||
$merc->setBrand('Mercedes');
|
||||
$merc->setModel('C-Class');
|
||||
|
||||
$volvo = new $class;
|
||||
$volvo->setBrand('Volvo');
|
||||
$volvo->setModel('XC90');
|
||||
|
||||
$this->_em->persist($bimmer);
|
||||
$this->_em->persist($crysler);
|
||||
$this->_em->persist($merc);
|
||||
$this->_em->persist($volvo);
|
||||
|
||||
return array($bimmer, $crysler, $merc, $volvo);
|
||||
}
|
||||
|
||||
private function createDrivers($class)
|
||||
{
|
||||
$john = new $class;
|
||||
$john->setName('John Doe');
|
||||
|
||||
$foo = new $class;
|
||||
$foo->setName('Foo Bar');
|
||||
|
||||
$this->_em->persist($foo);
|
||||
$this->_em->persist($john);
|
||||
|
||||
return array($john, $foo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1) Ride contains only columns that are part of its composite primary key
|
||||
* 2) We use fetch joins here
|
||||
*/
|
||||
public function testSelectFromInverseSideWithCompositePkAndSolelyIdentifierColumnsUsingFetchJoins()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
|
||||
$result = $qb->select('d, dr, c')
|
||||
->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
|
||||
->leftJoin('d.freeDriverRides', 'dr')
|
||||
->leftJoin('dr.car', 'c')
|
||||
->where('d.name = ?1')
|
||||
->setParameter(1, 'John Doe')
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('freeDriverRides', $result[0]);
|
||||
$this->assertCount(3, $result[0]['freeDriverRides']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1) PaidRide contains an extra column that is not part of the composite primary key
|
||||
* 2) Again we will use fetch joins
|
||||
*/
|
||||
public function testSelectFromInverseSideWithCompositePkUsingFetchJoins()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
|
||||
$result = $qb->select('d, dr, c')
|
||||
->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
|
||||
->leftJoin('d.driverRides', 'dr')
|
||||
->leftJoin('dr.car', 'c')
|
||||
->where('d.name = ?1')
|
||||
->setParameter(1, 'John Doe')
|
||||
->getQuery()->getArrayResult();
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertArrayHasKey('driverRides', $result[0]);
|
||||
$this->assertCount(3, $result[0]['driverRides']);
|
||||
}
|
||||
|
||||
/**
|
||||
* The other way around will fail too
|
||||
*/
|
||||
public function testSelectFromOwningSideUsingFetchJoins()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
|
||||
$result = $qb->select('r, d, c')
|
||||
->from('Doctrine\Tests\Models\Taxi\PaidRide', 'r')
|
||||
->leftJoin('r.driver', 'd')
|
||||
->leftJoin('r.car', 'c')
|
||||
->where('d.name = ?1')
|
||||
->setParameter(1, 'John Doe')
|
||||
->getQuery()->getArrayResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertArrayHasKey('driver', $result[0]);
|
||||
$this->assertArrayHasKey('car', $result[0]);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
180
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php
Normal file
180
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2579Test.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
* @group DDC-2579
|
||||
*/
|
||||
class DDC2579Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Type::addType(DDC2579Type::NAME, DDC2579Type::CLASSNAME);
|
||||
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(DDC2579Entity::CLASSNAME),
|
||||
$this->_em->getClassMetadata(DDC2579EntityAssoc::CLASSNAME),
|
||||
$this->_em->getClassMetadata(DDC2579AssocAssoc::CLASSNAME),
|
||||
));
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$id = new DDC2579Id("foo");
|
||||
$assoc = new DDC2579AssocAssoc($id);
|
||||
$assocAssoc = new DDC2579EntityAssoc($assoc);
|
||||
$entity = new DDC2579Entity($assocAssoc);
|
||||
$repository = $this->_em->getRepository(DDC2579Entity::CLASSNAME);
|
||||
|
||||
$this->_em->persist($assoc);
|
||||
$this->_em->persist($assocAssoc);
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
|
||||
$entity->value++;
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$id = $entity->id;
|
||||
$value = $entity->value;
|
||||
$criteria = array('assoc' => $assoc, 'id' => $id);
|
||||
$entity = $repository->findOneBy($criteria);
|
||||
|
||||
$this->assertInstanceOf(DDC2579Entity::CLASSNAME, $entity);
|
||||
$this->assertEquals($value, $entity->value);
|
||||
|
||||
$this->_em->remove($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertNull($repository->findOneBy($criteria));
|
||||
$this->assertCount(0, $repository->findAll());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2579Entity
|
||||
{
|
||||
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="ddc2579")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC2579EntityAssoc")
|
||||
* @JoinColumn(name="relation_id", referencedColumnName="association_id")
|
||||
*/
|
||||
public $assoc;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct(DDC2579EntityAssoc $assoc, $value = 0)
|
||||
{
|
||||
$this->id = $assoc->assocAssoc->associationId;
|
||||
$this->assoc = $assoc;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2579EntityAssoc
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity="DDC2579AssocAssoc")
|
||||
* @JoinColumn(name="association_id", referencedColumnName="associationId")
|
||||
*/
|
||||
public $assocAssoc;
|
||||
|
||||
public function __construct(DDC2579AssocAssoc $assocAssoc)
|
||||
{
|
||||
$this->assocAssoc = $assocAssoc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2579AssocAssoc
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="ddc2579")
|
||||
*/
|
||||
public $associationId;
|
||||
|
||||
public function __construct(DDC2579Id $id)
|
||||
{
|
||||
$this->associationId = $id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DDC2579Type extends StringType
|
||||
{
|
||||
const NAME = 'ddc2579';
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
public function convertToPhpValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
return new DDC2579Id($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
}
|
||||
|
||||
class DDC2579Id
|
||||
{
|
||||
const CLASSNAME = __CLASS__;
|
||||
|
||||
private $val;
|
||||
|
||||
public function __construct($val)
|
||||
{
|
||||
$this->val = $val;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->val;
|
||||
}
|
||||
}
|
||||
55
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2645Test.php
Normal file
55
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2645Test.php
Normal file
@@ -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;
|
||||
}
|
||||
122
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php
Normal file
122
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2660Test.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
63
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2692Test.php
Normal file
63
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2692Test.php
Normal file
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php
Normal file
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
146
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2775Test.php
Normal file
146
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2775Test.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
112
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2895Test.php
Normal file
112
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2895Test.php
Normal file
@@ -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
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2931Test.php
Executable file
118
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2931Test.php
Executable 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
199
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2984Test.php
Normal file
199
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2984Test.php
Normal 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();
|
||||
}
|
||||
}
|
||||
90
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2996Test.php
Normal file
90
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2996Test.php
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
137
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3033Test.php
Normal file
137
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3033Test.php
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -6,16 +6,21 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||
use Doctrine\Tests\Mocks\EntityManagerMock;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||
|
||||
require_once __DIR__ . '/../TestInit.php';
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
/**
|
||||
* Tests the lazy-loading capabilities of the PersistentCollection.
|
||||
* Tests the lazy-loading capabilities of the PersistentCollection and the initialization of collections.
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
* @author Austin Morris <austin.morris@gmail.com>
|
||||
*/
|
||||
class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
|
||||
class PersistentCollectionTest extends OrmTestCase
|
||||
{
|
||||
/**
|
||||
* @var PersistentCollection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
private $_connectionMock;
|
||||
private $_emMock;
|
||||
|
||||
@@ -27,6 +32,17 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->_emMock = EntityManagerMock::create($this->_connectionMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the PersistentCollection used for collection initialization tests.
|
||||
*/
|
||||
public function setUpPersistentCollection()
|
||||
{
|
||||
$classMetaData = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart');
|
||||
$this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection);
|
||||
$this->collection->setInitialized(false);
|
||||
$this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products'));
|
||||
}
|
||||
|
||||
public function testCanBePutInLazyLoadingMode()
|
||||
{
|
||||
$class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||
@@ -34,4 +50,34 @@ class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
|
||||
$collection->setInitialized(false);
|
||||
$this->assertFalse($collection->isInitialized());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that PersistentCollection::current() initializes the collection.
|
||||
*/
|
||||
public function testCurrentInitializesCollection()
|
||||
{
|
||||
$this->setUpPersistentCollection();
|
||||
$this->collection->current();
|
||||
$this->assertTrue($this->collection->isInitialized());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that PersistentCollection::key() initializes the collection.
|
||||
*/
|
||||
public function testKeyInitializesCollection()
|
||||
{
|
||||
$this->setUpPersistentCollection();
|
||||
$this->collection->key();
|
||||
$this->assertTrue($this->collection->isInitialized());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that PersistentCollection::next() initializes the collection.
|
||||
*/
|
||||
public function testNextInitializesCollection()
|
||||
{
|
||||
$this->setUpPersistentCollection();
|
||||
$this->collection->next();
|
||||
$this->assertTrue($this->collection->isInitialized());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertEquals('bar', $q->getHint('foo'));
|
||||
$this->assertEquals('baz', $q->getHint('bar'));
|
||||
$this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $q->getHints());
|
||||
$this->assertTrue($q->hasHint('foo'));
|
||||
$this->assertFalse($q->hasHint('barFooBaz'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -155,12 +155,12 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id',
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_managers c2_ INNER JOIN company_employees c3_ ON c2_.id = c3_.id INNER JOIN company_persons c4_ ON c2_.id = c4_.id AND (c0_.id = c4_.id)'
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id AND (c0_.id = c3_.id)'
|
||||
);
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id',
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ INNER JOIN company_employees c3_ ON c2_.id = c3_.id INNER JOIN company_persons c4_ ON c2_.id = c4_.id ON (c0_.id = c4_.id)'
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id ON (c0_.id = c3_.id)'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -2040,6 +2051,97 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2506
|
||||
*/
|
||||
public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1',
|
||||
'SELECT c0_.id AS id0 FROM company_organizations c1_ INNER JOIN company_events c0_ ON c1_.id = c0_.org_id AND (c0_.id = ?) LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id',
|
||||
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceLeftJoinWithCondition()
|
||||
{
|
||||
// Regression test for the bug
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id',
|
||||
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceLeftJoinWithConditionAndWhere()
|
||||
{
|
||||
// Ensure other WHERE predicates are passed through to the main WHERE clause
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id WHERE e.salary > 1000',
|
||||
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceInnerJoinWithCondition()
|
||||
{
|
||||
// Test inner joins too
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e INNER JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id',
|
||||
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceLeftJoinNonAssociationWithConditionAndWhere()
|
||||
{
|
||||
// Test that the discriminator IN() predicate is still added into
|
||||
// the where clause when not joining onto that table
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c LEFT JOIN Doctrine\Tests\Models\Company\CompanyEmployee e WITH e.id = c.salesPerson WHERE c.completed = true',
|
||||
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_contracts c0_ LEFT JOIN company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceJoinCreatesOnCondition()
|
||||
{
|
||||
// Test that the discriminator IN() predicate is still added
|
||||
// into the where clause when not joining onto a single table inheritance entity
|
||||
// via a join association
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s WHERE c.completed = true',
|
||||
"SELECT c0_.id AS id0, c0_.completed AS completed1, c0_.fixPrice AS fixPrice2, c0_.hoursWorked AS hoursWorked3, c0_.pricePerHour AS pricePerHour4, c0_.maxPrice AS maxPrice5, c0_.discr AS discr6 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-2235
|
||||
*/
|
||||
public function testSingleTableInheritanceCreatesOnConditionAndWhere()
|
||||
{
|
||||
// Test that when joining onto an entity using single table inheritance via
|
||||
// a join association that the discriminator IN() predicate is placed
|
||||
// into the ON clause of the join
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e, COUNT(c) FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN e.contracts c WHERE e.department = :department',
|
||||
"SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, COUNT(c2_.id) AS sclr5, c0_.discr AS discr6 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_contract_employees c3_ ON c1_.id = c3_.employee_id INNER JOIN company_contracts c2_ ON c2_.id = c3_.contract_id AND c2_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?",
|
||||
array(),
|
||||
array('department' => 'foobar')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1858
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -516,9 +516,9 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
)),
|
||||
array(array(
|
||||
'fieldName' => 'decimal',
|
||||
'phpType' => 'float',
|
||||
'phpType' => 'string',
|
||||
'dbType' => 'decimal',
|
||||
'value' => 33.33
|
||||
'value' => '12.34'
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -155,7 +155,13 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass',
|
||||
'Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass',
|
||||
'Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass',
|
||||
)
|
||||
),
|
||||
'taxi' => array(
|
||||
'Doctrine\Tests\Models\Taxi\PaidRide',
|
||||
'Doctrine\Tests\Models\Taxi\Ride',
|
||||
'Doctrine\Tests\Models\Taxi\Car',
|
||||
'Doctrine\Tests\Models\Taxi\Driver',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -284,6 +290,12 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
$conn->executeUpdate('DELETE FROM SingleRootClass');
|
||||
}
|
||||
|
||||
if (isset($this->_usedModelSets['taxi'])) {
|
||||
$conn->executeUpdate('DELETE FROM taxi_paid_ride');
|
||||
$conn->executeUpdate('DELETE FROM taxi_ride');
|
||||
$conn->executeUpdate('DELETE FROM taxi_car');
|
||||
$conn->executeUpdate('DELETE FROM taxi_driver');
|
||||
}
|
||||
|
||||
$this->_em->clear();
|
||||
}
|
||||
@@ -467,6 +479,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
throw $e;
|
||||
}
|
||||
|
||||
public function assertSQLEquals($expectedSql, $actualSql)
|
||||
{
|
||||
return $this->assertEquals(strtolower($expectedSql), strtolower($actualSql), "Lowercase comparison of SQL statements failed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the SQL Logger Stack this method retrieves the current query count executed in this test.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user