Compare commits

..

135 Commits

Author SHA1 Message Date
Benjamin Eberlei 84373d05a4 Release 2.4.1 2013-11-12 13:40:13 +01:00
Benjamin Eberlei e82e7147fa Merge branch 'DDC-2715' into 2.4 2013-10-29 09:25:13 +01:00
jan brunnert e23ed2250d Removed unnecessary is_object() check 2013-10-29 09:24:52 +01:00
jan brunnert 192bb6fd21 When the OptimisticLockingException is generated with the static function lockFailedVersionMismatch and the passed parameters are DateTime instances, the exception could not be thrown because the DateTime object is not implicitly converted to a string. 2013-10-29 09:24:52 +01:00
Benjamin Eberlei 0f3679f034 Merge branch 'DDC-2759' into 2.4 2013-10-26 11:17:34 +02:00
Benjamin Eberlei 1d2cd82706 [DDC-2759] Fix regression in ArrayHydrator introduced in DDC-1884 at SHA c7b4c9bf0f 2013-10-26 11:16:53 +02:00
Chris Collins b983d86612 Added a failing test case for DDC-2759. 2013-10-26 11:16:53 +02:00
Benjamin Eberlei b11f01643c Merge branch 'DDC-2668' into 2.4 2013-09-26 23:24:14 +02:00
Fabio B. Silva b58fb8f5d4 [DDC-2668] Fix trim leading zero string 2013-09-26 23:23:49 +02:00
Benjamin Eberlei 925a22b71d Merge branch 'DDC-2608' into 2.4 2013-09-08 16:01:38 +02:00
Benjamin Eberlei 0f0d8abd67 [DDC-2608][DDC-2662] Fix SequenceGenerator requiring "sequenceName" and now throw exception. Fix a bug in quoting the sequenceName. 2013-09-08 16:00:14 +02:00
Benjamin Eberlei 470c15ce05 Merge branch 'DDC-2660' into 2.4 2013-09-08 14:39:54 +02:00
Benjamin Eberlei 3cc5fc0252 [DDC-2660] Fix error with NativeSQL, ResultSetMappingBuilder and Associations as Primary Key. 2013-09-08 14:39:25 +02:00
Benjamin Eberlei fd0657089a Merge branch 'DDC-2661' into 2.4 2013-09-08 10:38:03 +02:00
Benjamin Eberlei de3b237292 [DDC-2661] Fix bug in YamlDriver not passing options from id to mapField() 2013-09-08 10:37:42 +02:00
Benjamin Eberlei 1221cc3a2a More excludes 2013-09-07 18:27:20 +02:00
Benjamin Eberlei 9efbc1fa71 Bump version to 2.4.1 2013-09-07 18:19:57 +02:00
Benjamin Eberlei 57705e0d78 Release 2.4.0 2013-09-07 18:19:56 +02:00
Benjamin Eberlei 82bb6b78cd Travis should prefer dist. 2013-09-07 13:20:35 +02:00
Benjamin Eberlei 64c56b21aa Remove minimum stability from 2.4 composer.json 2013-09-07 13:08:14 +02:00
Benjamin Eberlei b04e2e6364 Adjust composer.json to pending 2.4 stable release 2013-09-07 12:59:17 +02:00
Benjamin Eberlei a70f9b7f49 Fix branch alias 2013-09-07 12:57:56 +02:00
Benjamin Eberlei c88a7c1ffe New Build process
- Switch from Phing to Ant
- Remove PEAR packaging
- Add Composer archiving
2013-09-07 12:57:38 +02:00
Benjamin Eberlei c206728c96 Merge branch 'DDC-2638' into 2.4 2013-09-07 09:04:34 +02:00
Attila Fulop e8d420c641 Fix for entity generator discriminator column 2013-09-07 09:04:26 +02:00
Benjamin Eberlei fdcab7eae8 Merge branch 'DDC-2640' into 2.4 2013-09-07 09:01:01 +02:00
Maks Feltrin 45d7d5234f DO NOT OVERRIDE CUSTOM TREE WALKERS IN getIterator() 2013-09-07 09:00:06 +02:00
Benjamin Eberlei 159ca79b81 Merge origin/2.4 into local branch 2013-09-07 08:55:15 +02:00
Benjamin Eberlei 2b148a27e0 Merge Oracle test fixes to 2.4 branch 2013-09-07 08:54:23 +02:00
Guilherme Blanco 0aef57f60c Fixing missing table aliases when using Many2Many persister. 2013-08-26 10:29:45 -04:00
Benjamin Eberlei fef1e0286c Merge branch 'DDC-2235' into 2.4 2013-08-20 10:08:21 +02:00
Guilherme Blanco 4a38534150 Fixed DDC-2235. 2013-08-20 10:08:07 +02:00
Benjamin Eberlei 1de22adb16 Merge branch 'DDC-2506' into 2.4 2013-08-20 09:56:54 +02:00
Guilherme Blanco 62b4160887 Fixed DDC-2506 by manually updating code. Closes PR #708. 2013-08-20 09:56:39 +02:00
Benjamin Eberlei dbb7c4d2bf Merge branch 'DDC-2607' into 2.4 2013-08-20 09:51:19 +02:00
Dustin Thomson e8978ee365 Modified executeInserts method in JoinedSubclassPersister to only check for the presence of columns in a composite primary key 2013-08-20 09:51:01 +02:00
Benjamin Eberlei c095b88804 Merge branch 'DDC-2578' into 2.4 2013-08-10 18:14:24 +02:00
Guilherme Blanco efe4208ba6 Kept BC. 2013-08-10 18:14:07 +02:00
Guilherme Blanco 453a56670d Modified Hydrators to be per-query instances instead of a singleton-like approach. 2013-08-10 18:14:07 +02:00
Benjamin Eberlei ec36e2c866 Merge branch 'DDC-2579' into 2.4 2013-08-10 17:54:37 +02:00
Fabio B. Silva e250572cb4 fix DDC-2579 2013-08-10 17:53:50 +02:00
Benjamin Eberlei 758955e183 Merge branch 'DDC-2582' into 2.4 2013-08-10 17:48:20 +02:00
Guilherme Blanco 5b8d6a1486 CS fixes. 2013-08-10 17:48:03 +02:00
Guilherme Blanco 3f1003fee9 Fixed DDC-1884. 2013-08-10 17:48:03 +02:00
Benjamin Eberlei 7e241e89b8 Merge branch 'DDC-2548' into 2.4 2013-08-10 17:43:40 +02:00
Michaël Gallego 67c1e1d2b1 Allow to have non-distinct queries 2013-08-10 17:43:26 +02:00
Benjamin Eberlei 261eacdbfc Merge branch 'DDC-2565' into 2.4 2013-08-10 17:27:47 +02:00
Austin Morris 43df821691 convert PersistentCollection functional tests to unit tests 2013-08-10 17:27:30 +02:00
Austin Morris 11d09702da remove redundant require_once for TestInit.php 2013-08-10 17:27:30 +02:00
Austin Morris f9f14139cf do not initialize coll on add() 2013-08-10 17:27:30 +02:00
Austin Morris 39f4d46d36 Initialize coll when using Collection methods inside PersistentCollection 2013-08-10 17:27:30 +02:00
Austin Morris 1dae8d318f PersistentCollection - initialize coll - create failing tests 2013-08-10 17:27:30 +02:00
Benjamin Eberlei a361a7c1cb Merge branch 'DDC-2542' into 2.4 2013-08-10 17:02:34 +02:00
Roger Llopart Pla 6a73608baf Fixed name colision. 2013-08-10 17:02:10 +02:00
Roger Llopart Pla f9955152b2 Added a test which verifies that the tree walkers are kept. 2013-08-10 17:02:10 +02:00
Roger Llopart Pla 5aad1df149 Added docblock. 2013-08-10 17:02:10 +02:00
Roger Llopart Pla 243832555b Appending the Paginator tree walker hint, instead of removing all the other hints. 2013-08-10 17:02:10 +02:00
Benjamin Eberlei ae12fa6b5b Merge branch 'DDC-2577' into 2.4 2013-08-10 16:28:35 +02:00
Konstantin.Myakshin edaf9b6813 Skip not mapped public properties in SchemaValidator 2013-08-10 16:28:27 +02:00
Benjamin Eberlei b324a21abf Merge branch 'DDC-2587' into 2.4 2013-08-10 16:25:04 +02:00
J. Bruni ff34aaaa2c Updated EntityGeneratorTest::testEntityTypeAlias 2013-08-10 16:24:43 +02:00
J. Bruni 9767a814a6 Updated EntityGeneratorTest::testEntityTypeAlias 2013-08-10 16:24:43 +02:00
J Bruni e6007575e1 Corrected PHP type for "decimal" mapping type
"Basic Mapping" documentation says:
"decimal: Type that maps a SQL DECIMAL to a PHP string."
2013-08-10 16:24:43 +02:00
Benjamin Eberlei 29d6da0fa0 Merge pull request #703 from shulcsm/patch-1
Clear visitedCollections
2013-06-30 01:38:02 -07:00
Benjamin Eberlei 69fe5c48f4 Merge pull request #710 from sandermarechal/extra-lazy-get-fix
Fix extra lazy get
2013-06-30 01:37:26 -07:00
Benjamin Eberlei 8e1111c8d3 Merge pull request #711 from FabioBatSilva/coveralls
Coveralls code coverage
2013-06-27 23:52:15 -07:00
Fabio B. Silva e4bccdc7b3 coveralls code coverage 2013-06-27 20:18:21 -04:00
Sander Marechal 06ed21e883 Remove extra-lazy-get for ManyToMany relation 2013-06-27 14:19:39 +02:00
Sander Marechal 5635fa60a4 Check owning entitiy on extra lazy get with OneToMany relation 2013-06-27 14:17:41 +02:00
Benjamin Eberlei 4d93a4950b Merge branch 'DDC-2530' 2013-06-25 19:34:22 +02:00
Benjamin Eberlei a91050e7f4 [DDC-2350] Eager Collections are not marked as initialized, leading to multiple queries being executed. 2013-06-25 19:34:12 +02:00
Guilherme Blanco 20e5d98b7b Merge pull request #640 from denkiryokuhatsuden/patch-1
[Paginator]Add hidden field ordering for postgresql
2013-06-24 19:23:36 -07:00
Benjamin Eberlei 2f6e914d64 Dont allow failures in 5.5 anymore 2013-06-24 08:42:17 +02:00
Guilherme Blanco 457036aacb Merge pull request #702 from FabioBatSilva/DDC-2459
[DDC-2459]ANSI compliant quote strategy
2013-06-23 14:26:49 -07:00
Guilherme Blanco 2ce72f38a2 Merge pull request #705 from FabioBatSilva/DDC-2519
[DDC-2519] Partial association identifier
2013-06-23 14:25:58 -07:00
Fabio B. Silva 1cff8b4d98 Fix DDC-2519 2013-06-21 16:07:09 -04:00
Fabio B. Silva a165f63c8c ANSI compliant quote strategy 2013-06-21 16:05:59 -04:00
Benjamin Eberlei eaf8fd3c34 Merge pull request #706 from sandermarechal/extra-lazy-get
[DDC-1398] Extra-lazy get for indexed associations
2013-06-20 05:26:52 -07:00
Sander Marechal 70427871ce Extra test for indexBy identifier versus indexBy other fields 2013-06-20 14:20:00 +02:00
Sander Marechal 2879162015 No need to lookup metadata 2013-06-20 14:00:58 +02:00
Sander Marechal 3b92cfac5a Use find() if the indexBy field is the identifier 2013-06-20 13:45:38 +02:00
Sander Marechal 53c9ffda30 Get rid of variable 2013-06-20 10:20:16 +02:00
Sander Marechal 647c5e2cad Test actual data returned by get() 2013-06-20 10:18:08 +02:00
Sander Marechal 3555007f08 Return NULL for non-existent keys
The load() function already returns just one entity or NULL, so
the current() is not needed and the result can be returned directly.
2013-06-20 10:09:52 +02:00
Sander Marechal 523697d0b6 [DDC-1398] Extra-lazy get for indexed associations
If an association is EXTRA_LAZY and has an indexBy, then
you can call get() without loading the entire collection.
2013-06-20 09:29:56 +02:00
Benjamin Eberlei 1382d766b0 Merge pull request #704 from liuggio/patch-1
added badges stable release and total downloads
2013-06-19 09:02:33 -07:00
Giulio De Donato c743bb938b added badges 2013-06-19 16:40:57 +02:00
shulcsm 3340234785 Clear visitedCollections
Visited collections are cleared only in commit(). Commit clears up only if it actually has something to do. Processing large amounts of records without changing them cause visitedCollections to grow without any way of clearing.
2013-06-19 16:34:44 +03:00
Marco Pivetta a39ceb3159 Merge pull request #701 from rbroen/patch-2
list_bugs.php needs to call to getters for protected vars
2013-06-17 02:41:29 -07:00
Robert Broen 6ff5043ce8 list_bugs.php needs to call to getters for protected vars
list_bugs.php needs to call to getters for protected vars. This was changed in the "getting-started" code repository, but not in the "getting-started" tutorial.
2013-06-17 11:36:09 +02:00
Marco Pivetta 1a958f70fd Merge pull request #700 from rbroen/patch-2
Update getting-started.rst
2013-06-17 01:39:43 -07:00
Robert Broen 184e8eb26c Update getting-started.rst
The tutorial assumes Doctrine 2.4
2013-06-17 10:31:06 +02:00
Guilherme Blanco 7903a2b513 Merge pull request #695 from doctrine/RepositoryFactory
Implemented support for RepositoryFactory.
2013-06-14 09:19:43 -07:00
Guilherme Blanco 52b3fc1fc3 Updated since php doc tag. 2013-06-14 12:07:28 -04:00
Guilherme Blanco 09d67b10b0 Merge pull request #697 from michaelperrin/patch-1
Fix phpDoc syntax in ClassMetadataInfo.php
2013-06-14 06:57:14 -07:00
Michaël Perrin 37d7df6ac4 Fix phpDoc syntax in ClassMetadataInfo.php 2013-06-14 11:00:17 +03:00
Guilherme Blanco 3488049c18 Reduced granularity of DefaultRepositoryFactory reference to ObjectRepository instances, in cases where consumers are completely rewrote EntityRepository. 2013-06-13 23:59:08 -04:00
Guilherme Blanco a66fc03441 Reducing dependency on RepositoryFactory by providing EntityManager as a getRepository argument. 2013-06-13 23:53:53 -04:00
Guilherme Blanco 37e7e841c3 Fixed wrong interface. 2013-06-13 23:31:18 -04:00
Guilherme Blanco f2f1d8986c Merge pull request #694 from FabioBatSilva/DDC-2478
[DDC-2478] Support null association comparison
2013-06-13 20:12:48 -07:00
Guilherme Blanco 7eb744126b Implemented support for RepositoryFactory. 2013-06-13 21:47:40 -04:00
Fabio B. Silva f16c8e3efe Fix DDC-2478 2013-06-13 16:44:02 -04:00
Guilherme Blanco 6ef48561ba Merge pull request #688 from sellingsource/master
Implement QuoteStrategy on SqlWalker walkRangeVariableDeclaration
2013-06-12 12:03:24 -07:00
Guilherme Blanco 0a90279a99 Merge pull request #693 from doctrine/hotfix/DDC-2214
Adding failing test for DDC-2214
2013-06-12 12:00:31 -07:00
Marco Pivetta a1355d0bb9 Adding failing test for DDC-2214
Parameters being bound to an SQL query should have the same type as
the identifier of the objects being bound to the placeholders of a
DQL query - this is currently broken with proxies, as this test
demonstrates.
2013-06-12 20:51:45 +02:00
Guilherme Blanco 6937061b23 Merge pull request #690 from FabioBatSilva/DDC-2494
[DDC-2494] Apply type conversion to meta columns
2013-06-12 11:47:26 -07:00
Fabio B. Silva c1e688fc81 drop useless support for associations 2013-06-12 10:30:51 -04:00
Fabio B. Silva d961028b14 small optimization 2013-06-12 10:30:51 -04:00
Fabio B. Silva d685f592fe Fix DDC-2494 2013-06-12 10:30:51 -04:00
Guilherme Blanco b15758bb42 DDC-2476 Commented check under PostgreSQL. 2013-06-12 02:29:08 -04:00
Guilherme Blanco 3d86c82a7f DDC-2476 Better approach for reverse engineer. Some refactoring done to driver. 2013-06-12 02:00:36 -04:00
Guilherme Blanco 0d834d0bd4 DDC-2489 Added missing semicolon when dump-sql on schema update. 2013-06-12 00:31:25 -04:00
Guilherme Blanco 0248f743ba Merge pull request #691 from 51systems/master
IDENTITY function fix for Joined Inheritance
2013-06-11 21:04:27 -07:00
Dustin Thomson ed7a4bdcf3 Fix typo in test name 2013-06-11 18:38:05 -06:00
Dustin Thomson 529064aff2 Modified identity function to work with joined inheritance tables.
Added regression tests
2013-06-11 18:09:49 -06:00
Guilherme Blanco 4e99c5c127 DDC-1858 Added coverage to ticket. Functionality already implemented. 2013-06-11 01:21:47 -04:00
Guilherme Blanco 462173ad71 Merge pull request #689 from FabioBatSilva/DDC-1995
[WIP][DDC-1995 ] Support metadata class as parameter for instance of expression
2013-06-07 14:42:59 -07:00
Fabio B. Silva 710d0d1109 Fix DDC-1995 2013-06-07 17:24:05 -04:00
John Brown 4ef043fc3b updating sql walker to use quote strategy in joins 2013-06-07 08:56:58 -07:00
John Brown afb9c829e2 updating sql walker to use quote strategy in joins 2013-06-07 08:49:49 -07:00
John Brown 9bea612d74 Adding simple test to ensure quoting of table names still functions. Note: the funtionality of this change won't be noticiable unless a custom quote strategy is implemented 2013-06-06 15:51:23 -07:00
John Brown 77b905eaa8 Implement QuoteStrategy on SqlWalker walkRangeVariableDeclaration
Based on:
http://www.doctrine-project.org/jira/browse/DDC-1845
https://github.com/doctrine/doctrine2/commit/cb72219b118c158c9b5344c4b81ff2b1a9149ab0
2013-06-06 15:08:22 -07:00
Marco Pivetta 5c7b98b2a9 Merge pull request #687 from mnapoli/patch-3
Fixed rendering
2013-06-06 01:51:49 -07:00
Matthieu Napoli 424793c263 Fixed rendering
Fixed some broken rendering on http://docs.doctrine-project.org/en/latest/reference/yaml-mapping.html
2013-06-06 11:36:02 +03:00
Guilherme Blanco 753d63c2d4 Merge pull request #686 from FabioBatSilva/DDC-2475
[DDC-2475] Replace OrderBy mapping when OrderByClause is given
2013-06-04 22:03:53 -07:00
Fabio B. Silva 27511374ec Fix DDC-2475 2013-06-04 23:50:43 -04:00
Marco Pivetta 3d6436c2f3 Merge pull request #683 from greg0ire/error_message_improvement
Explicitely state what the problem is
2013-06-03 03:37:38 -07:00
Grégoire Paris a986fe013e Explicitely state what the problem is
People like me think the problem is that there is no association
mapping, when the problem in fact could be that there also is a field
mapping on the property.
This message makes it clearer why we are getting an error message.
2013-06-03 12:07:08 +02:00
Marco Pivetta 4e8b787d07 Merge pull request #679 from ajgarlag/getting-started
Fix getting started doc to work with current version
2013-05-28 00:36:17 -07:00
Antonio J. García Lagar c64c149ebf Fix getting started doc to work with current version 2013-05-28 08:57:17 +02:00
Benjamin Eberlei f269ecc3ac Bump dev version to 2.4.0 2013-05-27 21:47:16 +02:00
denkiryokuhatsuden 7af84e79e5 Fixed postgresql hidden scalar sort 2013-04-03 17:22:31 +09:00
denkiryokuhatsuden e54c11e3bb Add test for postgresql hidden scalar sorting 2013-04-03 17:21:51 +09:00
denkiryokuhatsuden 786d904328 Revert "Add hidden field ordering for postgresql"
This reverts commit 3e8796f781.
2013-04-03 17:14:31 +09:00
denkiryokuhatsuden 3e8796f781 Add hidden field ordering for postgresql
In postgresql environment, when some hidden fields are used in orderBy clause,
they're not property added because $rsm->scalarMappings don't have information about them.
2013-04-02 18:54:55 +09:00
77 changed files with 3273 additions and 699 deletions
+4
View File
@@ -0,0 +1,4 @@
# for php-coveralls
service_name: travis-ci
src_dir: lib
coverage_clover: build/logs/clover.xml
+4 -4
View File
@@ -5,9 +5,6 @@ php:
- 5.4
- 5.5
matrix:
  allow_failures:
    - php: 5.5
env:
- DB=mysql
- DB=pgsql
@@ -19,6 +16,9 @@ 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
after_script:
- php vendor/bin/coveralls -v
+5
View File
@@ -5,6 +5,11 @@ Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?bra
2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2)
2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
Master: [![Coverage Status](https://coveralls.io/repos/doctrine/doctrine2/badge.png?branch=master)](https://coveralls.io/r/doctrine/doctrine2?branch=master)
[![Latest Stable Version](https://poser.pugx.org/doctrine/orm/v/stable.png)](https://packagist.org/packages/doctrine/orm) [![Total Downloads](https://poser.pugx.org/doctrine/orm/downloads.png)](https://packagist.org/packages/doctrine/orm)
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
+1 -9
View File
@@ -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
+93 -106
View File
@@ -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>
+8 -5
View File
@@ -1,6 +1,6 @@
{
"name": "doctrine/orm",
"type": "library","version":"2.4.0-RC1",
"type": "library",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
@@ -11,16 +11,16 @@
{"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": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
@@ -33,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"]
}
}
+3 -5
View File
@@ -19,8 +19,6 @@ In order to work, this requires certain conventions:
convention and you are not forced to do this. You can change the
file extension easily enough.
-
.. code-block:: php
<?php
@@ -47,9 +45,9 @@ Simplified YAML Driver
The Symfony project sponsored a driver that simplifies usage of the YAML Driver.
The changes between the original driver are:
1. File Extension is .orm.yml
2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.yml
3. You can add a global file and add multiple entities in this file.
- File Extension is .orm.yml
- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml
- You can add a global file and add multiple entities in this file.
Configuration of this client works a little bit different:
+5 -7
View File
@@ -25,7 +25,7 @@ The code of this tutorial is `available on Github <https://github.com/doctrine/d
.. note::
This tutorial assumes you work with Doctrine 2.3 and above.
This tutorial assumes you work with Doctrine 2.4 and above.
Some of the code will not work with lower versions.
What is Doctrine?
@@ -187,9 +187,7 @@ doctrine command. Its a fairly simple file:
// cli-config.php
require_once "bootstrap.php";
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager)
));
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
You can then change into your project directory and call the
Doctrine command-line tool:
@@ -1110,10 +1108,10 @@ the first read-only use-case:
foreach($bugs AS $bug) {
echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n";
echo " Reported by: ".$bug->getReporter()->name."\n";
echo " Assigned to: ".$bug->getEngineer()->name."\n";
echo " Reported by: ".$bug->getReporter()->getName()."\n";
echo " Assigned to: ".$bug->getEngineer()->getName()."\n";
foreach($bug->getProducts() AS $product) {
echo " Platform: ".$product->name."\n";
echo " Platform: ".$product->getName()."\n";
}
echo "\n";
}
+13 -1
View File
@@ -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
);
+38 -12
View File
@@ -19,20 +19,22 @@
namespace Doctrine\ORM;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
use Doctrine\ORM\Mapping\EntityListenerResolver;
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\EntityListenerResolver;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\Repository\RepositoryFactory;
/**
* Configuration container for all configuration options of Doctrine.
@@ -779,4 +781,28 @@ class Configuration extends \Doctrine\DBAL\Configuration
return $this->_attributes['entityListenerResolver'];
}
/**
* Set the entity repository factory.
*
* @since 2.4
* @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory
*/
public function setRepositoryFactory(RepositoryFactory $repositoryFactory)
{
$this->_attributes['repositoryFactory'] = $repositoryFactory;
}
/**
* Get the entity repository factory.
*
* @since 2.4
* @return \Doctrine\ORM\Repository\RepositoryFactory
*/
public function getRepositoryFactory()
{
return isset($this->_attributes['repositoryFactory'])
? $this->_attributes['repositoryFactory']
: new DefaultRepositoryFactory();
}
}
+18 -43
View File
@@ -85,13 +85,6 @@ use Doctrine\Common\Util\ClassUtils;
*/
private $metadataFactory;
/**
* The EntityRepository instances.
*
* @var array
*/
private $repositories = array();
/**
* The UnitOfWork used to coordinate object-level transactions.
*
@@ -106,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.
*
@@ -120,6 +106,13 @@ use Doctrine\Common\Util\ClassUtils;
*/
private $proxyFactory;
/**
* The repository factory used to create dynamic repositories.
*
* @var \Doctrine\ORM\Repository\RepositoryFactory
*/
private $repositoryFactory;
/**
* The expression builder instance used to generate query expressions.
*
@@ -151,9 +144,9 @@ use Doctrine\Common\Util\ClassUtils;
*/
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
{
$this->conn = $conn;
$this->config = $config;
$this->eventManager = $eventManager;
$this->conn = $conn;
$this->config = $config;
$this->eventManager = $eventManager;
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
@@ -161,8 +154,9 @@ use Doctrine\Common\Util\ClassUtils;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory(
$this->repositoryFactory = $config->getRepositoryFactory();
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory(
$this,
$config->getProxyDir(),
$config->getProxyNamespace(),
@@ -758,28 +752,11 @@ use Doctrine\Common\Util\ClassUtils;
*
* @param string $entityName The name of the entity.
*
* @return EntityRepository The repository class.
* @return \Doctrine\ORM\EntityRepository The repository class.
*/
public function getRepository($entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
$repositoryClassName = $metadata->customRepositoryClassName;
if ($repositoryClassName === null) {
$repositoryClassName = $this->config->getDefaultRepositoryClassName();
}
$repository = new $repositoryClassName($this, $metadata);
$this->repositories[$entityName] = $repository;
return $repository;
return $this->repositoryFactory->getRepository($this, $entityName);
}
/**
@@ -856,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)];
@@ -114,9 +114,8 @@ class SimpleObjectHydrator extends AbstractHydrator
}
// Convert field to a valid PHP value
if (isset($cache[$column]['field'])) {
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
$value = $type->convertToPHPValue($value, $this->_platform);
if (isset($cache[$column]['type'])) {
$value = Type::getType($cache[$column]['type'])->convertToPHPValue($value, $this->_platform);
}
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
@@ -145,44 +144,36 @@ class SimpleObjectHydrator extends AbstractHydrator
*/
protected function hydrateColumnInfo($entityName, $column)
{
switch (true) {
case (isset($this->_rsm->fieldMappings[$column])):
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;
// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}
if (isset($this->_rsm->fieldMappings[$column])) {
$name = $this->_rsm->fieldMappings[$column];
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;
return array(
'class' => $class,
'name' => $this->_rsm->fieldMappings[$column],
'field' => true,
);
case (isset($this->_rsm->relationMap[$column])):
$class = isset($this->_rsm->relationMap[$column])
? $this->_rsm->relationMap[$column]
: $this->class;
// If class is not self referencing, ignore
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
return null;
}
// TODO: Decide what to do with associations. It seems original code is incomplete.
// One solution is to load the association, but it might require extra efforts.
return array('name' => $column);
case (isset($this->_rsm->metaMappings[$column])):
return array(
'name' => $this->_rsm->metaMappings[$column]
);
default:
// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}
return array(
'name' => $name,
'type' => $class->fieldMappings[$name]['type']
);
}
if (isset($this->_rsm->metaMappings[$column])) {
return array(
'name' => $this->_rsm->metaMappings[$column],
'type' => (isset($this->_rsm->typeMappings[$column]) ? $this->_rsm->typeMappings[$column] : null)
);
}
// An ObjectHydrator should be used instead of SimpleObjectHydrator
if (isset($this->_rsm->relationMap[$column])) {
throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column));
}
return null;
}
}
@@ -0,0 +1,97 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* ANSI compliant quote strategy, this strategy does not apply any quote.
* To use this strategy all mapped tables and columns should be ANSI compliant.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class AnsiQuoteStrategy implements QuoteStrategy
{
/**
* {@inheritdoc}
*/
public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform)
{
return $class->fieldMappings[$fieldName]['columnName'];
}
/**
* {@inheritdoc}
*/
public function getTableName(ClassMetadata $class, AbstractPlatform $platform)
{
return $class->table['name'];
}
/**
* {@inheritdoc}
*/
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform)
{
return $definition['sequenceName'];
}
/**
* {@inheritdoc}
*/
public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
{
return $joinColumn['name'];
}
/**
* {@inheritdoc}
*/
public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform)
{
return $joinColumn['referencedColumnName'];
}
/**
* {@inheritdoc}
*/
public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform)
{
return $association['joinTable']['name'];
}
/**
* {@inheritdoc}
*/
public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform)
{
return $class->identifier;
}
/**
* {@inheritdoc}
*/
public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null)
{
return $platform->getSQLResultCasing($columnName . $counter);
}
}
+21 -3
View File
@@ -358,7 +358,7 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>scale</b> (integer, optional, schema-only)
* The scale of a decimal column. Only valid if the column type is decimal.
*
[* - <b>'unique'] (string, optional, schema-only)</b>
* - <b>'unique'</b> (string, optional, schema-only)
* Whether a unique constraint should be generated for the column.
*
* @var array
@@ -2790,8 +2790,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;
}
@@ -3026,4 +3030,18 @@ class ClassMetadataInfo implements ClassMetadata
return $className;
}
/**
* @param string $name
*
* @return mixed
*/
public function getMetadataValue($name) {
if (isset($this->$name)) {
return $this->$name;
}
return null;
}
}
+380 -253
View File
@@ -19,12 +19,15 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Common\Util\Inflector;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
/**
@@ -84,249 +87,15 @@ class DatabaseDriver implements MappingDriver
}
/**
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
* Set the namespace for the generated entities.
*
* @param array $entityTables
* @param array $manyToManyTables
* @param string $namespace
*
* @return void
*/
public function setTables($entityTables, $manyToManyTables)
public function setNamespace($namespace)
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables as $table) {
$className = $this->getClassNameForTable($table->getName());
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
foreach ($manyToManyTables as $table) {
$this->manyToManyTables[$table->getName()] = $table;
}
}
/**
* @return void
*
* @throws \Doctrine\ORM\Mapping\MappingException
*/
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables as $tableName => $table) {
/* @var $table \Doctrine\DBAL\Schema\Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $table->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
$this->reverseEngineerMappingFromDatabase();
if (!isset($this->classToTableNames[$className])) {
throw new \InvalidArgumentException("Unknown class " . $className);
}
$tableName = $this->classToTableNames[$className];
$metadata->name = $className;
$metadata->table['name'] = $tableName;
$columns = $this->tables[$tableName]->getColumns();
$indexes = $this->tables[$tableName]->getIndexes();
try {
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
$ids = array();
$fieldMappings = array();
foreach ($columns as $column) {
$fieldMapping = array();
if (in_array($column->getName(), $allForeignKeyColumns)) {
continue;
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
}
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) {
$fieldMapping['length'] = $column->getLength();
$fieldMapping['fixed'] = $column->getFixed();
} else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) {
$fieldMapping['unsigned'] = $column->getUnsigned();
}
$fieldMapping['nullable'] = $column->getNotNull() ? false : true;
if (isset($fieldMapping['id'])) {
$ids[] = $fieldMapping;
} else {
$fieldMappings[] = $fieldMapping;
}
}
if ($ids) {
// We need to check for the columns here, because we might have associations as id as well.
if (count($primaryKeyColumns) == 1) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
}
foreach ($ids as $id) {
$metadata->mapField($id);
}
}
foreach ($fieldMappings as $fieldMapping) {
$metadata->mapField($fieldMapping);
}
foreach ($this->manyToManyTables as $manyTable) {
foreach ($manyTable->getForeignKeys() as $foreignKey) {
// foreign key maps to the table of the current entity, many to many association probably exists
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
$myFk = $foreignKey;
$otherFk = null;
foreach ($manyTable->getForeignKeys() as $foreignKey) {
if ($foreignKey != $myFk) {
$otherFk = $foreignKey;
break;
}
}
if (!$otherFk) {
// the definition of this many to many table does not contain
// enough foreign key information to continue reverse engineering.
continue;
}
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
if (current($manyTable->getColumns())->getName() == $localColumn) {
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['joinTable'] = array(
'name' => strtolower($manyTable->getName()),
'joinColumns' => array(),
'inverseJoinColumns' => array(),
);
$fkCols = $myFk->getForeignColumns();
$cols = $myFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$fkCols = $otherFk->getForeignColumns();
$cols = $otherFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
} else {
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
}
$metadata->mapManyToMany($associationMapping);
break;
}
}
}
foreach ($foreignKeys as $foreignKey) {
$foreignTable = $foreignKey->getForeignTableName();
$cols = $foreignKey->getColumns();
$fkCols = $foreignKey->getForeignColumns();
$localColumn = current($cols);
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
$associationMapping['fieldName'] = $associationMapping['fieldName'] . "2";
}
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
$associationMapping['id'] = true;
}
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
//Here we need to check if $cols are the same as $primaryKeyColumns
if (!array_diff($cols,$primaryKeyColumns)) {
$metadata->mapOneToOne($associationMapping);
} else {
$metadata->mapManyToOne($associationMapping);
}
}
$this->namespace = $namespace;
}
/**
@@ -374,6 +143,376 @@ class DatabaseDriver implements MappingDriver
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
}
/**
* Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager.
*
* @param array $entityTables
* @param array $manyToManyTables
*
* @return void
*/
public function setTables($entityTables, $manyToManyTables)
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables as $table) {
$className = $this->getClassNameForTable($table->getName());
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
foreach ($manyToManyTables as $table) {
$this->manyToManyTables[$table->getName()] = $table;
}
}
/**
* {@inheritDoc}
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
$this->reverseEngineerMappingFromDatabase();
if ( ! isset($this->classToTableNames[$className])) {
throw new \InvalidArgumentException("Unknown class " . $className);
}
$tableName = $this->classToTableNames[$className];
$metadata->name = $className;
$metadata->table['name'] = $tableName;
$this->buildIndexes($metadata);
$this->buildFieldMappings($metadata);
$this->buildToOneAssociationMappings($metadata);
foreach ($this->manyToManyTables as $manyTable) {
foreach ($manyTable->getForeignKeys() as $foreignKey) {
// foreign key maps to the table of the current entity, many to many association probably exists
if ( ! (strtolower($tableName) === strtolower($foreignKey->getForeignTableName()))) {
continue;
}
$myFk = $foreignKey;
$otherFk = null;
foreach ($manyTable->getForeignKeys() as $foreignKey) {
if ($foreignKey != $myFk) {
$otherFk = $foreignKey;
break;
}
}
if ( ! $otherFk) {
// the definition of this many to many table does not contain
// enough foreign key information to continue reverse engineering.
continue;
}
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
if (current($manyTable->getColumns())->getName() == $localColumn) {
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['joinTable'] = array(
'name' => strtolower($manyTable->getName()),
'joinColumns' => array(),
'inverseJoinColumns' => array(),
);
$fkCols = $myFk->getForeignColumns();
$cols = $myFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$fkCols = $otherFk->getForeignColumns();
$cols = $otherFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
} else {
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
}
$metadata->mapManyToMany($associationMapping);
break;
}
}
}
/**
* @return void
*
* @throws \Doctrine\ORM\Mapping\MappingException
*/
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables as $tableName => $table) {
$foreignKeys = ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints())
? $table->getForeignKeys()
: array();
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
/**
* Build indexes from a class metadata.
*
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
*/
private function buildIndexes(ClassMetadataInfo $metadata)
{
$tableName = $metadata->table['name'];
$indexes = $this->tables[$tableName]->getIndexes();
foreach($indexes as $index){
if ($index->isPrimary()) {
continue;
}
$indexName = $index->getName();
$indexColumns = $index->getColumns();
$constraintType = $index->isUnique()
? 'uniqueConstraints'
: 'indexes';
$metadata->table[$constraintType][$indexName]['columns'] = $indexColumns;
}
}
/**
* Build field mapping from class metadata.
*
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
*/
private function buildFieldMappings(ClassMetadataInfo $metadata)
{
$tableName = $metadata->table['name'];
$columns = $this->tables[$tableName]->getColumns();
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
$foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]);
$allForeignKeys = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeys = array_merge($allForeignKeys, $foreignKey->getLocalColumns());
}
$ids = array();
$fieldMappings = array();
foreach ($columns as $column) {
if (in_array($column->getName(), $allForeignKeys)) {
continue;
}
$fieldMapping = $this->buildFieldMapping($tableName, $column);
if ($primaryKeys && in_array($column->getName(), $primaryKeys)) {
$fieldMapping['id'] = true;
$ids[] = $fieldMapping;
}
$fieldMappings[] = $fieldMapping;
}
// We need to check for the columns here, because we might have associations as id as well.
if ($ids && count($primaryKeys) == 1) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
}
foreach ($fieldMappings as $fieldMapping) {
$metadata->mapField($fieldMapping);
}
}
/**
* Build field mapping from a schema column definition
*
* @param string $tableName
* @param \Doctrine\DBAL\Schema\Column $column
*
* @return array
*/
private function buildFieldMapping($tableName, Column $column)
{
$fieldMapping = array(
'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false),
'columnName' => $column->getName(),
'type' => strtolower((string) $column->getType()),
'nullable' => ( ! $column->getNotNull()),
);
// Type specific elements
switch ($fieldMapping['type']) {
case Type::TARRAY:
case Type::BLOB:
case Type::GUID:
case Type::JSON_ARRAY:
case Type::OBJECT:
case Type::SIMPLE_ARRAY:
case Type::STRING:
case Type::TEXT:
$fieldMapping['length'] = $column->getLength();
$fieldMapping['fixed'] = $column->getFixed();
break;
case Type::DECIMAL:
case Type::FLOAT:
$fieldMapping['precision'] = $column->getPrecision();
$fieldMapping['scale'] = $column->getScale();
break;
case Type::INTEGER:
case Type::BIGINT:
case Type::SMALLINT:
$fieldMapping['unsigned'] = $column->getUnsigned();
break;
}
// Comment
if (($comment = $column->getComment()) !== null) {
$fieldMapping['comment'] = $comment;
}
// Default
if (($default = $column->getDefault()) !== null) {
$fieldMapping['default'] = $default;
}
return $fieldMapping;
}
/**
* Build to one (one to one, many to one) association mapping from class metadata.
*
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata
*/
private function buildToOneAssociationMappings(ClassMetadataInfo $metadata)
{
$tableName = $metadata->table['name'];
$primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]);
$foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]);
foreach ($foreignKeys as $foreignKey) {
$foreignTableName = $foreignKey->getForeignTableName();
$fkColumns = $foreignKey->getColumns();
$fkForeignColumns = $foreignKey->getForeignColumns();
$localColumn = current($fkColumns);
$associationMapping = array(
'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true),
'targetEntity' => $this->getClassNameForTable($foreignTableName),
);
if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) {
$associationMapping['fieldName'] .= '2'; // "foo" => "foo2"
}
if ($primaryKeys && in_array($localColumn, $primaryKeys)) {
$associationMapping['id'] = true;
}
for ($i = 0; $i < count($fkColumns); $i++) {
$associationMapping['joinColumns'][] = array(
'name' => $fkColumns[$i],
'referencedColumnName' => $fkForeignColumns[$i],
);
}
// Here we need to check if $fkColumns are the same as $primaryKeys
if ( ! array_diff($fkColumns, $primaryKeys)) {
$metadata->mapOneToOne($associationMapping);
} else {
$metadata->mapManyToOne($associationMapping);
}
}
}
/**
* Retreive schema table definition foreign keys.
*
* @param \Doctrine\DBAL\Schema\Table $table
*
* @return array
*/
private function getTableForeignKeys(Table $table)
{
return ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints())
? $table->getForeignKeys()
: array();
}
/**
* Retreive schema table definition primary keys.
*
* @param \Doctrine\DBAL\Schema\Table $table
*
* @return array
*/
private function getTablePrimaryKeys(Table $table)
{
try {
return $table->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
// Do nothing
}
return array();
}
/**
* Returns the mapped class name for a table if it exists. Otherwise return "classified" version.
*
@@ -413,16 +552,4 @@ class DatabaseDriver implements MappingDriver
}
return Inflector::camelize($columnName);
}
/**
* Set the namespace for the generated entities.
*
* @param string $namespace
*
* @return void
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
}
@@ -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);
}
+18
View File
@@ -517,6 +517,18 @@ final class PersistentCollection implements Collection, Selectable
*/
public function get($key)
{
if ( ! $this->initialized
&& $this->association['type'] === Mapping\ClassMetadataInfo::ONE_TO_MANY
&& $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY
&& isset($this->association['indexBy'])
) {
if (!$this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->association['indexBy'])) {
return $this->em->find($this->typeClass->name, $key);
}
return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key);
}
$this->initialize();
return $this->coll->get($key);
@@ -745,6 +757,8 @@ final class PersistentCollection implements Collection, Selectable
*/
public function key()
{
$this->initialize();
return $this->coll->key();
}
@@ -753,6 +767,8 @@ final class PersistentCollection implements Collection, Selectable
*/
public function current()
{
$this->initialize();
return $this->coll->current();
}
@@ -761,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]);
@@ -1334,16 +1334,22 @@ class BasicEntityPersister
return '';
}
$columnList = array();
$columnList = array();
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
foreach ($assoc['joinColumns'] as $joinColumn) {
$type = null;
$isIdentifier = isset($assoc['id']) && $assoc['id'] === true;
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
$columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $quotedColumn . ' AS ' . $resultColumnName;
$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true);
if (isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
$type = $targetClass->fieldMappings[$targetClass->fieldNames[$joinColumn['referencedColumnName']]]['type'];
}
$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
}
return implode(', ', $columnList);
@@ -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;
}
}
@@ -32,6 +32,24 @@ use Doctrine\ORM\UnitOfWork;
*/
class OneToManyPersister extends AbstractCollectionPersister
{
/**
* {@inheritdoc}
*
* @override
*/
public function get(PersistentCollection $coll, $index)
{
$mapping = $coll->getMapping();
$uow = $this->em->getUnitOfWork();
$persister = $uow->getEntityPersister($mapping['targetEntity']);
if (!isset($mapping['indexBy'])) {
throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections.");
}
return $persister->load(array($mapping['mappedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, null, array(), 0, 1);
}
/**
* Generates the SQL UPDATE that updates a particular row's foreign
* key to null.
@@ -66,6 +66,7 @@ class SqlExpressionVisitor extends ExpressionVisitor
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
if (isset($this->classMetadata->associationMappings[$field]) &&
$value !== null &&
! is_object($value) &&
! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) {
+14 -7
View File
@@ -26,6 +26,8 @@ use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\ParserResult;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ParameterTypeInferer;
/**
* A Query object represents a DQL query.
@@ -268,6 +270,10 @@ final class Query extends AbstractQuery
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
if ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
}
// Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings();
@@ -277,10 +283,6 @@ final class Query extends AbstractQuery
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
if ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
}
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
}
@@ -299,16 +301,21 @@ final class Query extends AbstractQuery
$types = array();
foreach ($this->parameters as $parameter) {
$key = $parameter->getName();
$key = $parameter->getName();
$value = $parameter->getValue();
if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key);
}
$value = $this->processParameterValue($parameter->getValue());
if (isset($this->_resultSetMapping->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
$value = $value->getMetadataValue($this->_resultSetMapping->metadataParameterMapping[$key]);
}
$value = $this->processParameterValue($value);
$type = ($parameter->getValue() === $value)
? $parameter->getType()
: Query\ParameterTypeInferer::inferType($value);
: ParameterTypeInferer::inferType($value);
foreach ($paramMappings[$key] as $position) {
$types[$position] = $type;
@@ -82,7 +82,10 @@ class IdentityFunction extends FunctionNode
}
}
$tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
//The table with the relation may be a subclass, so get the table name from the association definition
$tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName();
$tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform);
return $tableAlias . '.' . $columnName;
@@ -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;
}
/**
+19 -6
View File
@@ -670,6 +670,12 @@ class Parser
continue;
}
if (isset($class->associationMappings[$field]) &&
$class->associationMappings[$field]['isOwningSide'] &&
$class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) {
continue;
}
$this->semanticalError(
"There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token']
);
@@ -1538,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();
@@ -1616,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();
+27 -4
View File
@@ -153,6 +153,13 @@ class ResultSetMapping
*/
public $newObjectMappings = array();
/**
* Maps metadata parameter names to the metadata attribute.
*
* @var array
*/
public $metadataParameterMapping = array();
/**
* Adds an entity result to this ResultSetMapping.
*
@@ -371,6 +378,17 @@ class ResultSetMapping
return $this;
}
/**
* Adds a metadata parameter mappings.
*
* @param mixed $parameter The parameter name in the SQL result set.
* @param string $attribute The metadata attribute.
*/
public function addMetadataParameterMapping($parameter, $attribute)
{
$this->metadataParameterMapping[$parameter] = $attribute;
}
/**
* Checks whether a column with a given name is mapped as a scalar result.
*
@@ -525,14 +543,15 @@ class ResultSetMapping
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
* @param string $alias
* @param string $columnName
* @param string $fieldName
* @param string $alias The result alias with which the meta result should be placed in the result structure.
* @param string $columnName The name of the column in the SQL result set.
* @param string $fieldName The name of the field on the declaring class.
* @param bool $isIdentifierColumn
* @param string $type The column type
*
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null)
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
@@ -541,6 +560,10 @@ class ResultSetMapping
$this->isIdentifierColumn[$alias][$columnName] = true;
}
if ($type) {
$this->typeMappings[$columnName] = $type;
}
return $this;
}
}
@@ -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)
);
}
}
}
+131 -71
View File
@@ -24,6 +24,7 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
@@ -117,6 +118,13 @@ class SqlWalker implements TreeWalker
*/
private $scalarResultAliasMap = array();
/**
* Map from Table-Alias + Column-Name to OrderBy-Direction.
*
* @var array
*/
private $orderedColumnsMap = array();
/**
* Map from DQL-Alias + Field-Name to SQL Column Alias.
*
@@ -388,25 +396,36 @@ class SqlWalker implements TreeWalker
*/
private function _generateOrderedCollectionOrderByItems()
{
$sqlParts = array();
$orderedColumns = array();
foreach ($this->selectedClasses as $selectedClass) {
$dqlAlias = $selectedClass['dqlAlias'];
$qComp = $this->queryComponents[$dqlAlias];
$dqlAlias = $selectedClass['dqlAlias'];
$qComp = $this->queryComponents[$dqlAlias];
$persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name);
if ( ! isset($qComp['relation']['orderBy'])) continue;
if ( ! isset($qComp['relation']['orderBy'])) {
continue;
}
foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) {
$columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform);
$tableName = ($qComp['metadata']->isInheritanceTypeJoined())
? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName)
? $persister->getOwningTable($fieldName)
: $qComp['metadata']->getTableName();
$sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation;
$orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName;
// OrderByClause should replace an ordered relation. see - DDC-2475
if (isset($this->orderedColumnsMap[$orderedColumn])) {
continue;
}
$this->orderedColumnsMap[$orderedColumn] = $orientation;
$orderedColumns[] = $orderedColumn . ' ' . $orientation;
}
}
return implode(', ', $sqlParts);
return implode(', ', $orderedColumns);
}
/**
@@ -495,44 +514,52 @@ class SqlWalker implements TreeWalker
*/
public function walkSelectStatement(AST\SelectStatement $AST)
{
$sql = $this->walkSelectClause($AST->selectClause);
$sql .= $this->walkFromClause($AST->fromClause);
$sql .= $this->walkWhereClause($AST->whereClause);
$sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
$sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : '';
$limit = $this->query->getMaxResults();
$offset = $this->query->getFirstResult();
$lockMode = $this->query->getHint(Query::HINT_LOCK_MODE);
$sql = $this->walkSelectClause($AST->selectClause)
. $this->walkFromClause($AST->fromClause)
. $this->walkWhereClause($AST->whereClause);
if (($orderByClause = $AST->orderByClause) !== null) {
$sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
} else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
if ($AST->groupByClause) {
$sql .= $this->walkGroupByClause($AST->groupByClause);
}
if ($AST->havingClause) {
$sql .= $this->walkHavingClause($AST->havingClause);
}
if ($AST->orderByClause) {
$sql .= $this->walkOrderByClause($AST->orderByClause);
}
if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) {
$sql .= ' ORDER BY ' . $orderBySql;
}
$sql = $this->platform->modifyLimitQuery(
$sql, $this->query->getMaxResults(), $this->query->getFirstResult()
);
if ($limit !== null || $offset !== null) {
$sql = $this->platform->modifyLimitQuery($sql, $limit, $offset);
}
if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) {
switch ($lockMode) {
case LockMode::PESSIMISTIC_READ:
$sql .= ' ' . $this->platform->getReadLockSQL();
break;
if ($lockMode === false || $lockMode === LockMode::NONE) {
return $sql;
}
case LockMode::PESSIMISTIC_WRITE:
$sql .= ' ' . $this->platform->getWriteLockSQL();
break;
if ($lockMode === LockMode::PESSIMISTIC_READ) {
return $sql . ' ' . $this->platform->getReadLockSQL();
}
case LockMode::OPTIMISTIC:
foreach ($this->selectedClasses as $selectedClass) {
if ( ! $selectedClass['class']->isVersioned) {
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
break;
case LockMode::NONE:
break;
if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
return $sql . ' ' . $this->platform->getWriteLockSQL();
}
default:
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
if ($lockMode !== LockMode::OPTIMISTIC) {
throw QueryException::invalidLockMode();
}
foreach ($this->selectedClasses as $selectedClass) {
if ( ! $selectedClass['class']->isVersioned) {
throw OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
@@ -815,10 +842,12 @@ class SqlWalker implements TreeWalker
$class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
$dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
$this->rootAliases[] = $dqlAlias;
if ($rangeVariableDeclaration->isRoot) {
$this->rootAliases[] = $dqlAlias;
}
$sql = $class->getQuotedTableName($this->platform) . ' '
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
$sql = $this->quoteStrategy->getTableName($class,$this->platform) . ' '
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
@@ -831,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 = '';
@@ -848,7 +878,7 @@ class SqlWalker implements TreeWalker
$relation = $this->queryComponents[$joinedDqlAlias]['relation'];
$targetClass = $this->em->getClassMetadata($relation['targetEntity']);
$sourceClass = $this->em->getClassMetadata($relation['sourceEntity']);
$targetTableName = $targetClass->getQuotedTableName($this->platform);
$targetTableName = $this->quoteStrategy->getTableName($targetClass,$this->platform);
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
@@ -903,7 +933,7 @@ class SqlWalker implements TreeWalker
// Join relation table
$joinTable = $assoc['joinTable'];
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
$joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->platform);
$joinTableName = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform);
$conditions = array();
$relationColumns = ($relation['isOwningSide'])
@@ -952,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);
@@ -998,12 +1035,15 @@ class SqlWalker implements TreeWalker
*/
public function walkOrderByItem($orderByItem)
{
$type = strtoupper($orderByItem->type);
$expr = $orderByItem->expression;
$sql = ($expr instanceof AST\Node)
? $expr->dispatch($this)
: $this->walkResultVariable($this->queryComponents[$expr]['token']['value']);
return $sql . ' ' . strtoupper($orderByItem->type);
$this->orderedColumnsMap[$sql] = $type;
return $sql . ' ' . $type;
}
/**
@@ -1028,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;
}
@@ -1942,24 +1995,31 @@ class SqlWalker implements TreeWalker
$sqlParameterList = array();
foreach ($instanceOfExpr->value as $parameter) {
if ($parameter instanceof AST\InputParameter) {
$this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue');
$sqlParameterList[] = $this->walkInputParameter($parameter);
} else {
// Get name from ClassMetadata to resolve aliases.
$entityClassName = $this->em->getClassMetadata($parameter)->name;
if ($entityClassName == $class->name) {
$sqlParameterList[] = $this->conn->quote($class->discriminatorValue);
} else {
$discrMap = array_flip($class->discriminatorMap);
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]);
}
continue;
}
// Get name from ClassMetadata to resolve aliases.
$entityClassName = $this->em->getClassMetadata($parameter)->name;
$discriminatorValue = $class->discriminatorValue;
if ($entityClassName !== $class->name) {
$discrMap = array_flip($class->discriminatorMap);
if ( ! isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$discriminatorValue = $discrMap[$entityClassName];
}
$sqlParameterList[] = $this->conn->quote($discriminatorValue);
}
$sql .= '(' . implode(', ', $sqlParameterList) . ')';
@@ -0,0 +1,77 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Repository;
use Doctrine\ORM\EntityManagerInterface;
/**
* This factory is used to create default repository objects for entities at runtime.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.4
*/
class DefaultRepositoryFactory implements RepositoryFactory
{
/**
* The list of EntityRepository instances.
*
* @var array<\Doctrine\Common\Persistence\ObjectRepository>
*/
private $repositoryList = array();
/**
* {@inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositoryList[$entityName])) {
return $this->repositoryList[$entityName];
}
$repository = $this->createRepository($entityManager, $entityName);
$this->repositoryList[$entityName] = $repository;
return $repository;
}
/**
* Create a new repository instance for an entity class.
*
* @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance.
* @param string $entityName The name of the entity.
*
* @return \Doctrine\Common\Persistence\ObjectRepository
*/
protected function createRepository(EntityManagerInterface $entityManager, $entityName)
{
$metadata = $entityManager->getClassMetadata($entityName);
$repositoryClassName = $metadata->customRepositoryClassName;
if ($repositoryClassName === null) {
$configuration = $entityManager->getConfiguration();
$repositoryClassName = $configuration->getDefaultRepositoryClassName();
}
return new $repositoryClassName($entityManager, $metadata);
}
}
@@ -0,0 +1,41 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Repository;
use Doctrine\ORM\EntityManagerInterface;
/**
* Interface for entity repository factory.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.4
*/
interface RepositoryFactory
{
/**
* Gets the repository for an entity class.
*
* @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance.
* @param string $entityName The name of the entity.
*
* @return \Doctrine\Common\Persistence\ObjectRepository
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName);
}
@@ -123,7 +123,7 @@ EOT
$force = true === $input->getOption('force');
if ($dumpSql) {
$output->writeln(implode(';' . PHP_EOL, $sqls));
$output->writeln(implode(';' . PHP_EOL, $sqls) . ';');
}
if ($force) {
@@ -138,7 +138,7 @@ EOT
if ($dumpSql || $force) {
return 0;
}
$output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
$output->writeln(' Use the incremental update to detect changes during development and use');
$output->writeln(' the SQL DDL provided to manually update your database in production.');
+2 -2
View File
@@ -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'];
@@ -92,7 +92,25 @@ class LimitSubqueryOutputWalker extends SqlWalker
*/
public function walkSelectStatement(SelectStatement $AST)
{
$innerSql = parent::walkSelectStatement($AST);
if ($this->platform instanceof PostgreSqlPlatform) {
// Set every select expression as visible(hidden = false) to
// make $AST to have scalar mappings properly
$hiddens = array();
foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
$hiddens[$idx] = $expr->hiddenAliasResultVariable;
$expr->hiddenAliasResultVariable = false;
}
$innerSql = parent::walkSelectStatement($AST);
// Restore hiddens
foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
$expr->hiddenAliasResultVariable = $hiddens[$idx];
}
} else {
$innerSql = parent::walkSelectStatement($AST);
}
// Find out the SQL alias of the identifier column of the root entity.
// It may be possible to make this work with multiple root entities but that
+22 -4
View 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);
}
}
+6 -1
View File
@@ -115,7 +115,7 @@ class SchemaValidator
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association.";
"field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association, but as field.";
}
if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) {
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
@@ -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.";
}
+3
View File
@@ -2356,6 +2356,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->visitedCollections =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
@@ -2773,6 +2774,8 @@ class UnitOfWork implements PropertyChangedListener
$persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
break;
}
$collection->setInitialized(true);
}
/**
+1 -1
View File
@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.4.0-RC1';
const VERSION = '2.4.1';
/**
* Compares a Doctrine version with the current one.
+42
View 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;
}
}
@@ -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;
}
}
@@ -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
View 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;
}
}
@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
class DatabaseDriverTest extends DatabaseDriverTestCase
{
@@ -145,4 +146,70 @@ class DatabaseDriverTest extends DatabaseDriverTestCase
$this->assertEquals(0, count($metadatas['DbdriverBaz']->associationMappings), "no association mappings should be detected.");
}
public function testLoadMetadataFromDatabaseDetail()
{
if ( ! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$this->markTestSkipped('Platform does not support foreign keys.');
}
$table = new \Doctrine\DBAL\Schema\Table("dbdriver_foo");
$table->addColumn('id', 'integer', array('unsigned' => true));
$table->setPrimaryKey(array('id'));
$table->addColumn('column_unsigned', 'integer', array('unsigned' => true));
$table->addColumn('column_comment', 'string', array('comment' => 'test_comment'));
$table->addColumn('column_default', 'string', array('default' => 'test_default'));
$table->addColumn('column_decimal', 'decimal', array('precision' => 4, 'scale' => 3));
$table->addColumn('column_index1', 'string');
$table->addColumn('column_index2', 'string');
$table->addIndex(array('column_index1','column_index2'), 'index1');
$table->addColumn('column_unique_index1', 'string');
$table->addColumn('column_unique_index2', 'string');
$table->addUniqueIndex(array('column_unique_index1', 'column_unique_index2'), 'unique_index1');
$this->_sm->dropAndCreateTable($table);
$metadatas = $this->extractClassMetadata(array("DbdriverFoo"));
$this->assertArrayHasKey('DbdriverFoo', $metadatas);
$metadata = $metadatas['DbdriverFoo'];
$this->assertArrayHasKey('id', $metadata->fieldMappings);
$this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']);
$this->assertEquals('id', strtolower($metadata->fieldMappings['id']['columnName']));
$this->assertEquals('integer', (string) $metadata->fieldMappings['id']['type']);
// FIXME: Condition here is fugly.
// NOTE: PostgreSQL does not support UNSIGNED
if ( ! $this->_em->getConnection()->getDatabasePlatform() instanceof PostgreSqlPlatform) {
$this->assertArrayHasKey('columnUnsigned', $metadata->fieldMappings);
$this->assertTrue($metadata->fieldMappings['columnUnsigned']['unsigned']);
}
$this->assertArrayHasKey('columnComment', $metadata->fieldMappings);
$this->assertEquals('test_comment', $metadata->fieldMappings['columnComment']['comment']);
$this->assertArrayHasKey('columnDefault', $metadata->fieldMappings);
$this->assertEquals('test_default', $metadata->fieldMappings['columnDefault']['default']);
$this->assertArrayHasKey('columnDecimal', $metadata->fieldMappings);
$this->assertEquals(4, $metadata->fieldMappings['columnDecimal']['precision']);
$this->assertEquals(3, $metadata->fieldMappings['columnDecimal']['scale']);
$this->assertTrue( ! empty($metadata->table['indexes']['index1']['columns']));
$this->assertEquals(
array('column_index1','column_index2'),
$metadata->table['indexes']['index1']['columns']
);
$this->assertTrue( ! empty($metadata->table['uniqueConstraints']['unique_index1']['columns']));
$this->assertEquals(
array('column_unique_index1', 'column_unique_index2'),
$metadata->table['uniqueConstraints']['unique_index1']['columns']
);
}
}
@@ -842,6 +842,35 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(2, count($users));
}
/**
* @group DDC-2478
*/
public function testMatchingCriteriaNullAssocComparison()
{
$fixtures = $this->loadFixtureUserEmail();
$user = $this->_em->merge($fixtures[0]);
$repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$criteriaIsNull = Criteria::create()->where(Criteria::expr()->isNull('email'));
$criteriaEqNull = Criteria::create()->where(Criteria::expr()->eq('email', null));
$user->setEmail(null);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$usersIsNull = $repository->matching($criteriaIsNull);
$usersEqNull = $repository->matching($criteriaEqNull);
$this->assertCount(1, $usersIsNull);
$this->assertCount(1, $usersEqNull);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $usersIsNull[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $usersEqNull[0]);
$this->assertNull($usersIsNull[0]->getEmail());
$this->assertNull($usersEqNull[0]->getEmail());
}
/**
* @group DDC-2055
*/
@@ -17,6 +17,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
private $groupId;
private $articleId;
private $topic;
private $phonenumber;
public function setUp()
{
$this->useModelSet('cms');
@@ -24,7 +27,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['groups']['indexBy'] = 'name';
$class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['articles']['indexBy'] = 'topic';
$class->associationMappings['phonenumbers']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['phonenumbers']['indexBy'] = 'phonenumber';
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
@@ -39,6 +46,11 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['phonenumbers']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
unset($class->associationMappings['groups']['indexBy']);
unset($class->associationMappings['articles']['indexBy']);
unset($class->associationMappings['phonenumbers']['indexBy']);
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
@@ -174,8 +186,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertEquals(2, count($someGroups));
$this->assertTrue($user->groups->contains($someGroups[0]));
$this->assertTrue($user->groups->contains($someGroups[1]));
$this->assertTrue($user->groups->contains(array_shift($someGroups)));
$this->assertTrue($user->groups->contains(array_shift($someGroups)));
}
/**
@@ -512,6 +524,52 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($qc + 1, $this->getCurrentQueryCount());
}
/**
* @group DDC-1398
*/
public function testGetIndexByIdentifier()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$queryCount = $this->getCurrentQueryCount();
$phonenumber = $user->phonenumbers->get($this->phonenumber);
$this->assertFalse($user->phonenumbers->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertSame($phonenumber, $this->_em->find('Doctrine\Tests\Models\CMS\CmsPhonenumber', $this->phonenumber));
$article = $user->phonenumbers->get($this->phonenumber);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Getting the same entity should not cause an extra query to be executed");
}
/**
* @group DDC-1398
*/
public function testGetIndexByOneToMany()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$queryCount = $this->getCurrentQueryCount();
$article = $user->articles->get($this->topic);
$this->assertFalse($user->articles->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId));
}
/**
* @group DDC-1398
*/
public function testGetNonExistentIndexBy()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertNull($user->articles->get(-1));
}
private function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();
@@ -561,23 +619,35 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($group3);
$article1 = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article1->topic = "Test";
$article1->text = "Test";
$article1->topic = "Test1";
$article1->text = "Test1";
$article1->setAuthor($user1);
$article2 = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article2->topic = "Test";
$article2->text = "Test";
$article2->topic = "Test2";
$article2->text = "Test2";
$article2->setAuthor($user1);
$this->_em->persist($article1);
$this->_em->persist($article2);
$phonenumber1 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
$phonenumber1->phonenumber = '12345';
$phonenumber2 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
$phonenumber2->phonenumber = '67890';
$this->_em->persist($phonenumber1);
$this->_em->persist($phonenumber2);
$this->_em->flush();
$this->_em->clear();
$this->articleId = $article1->id;
$this->userId = $user1->getId();
$this->groupId = $group1->id;
$this->topic = $article1->topic;
$this->phonenumber = $phonenumber1->phonenumber;
}
}
}
@@ -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);
}
}
@@ -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
{
}
}
@@ -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]);
}
}
@@ -0,0 +1,81 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\Company\CompanyPerson;
use Doctrine\Tests\Models\Company\CompanyEmployee;
/**
* @group DDC-1995
*/
class DDC1995Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('company');
parent::setUp();
}
public function testIssue()
{
$person = new CompanyPerson;
$person->setName('p1');
$employee = new CompanyEmployee;
$employee->setName('Foo');
$employee->setDepartment('bar');
$employee->setSalary(1000);
$this->_em->persist($person);
$this->_em->persist($employee);
$this->_em->flush();
$this->_em->clear();
$dql = 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1';
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee');
$result = $this->_em->createQuery($dql)
->setParameter(1, $class)
->getResult();
$this->assertCount(1, $result);
$this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result[0]);
}
public function testQueryCache()
{
$person = new CompanyPerson;
$person->setName('p1');
$employee = new CompanyEmployee;
$employee->setName('Foo');
$employee->setDepartment('bar');
$employee->setSalary(1000);
$this->_em->persist($person);
$this->_em->persist($employee);
$this->_em->flush();
$this->_em->clear();
$dql = 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF :type';
$class1 = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee');
$class2 = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyPerson');
$result1 = $this->_em->createQuery($dql)
->setParameter('type', $class1)
->useQueryCache(true)
->getResult();
$result2 = $this->_em->createQuery($dql)
->setParameter('type', $class2)
->useQueryCache(true)
->getResult();
$this->assertCount(1, $result1);
$this->assertCount(1, $result2);
$this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result1[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyPerson', $result2[0]);
$this->assertNotInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $result2[0]);
}
}
@@ -0,0 +1,71 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* Verifies that the type of parameters being bound to an SQL query is the same
* of the identifier of the entities used as parameters in the DQL query, even
* if the bound objects are proxies.
*
* @author Marco Pivetta <ocramius@gmail.com>
*
* @group DDC-2214
*/
class DDC2214Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2214Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2214Bar'),
));
}
public function testIssue()
{
$foo = new DDC2214Foo();
$bar = new DDC2214Bar();
$foo->bar = $bar;
$this->_em->persist($foo);
$this->_em->persist($bar);
$this->_em->flush();
$this->_em->clear();
/* @var $foo \Doctrine\Tests\ORM\Functional\Ticket\DDC2214Foo */
$foo = $this->_em->find(__NAMESPACE__ . '\\DDC2214Foo', $foo->id);
$bar = $foo->bar;
$logger = $this->_em->getConnection()->getConfiguration()->getSQLLogger();
$related = $this
->_em
->createQuery('SELECT b FROM '.__NAMESPACE__ . '\DDC2214Bar b WHERE b.id IN(:ids)')
->setParameter('ids', array($bar))
->getResult();
$query = end($logger->queries);
$this->assertEquals(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY, $query['types'][0]);
}
}
/** @Entity */
class DDC2214Foo
{
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
public $id;
/** @ManyToOne(targetEntity="DDC2214Bar") */
public $bar;
}
/** @Entity */
class DDC2214Bar
{
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
public $id;
}
@@ -0,0 +1,68 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group DDC-2350
*/
class DDC2350Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2350Bug'),
));
}
public function testEagerCollectionsAreOnlyRetrievedOnce()
{
$user = new DDC2350User();
$bug1 = new DDC2350Bug();
$bug1->user = $user;
$bug2 = new DDC2350Bug();
$bug2->user = $user;
$this->_em->persist($user);
$this->_em->persist($bug1);
$this->_em->persist($bug2);
$this->_em->flush();
$this->_em->clear();
$cnt = $this->getCurrentQueryCount();
$user = $this->_em->find(__NAMESPACE__ . '\DDC2350User', $user->id);
$this->assertEquals($cnt + 2, $this->getCurrentQueryCount());
$this->assertEquals(2, count($user->reportedBugs));
$this->assertEquals($cnt + 2, $this->getCurrentQueryCount());
}
}
/**
* @Entity
*/
class DDC2350User
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToMany(targetEntity="DDC2350Bug", mappedBy="user", fetch="EAGER") */
public $reportedBugs;
}
/**
* @Entity
*/
class DDC2350Bug
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2350User", inversedBy="reportedBugs") */
public $user;
}
@@ -19,18 +19,21 @@ class DDC2359Test extends \PHPUnit_Framework_TestCase
$mockDriver = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriver');
$mockMetadata = $this->getMock('Doctrine\\ORM\\Mapping\\ClassMetadata', array(), array(), '', false);
$entityManager = $this->getMock('Doctrine\\ORM\\EntityManager', array(), array(), '', false);
/* @var $metadataFactory \Doctrine\ORM\Mapping\ClassMetadataFactory|\PHPUnit_Framework_MockObject_MockObject */
$metadataFactory = $this->getMock(
'Doctrine\\ORM\\Mapping\\ClassMetadataFactory',
array('newClassMetadataInstance', 'wakeupReflection')
);
$configuration = $this->getMock('Doctrine\\ORM\\Configuration');
$configuration = $this->getMock('Doctrine\\ORM\\Configuration', array('getMetadataDriverImpl'));
$connection = $this->getMock('Doctrine\\DBAL\\Connection', array(), array(), '', false);
$configuration
->expects($this->any())
->method('getMetadataDriverImpl')
->will($this->returnValue($mockDriver));
$entityManager->expects($this->any())->method('getConfiguration')->will($this->returnValue($configuration));
$entityManager->expects($this->any())->method('getConnection')->will($this->returnValue($connection));
$entityManager
@@ -0,0 +1,211 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* @group DDC-2494
*/
class DDC2494Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
DDC2494TinyIntType::$calls = array();
Type::addType('ddc2494_tinyint', __NAMESPACE__ . '\DDC2494TinyIntType');
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(DDC2494Currency::CLASSNAME),
$this->_em->getClassMetadata(DDC2494Campaign::CLASSNAME),
));
}
public function testIssue()
{
$currency = new DDC2494Currency(1, 2);
$this->_em->persist($currency);
$this->_em->flush();
$campaign = new DDC2494Campaign($currency);
$this->_em->persist($campaign);
$this->_em->flush();
$this->_em->close();
$this->assertArrayHasKey('convertToDatabaseValue', DDC2494TinyIntType::$calls);
$this->assertCount(3, DDC2494TinyIntType::$calls['convertToDatabaseValue']);
$item = $this->_em->find(DDC2494Campaign::CLASSNAME, $campaign->getId());
$this->assertInstanceOf(DDC2494Campaign::CLASSNAME, $item);
$this->assertInstanceOf(DDC2494Currency::CLASSNAME, $item->getCurrency());
$queryCount = $this->getCurrentQueryCount();
$this->assertInstanceOf('\Doctrine\Common\Proxy\Proxy', $item->getCurrency());
$this->assertFalse($item->getCurrency()->__isInitialized());
$this->assertArrayHasKey('convertToPHPValue', DDC2494TinyIntType::$calls);
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertInternalType('integer', $item->getCurrency()->getId());
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertFalse($item->getCurrency()->__isInitialized());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
$this->assertInternalType('integer', $item->getCurrency()->getTemp());
$this->assertCount(3, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertTrue($item->getCurrency()->__isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
}
/**
* @Table(name="ddc2494_currency")
* @Entity
*/
class DDC2494Currency
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="integer", type="ddc2494_tinyint")
*/
protected $id;
/**
* @Column(name="temp", type="ddc2494_tinyint", nullable=false)
*/
protected $temp;
/**
* @var \Doctrine\Common\Collections\Collection
*
* @OneToMany(targetEntity="DDC2494Campaign", mappedBy="currency")
*/
protected $campaigns;
public function __construct($id, $temp)
{
$this->id = $id;
$this->temp = $temp;
}
public function getId()
{
return $this->id;
}
public function getTemp()
{
return $this->temp;
}
public function getCampaigns()
{
return $this->campaigns;
}
}
/**
* @Table(name="ddc2494_campaign")
* @Entity
*/
class DDC2494Campaign
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
protected $id;
/**
* @var \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency
*
* @ManyToOne(targetEntity="DDC2494Currency", inversedBy="campaigns")
* @JoinColumn(name="currency_id", referencedColumnName="id", nullable=false)
*/
protected $currency;
public function __construct(DDC2494Currency $currency)
{
$this->currency = $currency;
}
public function getId()
{
return $this->id;
}
/**
* @return \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency
*/
public function getCurrency()
{
return $this->currency;
}
}
class DDC2494TinyIntType extends Type
{
public static $calls = array();
/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration);
}
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
$return = (string) $value;
self::$calls[__FUNCTION__][] = array(
'value' => $value,
'return' => $return,
'platform' => $platform,
);
return $return;
}
/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
$return = (integer) $value;
self::$calls[__FUNCTION__][] = array(
'value' => $value,
'return' => $return,
'platform' => $platform,
);
return $return;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'ddc2494_tinyint';
}
}
@@ -0,0 +1,85 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\Legacy\LegacyUser;
use Doctrine\Tests\Models\Legacy\LegacyUserReference;
/**
* @group DDC-2519
*/
class DDC2519Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $userId;
public function setUp()
{
$this->useModelSet('legacy');
parent::setUp();
$this->loadFixture();
}
/**
* @group DDC-2519
*/
public function testIssue()
{
$dql = 'SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l';
$result = $this->_em->createQuery($dql)->getResult();
$this->assertCount(2, $result);
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUserReference', $result[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUserReference', $result[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[0]->source());
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[0]->target());
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[1]->source());
$this->assertInstanceOf('Doctrine\Tests\Models\Legacy\LegacyUser', $result[1]->target());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[0]->source());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[0]->target());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[1]->source());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $result[1]->target());
$this->assertFalse($result[0]->target()->__isInitialized());
$this->assertFalse($result[0]->source()->__isInitialized());
$this->assertFalse($result[1]->target()->__isInitialized());
$this->assertFalse($result[1]->source()->__isInitialized());
$this->assertNotNull($result[0]->source()->getId());
$this->assertNotNull($result[0]->target()->getId());
$this->assertNotNull($result[1]->source()->getId());
$this->assertNotNull($result[1]->target()->getId());
}
public function loadFixture()
{
$user1 = new LegacyUser();
$user1->_username = 'FabioBatSilva';
$user1->_name = 'Fabio B. Silva';
$user1->_status = 'active';
$user2 = new LegacyUser();
$user2->_username = 'doctrinebot';
$user2->_name = 'Doctrine Bot';
$user2->_status = 'active';
$user3 = new LegacyUser();
$user3->_username = 'test';
$user3->_name = 'Tester';
$user3->_status = 'active';
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($user3);
$this->_em->flush();
$this->_em->persist(new LegacyUserReference($user1, $user2, 'foo'));
$this->_em->persist(new LegacyUserReference($user1, $user3, 'bar'));
$this->_em->flush();
$this->_em->clear();
}
}
@@ -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;
}
}
@@ -0,0 +1,122 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @group
*/
class DDC2660Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Product'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660Customer'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder')
));
} catch(\Exception $e) {
return;
}
for ($i = 0; $i < 5; $i++) {
$product = new DDC2660Product();
$customer = new DDC2660Customer();
$order = new DDC2660CustomerOrder($product, $customer, 'name' . $i);
$this->_em->persist($product);
$this->_em->persist($customer);
$this->_em->flush();
$this->_em->persist($order);
$this->_em->flush();
}
$this->_em->clear();
}
public function testIssueWithExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id, o.name FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
public function testIssueWithoutExtraColumn()
{
$sql = "SELECT o.product_id, o.customer_id FROM ddc_2660_customer_order o";
$rsm = new ResultSetMappingBuilder($this->_getEntityManager());
$rsm->addRootEntityFromClassMetadata(__NAMESPACE__ . '\DDC2660CustomerOrder', 'c');
$query = $this->_em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
$this->assertCount(5, $result);
foreach ($result as $order) {
$this->assertNotNull($order);
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC2660CustomerOrder', $order);
}
}
}
/**
* @Entity @Table(name="ddc_2660_product")
*/
class DDC2660Product
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer") */
class DDC2660Customer
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity @Table(name="ddc_2660_customer_order") */
class DDC2660CustomerOrder
{
/**
* @Id @ManyToOne(targetEntity="DDC2660Product")
*/
public $product;
/**
* @Id @ManyToOne(targetEntity="DDC2660Customer")
*/
public $customer;
/**
* @Column(type="string")
*/
public $name;
public function __construct(DDC2660Product $product, DDC2660Customer $customer, $name)
{
$this->product = $product;
$this->customer = $customer;
$this->name = $name;
}
}
@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-2759
*/
class DDC2759Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setup()
{
parent::setup();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'),
));
} catch(\Exception $e) {
return;
}
$qualification = new DDC2759Qualification();
$qualificationMetadata = new DDC2759QualificationMetadata($qualification);
$category1 = new DDC2759Category();
$category2 = new DDC2759Category();
$metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1);
$metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2);
$this->_em->persist($qualification);
$this->_em->persist($qualificationMetadata);
$this->_em->persist($category1);
$this->_em->persist($category2);
$this->_em->persist($metadataCategory1);
$this->_em->persist($metadataCategory2);
$this->_em->flush();
$this->_em->clear();
}
public function testCorrectNumberOfAssociationsIsReturned()
{
$repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification');
$builder = $repository->createQueryBuilder('q')
->select('q, qm, qmc')
->innerJoin('q.metadata', 'qm')
->innerJoin('qm.metadataCategories', 'qmc');
$result = $builder->getQuery()
->getArrayResult();
$this->assertCount(2, $result[0]['metadata']['metadataCategories']);
}
}
/** @Entity @Table(name="ddc_2759_qualification") */
class DDC2759Qualification
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */
public $metadata;
}
/** @Entity @Table(name="ddc_2759_category") */
class DDC2759Category
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */
public $metadataCategories;
}
/** @Entity @Table(name="ddc_2759_qualification_metadata") */
class DDC2759QualificationMetadata
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */
public $content;
/** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */
protected $metadataCategories;
public function __construct(DDC2759Qualification $content)
{
$this->content = $content;
}
}
/** @Entity @Table(name="ddc_2759_metadata_category") */
class DDC2759MetadataCategory
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */
public $metadata;
/** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */
public $category;
public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category)
{
$this->metadata = $metadata;
$this->category = $category;
}
}
@@ -0,0 +1,148 @@
<?php
namespace Doctrine\Tests\ORM\Mapping;
use Doctrine\ORM\Mapping\AnsiQuoteStrategy;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Tests\OrmTestCase;
/**
* @group DDC-1845
* @group DDC-2459
*/
class AnsiQuoteStrategyTest extends OrmTestCase
{
/**
* @var \Doctrine\ORM\Mapping\DefaultQuoteStrategy
*/
private $strategy;
/**
* @var \Doctrine\DBAL\Platforms\AbstractPlatform
*/
private $platform;
protected function setUp()
{
parent::setUp();
$em = $this->_getTestEntityManager();
$this->platform = $em->getConnection()->getDatabasePlatform();
$this->strategy = new AnsiQuoteStrategy();
}
/**
* @param string $className
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
private function createClassMetadata($className)
{
$class = new ClassMetadata($className);
$class->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
return $class;
}
public function testGetColumnName()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$class->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
$class->mapField(array('fieldName' => 'id', 'columnName' => 'id', 'id' => true));
$this->assertEquals('id' ,$this->strategy->getColumnName('id', $class, $this->platform));
$this->assertEquals('name' ,$this->strategy->getColumnName('name', $class, $this->platform));
}
public function testGetTableName()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$class->setPrimaryTable(array('name'=>'cms_user'));
$this->assertEquals('cms_user' ,$this->strategy->getTableName($class, $this->platform));
}
public function testJoinTableName()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$class->mapManyToMany(array(
'fieldName' => 'user',
'targetEntity' => 'CmsUser',
'inversedBy' => 'users',
'joinTable' => array(
'name' => 'cmsaddress_cmsuser'
)
));
$this->assertEquals('cmsaddress_cmsuser', $this->strategy->getJoinTableName($class->associationMappings['user'], $class, $this->platform));
}
public function testIdentifierColumnNames()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$class->mapField(array(
'id' => true,
'fieldName' => 'id',
'columnName' => 'id',
));
$this->assertEquals(array('id'), $this->strategy->getIdentifierColumnNames($class, $this->platform));
}
public function testColumnAlias()
{
$this->assertEquals('columnName1', $this->strategy->getColumnAlias('columnName', 1, $this->platform));
}
public function testJoinColumnName()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$class->mapOneToOne(array(
'id' => true,
'fieldName' => 'article',
'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
'joinColumns' => array(array(
'name' => 'article'
)),
));
$joinColumn = $class->associationMappings['article']['joinColumns'][0];
$this->assertEquals('article',$this->strategy->getJoinColumnName($joinColumn, $class, $this->platform));
}
public function testReferencedJoinColumnName()
{
$cm = $this->createClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$cm->mapOneToOne(array(
'id' => true,
'fieldName' => 'article',
'targetEntity' => 'Doctrine\Tests\Models\DDC117\DDC117Article',
'joinColumns' => array(array(
'name' => 'article'
)),
));
$joinColumn = $cm->associationMappings['article']['joinColumns'][0];
$this->assertEquals('id',$this->strategy->getReferencedJoinColumnName($joinColumn, $cm, $this->platform));
}
public function testGetSequenceName()
{
$class = $this->createClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$definition = array(
'sequenceName' => 'user_id_seq',
'allocationSize' => 1,
'initialValue' => 2
);
$class->setSequenceGeneratorDefinition($definition);
$this->assertEquals('user_id_seq',$this->strategy->getSequenceName($definition, $class, $this->platform));
}
}
@@ -1066,6 +1066,31 @@ 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);
}
}
class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
@@ -1092,4 +1117,4 @@ class MyPrefixNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
{
return strtolower($this->classToTableName($className)) . '_' . $propertyName;
}
}
}
@@ -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());
}
}
@@ -590,6 +590,14 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertValidDQL("select COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u");
}
/**
* @gorup DDC-1858
*/
public function testHavingSupportIsNullExpression()
{
$this->assertValidDQL("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL");
}
}
/** @Entity */
@@ -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()
{
@@ -1292,6 +1303,22 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
/**
* @group DDC-2519
*/
public function testPartialWithAssociationIdentifier()
{
$this->assertSqlGeneration(
"SELECT PARTIAL l.{_source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l",
'SELECT l0_.iUserIdSource AS iUserIdSource0, l0_.iUserIdTarget AS iUserIdTarget1 FROM legacy_users_reference l0_'
);
$this->assertSqlGeneration(
"SELECT PARTIAL l.{_description, _source, _target} FROM Doctrine\Tests\Models\Legacy\LegacyUserReference l",
'SELECT l0_.description AS description0, l0_.iUserIdSource AS iUserIdSource1, l0_.iUserIdTarget AS iUserIdTarget2 FROM legacy_users_reference l0_'
);
}
/**
* @group DDC-1339
*/
@@ -1303,6 +1330,21 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
public function testIdentityFunctionInJoinedSubclass()
{
//relation is in the subclass (CompanyManager) we are querying
$this->assertSqlGeneration(
'SELECT m, IDENTITY(m.car) as car_id FROM Doctrine\Tests\Models\Company\CompanyManager m',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c2_.car_id AS sclr6, c0_.discr AS discr7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id'
);
//relation is in the base class (CompanyPerson).
$this->assertSqlGeneration(
'SELECT m, IDENTITY(m.spouse) as spouse_id FROM Doctrine\Tests\Models\Company\CompanyManager m',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.spouse_id AS sclr6, c0_.discr AS discr7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id'
);
}
/**
* @group DDC-1339
*/
@@ -1831,6 +1873,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
/**
* @group DDC-1845
*/
public function testQuotedTableDeclaration()
{
$this->assertSqlGeneration(
'SELECT u FROM Doctrine\Tests\Models\Quote\User u',
'SELECT q0_."user-id" AS userid0, q0_."user-name" AS username1 FROM "quote-user" q0_'
);
}
/**
* @group DDC-1845
*/
@@ -1916,7 +1969,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$connMock = $this->_em->getConnection();
$orgPlatform = $connMock->getDatabasePlatform();
$connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\MySqlPlatform);
$this->assertSqlGeneration(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1",
@@ -1926,7 +1979,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"SELECT CONCAT(u.id, u.name, u.status) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1",
"SELECT CONCAT(c0_.id, c0_.name, c0_.status) AS sclr0 FROM cms_users c0_ WHERE c0_.id = ?"
);
$connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform);
$this->assertSqlGeneration(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, u.status, 's') = ?1",
@@ -1936,7 +1989,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"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_ WHERE c0_.id = ?"
);
$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",
@@ -1946,7 +1999,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
"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 = ?"
);
$connMock->setDatabasePlatform($orgPlatform);
}
@@ -1971,6 +2024,134 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
);
}
/**
* @group DDC-2475
*/
public function testOrderByClauseShouldReplaceOrderByRelationMapping()
{
$this->assertSqlGeneration(
'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b',
'SELECT r0_.id AS id0, r1_.id AS id1, r1_.passengerName AS passengerName2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC'
);
$this->assertSqlGeneration(
'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b ORDER BY b.passengerName DESC',
'SELECT r0_.id AS id0, r1_.id AS id1, r1_.passengerName AS passengerName2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC'
);
}
/**
* @group DDC-1858
*/
public function testHavingSupportIsNullExpression()
{
$this->assertSqlGeneration(
'SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING u.username IS NULL',
'SELECT c0_.name AS name0 FROM cms_users c0_ HAVING c0_.username IS NULL'
);
}
/**
* @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
*/
public function testHavingSupportResultVariableInExpression()
{
$this->assertSqlGeneration(
'SELECT u.name AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u HAVING foo IN (?1)',
'SELECT c0_.name AS name0 FROM cms_users c0_ HAVING name0 IN (?)'
);
}
}
class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
@@ -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'
),
));
}
@@ -74,6 +74,25 @@ class LimitSubqueryOutputWalkerTest extends PaginationTestCase
$this->entityManager->getConnection()->setDatabasePlatform($odp);
}
public function testLimitSubqueryWithHiddenScalarSortPg()
{
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
$this->entityManager->getConnection()->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform);
$query = $this->entityManager->createQuery(
'SELECT u, g, COUNT(g.id) AS hidden g_quantity FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g ORDER BY g_quantity, u.id DESC'
);
$limitQuery = clone $query;
$limitQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker');
$this->assertEquals(
"SELECT DISTINCT id1, sclr0 FROM (SELECT COUNT(g0_.id) AS sclr0, u1_.id AS id1, g0_.id AS id2 FROM User u1_ INNER JOIN user_group u2_ ON u1_.id = u2_.user_id INNER JOIN groups g0_ ON g0_.id = u2_.group_id ORDER BY sclr0 ASC, u1_.id DESC) dctrn_result ORDER BY sclr0 ASC, id1 DESC",
$limitQuery->getSql()
);
$this->entityManager->getConnection()->setDatabasePlatform($odp);
}
public function testLimitSubqueryPg()
{
$odp = $this->entityManager->getConnection()->getDatabasePlatform();
+18 -1
View File
@@ -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.
*
+4
View File
@@ -16,6 +16,10 @@
<var name="tmpdb_port" value="3306"/>
</php>
<logging>
<log type="coverage-clover" target="../../build/logs/clover.xml"/>
</logging>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
+5
View File
@@ -18,6 +18,11 @@
<var name="tmpdb_name" value="doctrine_tests_tmp" />
<var name="tmpdb_port" value="5432"/>
</php>
<logging>
<log type="coverage-clover" target="../../build/logs/clover.xml"/>
</logging>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
+6
View File
@@ -1,10 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit>
<logging>
<log type="coverage-clover" target="../../build/logs/clover.xml"/>
</logging>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>