Compare commits

...

152 Commits
3.5.1 ... 2.1.4

Author SHA1 Message Date
Benjamin Eberlei
bff55577cd Release 2.1.4 2011-11-23 19:44:26 +00:00
Benjamin Eberlei
7ff30e273a Merge branch 'DDC-1509' into 2.1.x 2011-11-23 20:38:58 +01:00
Benjamin Eberlei
9c81989349 DDC-1509 - Fix regression in doMerge() introduced with the DDC-1383 bugfix 2011-11-23 20:37:37 +01:00
Benjamin Eberlei
e0bb84e1da Bump Dev Version to 2.1.4-DEV 2011-11-21 15:08:18 +00:00
Benjamin Eberlei
692f423047 Release 2.1.3 2011-11-21 15:08:06 +00:00
Benjamin Eberlei
0814e8abc7 Bump dependencies to Common 2.1.3 and DBAL 2.1.5 2011-11-21 16:05:25 +01:00
Benjamin Eberlei
d4a1ea8995 Merge branch 'DDC-1500' into 2.1.x 2011-11-21 15:06:15 +01:00
Benjamin Eberlei
c2febabdd0 DDC-1500 - Fix potential security problem in EntityRepository ORDER BY orientations 2011-11-21 15:05:43 +01:00
Benjamin Eberlei
633e342d82 Make 2.1.x forwards compatible with Common 2.2-DEV 2011-11-19 13:06:01 +01:00
Guilherme Blanco
2e7318dcc2 Fixed DDC-1492. 2011-11-19 08:17:02 +01:00
Benjamin Eberlei
6b3edbbbf9 Merge branch 'DBAL-171' into 2.1.x 2011-11-18 17:45:14 +01:00
Benjamin Eberlei
fdcb46e780 DBAL-171 - Fix bug where params where resorted but types where not in DQL Query 2011-11-18 17:44:42 +01:00
Benjamin Eberlei
6da0314deb Merge branch 'DDC-1496' into 2.1.x 2011-11-18 15:45:04 +01:00
Benjamin Eberlei
b61d109fe2 DDC-1496 - Fix bug with OneToMany collections having orphanRemoval=true and Collection#clear() being called. 2011-11-18 15:44:36 +01:00
Benjamin Eberlei
960c94f661 Fix side-effect test failures in functional tests on PostgreSQL 2011-11-18 14:11:08 +01:00
Benjamin Eberlei
515b0205c6 Merge branch 'DDC-1452' into 2.1.x 2011-11-18 14:04:15 +01:00
Benjamin Eberlei
d76c5b11f6 onUpdate doesnt exist anymore 2011-11-18 14:04:05 +01:00
Benjamin Eberlei
88043577a4 DDC-1452 - Backport fix 2011-11-18 14:02:21 +01:00
Benjamin Eberlei
d0b9f7cb2b Generalize config files 2011-11-13 22:42:46 +01:00
Benjamin Eberlei
10a3f5e10e Each branch needs a .travis.yml 2011-11-13 22:39:27 +01:00
Alexander
eeddaecd26 Fixed failing tests in PHPUnit 3.6.2 (expecting \Exception was deprecated) 2011-11-13 22:27:15 +01:00
Benjamin Eberlei
50f7f3a48e Fix assertType() to assertInstanceOf() 2011-11-13 22:26:51 +01:00
Benjamin Eberlei
e94a293446 Remove merge artifact (funny this went into the docblock, not affecting the tests at all) 2011-11-13 17:28:36 +01:00
Benjamin Eberlei
08e5e47780 Merge branch 'thiagofesta-master' into 2.1.x 2011-11-13 17:19:58 +01:00
Benjamin Eberlei
e3a5ffbb55 DDC-1477 - Adjust patch to really fix bug in Proxy generation 2011-11-13 17:19:29 +01:00
Benjamin Eberlei
bf06c3418b Merge branch 'DDC-1458' into 2.1.x 2011-11-11 08:19:48 +01:00
Alexander
6a8172902a Cleanup 2011-11-11 08:16:44 +01:00
Benjamin Eberlei
d9328ea5e7 Autoload to composer.json 2011-11-01 15:06:42 +01:00
Benjamin Eberlei
8cd8c993c8 Merge branch 'DBAL-1420' into 2.1.x 2011-10-31 23:13:59 +01:00
Benjamin Eberlei
f45d8f488b DBAL-1420 - Use safe mode for schema validation. Dropping stuff isnt necessary here. 2011-10-31 23:13:51 +01:00
Benjamin Eberlei
6257daf097 Fix xml fix again 2011-10-31 22:14:46 +01:00
Benjamin Eberlei
d46910f6bb Merge branch 'XML-IndexBy-Fix' into 2.1.x 2011-10-31 22:11:51 +01:00
Guilherme Blanco
5c4e3d9bd0 Fixed bug in XMLDriver where relation indexes are treathed as elements but documented as attributes. 2011-10-31 22:11:21 +01:00
Benjamin Eberlei
7d0d06cd9a Merge branch 'DDC-1346' into 2.1.x 2011-10-31 21:56:04 +01:00
Guilherme Blanco
e4f49231bf Added support for ResultVariable referencing in ArithmeticPrimary. Fixes DDC-1346. 2011-10-31 21:55:27 +01:00
Benjamin Eberlei
ca600f466e Merge branch 'DDC-1316' into 2.1.x 2011-10-31 21:48:23 +01:00
Guilherme Blanco
01d6b73f78 Small changes for code readability. Added type binding in JoinedSubclassPersister, which was missing. Fixes DDC-1316. 2011-10-31 21:47:45 +01:00
Benjamin Eberlei
c36b1f1d00 Merge branch 'DDC-1462' into 2.1.x 2011-10-31 21:35:38 +01:00
Benjamin Eberlei
588f7fc765 DDC-1462 - Fix bug in slice when calling on a dirty collection that is marked extra lazy 2011-10-31 21:34:53 +01:00
Benjamin Eberlei
1becb8dfd5 Merge branch 'DDC-1399' into 2.1.x 2011-10-31 21:17:56 +01:00
Benjamin Eberlei
394447d92d DDC-1399 - Fix extra lazy collections when inner collection contains values but persistent collection is marked not dirty because of flush() 2011-10-31 21:17:24 +01:00
Benjamin Eberlei
f834c61a2b Merge branch 'DDC-1435' into 2.1.x 2011-10-29 12:08:00 +02:00
Guilherme Blanco
67cf5b7160 Implemented support to entities with association marked as @Id support in many situations. Fixed DDC-1435. 2011-10-29 12:07:32 +02:00
Benjamin Eberlei
e3a04c3458 Merge branch 'DQLFixes' into 2.1.x 2011-10-29 11:56:55 +02:00
Benjamin Eberlei
cb7e27460e DDC-1079 - SimpleSelectExpression related fixes and refactorings in SqlWalker 2011-10-29 11:55:16 +02:00
Benjamin Eberlei
9dc5c5ddce Merge branch 'DDC-1410' into 2.1.x 2011-10-28 23:47:25 +02:00
Benjamin Eberlei
bbd01ea457 DDC-1410 - Remove code-inlining that caused problems 2011-10-28 23:45:57 +02:00
Benjamin Eberlei
360c578c5d DDC-1454 - Fix exists() with Joined table inheritance 2011-10-28 00:59:25 +02:00
Benjamin Eberlei
ca289bf1ea Merge branch 'DDC-1358' into 2.1.x 2011-10-16 11:17:53 +02:00
Benjamin Eberlei
17341cf051 DDC-1358 - Fix bug where multiple NULL root entity combined with scalar results will break the object and array hydrator.
This case likeli only occurs when doing native queries. A guard clause that prevents hydration from breaking
when RIGHT JOIN queries with null root entities appear has been added aswell.
2011-10-16 11:17:25 +02:00
Benjamin Eberlei
5638b8c5c0 Add tests for DDC-1389 to 2.1.x 2011-10-16 09:37:22 +02:00
Benjamin Eberlei
ccaf00cfab Merge remote-tracking branch 'origin/2.1.x' into 2.1.x 2011-10-15 19:58:54 +02:00
Benjamin Eberlei
b57f4ecb52 Merge pull request #123 from lenar/2.1.x
Fix couple of bugs with composite keys
2011-10-15 10:58:11 -07:00
Benjamin Eberlei
7ae96dd9c7 Merge branch 'DDC-1383' into 2.1.x 2011-10-15 19:16:04 +02:00
Benjamin Eberlei
0b3b4ecef6 DDC-1383 - Proxy Generation in merge was flawed with inheritance 2011-10-15 19:15:43 +02:00
Benjamin Eberlei
8e3fc30477 Merge branch 'DDC-1411' into 2.1.x 2011-10-15 17:53:47 +02:00
Benjamin Eberlei
db2c51d860 DDC-1411 - Fixed onDelete handling in EntityGenerator 2011-10-15 17:53:25 +02:00
Benjamin Eberlei
b6ac1b4962 Merge branch 'DDC-1414' into 2.1.x 2011-10-15 17:47:48 +02:00
Benjamin Eberlei
7734e3e2ec DDC-1414 - Missing push to $newNodes 2011-10-15 17:47:41 +02:00
Benjamin Eberlei
45494fd6a0 Merge branch 'DDC-1421' into 2.1.x 2011-10-15 17:01:54 +02:00
Benjamin Eberlei
efad33a70e DDC-1421 - Fix potential bug and code-smells 2011-10-15 17:01:39 +02:00
Benjamin Eberlei
ba9feb1519 Fix notice 2011-10-15 16:02:32 +02:00
Benjamin Eberlei
4683d76c52 Remove unncessary line 2011-10-15 15:59:17 +02:00
Benjamin Eberlei
4ddd4a68ef Merge branch 'DDC-1386' into 2.1.x 2011-10-15 15:53:53 +02:00
Benjamin Eberlei
d9f9228d95 Add way to keep track of read only objects in the UnitOfWork which are never updated during flush.
Changed the behavior of EntityManager#getPartialReference to be read-only. No changes are ever
done to this entities. Changed UnitOfWork#computeChangeSet to never create a changeset for
fields that are partially omitted from a DQL or NativeQuery.

To check if an entity is read only use the new API:

    if ($entityManager->getUnitOfWork()->isReadOnly($entity))
2011-10-15 15:53:32 +02:00
Benjamin Eberlei
ef4d8410f6 Merge branch 'DDC-1402' into 2.1.x 2011-10-10 17:55:25 +02:00
Benjamin Eberlei
a62cd45476 Fix DDC-1402 - No caching for SingleTablePersister::_getSelectColumnList 2011-10-10 17:53:53 +02:00
Benjamin Eberlei
7369a2de68 Add composer.json 2011-10-10 17:22:51 +02:00
Benjamin Eberlei
623ab9cee9 Bump Dev Version to 2.1.3-DEV 2011-09-25 18:24:26 +00:00
Benjamin Eberlei
144d0de0ab Release 2.1.2 2011-09-25 18:24:18 +00:00
Benjamin Eberlei
5c6164ce07 Bump Dev Version to 2.1.3-DEV 2011-09-25 18:23:05 +00:00
Benjamin Eberlei
e960ba24eb Release 2.1.2 2011-09-25 18:21:30 +00:00
Benjamin Eberlei
e55b700670 Update dependencies of Common and ORM 2011-09-25 20:20:17 +02:00
Benjamin Eberlei
e4dd5e83e4 Merge branch 'DDC-1381' into 2.1.x 2011-09-25 19:11:00 +02:00
Fabien Pennequin
082c2ded8d Fixed php notice in ClassMetadataFactory 2011-09-25 19:10:32 +02:00
Benjamin Eberlei
7f9cb9567a DDC-1337 - Fix MultiTableDeleteExecutor and MultiTableUpdateExecutor in MySQL transaction case 2011-09-25 19:08:16 +02:00
Benjamin Eberlei
90c50b013d Merge branch 'DDC-1392' into 2.1.x 2011-09-25 18:09:43 +02:00
Benjamin Eberlei
6a05e7f393 DDC-1392 - Fix bug with merging unitialized proxies 2011-09-25 18:09:13 +02:00
Benjamin Eberlei
a8e4d8f52d Merge branch 'DDC-1367' into 2.1.x 2011-09-25 16:41:12 +02:00
Benjamin Eberlei
47314c116c DDC-1367 - Bugfix 2011-09-25 16:40:14 +02:00
Benjamin Eberlei
c2eb6e6104 Merge branch 'DDC-1346' into 2.1.x 2011-09-25 15:11:58 +02:00
Guilherme Blanco
3b6a1e11f3 Added support for ResultVariable referencing in ArithmeticPrimary. Fixes DDC-1346. 2011-09-25 15:11:36 +02:00
Benjamin Eberlei
87650428b2 Merge branch 'DDC-1321' into 2.1.x 2011-09-25 15:07:12 +02:00
Guilherme Blanco
1de27437b5 Fixed bug with orphanRemoval not removing associated Entity on OneToMany and OneToOne relationships. As defined in ClassMatedataInfo, in these situations, when orphanRemoval=true, cascade=remove is implicit. This fixes DDC-1321. 2011-09-25 15:07:00 +02:00
lenar
52c75d1633 use the correct targetEntity 2011-09-14 11:43:37 +03:00
lenar
8875d549eb identifier referencing foreign entity can be defined in parent class too 2011-09-14 11:43:25 +03:00
Benjamin Eberlei
cf10ee4d02 Merge branch 'DDC-1356' into 2.1.x 2011-09-04 14:33:00 +02:00
Guilherme Blanco
1faa3a945e Added support to user provide an array of Entities as a DQL parameter. Fixes DDC-1356. 2011-09-04 14:32:45 +02:00
Benjamin Eberlei
22f37d4178 Merge branch 'DDC-1354' into 2.1.x 2011-09-04 14:30:38 +02:00
Guilherme Blanco
6ad21462fe Fixes DDC-1354. 2011-09-04 14:30:16 +02:00
Benjamin Eberlei
e9c29135f7 Merge branch 'DDC-1350' into 2.1.x 2011-08-30 20:41:10 +02:00
Benjamin Eberlei
12af7d24c0 DDC-1350 - Bugfixes in Doctrine\ORM\Tools\Setup 2011-08-30 20:41:02 +02:00
Benjamin Eberlei
4f3282d328 Bump DBAL dependency to 2.1.2 2011-08-29 23:00:27 +02:00
Benjamin Eberlei
60e7efa5b9 Merge branch 'DDC-1225' into 2.1.x 2011-08-29 21:55:51 +02:00
Guilherme Blanco
0a4c1028a1 Fixed issue with duplicated commas if Entity has no fields. 2011-08-29 21:54:41 +02:00
Benjamin Eberlei
2c3757d087 Merge branch 'DDC-1341' into 2.1.x 2011-08-28 21:50:04 +02:00
Benjamin Eberlei
bf892a8ece Namespace shortcut for repository was not merged into 2.1.x yet 2011-08-28 21:49:53 +02:00
Guilherme Blanco
878710d013 Fixed issue with CTI during DQL update that was incorrectly setting parameter types during multi table execution. Fixes DDC-1341. 2011-08-28 21:48:24 +02:00
Benjamin Eberlei
be800a991c Merge branch 'FixPostgresFailures' into 2.1.x 2011-08-28 15:57:44 +02:00
Benjamin Eberlei
e295168c19 DDC-1348 - Fix bug with UnitOfWork::getEntityState() 2011-08-28 15:57:33 +02:00
Benjamin Eberlei
c03d7dd086 Merge branch 'DDC-1306' into 2.1.x 2011-08-27 20:46:09 +02:00
Benjamin Eberlei
70938fa7f9 DDC-1306, DDC-1113 - Fix issues with inheritance and commit order 2011-08-27 20:44:47 +02:00
Benjamin Eberlei
83dbde9cda Bump Dev Version to 2.1.2-DEV 2011-08-26 06:11:43 +00:00
Benjamin Eberlei
05d12e20be Release 2.1.1 2011-08-26 06:07:58 +00:00
Benjamin Eberlei
9b3b6052da Fix tests after updating to latest DBAL 2.1.1 release 2011-08-26 08:03:25 +02:00
Benjamin Eberlei
ffdc5e8d32 Merge remote-tracking branch 'origin/2.1.x' into 2.1.x 2011-08-24 20:45:45 +02:00
Benjamin Eberlei
6380795827 Merge branch 'DDC-1333' into 2.1.x 2011-08-21 15:06:57 +02:00
Benjamin Eberlei
6e9575b121 DDC-1333 - Fix bug in xsd 2011-08-21 15:06:50 +02:00
Benjamin Eberlei
099e2c1bd3 Merge branch 'DDC-1340' into 2.1.x 2011-08-21 15:02:47 +02:00
Benjamin Eberlei
90c47a0510 DDC-1340 - Fix bug with merge() and optimistic lock exception 2011-08-21 15:02:39 +02:00
Alexander
67133f8886 [DDC-1301] Fixed tests teardown for mysql suite 2011-08-14 19:28:15 +02:00
Benjamin Eberlei
4b4efe5483 Merge branch 'DDC-1300' into 2.1.x 2011-08-06 20:25:35 +02:00
Benjamin Eberlei
6291139dd1 DDC-1300 - Fix bug in fetch join hydration of entities with foreign key identifier 2011-08-06 20:23:47 +02:00
Benjamin Eberlei
90f96c4fba Add phar packaging target and distribute phar into download folder 2011-08-01 23:09:17 +02:00
Benjamin Eberlei
23de4a0fe4 DDC-1313 - Optimize behavior of DriverChain::getAllClassNames() 2011-08-01 21:46:41 +02:00
Benjamin Eberlei
68faa589f5 Merge branch 'DDC-1302' into 2.1.x 2011-07-31 11:35:25 +02:00
Benjamin Eberlei
7633f7b7ae DDC-1302 - Fix bug in XmlDriver not handling orphan removal 2011-07-31 11:34:56 +02:00
Benjamin Eberlei
5f665a9f3c Merge branch 'DDC-1301' into 2.1.x 2011-07-28 23:27:04 +02:00
Alexander
4819594a71 [DDC-1301] Prefixed all Legacy models properties with _ 2011-07-28 23:26:43 +02:00
Alexander
64d3715e79 [DDC-1301] Fixed count() for fetch="EXTRA_LAZY" on OneToMany association 2011-07-28 23:26:43 +02:00
Alexander
b22f692406 [DDC-1301] Added tests for fetch="EXTRA_LAZY" count() on a "legacy" database 2011-07-28 23:26:43 +02:00
Benjamin Eberlei
a1ed28a39b Merge branch 'DDC-1275' into 2.1.x 2011-07-28 23:24:38 +02:00
Michael Ridgway
9b41b6106c F[DDC-1275] ixed check for owning side of a toOne relationship 2011-07-28 23:23:28 +02:00
Michael Ridgway
a24557bc27 Removing debug comment 2011-07-28 23:23:28 +02:00
Michael Ridgway
295281c890 DDC-1275: Added join columns to result set mapping 2011-07-28 23:23:28 +02:00
Benjamin Eberlei
57c5491494 Merge branch 'DDC-1298' into 2.1.x 2011-07-27 23:29:06 +02:00
Benjamin Eberlei
b43fffce8b DDC-1298 - Fix bug in SQLWalker with derived entities that have no fields of their own. 2011-07-27 23:28:40 +02:00
Benjamin Eberlei
e426c26ade Merge branch 'DDC-1238' into 2.1.x 2011-07-27 20:52:39 +02:00
Benjamin Eberlei
e23f5bc825 DDC-1238 - Fixed a bug introduced when refactoring persisters hydration. This occurs when you call $em->clear() and you start accessing a proxy. 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
ce4f98aaab Update tests 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
16d18c7f83 DDC-1238 - Reproducible case, its correct through 2011-07-27 20:52:16 +02:00
Benjamin Eberlei
6d62484065 Started trying to reproduce this issue 2011-07-27 20:52:15 +02:00
Benjamin Eberlei
6d1067b90c Merge branch 'DDC-1215' into 2.1.x 2011-07-26 23:01:07 +02:00
Benjamin Eberlei
3c3e5cbf41 [DDC-1215] Fix EntityGenerator inheritance regenerating properties and methods 2011-07-26 23:00:53 +02:00
Benjamin Eberlei
aabf39940a Merge branch 'DDC-1280' into 2.1.x 2011-07-26 22:31:16 +02:00
Benjamin Eberlei
1fedd0e7d3 [DDC-1280] Only generate linefeeds in proxies for consistency. 2011-07-26 22:31:06 +02:00
Benjamin Eberlei
a0a03947a3 Merge branch 'DDC-1276' into 2.1.x 2011-07-26 22:16:31 +02:00
Benjamin Eberlei
420da54620 DDC-1276 - Fix bug where merge managed and new entitiy share the same collection that is cascaded, cleared during the process and then empty afterwards. 2011-07-26 22:16:16 +02:00
Benjamin Eberlei
7bb77fc437 Merge branch 'EntityGenerator' into 2.1.x 2011-07-12 23:47:01 +02:00
Benjamin Eberlei
bb3f6957d4 DDC-1244 - Fix bug with entities without namespace 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
5ef63c1c0d DDC-1254 - Dont throw exception about missing id in disconnected metadata factory 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
09c957fdc0 DDC-1268 - Singular add*() method name through using targetEntity shortname 2011-07-12 23:46:53 +02:00
Benjamin Eberlei
64bc782bdb Merge branch 'DDC-1240' into 2.1.x 2011-07-12 22:51:48 +02:00
Benjamin Eberlei
8fb5b40fc1 DDC-1240 - Fix optimistic lock exception loosing the message 2011-07-12 22:51:40 +02:00
Benjamin Eberlei
448811eb6a Merge branch 'DDC-1250' into 2.1.x 2011-07-09 22:14:02 +02:00
Benjamin Eberlei
ecc79c8ca0 DDC-1250 - Fix bug with inverse one to one loading and ambigious column names in certain scenarios 2011-07-09 22:13:54 +02:00
Benjamin Eberlei
d2320128cf Merge branch 'DDC-1257' into 2.1.x 2011-07-09 15:15:05 +02:00
Benjamin Eberlei
e5df347ea3 DDC-1257 - Fix bug where validation callbacks are added multiple times in EntityGenerator 2011-07-09 15:14:55 +02:00
Benjamin Eberlei
9e2b98ca16 Merge branch 'DDC-1251' into 2.1.x 2011-07-09 14:54:27 +02:00
Benjamin Eberlei
515b44126b DDC-1251 - Fix bug in token parsing of EntityGenerator 2011-07-09 14:54:10 +02:00
Benjamin Eberlei
1feac7ae9e DDC-1022 - Call __wakeup() with the same semantics then ClassMetadata::newInstance() does inside UnitOfWork 2011-07-09 12:24:47 +02:00
Benjamin Eberlei
1929ab6a75 Set DEV Version 2011-07-04 21:37:08 +00:00
115 changed files with 3986 additions and 791 deletions

19
.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
language: php
php:
- 5.3
- 5.4
env:
- DB=mysql
- DB=pgsql
- DB=sqlite
before_script:
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi"
- 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"
- git submodule update --init
script: phpunit --configuration tests/travis/$DB.travis.xml

View File

@@ -107,6 +107,23 @@
<target name="build" depends="test, build-orm"/>
<target name="package-phar" depends="build-orm">
<pharpackage basedir="${build.dir}/doctrine-orm/" destfile="${dist.dir}/doctrine-orm-${version}.phar" clistub="${build.dir}/doctrine-orm/bin/doctrine.php" signature="sha512">
<fileset dir="${build.dir}/doctrine-orm">
<include name="**/**" />
</fileset>
<metadata>
<element name="version" value="${version}" />
<element name="authors">
<element name="Guilherme Blanco"><element name="e-mail" value="guilhermeblanco@gmail.com" /></element>
<element name="Benjamin Eberlei"><element name="e-mail" value="kontakt@beberlei.de" /></element>
<element name="Jonathan H. Wage"><element name="e-mail" value="jonwage@gmail.com" /></element>
<element name="Roman Borschel"><element name="e-mail" value="roman@code-factory.org" /></element>
</element>
</metadata>
</pharpackage>
</target>
<!--
Runs the full test suite.
-->
@@ -206,6 +223,7 @@
<target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
<copy file="${dist.dir}/doctrine-orm-${version}.phar" todir="${project.download_dir}" />
</target>
<target name="update-dev-version">
@@ -217,7 +235,7 @@
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
<target name="release" depends="git-tag,build-packages,package-phar,distribute-download,pirum-release,update-dev-version" />
<!--
Builds distributable PEAR packages for the Symfony Dependencies

23
composer.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "doctrine/orm",
"type": "library",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
"license": "LGPL",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": "2.1.*",
"doctrine/dbal": "2.1.*"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }
}
}

View File

@@ -92,7 +92,7 @@
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />

View File

@@ -162,6 +162,16 @@ abstract class AbstractQuery
{
return $this->_params;
}
/**
* Get all defined parameter types.
*
* @return array The defined query parameter types.
*/
public function getParameterTypes()
{
return $this->_paramTypes;
}
/**
* Gets a query parameter.
@@ -174,6 +184,17 @@ abstract class AbstractQuery
return isset($this->_params[$key]) ? $this->_params[$key] : null;
}
/**
* Gets a query parameter type.
*
* @param mixed $key The key (index or name) of the bound parameter.
* @return mixed The parameter type of the bound parameter.
*/
public function getParameterType($key)
{
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null;
}
/**
* Gets the SQL query that corresponds to this query object.
* The returned SQL syntax depends on the connection driver that is used

View File

@@ -23,6 +23,7 @@ use Doctrine\Common\Cache\Cache,
Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\Common\Annotations\AnnotationReader,
Doctrine\Common\Annotations\SimpleAnnotationReader,
Doctrine\ORM\Mapping\Driver\Driver,
Doctrine\ORM\Mapping\Driver\AnnotationDriver;
@@ -85,7 +86,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the namespace where proxy classes reside.
*
*
* @return string
*/
public function getProxyNamespace()
@@ -96,7 +97,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the namespace where proxy classes reside.
*
*
* @param string $ns
*/
public function setProxyNamespace($ns)
@@ -118,22 +119,23 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Add a new default annotation driver with a correctly configured annotation reader.
*
*
* @param array $paths
* @return Mapping\Driver\AnnotationDriver
*/
public function newDefaultAnnotationDriver($paths = array())
{
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
$reader = new AnnotationReader();
$reader = new SimpleAnnotationReader();
$reader->addNamespace('Doctrine\ORM\Mapping');
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
} else if (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-DEV', '>=')) {
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
$reader = new AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$reader->setIgnoreNotImportedAnnotations(true);
@@ -162,7 +164,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Resolves a registered namespace alias to the full namespace.
*
* @param string $entityNamespaceAlias
* @param string $entityNamespaceAlias
* @return string
* @throws MappingException
*/
@@ -185,10 +187,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
$this->_attributes['entityNamespaces'] = $entityNamespaces;
}
/**
* Retrieves the list of registered entity namespace aliases.
*
*
* @return array
*/
public function getEntityNamespaces()
@@ -360,7 +362,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom string DQL function.
*
*
* @param string $name
* @return string
*/
@@ -403,7 +405,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom numeric DQL function.
*
*
* @param string $name
* @return string
*/
@@ -446,7 +448,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom date/time DQL function.
*
*
* @param string $name
* @return string
*/
@@ -497,7 +499,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Set a class metadata factory.
*
*
* @param string $cmf
*/
public function setClassMetadataFactoryName($cmfName)

View File

@@ -128,7 +128,7 @@ class EntityManager implements ObjectManager
$this->metadataFactory = new $metadataFactoryClassName;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
@@ -203,18 +203,18 @@ class EntityManager implements ObjectManager
public function transactional(Closure $func)
{
$this->conn->beginTransaction();
try {
$return = $func($this);
$this->flush();
$this->conn->commit();
return $return ?: true;
} catch (Exception $e) {
$this->close();
$this->conn->rollback();
throw $e;
}
}
@@ -244,7 +244,7 @@ class EntityManager implements ObjectManager
*
* The class name must be the fully-qualified class name without a leading backslash
* (as it is returned by get_class($obj)) or an aliased class name.
*
*
* Examples:
* MyProject\Domain\User
* sales:PriceRequest
@@ -413,6 +413,7 @@ class EntityManager implements ObjectManager
$entity = $class->newInstance();
$class->setIdentifierValues($entity, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
$this->unitOfWork->markReadOnly($entity);
return $entity;
}
@@ -449,7 +450,7 @@ class EntityManager implements ObjectManager
*
* The entity will be entered into the database at or before transaction
* commit or as a result of the flush operation.
*
*
* NOTE: The persist operation always considers entities that are not yet known to
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
*
@@ -631,7 +632,7 @@ class EntityManager implements ObjectManager
/**
* Check if the Entity manager is open or closed.
*
*
* @return bool
*/
public function isOpen()
@@ -712,6 +713,14 @@ class EntityManager implements ObjectManager
return $this->proxyFactory;
}
/**
* {@inheritDoc}
*/
public function initializeObject($entity)
{
$this->unitOfWork->initializeObject($entity);
}
/**
* Factory method to create EntityManager instances.
*

View File

@@ -164,6 +164,11 @@ abstract class AbstractHydrator
* field names during this procedure as well as any necessary conversions on
* the values applied.
*
* @param array $data SQL Result Row
* @param array &$cache Cache for column to field result information
* @param array &$id Dql-Alias => ID-Hash
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
*
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias.
*/
@@ -287,4 +292,25 @@ abstract class AbstractHydrator
return $rowData;
}
protected function registerManaged($class, $entity, $data)
{
if ($class->isIdentifierComposite) {
$id = array();
foreach ($class->identifier as $fieldName) {
if (isset($class->associationMappings[$fieldName])) {
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
} else {
$id[$fieldName] = $data[$fieldName];
}
}
} else {
if (isset($class->associationMappings[$class->identifier[0]])) {
$id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
} else {
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
}
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
}

View File

@@ -92,6 +92,11 @@ class ArrayHydrator extends AbstractHydrator
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
$path = $parent . '.' . $dqlAlias;
// missing parent data, skipping as RIGHT JOIN hydration is not supported.
if ( ! isset($nonemptyComponents[$parent]) ) {
continue;
}
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
@@ -154,6 +159,17 @@ class ArrayHydrator extends AbstractHydrator
// It's a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root
// 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(0 => null);
} else {
$result[] = null;
}
++$this->_resultCounter;
continue;
}
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {

View File

@@ -62,7 +62,7 @@ class ObjectHydrator extends AbstractHydrator
if (!isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
@@ -82,26 +82,18 @@ class ObjectHydrator extends AbstractHydrator
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
if ($sourceClass->subClasses) {
foreach ($sourceClass->subClasses as $sourceSubclassName) {
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
}
}
$this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true;
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
// Mark any non-collection opposite sides as fetched, too.
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
$this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
} else {
if ($assoc['inversedBy']) {
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
if ($class->subClasses) {
foreach ($class->subClasses as $targetSubclassName) {
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
}
}
$this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
}
}
}
@@ -116,13 +108,13 @@ class ObjectHydrator extends AbstractHydrator
protected function _cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup();
$this->_identifierMap =
$this->_initializedCollections =
$this->_existingCollections =
$this->_resultPointers = array();
if ($eagerLoad) {
$this->_em->getUnitOfWork()->triggerEagerLoads();
}
@@ -154,7 +146,7 @@ class ObjectHydrator extends AbstractHydrator
* @param object $entity The entity to which the collection belongs.
* @param string $name The name of the field on the entity that holds the collection.
*/
private function _initRelatedCollection($entity, $class, $fieldName)
private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
{
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
@@ -175,7 +167,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
! $value->isInitialized()) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
@@ -192,7 +184,7 @@ class ObjectHydrator extends AbstractHydrator
/**
* Gets an entity instance.
*
*
* @param $data The instance data.
* @param $dqlAlias The DQL alias of the entity's class.
* @return object The entity.
@@ -205,18 +197,33 @@ class ObjectHydrator extends AbstractHydrator
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
$class = $this->_ce[$className];
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$this->_hints['fetchAlias'] = $dqlAlias;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
{
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_ce[$className];
/* @var $class ClassMetadata */
if ($class->isIdentifierComposite) {
$idHash = '';
foreach ($class->identifier as $fieldName) {
$idHash .= $data[$fieldName] . ' ';
if (isset($class->associationMappings[$fieldName])) {
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
} else {
$idHash .= $data[$fieldName] . ' ';
}
}
return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
} else if (isset($class->associationMappings[$class->identifier[0]])) {
return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
} else {
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
}
@@ -226,7 +233,7 @@ class ObjectHydrator extends AbstractHydrator
* Gets a ClassMetadata instance from the local cache.
* If the instance is not yet in the local cache, it is loaded into the
* local cache.
*
*
* @param string $className The name of the class.
* @return ClassMetadata
*/
@@ -240,21 +247,21 @@ class ObjectHydrator extends AbstractHydrator
/**
* Hydrates a single row in an SQL result set.
*
*
* @internal
* First, the data of the row is split into chunks where each chunk contains data
* that belongs to a particular component/class. Afterwards, all these chunks
* are processed, one after the other. For each chunk of class data only one of the
* following code paths is executed:
*
*
* Path A: The data chunk belongs to a joined/associated object and the association
* is collection-valued.
* Path B: The data chunk belongs to a joined/associated object and the association
* is single-valued.
* Path C: The data chunk belongs to a root result element/object that appears in the topmost
* level of the hydrated result. A typical example are the objects of the type
* specified by the FROM clause in a DQL query.
*
* specified by the FROM clause in a DQL query.
*
* @param array $data The data of the row to process.
* @param array $cache The cache to use.
* @param array $result The result array to fill.
@@ -288,10 +295,16 @@ class ObjectHydrator extends AbstractHydrator
// seen for this parent-child relationship
$path = $parentAlias . '.' . $dqlAlias;
// We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
if (!isset($nonemptyComponents[$parentAlias])) {
// TODO: Add special case code where we hydrate the right join objects into identity map at least
continue;
}
// Get a reference to the parent object to which the joined element belongs.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
$first = reset($this->_resultPointers);
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
$parentObject = $first[key($first)];
} else if (isset($this->_resultPointers[$parentAlias])) {
$parentObject = $this->_resultPointers[$parentAlias];
} else {
@@ -313,7 +326,7 @@ class ObjectHydrator extends AbstractHydrator
if (isset($this->_initializedCollections[$collKey])) {
$reflFieldValue = $this->_initializedCollections[$collKey];
} else if ( ! isset($this->_existingCollections[$collKey])) {
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
}
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
@@ -349,10 +362,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
}
} else if ( ! $reflField->getValue($parentObject)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$coll->setOwner($parentObject, $relation);
$reflField->setValue($parentObject, $coll);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
}
} else {
// PATH B: Single-valued association
@@ -394,6 +404,18 @@ class ObjectHydrator extends AbstractHydrator
// PATH C: Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
// 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(0 => null);
} else {
$result[] = null;
}
++$this->_resultCounter;
continue;
}
// check for existing result from the iterations before
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {

View File

@@ -23,11 +23,10 @@ namespace Doctrine\ORM\Internal\Hydration;
use \PDO;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query;
class SimpleObjectHydrator extends AbstractHydrator
{
const REFRESH_ENTITY = 'doctrine_refresh_entity';
/**
* @var ClassMetadata
*/
@@ -123,17 +122,8 @@ class SimpleObjectHydrator extends AbstractHydrator
}
}
if (isset($this->_hints[self::REFRESH_ENTITY])) {
$this->_hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[$fieldName] = $data[$fieldName];
}
} else {
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
}
$this->_em->getUnitOfWork()->registerManaged($this->_hints[self::REFRESH_ENTITY], $id, $data);
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);

View File

@@ -43,7 +43,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* @var EntityManager
*/
private $em;
/**
* @var AbstractPlatform
*/
@@ -73,7 +73,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* @var bool
*/
private $initialized = false;
/**
* @param EntityManager $$em
*/
@@ -101,16 +101,16 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
{
return $this->cacheDriver;
}
public function getLoadedMetadata()
{
return $this->loadedMetadata;
}
/**
* Forces the factory to load the metadata of all classes known to the underlying
* mapping driver.
*
*
* @return array The ClassMetadata instances of all mapped classes.
*/
public function getAllMetadata()
@@ -188,7 +188,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Checks whether the factory has the metadata for a class loaded already.
*
*
* @param string $className
* @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
*/
@@ -199,7 +199,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Sets the metadata descriptor for a specific class.
*
*
* NOTE: This is only useful in very special cases, like when generating proxy classes.
*
* @param string $className
@@ -306,6 +306,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->setPrimaryTable($parent->table);
}
if ($parent && $parent->containsForeignIdentifier) {
$class->containsForeignIdentifier = true;
}
$class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
@@ -313,29 +317,8 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
$this->validateRuntimeMetadata($class, $parent);
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
$this->loadedMetadata[$className] = $class;
$parent = $class;
@@ -351,6 +334,38 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
return $loaded;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($class->name);
}
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
}
/**
* Creates a new ClassMetadata instance for the given class name.
*
@@ -467,4 +482,14 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
throw new ORMException("Unknown generator type: " . $class->generatorType);
}
}
/**
* {@inheritDoc}
*/
public function isTransient($className)
{
$this->initialize();
return $this->driver->isTransient($className);
}
}

View File

@@ -119,7 +119,7 @@ class ClassMetadataInfo implements ClassMetadata
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
@@ -206,7 +206,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
*
* @var array
*/
public $namedQueries = array();
@@ -361,7 +361,7 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>mappedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the owning side.
* This key must be specified on the inverse side of a bidirectional association.
*
*
* - <b>inversedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the inverse side.
* This key must be specified on the owning side of a bidirectional association.
@@ -388,7 +388,7 @@ class ClassMetadataInfo implements ClassMetadata
* Specification of a field on target-entity that is used to index the collection by.
* This field HAS to be either the primary key or a unique column. Otherwise the collection
* does not contain all the entities that are actually related.
*
*
* A join table definition has the following structure:
* <pre>
* array(
@@ -430,7 +430,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The definition of the sequence generator of this class. Only used for the
* SEQUENCE generation strategy.
*
*
* The definition has the following structure:
* <code>
* array(
@@ -774,9 +774,13 @@ class ClassMetadataInfo implements ClassMetadata
// If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity.
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
&& strlen($this->namespace) > 0) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
if (isset($mapping['targetEntity'])) {
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
// Complete id mapping
@@ -809,7 +813,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
// Mandatory and optional attributes for either side
if ( ! $mapping['mappedBy']) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
@@ -825,7 +829,7 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
}
// Fetch mode. Default fetch mode to LAZY, if not set.
if ( ! isset($mapping['fetch'])) {
$mapping['fetch'] = self::FETCH_LAZY;
@@ -848,7 +852,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
return $mapping;
}
@@ -862,11 +866,11 @@ class ClassMetadataInfo implements ClassMetadata
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$mapping['isOwningSide'] = true;
}
if ($mapping['isOwningSide']) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
@@ -908,9 +912,8 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
@@ -934,17 +937,16 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($mapping['mappedBy'])) {
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
@@ -962,7 +964,7 @@ class ClassMetadataInfo implements ClassMetadata
} else {
$targetShortName = strtolower($mapping['targetEntity']);
}
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']['name'])) {
$mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
@@ -1113,23 +1115,23 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getIdentifierColumnNames()
{
if ($this->isIdentifierComposite) {
$columnNames = array();
foreach ($this->identifier as $idField) {
if (isset($this->associationMappings[$idField])) {
// no composite pk as fk entity assumption:
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
} else {
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
}
$columnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
continue;
}
return $columnNames;
} else if(isset($this->fieldMappings[$this->identifier[0]])) {
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
} else {
// no composite pk as fk entity assumption:
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']);
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
$columnNames = array_merge($columnNames, $assocColumnNames);
}
return $columnNames;
}
/**
@@ -1625,11 +1627,13 @@ class ClassMetadataInfo implements ClassMetadata
public function setDiscriminatorMap(array $map)
{
foreach ($map as $value => $className) {
if (strpos($className, '\\') === false && strlen($this->namespace)) {
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$value] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;
} else {
@@ -1708,7 +1712,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Return the single association join column (if any).
*
*
* @param string $fieldName
* @return string
*/
@@ -1750,7 +1754,7 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($this->associationMappings AS $assocName => $mapping) {
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
return $assocName;
}
}
@@ -1840,34 +1844,34 @@ class ClassMetadataInfo implements ClassMetadata
{
$this->isReadOnly = true;
}
/**
* A numerically indexed list of field names of this persistent class.
*
*
* This array includes identifier fields if present on this class.
*
*
* @return array
*/
public function getFieldNames()
{
return array_keys($this->fieldMappings);
}
/**
* A numerically indexed list of association names of this persistent class.
*
*
* This array includes identifier associations if present on this class.
*
*
* @return array
*/
public function getAssociationNames()
{
return array_keys($this->associationMappings);
}
/**
* Returns the target class name of the given association.
*
*
* @param string $assocName
* @return string
*/
@@ -1878,10 +1882,10 @@ class ClassMetadataInfo implements ClassMetadata
}
return $this->associationMappings[$assocName]['targetEntity'];
}
/**
* Get fully-qualified class name of this persistent class.
*
*
* @return string
*/
public function getName()
@@ -1889,10 +1893,46 @@ class ClassMetadataInfo implements ClassMetadata
return $this->name;
}
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* Gets the (possibly quoted) column name of a mapped field for safe use
* in an SQL statement.
*
*
* @param string $field
* @param AbstractPlatform $platform
* @return string
@@ -1903,11 +1943,11 @@ class ClassMetadataInfo implements ClassMetadata
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
$this->fieldMappings[$field]['columnName'];
}
/**
* Gets the (possibly quoted) primary table name of this class for safe use
* in an SQL statement.
*
*
* @param AbstractPlatform $platform
* @return string
*/
@@ -1930,4 +1970,14 @@ class ClassMetadataInfo implements ClassMetadata
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
}
public function isAssociationInverseSide($fieldName)
{
return isset($this->associationMappings[$fieldName]) && !$this->associationMappings[$fieldName]['isOwningSide'];
}
public function getAssociationMappedByTargetField($fieldName)
{
return $this->associationMappings[$fieldName]['mappedBy'];
}
}

View File

@@ -88,15 +88,20 @@ class DriverChain implements Driver
public function getAllClassNames()
{
$classNames = array();
$driverClasses = array();
foreach ($this->_drivers AS $namespace => $driver) {
$driverClasses = $driver->getAllClassNames();
foreach ($driverClasses AS $className) {
$oid = spl_object_hash($driver);
if (!isset($driverClasses[$oid])) {
$driverClasses[$oid] = $driver->getAllClassNames();
}
foreach ($driverClasses[$oid] AS $className) {
if (strpos($className, $namespace) === 0) {
$classNames[] = $className;
$classNames[$className] = true;
}
}
}
return array_unique($classNames);
return array_keys($classNames);
}
/**

View File

@@ -86,7 +86,7 @@ class XmlDriver extends AbstractFileDriver
if (isset($xmlRoot['schema'])) {
$metadata->table['schema'] = (string)$xmlRoot['schema'];
}*/
if (isset($xmlRoot['inheritance-type'])) {
$inheritanceType = (string)$xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
@@ -285,8 +285,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
}
if (isset($oneToOneElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement->{'orphan-removal'};
if (isset($oneToOneElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphan-removal'];
}
$metadata->mapOneToOne($mapping);
@@ -310,8 +310,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
}
if (isset($oneToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
if (isset($oneToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphan-removal'];
}
if (isset($oneToManyElement->{'order-by'})) {
@@ -322,8 +322,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($oneToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$oneToManyElement->{'index-by'};
if (isset($oneToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$oneToManyElement['index-by'];
} else if (isset($oneToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapOneToMany($mapping);
@@ -429,8 +431,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
if (isset($manyToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$manyToManyElement['index-by'];
} else if (isset($manyToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapManyToMany($mapping);

View File

@@ -60,6 +60,15 @@ class ORMException extends Exception
return new self("Unrecognized field: $field");
}
/**
* @param string $className
* @param string $field
*/
public static function invalidOrientation($className, $field)
{
return new self("Invalid order by orientation specified for " . $className . "#" . $field);
}
public static function invalidFlushMode($mode)
{
return new self("'$mode' is an invalid flush mode.");

View File

@@ -33,6 +33,7 @@ class OptimisticLockException extends ORMException
public function __construct($msg, $entity)
{
parent::__construct($msg);
$this->entity = $entity;
}

View File

@@ -93,21 +93,21 @@ final class PersistentCollection implements Collection
/**
* Whether the collection has already been initialized.
*
*
* @var boolean
*/
private $initialized = true;
/**
* The wrapped Collection instance.
*
*
* @var Collection
*/
private $coll;
/**
* Creates a new persistent collection.
*
*
* @param EntityManager $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param array The collection elements.
@@ -144,7 +144,7 @@ final class PersistentCollection implements Collection
{
return $this->owner;
}
public function getTypeClass()
{
return $this->typeClass;
@@ -154,7 +154,7 @@ final class PersistentCollection implements Collection
* INTERNAL:
* Adds an element to a collection during hydration. This will automatically
* complete bidirectional associations in the case of a one-to-many association.
*
*
* @param mixed $element The element to add.
*/
public function hydrateAdd($element)
@@ -172,7 +172,7 @@ final class PersistentCollection implements Collection
$this->owner);
}
}
/**
* INTERNAL:
* Sets a keyed element in the collection during hydration.
@@ -271,7 +271,7 @@ final class PersistentCollection implements Collection
{
return $this->association;
}
/**
* Marks this collection as changed/dirty.
*/
@@ -306,17 +306,17 @@ final class PersistentCollection implements Collection
{
$this->isDirty = $dirty;
}
/**
* Sets the initialized flag of the collection, forcing it into that state.
*
*
* @param boolean $bool
*/
public function setInitialized($bool)
{
$this->initialized = $bool;
}
/**
* Checks whether this collection has been initialized.
*
@@ -377,7 +377,7 @@ final class PersistentCollection implements Collection
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
->deleteRows($this, $element);
}*/
$this->initialize();
$removed = $this->coll->removeElement($element);
if ($removed) {
@@ -410,7 +410,7 @@ final class PersistentCollection implements Collection
->getCollectionPersister($this->association)
->contains($this, $element);
}
$this->initialize();
return $this->coll->contains($element);
}
@@ -468,7 +468,7 @@ final class PersistentCollection implements Collection
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->count($this) + $this->coll->count();
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
}
$this->initialize();
@@ -503,7 +503,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->isEmpty();
}
/**
* {@inheritdoc}
*/
@@ -530,7 +530,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->filter($p);
}
/**
* {@inheritdoc}
*/
@@ -548,7 +548,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->partition($p);
}
/**
* {@inheritdoc}
*/
@@ -567,6 +567,9 @@ final class PersistentCollection implements Collection
return;
}
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
// hence for event listeners we need the objects in memory.
$this->initialize();
foreach ($this->coll as $element) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -579,7 +582,7 @@ final class PersistentCollection implements Collection
$this->takeSnapshot();
}
}
/**
* Called by PHP when this collection is serialized. Ensures that only the
* elements are properly serialized.
@@ -591,7 +594,7 @@ final class PersistentCollection implements Collection
{
return array('coll', 'initialized');
}
/* ArrayAccess implementation */
/**
@@ -629,12 +632,12 @@ final class PersistentCollection implements Collection
{
return $this->remove($offset);
}
public function key()
{
return $this->coll->key();
}
/**
* Gets the element of the collection at the current iterator position.
*/
@@ -642,7 +645,7 @@ final class PersistentCollection implements Collection
{
return $this->coll->current();
}
/**
* Moves the internal iterator position to the next element.
*/
@@ -650,7 +653,7 @@ final class PersistentCollection implements Collection
{
return $this->coll->next();
}
/**
* Retrieves the wrapped Collection instance.
*/
@@ -672,7 +675,10 @@ final class PersistentCollection implements Collection
*/
public function slice($offset, $length = null)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
if ( ! $this->initialized &&
! $this->isDirty &&
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->slice($this, $offset, $length);

View File

@@ -65,9 +65,11 @@ abstract class AbstractCollectionPersister
public function delete(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$sql = $this->_getDeleteSQL($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
}
@@ -96,9 +98,11 @@ abstract class AbstractCollectionPersister
public function update(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$this->deleteRows($coll);
//$this->updateRows($coll);
$this->insertRows($coll);
@@ -108,6 +112,7 @@ abstract class AbstractCollectionPersister
{
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
@@ -120,6 +125,7 @@ abstract class AbstractCollectionPersister
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
}

View File

@@ -39,10 +39,12 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
protected function _prepareInsertData($entity)
{
$data = parent::_prepareInsertData($entity);
// Populate the discriminator column
$discColumn = $this->_class->discriminatorColumn;
$this->_columnTypes[$discColumn['name']] = $discColumn['type'];
$data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue;
return $data;
}
@@ -63,7 +65,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
return "$sql AS $columnAlias";
return $sql . ' AS ' . $columnAlias;
}
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
@@ -72,6 +74,6 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
return $tableAlias . ".$joinColumnName AS $columnAlias";
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
}
}

View File

@@ -91,7 +91,7 @@ class BasicEntityPersister
/**
* The database platform.
*
*
* @var Doctrine\DBAL\Platforms\AbstractPlatform
*/
protected $_platform;
@@ -109,12 +109,12 @@ class BasicEntityPersister
* @var array
*/
protected $_queuedInserts = array();
/**
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
*
*
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
*
*
* @var Query\ResultSetMapping
*/
protected $_rsm;
@@ -122,7 +122,7 @@ class BasicEntityPersister
/**
* The map of column names to DBAL mapping types of all prepared columns used
* when INSERTing or UPDATEing an entity.
*
*
* @var array
* @see _prepareInsertData($entity)
* @see _prepareUpdateData($entity)
@@ -132,7 +132,7 @@ class BasicEntityPersister
/**
* The INSERT SQL statement used for entities handled by this persister.
* This SQL is only generated once per request, if at all.
*
*
* @var string
*/
private $_insertSql;
@@ -140,29 +140,29 @@ class BasicEntityPersister
/**
* The SELECT column list SQL fragment used for querying entities by this persister.
* This SQL fragment is only generated once per request, if at all.
*
*
* @var string
*/
protected $_selectColumnListSql;
/**
* The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one
* associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations.
*
*
* @var string
*/
protected $_selectJoinSql;
/**
* Counter for creating unique SQL table and column aliases.
*
*
* @var integer
*/
protected $_sqlAliasCounter = 0;
/**
* Map from class names (FQCN) to the corresponding generated SQL table aliases.
*
*
* @var array
*/
protected $_sqlTableAliases = array();
@@ -170,7 +170,7 @@ class BasicEntityPersister
/**
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
*
*
* @param Doctrine\ORM\EntityManager $em
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
@@ -204,7 +204,7 @@ class BasicEntityPersister
/**
* Executes all queued entity insertions and returns any generated post-insert
* identifiers that were created as a result of the insertions.
*
*
* If no inserts are queued, invoking this method is a NOOP.
*
* @return array An array of any generated post-insert IDs. This will be an empty array
@@ -228,6 +228,7 @@ class BasicEntityPersister
if (isset($insertData[$tableName])) {
$paramIndex = 1;
foreach ($insertData[$tableName] as $column => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]);
}
@@ -255,7 +256,7 @@ class BasicEntityPersister
/**
* Retrieves the default version value which was created
* by the preceding INSERT statement and assigns it back in to the
* by the preceding INSERT statement and assigns it back in to the
* entities version field.
*
* @param object $entity
@@ -269,7 +270,7 @@ class BasicEntityPersister
/**
* Fetch the current version value of a versioned entity.
*
*
* @param Doctrine\ORM\Mapping\ClassMetadata $versionedClass
* @param mixed $id
* @return mixed
@@ -294,7 +295,7 @@ class BasicEntityPersister
* The data to update is retrieved through {@link _prepareUpdateData}.
* Subclasses that override this method are supposed to obtain the update data
* in the same way, through {@link _prepareUpdateData}.
*
*
* Subclasses are also supposed to take care of versioning when overriding this method,
* if necessary. The {@link _updateTable} method can be used to apply the data retrieved
* from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning.
@@ -392,7 +393,7 @@ class BasicEntityPersister
// @Todo this only covers scenarios with no inheritance or of the same level. Is there something
// like self-referential relationship between different levels of an inheritance hierachy? I hope not!
$selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
if ( ! $mapping['isOwningSide']) {
$relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$mapping = $relatedClass->associationMappings[$mapping['mappedBy']];
@@ -441,7 +442,7 @@ class BasicEntityPersister
* Prepares the changeset of an entity for database insertion (UPDATE).
*
* The changeset is obtained from the currently running UnitOfWork.
*
*
* During this preparation the array that is passed as the second parameter is filled with
* <columnName> => <value> pairs, grouped by table name.
*
@@ -567,9 +568,10 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
}
if ($this->_selectJoinSql) {
@@ -587,13 +589,12 @@ class BasicEntityPersister
*
* @param array $assoc The association to load.
* @param object $sourceEntity The entity that owns the association (not necessarily the "owning side").
* @param object $targetEntity The existing ghost entity (proxy) to load, if any.
* @param array $identifier The identifier of the entity to load. Must be provided if
* the association to load represents the owning side, otherwise
* the identifier is derived from the $sourceEntity.
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
*/
public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array())
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
{
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
return $foundEntity;
@@ -621,7 +622,7 @@ class BasicEntityPersister
}
*/
$targetEntity = $this->load($identifier, $targetEntity, $assoc, $hints);
$targetEntity = $this->load($identifier, null, $assoc, $hints);
// Complete bidirectional association, if necessary
if ($targetEntity !== null && $isInverseSingleValued) {
@@ -633,7 +634,11 @@ class BasicEntityPersister
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
// unset the old value and set the new sql aliased value here. By definition
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
unset($identifier[$targetKeyColumn]);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
@@ -641,7 +646,7 @@ class BasicEntityPersister
}
}
$targetEntity = $this->load($identifier, $targetEntity, $assoc);
$targetEntity = $this->load($identifier, null, $assoc);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
@@ -653,7 +658,7 @@ class BasicEntityPersister
/**
* Refreshes a managed entity.
*
*
* @param array $id The identifier of the entity as an associative array from
* column or field names to values.
* @param object $entity The entity to refresh.
@@ -663,7 +668,7 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($id);
list($params, $types) = $this->expandParameters($id);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
@@ -678,7 +683,7 @@ class BasicEntityPersister
/**
* Loads a list of entities by a list of field criteria.
*
*
* @param array $criteria
* @param array $orderBy
* @param int $limit
@@ -702,7 +707,7 @@ class BasicEntityPersister
/**
* Get (sliced or full) elements of the given collection.
*
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
@@ -717,7 +722,7 @@ class BasicEntityPersister
/**
* Load an array of entities from a given dbal statement.
*
*
* @param array $assoc
* @param Doctrine\DBAL\Statement $stmt
* @return array
@@ -739,13 +744,13 @@ class BasicEntityPersister
/**
* Hydrate a collection from a given dbal statement.
*
*
* @param array $assoc
* @param Doctrine\DBAL\Statement $stmt
* @param PersistentCollection $coll
*/
private function loadCollectionFromStatement($assoc, $stmt, $coll)
{
{
$hints = array('deferEagerLoads' => true, 'collection' => $coll);
if (isset($assoc['indexBy'])) {
@@ -787,7 +792,7 @@ class BasicEntityPersister
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
@@ -809,7 +814,7 @@ class BasicEntityPersister
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
@@ -868,11 +873,10 @@ class BasicEntityPersister
/**
* Gets the ORDER BY SQL snippet for ordered collections.
*
*
* @param array $orderBy
* @param string $baseTableAlias
* @return string
* @todo Rename: _getOrderBySQL
*/
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
{
@@ -882,6 +886,11 @@ class BasicEntityPersister
throw ORMException::unrecognizedField($fieldName);
}
$orientation = strtoupper(trim($orientation));
if ($orientation != 'ASC' && $orientation != 'DESC') {
throw ORMException::invalidOrientation($this->_class->name, $fieldName);
}
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
$this->_getSQLTableAlias($this->_class->fieldMappings[$fieldName]['inherited'])
: $baseTableAlias;
@@ -902,7 +911,7 @@ class BasicEntityPersister
* list SQL fragment. Note that in the implementation of BasicEntityPersister
* the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}.
* Subclasses may or may not do the same.
*
*
* @return string The SQL fragment.
* @todo Rename: _getSelectColumnsSQL()
*/
@@ -930,21 +939,21 @@ class BasicEntityPersister
if ($columnList) $columnList .= ', ';
$columnList .= $assocColumnSQL;
}
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
continue; // now this is why you shouldn't use inheritance
}
$assocAlias = 'e' . ($eagerAliasCounter++);
$this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
foreach ($eagerEntity->fieldNames AS $field) {
if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
}
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
if ($assoc2ColumnSQL) {
@@ -956,7 +965,7 @@ class BasicEntityPersister
$first = true;
if ($assoc['isOwningSide']) {
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if (!$first) {
$this->_selectJoinSql .= ' AND ';
@@ -968,7 +977,7 @@ class BasicEntityPersister
} else {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
@@ -987,7 +996,7 @@ class BasicEntityPersister
return $this->_selectColumnListSql;
}
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
{
$columnList = '';
@@ -996,9 +1005,10 @@ class BasicEntityPersister
if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
}
}
return $columnList;
@@ -1020,9 +1030,9 @@ class BasicEntityPersister
$owningAssoc = $this->_em->getClassMetadata($manyToMany['targetEntity'])->associationMappings[$manyToMany['mappedBy']];
$joinClauses = $owningAssoc['relationToSourceKeyColumns'];
}
$joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform);
$joinSql = '';
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND ';
@@ -1043,7 +1053,7 @@ class BasicEntityPersister
/**
* Gets the INSERT SQL used by the persister to persist a new entity.
*
*
* @return string
*/
protected function _getInsertSQL()
@@ -1120,7 +1130,7 @@ class BasicEntityPersister
/**
* Gets the SQL table alias for the given class name.
*
*
* @param string $className
* @return string The SQL table alias.
* @todo Reconsider. Binding table aliases to class names is not such a good idea.
@@ -1130,7 +1140,7 @@ class BasicEntityPersister
if ($assocName) {
$className .= '#'.$assocName;
}
if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className];
}
@@ -1214,7 +1224,7 @@ class BasicEntityPersister
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
// very careless developers could potentially open up this normally hidden api for userland attacks,
// therefore checking for spaces and function calls which are not allowed.
// found a join column condition, not really a "field"
$conditionSql .= $field;
} else {
@@ -1280,7 +1290,7 @@ class BasicEntityPersister
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
}
$criteria[$tableAlias . "." . $targetKeyColumn] = $value;
} else {
@@ -1316,7 +1326,7 @@ class BasicEntityPersister
if (is_array($value)) {
$type += Connection::ARRAY_PARAM_OFFSET;
}
$params[] = $value;
$types[] = $type;
}
@@ -1336,8 +1346,7 @@ class BasicEntityPersister
$criteria = array_merge($criteria, $extraConditions);
}
$sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform)
. ' ' . $this->_getSQLTableAlias($this->_class->name)
$sql = 'SELECT 1 ' . $this->getLockTablesSql()
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria));

View File

@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\LockMode,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\ResultSetMapping;
/**
@@ -143,9 +144,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Execute insert on root table
$paramIndex = 1;
foreach ($insertData[$rootTableName] as $columnName => $value) {
$rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
}
$rootTableStmt->execute();
if ($isPostInsertId) {
@@ -160,12 +163,17 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($subTableStmts as $tableName => $stmt) {
$data = isset($insertData[$tableName]) ? $insertData[$tableName] : array();
$paramIndex = 1;
foreach ((array) $id as $idVal) {
$stmt->bindValue($paramIndex++, $idVal);
foreach ((array) $id as $idName => $idVal) {
$type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING;
$stmt->bindValue($paramIndex++, $idVal, $type);
}
foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
}
$stmt->execute();
}
}
@@ -191,7 +199,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{
$updateData = $this->_prepareUpdateData($entity);
if ($isVersioned = $this->_class->isVersioned) {
if (($isVersioned = $this->_class->isVersioned) != false) {
$versionedClass = $this->_getVersionedClassMetadata();
$versionedTable = $versionedClass->table['name'];
}
@@ -200,6 +208,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($updateData as $tableName => $data) {
$this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName);
}
// Make sure the table with the version column is updated even if no columns on that
// table were affected.
if ($isVersioned && ! isset($updateData[$versionedTable])) {
@@ -229,6 +238,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
} else {
// Delete from all tables individually, starting from this class' table up to the root table.
$this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id);
foreach ($this->_class->parentClasses as $parentClass) {
$this->_conn->delete($this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id);
}

View File

@@ -39,9 +39,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/
protected function _getDeleteRowSQL(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$mapping = $coll->getMapping();
$joinTable = $mapping['joinTable'];
$columns = $mapping['joinTableColumns'];
$columns = $mapping['joinTableColumns'];
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
}

View File

@@ -124,24 +124,26 @@ class OneToManyPersister extends AbstractCollectionPersister
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$params = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
$where = '';
foreach ($class->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
if ($where != '') {
$where .= ' AND ';
}
$where .= $joinColumn['name'] . " = ?";
if ($class->containsForeignIdentifier) {
$params[] = $id[$class->getFieldForColumn($joinColumn['referencedColumnName'])];
if ($targetClass->containsForeignIdentifier) {
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
} else {
$params[] = $id[$class->fieldNames[$joinColumn['referencedColumnName']]];
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
}
$sql = "SELECT count(*) FROM " . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
return $this->_conn->fetchColumn($sql, $params);
}
@@ -180,4 +182,4 @@ class OneToManyPersister extends AbstractCollectionPersister
return $uow->getEntityPersister($mapping['targetEntity'])
->exists($element, array($mapping['mappedBy'] => $id));
}
}
}

View File

@@ -41,6 +41,10 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
/** {@inheritdoc} */
protected function _getSelectColumnListSQL()
{
if ($this->_selectColumnListSql !== null) {
return $this->_selectColumnListSql;
}
$columnList = parent::_getSelectColumnListSQL();
// Append discriminator column
@@ -74,7 +78,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
}
}
return $columnList;
$this->_selectColumnListSql = $columnList;
return $this->_selectColumnListSql;
}
/** {@inheritdoc} */

View File

@@ -165,14 +165,16 @@ class ProxyFactory
{
$methods = '';
$methodNames = array();
foreach ($class->reflClass->getMethods() as $method) {
/* @var $method ReflectionMethod */
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone"))) {
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) {
continue;
}
$methodNames[$method->getName()] = true;
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= PHP_EOL . ' public function ';
$methods .= "\n" . ' public function ';
if ($method->returnsReference()) {
$methods .= '&';
}
@@ -208,10 +210,10 @@ class ProxyFactory
}
$methods .= $parameterString . ')';
$methods .= PHP_EOL . ' {' . PHP_EOL;
$methods .= ' $this->__load();' . PHP_EOL;
$methods .= "\n" . ' {' . "\n";
$methods .= ' $this->__load();' . "\n";
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= PHP_EOL . ' }' . PHP_EOL;
$methods .= "\n" . ' }' . "\n";
}
}
@@ -274,6 +276,14 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
if (method_exists($this, "__wakeup")) {
// call this after __isInitialized__to avoid infinite recursion
// but before loading to emulate what ClassMetadata::newInstance()
// provides.
$this->__wakeup();
}
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}

View File

@@ -44,19 +44,28 @@ final class Query extends AbstractQuery
* is called.
*/
const STATE_DIRTY = 2;
/* Query HINTS */
/**
* The refresh hint turns any query into a refresh query with the result that
* any local changes in entities are overridden with the fetched values.
*
*
* @var string
*/
const HINT_REFRESH = 'doctrine.refresh';
/**
* Internal hint: is set to the proxy entity that is currently triggered for loading
*
* @var string
*/
const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
/**
* The forcePartialLoad query hint forces a particular query to return
* partial objects.
*
*
* @var string
* @todo Rename: HINT_OPTIMIZE
*/
@@ -64,9 +73,9 @@ final class Query extends AbstractQuery
/**
* The includeMetaColumns query hint causes meta columns like foreign keys and
* discriminator columns to be selected and returned as part of the query result.
*
*
* This hint does only apply to non-object queries.
*
*
* @var string
*/
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
@@ -113,12 +122,12 @@ final class Query extends AbstractQuery
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information.
*/
private $_parserResult;
/**
* @var integer The first result to return (the "offset").
*/
private $_firstResult = null;
/**
* @var integer The maximum number of results to return (the "limit").
*/
@@ -138,7 +147,7 @@ final class Query extends AbstractQuery
* @var int Query Cache lifetime.
*/
private $_queryCacheTTL;
/**
* @var boolean Whether to use a query cache, if available. Defaults to TRUE.
*/
@@ -182,7 +191,7 @@ final class Query extends AbstractQuery
/**
* Parses the DQL query, if necessary, and stores the parser result.
*
*
* Note: Populates $this->_parserResult as a side-effect.
*
* @return Doctrine\ORM\Query\ParserResult
@@ -192,11 +201,12 @@ final class Query extends AbstractQuery
if ($this->_state === self::STATE_CLEAN) {
return $this->_parserResult;
}
// Check query cache.
if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) {
$hash = $this->_getQueryCacheId();
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
if ($cached === false) {
// Cache miss.
$parser = new Parser($this);
@@ -210,8 +220,9 @@ final class Query extends AbstractQuery
$parser = new Parser($this);
$this->_parserResult = $parser->parse();
}
$this->_state = self::STATE_CLEAN;
return $this->_parserResult;
}
@@ -229,43 +240,7 @@ final class Query extends AbstractQuery
throw QueryException::invalidParameterNumber();
}
$sqlParams = $types = array();
foreach ($this->_params as $key => $value) {
if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key);
}
if (isset($this->_paramTypes[$key])) {
foreach ($paramMappings[$key] as $position) {
$types[$position] = $this->_paramTypes[$key];
}
}
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$sqlPositions = $paramMappings[$key];
$cSqlPos = count($sqlPositions);
$cIdValues = count($idValues);
$idValues = array_values($idValues);
for ($i = 0; $i < $cSqlPos; $i++) {
$sqlParams[$sqlPositions[$i]] = $idValues[ ($i % $cIdValues) ];
}
} else {
foreach ($paramMappings[$key] as $position) {
$sqlParams[$position] = $value;
}
}
}
if ($sqlParams) {
ksort($sqlParams);
$sqlParams = array_values($sqlParams);
}
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
if ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
@@ -274,6 +249,83 @@ final class Query extends AbstractQuery
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
}
/**
* Processes query parameter mappings
*
* @param array $paramMappings
* @return array
*/
private function processParameterMappings($paramMappings)
{
$sqlParams = $types = array();
foreach ($this->_params as $key => $value) {
if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key);
}
if (isset($this->_paramTypes[$key])) {
foreach ($paramMappings[$key] as $position) {
$types[$position] = $this->_paramTypes[$key];
}
}
$sqlPositions = $paramMappings[$key];
$value = array_values($this->processParameterValue($value));
$countValue = count($value);
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
$sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
}
}
if (count($sqlParams) != count($types)) {
throw QueryException::parameterTypeMissmatch();
}
if ($sqlParams) {
ksort($sqlParams);
$sqlParams = array_values($sqlParams);
ksort($types);
$types = array_values($types);
}
return array($sqlParams, $types);
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
if (is_array($value)) {
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
// TODO: What about Entities that have composite primary key?
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return array($value);
}
if ( ! (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)))) {
return array($value);
}
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
}
$class = $this->_em->getClassMetadata(get_class($value));
return array_values($class->getIdentifierValues($value));
}
/**
* Defines a cache driver to be used for caching queries.
*
@@ -285,10 +337,10 @@ final class Query extends AbstractQuery
$this->_queryCache = $queryCache;
return $this;
}
/**
* Defines whether the query should make use of a query cache, if available.
*
*
* @param boolean $bool
* @return @return Query This query instance.
*/
@@ -422,7 +474,7 @@ final class Query extends AbstractQuery
{
return stripos($this->getDQL(), $dql) === false ? false : true;
}
/**
* Sets the position of the first result to retrieve (the "offset").
*
@@ -435,21 +487,21 @@ final class Query extends AbstractQuery
$this->_state = self::STATE_DIRTY;
return $this;
}
/**
* Gets the position of the first result the query object was set to retrieve (the "offset").
* Returns NULL if {@link setFirstResult} was not applied to this query.
*
*
* @return integer The position of the first result.
*/
public function getFirstResult()
{
return $this->_firstResult;
}
/**
* Sets the maximum number of results to retrieve (the "limit").
*
*
* @param integer $maxResults
* @return Query This query object.
*/
@@ -459,11 +511,11 @@ final class Query extends AbstractQuery
$this->_state = self::STATE_DIRTY;
return $this;
}
/**
* Gets the maximum number of results the query object was set to retrieve (the "limit").
* Returns NULL if {@link setMaxResults} was not applied to this query.
*
*
* @return integer Maximum number of results.
*/
public function getMaxResults()
@@ -484,7 +536,7 @@ final class Query extends AbstractQuery
$this->setHint(self::HINT_INTERNAL_ITERATION, true);
return parent::iterate($params, $hydrationMode);
}
/**
* {@inheritdoc}
*/
@@ -493,7 +545,7 @@ final class Query extends AbstractQuery
$this->_state = self::STATE_DIRTY;
return parent::setHint($name, $value);
}
/**
* {@inheritdoc}
*/
@@ -548,7 +600,7 @@ final class Query extends AbstractQuery
ksort($this->_hints);
return md5(
$this->getDql() . var_export($this->_hints, true) .
$this->getDql() . var_export($this->_hints, true) .
'&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
'&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT'
);
@@ -564,4 +616,4 @@ final class Query extends AbstractQuery
parent::__clone();
$this->_state = self::STATE_DIRTY;
}
}
}

View File

@@ -98,7 +98,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
}
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
}
/**

View File

@@ -106,7 +106,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
//FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage.
if ($newValue instanceof AST\InputParameter) {
$paramKey = $newValue->name;
$this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey);
$this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey);
$this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
++$this->_numParametersInUpdateClause;
}
@@ -136,7 +137,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
}
/**
@@ -154,11 +155,23 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$conn->executeUpdate($this->_createTempTableSql);
// Insert identifiers. Parameters from the update clause are cut off.
$numUpdated = $conn->executeUpdate($this->_insertSql, array_slice($params, $this->_numParametersInUpdateClause), $types);
$numUpdated = $conn->executeUpdate(
$this->_insertSql,
array_slice($params, $this->_numParametersInUpdateClause),
array_slice($types, $this->_numParametersInUpdateClause)
);
// Execute UPDATE statements
for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) {
$conn->executeUpdate($this->_sqlStatements[$i], isset($this->_sqlParameters[$i]) ? $this->_sqlParameters[$i] : array());
$parameters = array();
$types = array();
if (isset($this->_sqlParameters[$i])) {
$parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array();
$types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array();
}
$conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types);
}
// Drop temporary table

View File

@@ -2290,7 +2290,7 @@ class Parser
/**
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
*/
public function ArithmeticPrimary()
{
@@ -2314,7 +2314,11 @@ class Parser
if ($peek['value'] == '.') {
return $this->SingleValuedPathExpression();
}
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
return $this->ResultVariable();
}
return $this->StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:

View File

@@ -72,6 +72,11 @@ class QueryException extends \Doctrine\ORM\ORMException
return new self("Invalid parameter: token ".$key." is not defined in the query.");
}
public static function parameterTypeMissmatch()
{
return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal.");
}
public static function invalidPathExpression($pathExpr)
{
return new self(
@@ -135,7 +140,7 @@ class QueryException extends \Doctrine\ORM\ORMException
"in the query."
);
}
public static function instanceOfUnrelatedClass($className, $rootClass)
{
return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " .

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
@@ -52,21 +53,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
{
$this->addEntityResult($class, $alias);
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
}
$platform = $this->em->getConnection()->getDatabasePlatform();
foreach ($classMetadata->getColumnNames() AS $columnName) {
$propertyName = $classMetadata->getFieldName($columnName);
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
@@ -81,6 +68,14 @@ class ResultSetMappingBuilder extends ResultSetMapping
public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array())
{
$this->addJoinedEntityResult($class, $alias, $parentAlias, $relation);
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
* Adds all fields of the given class to the result set mapping (columns and meta fields)
*/
protected function addAllClassFields($class, $alias, $renamedColumns = array())
{
$classMetadata = $this->em->getClassMetadata($class);
if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) {
throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.');
@@ -96,5 +91,17 @@ class ResultSetMappingBuilder extends ResultSetMapping
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
}
foreach ($classMetadata->associationMappings AS $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] AS $joinColumn) {
$columnName = $joinColumn['name'];
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName));
}
}
}
}
}

View File

@@ -157,32 +157,24 @@ class SqlWalker implements TreeWalker
*/
public function getExecutor($AST)
{
$isDeleteStatement = $AST instanceof AST\DeleteStatement;
$isUpdateStatement = $AST instanceof AST\UpdateStatement;
if ($isDeleteStatement) {
$primaryClass = $this->_em->getClassMetadata(
$AST->deleteClause->abstractSchemaName
);
if ($primaryClass->isInheritanceTypeJoined()) {
return new Exec\MultiTableDeleteExecutor($AST, $this);
} else {
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
} else if ($isUpdateStatement) {
$primaryClass = $this->_em->getClassMetadata(
$AST->updateClause->abstractSchemaName
);
if ($primaryClass->isInheritanceTypeJoined()) {
return new Exec\MultiTableUpdateExecutor($AST, $this);
} else {
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
switch (true) {
case ($AST instanceof AST\DeleteStatement):
$primaryClass = $this->_em->getClassMetadata($AST->deleteClause->abstractSchemaName);
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableDeleteExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
case ($AST instanceof AST\UpdateStatement):
$primaryClass = $this->_em->getClassMetadata($AST->updateClause->abstractSchemaName);
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableUpdateExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
default:
return new Exec\SingleSelectExecutor($AST, $this);
}
return new Exec\SingleSelectExecutor($AST, $this);
}
/**
@@ -255,13 +247,10 @@ class SqlWalker implements TreeWalker
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName
. ' = '
. $tableAlias . '.' . $columnName;
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
}
@@ -273,13 +262,11 @@ class SqlWalker implements TreeWalker
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName
. ' = '
. $tableAlias . '.' . $columnName;
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
}
}
@@ -526,9 +513,8 @@ class SqlWalker implements TreeWalker
*/
public function walkSelectClause($selectClause)
{
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode(
', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)
);
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
@@ -554,7 +540,8 @@ class SqlWalker implements TreeWalker
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
@@ -574,7 +561,9 @@ class SqlWalker implements TreeWalker
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
@@ -589,7 +578,9 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
@@ -598,6 +589,8 @@ class SqlWalker implements TreeWalker
}
}
}
$sql .= implode(', ', $sqlSelectExpressions);
return $sql;
}
@@ -1227,67 +1220,77 @@ class SqlWalker implements TreeWalker
*/
public function walkSimpleSelectExpression($simpleSelectExpression)
{
$sql = '';
$expr = $simpleSelectExpression->expression;
$sql = ' ';
switch (true) {
case ($expr instanceof AST\PathExpression):
$sql .= $this->walkPathExpression($expr);
break;
case ($expr instanceof AST\AggregateExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
break;
case ($expr instanceof AST\Subselect):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
break;
case ($expr instanceof AST\Functions\FunctionNode):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\ArithmeticPrimary):
case ($expr instanceof AST\Literal):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
break;
default: // IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$sqlParts = array();
if ($expr instanceof AST\PathExpression) {
$sql .= $this->walkPathExpression($expr);
} else if ($expr instanceof AST\AggregateExpression) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
} else if ($expr instanceof AST\Subselect) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if (
$expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor ||
$expr instanceof AST\ArithmeticPrimary
) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else {
// IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$first = true;
foreach ($class->identifier as $identifier) {
if ($first) $first = false; else $sql .= ', ';
$sql .= $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform);
}
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $tableAlias . '.' . $columnName;
}
$sql .= implode(', ', $sqlParts);
break;
}
return ' ' . $sql;
return $sql;
}
/**
@@ -1310,25 +1313,24 @@ class SqlWalker implements TreeWalker
*/
public function walkGroupByClause($groupByClause)
{
$sql = '';
$sqlParts = array();
foreach ($groupByClause->groupByItems AS $groupByItem) {
if (is_string($groupByItem)) {
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
if ($sql != '') {
$sql .= ', ';
}
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
$sql .= $this->walkGroupByItem($groupByItem);
}
} else {
if ($sql != '') {
$sql .= ', ';
}
$sql .= $this->walkGroupByItem($groupByItem);
if ( ! is_string($groupByItem)) {
$sqlParts[] = $this->walkGroupByItem($groupByItem);
continue;
}
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
$sqlParts[] = $this->walkGroupByItem($groupByItem);
}
}
return ' GROUP BY ' . $sql;
return ' GROUP BY ' . implode(', ', $sqlParts);
}
/**
@@ -1350,12 +1352,11 @@ class SqlWalker implements TreeWalker
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause)
{
$sql = 'DELETE FROM ';
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
return $sql;
@@ -1369,17 +1370,14 @@ class SqlWalker implements TreeWalker
*/
public function walkUpdateClause($updateClause)
{
$sql = 'UPDATE ';
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'UPDATE ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
$sql .= ' SET ' . implode(
', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)
);
$sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems));
return $sql;
}
@@ -1395,16 +1393,21 @@ class SqlWalker implements TreeWalker
$useTableAliasesBefore = $this->_useSqlTableAliases;
$this->_useSqlTableAliases = false;
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$newValue = $updateItem->newValue;
if ($newValue === null) {
$sql .= 'NULL';
} else if ($newValue instanceof AST\Node) {
$sql .= $newValue->dispatch($this);
} else {
$sql .= $this->_conn->quote($newValue);
switch (true) {
case ($newValue instanceof AST\Node):
$sql .= $newValue->dispatch($this);
break;
case ($newValue === null):
$sql .= 'NULL';
break;
default:
$sql .= $this->_conn->quote($newValue);
break;
}
$this->_useSqlTableAliases = $useTableAliasesBefore;
@@ -1423,7 +1426,15 @@ class SqlWalker implements TreeWalker
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases);
$condSql = $this->walkConditionalExpression($whereClause->conditionalExpression);
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
if ($condSql) {
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
}
if ($discrSql) {
return ' WHERE ' . $discrSql;
}
return '';
}
/**
@@ -1436,11 +1447,11 @@ class SqlWalker implements TreeWalker
{
// Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined
return ( ! ($condExpr instanceof AST\ConditionalExpression))
? $this->walkConditionalTerm($condExpr)
: implode(
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
);
if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
return $this->walkConditionalTerm($condExpr);
}
return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms));
}
/**
@@ -1453,11 +1464,11 @@ class SqlWalker implements TreeWalker
{
// Phase 2 AST optimization: Skip processment of ConditionalTerm
// if only one ConditionalFactor is defined
return ( ! ($condTerm instanceof AST\ConditionalTerm))
? $this->walkConditionalFactor($condTerm)
: implode(
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
);
if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
return $this->walkConditionalFactor($condTerm);
}
return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors));
}
/**
@@ -1485,7 +1496,9 @@ class SqlWalker implements TreeWalker
{
if ($primary->isSimpleConditionalExpression()) {
return $primary->simpleConditionalExpression->dispatch($this);
} else if ($primary->isConditionalExpression()) {
}
if ($primary->isConditionalExpression()) {
$condExpr = $primary->conditionalExpression;
return '(' . $this->walkConditionalExpression($condExpr) . ')';
@@ -1558,12 +1571,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
} else { // many-to-many
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
@@ -1616,12 +1628,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
}
@@ -1674,14 +1685,12 @@ class SqlWalker implements TreeWalker
*/
public function walkInExpression($inExpr)
{
$sql = $this->walkPathExpression($inExpr->pathExpression)
$sql = $this->walkPathExpression($inExpr->pathExpression)
. ($inExpr->not ? ' NOT' : '') . ' IN (';
if ($inExpr->subselect) {
$sql .= $this->walkSubselect($inExpr->subselect);
} else {
$sql .= implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
}
$sql .= ($inExpr->subselect)
? $this->walkSubselect($inExpr->subselect)
: implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
$sql .= ')';
@@ -1749,9 +1758,9 @@ class SqlWalker implements TreeWalker
*/
public function walkInParameter($inParam)
{
return $inParam instanceof AST\InputParameter ?
$this->walkInputParameter($inParam) :
$this->walkLiteral($inParam);
return $inParam instanceof AST\InputParameter
? $this->walkInputParameter($inParam)
: $this->walkLiteral($inParam);
}
/**
@@ -1840,23 +1849,19 @@ class SqlWalker implements TreeWalker
*/
public function walkComparisonExpression($compExpr)
{
$sql = '';
$leftExpr = $compExpr->leftExpression;
$leftExpr = $compExpr->leftExpression;
$rightExpr = $compExpr->rightExpression;
if ($leftExpr instanceof AST\Node) {
$sql .= $leftExpr->dispatch($this);
} else {
$sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
}
$sql = '';
$sql .= ($leftExpr instanceof AST\Node)
? $leftExpr->dispatch($this)
: (is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr));
$sql .= ' ' . $compExpr->operator . ' ';
if ($rightExpr instanceof AST\Node) {
$sql .= $rightExpr->dispatch($this);
} else {
$sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
}
$sql .= ($rightExpr instanceof AST\Node)
? $rightExpr->dispatch($this)
: (is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr));
return $sql;
}
@@ -1895,11 +1900,11 @@ class SqlWalker implements TreeWalker
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
return ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression))
? $this->walkArithmeticTerm($simpleArithmeticExpr)
: implode(
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
);
if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
return $this->walkArithmeticTerm($simpleArithmeticExpr);
}
return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms));
}
/**
@@ -1911,16 +1916,22 @@ class SqlWalker implements TreeWalker
public function walkArithmeticTerm($term)
{
if (is_string($term)) {
if (isset($this->_queryComponents[$term])) {
$columnName = $this->_queryComponents[$term]['token']['value'];
return $this->_scalarResultAliasMap[$columnName];
}
return $term;
}
// Phase 2 AST optimization: Skip processment of ArithmeticTerm
// if only one ArithmeticFactor is defined
return ( ! ($term instanceof AST\ArithmeticTerm))
? $this->walkArithmeticFactor($term)
: implode(
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
);
if ( ! ($term instanceof AST\ArithmeticTerm)) {
return $this->walkArithmeticFactor($term);
}
return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors));
}
/**
@@ -1937,10 +1948,13 @@ class SqlWalker implements TreeWalker
// Phase 2 AST optimization: Skip processment of ArithmeticFactor
// if only one ArithmeticPrimary is defined
return ( ! ($factor instanceof AST\ArithmeticFactor))
? $this->walkArithmeticPrimary($factor)
: ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''))
. $this->walkArithmeticPrimary($factor->arithmeticPrimary);
if ( ! ($factor instanceof AST\ArithmeticFactor)) {
return $this->walkArithmeticPrimary($factor);
}
$sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
}
/**
@@ -1953,7 +1967,9 @@ class SqlWalker implements TreeWalker
{
if ($primary instanceof AST\SimpleArithmeticExpression) {
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
} else if ($primary instanceof AST\Node) {
}
if ($primary instanceof AST\Node) {
return $primary->dispatch($this);
}

View File

@@ -52,6 +52,17 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
return $metadata;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
// validate nothing
}
/**
* @override
*/

View File

@@ -438,7 +438,7 @@ public function <methodName>()
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . '\\' . $token[1];
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
$this->_staticReflection[$lastSeenClass]['properties'] = array();
$this->_staticReflection[$lastSeenClass]['methods'] = array();
}
@@ -451,7 +451,7 @@ public function <methodName>()
} else if ($token[0] == T_FUNCTION) {
if ($tokens[$i+2][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
} else if ($tokens[$i+2][0] == T_AMPERSAND && $tokens[$i+3][0] == T_STRING) {
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
}
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
@@ -462,6 +462,14 @@ public function <methodName>()
private function _hasProperty($property, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate property if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasProperty($property)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($property, $this->_staticReflection[$metadata->name]['properties'])
@@ -470,6 +478,14 @@ public function <methodName>()
private function _hasMethod($method, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate method if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasMethod($method)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($method, $this->_staticReflection[$metadata->name]['methods'])
@@ -686,11 +702,18 @@ public function <methodName>()
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
{
$methodName = $type . Inflector::classify($fieldName);
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
$addMethod = end($addMethod);
$methodName = $type . $addMethod;
} else {
$methodName = $type . Inflector::classify($fieldName);
}
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$var = sprintf('_%sMethodTemplate', $type);
$template = self::$$var;
@@ -723,6 +746,7 @@ public function <methodName>()
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$replacements = array(
'<name>' => $this->_annotationsPrefix . $name,
@@ -759,7 +783,7 @@ public function <methodName>()
}
if (isset($joinColumn['onDelete'])) {
$joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false');
$joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
}
if (isset($joinColumn['onUpdate'])) {

View File

@@ -214,6 +214,6 @@ class SchemaValidator
$schemaTool = new SchemaTool($this->em);
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
return (count($schemaTool->getUpdateSchemaSql($allMetadata, false)) == 0);
return (count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0);
}
}

View File

@@ -97,7 +97,7 @@ class Setup
require_once $directory . "/Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine");
$loader = new ClassLoader("Doctrine", $directory);
$loader->register();
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");
@@ -115,7 +115,7 @@ class Setup
*/
static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
return $config;
}
@@ -131,7 +131,7 @@ class Setup
*/
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new XmlDriver($paths));
return $config;
}
@@ -147,7 +147,7 @@ class Setup
*/
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new YamlDriver($paths));
return $config;
}
@@ -162,6 +162,7 @@ class Setup
*/
static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$proxyDir = $proxyDir ?: sys_get_temp_dir();
if ($isDevMode === false && $cache === null) {
if (extension_loaded('apc')) {
$cache = new \Doctrine\Common\Cache\ApcCache;
@@ -175,16 +176,16 @@ class Setup
} else {
$cache = new ArrayCache;
}
$cache->setNamespace("dc2_"); // to avoid collisions
} else if ($cache === null) {
$cache = new ArrayCache;
}
$cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions
$config = new Configuration();
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
$config->setResultCacheImpl($cache);
$config->setProxyDir( $proxyDir ?: sys_get_temp_dir() );
$config->setProxyDir( $proxyDir );
$config->setProxyNamespace('DoctrineProxies');
$config->setAutoGenerateProxyClasses($isDevMode);

View File

@@ -116,7 +116,7 @@ class UnitOfWork implements PropertyChangedListener
* Map of entities that are scheduled for dirty checking at commit time.
* This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT.
* Keys are object ids (spl_object_hash).
*
*
* @var array
* @todo rename: scheduledForSynchronization
*/
@@ -135,10 +135,10 @@ class UnitOfWork implements PropertyChangedListener
* @var array
*/
private $entityUpdates = array();
/**
* Any pending extra updates that have been scheduled by persisters.
*
*
* @var array
*/
private $extraUpdates = array();
@@ -201,26 +201,31 @@ class UnitOfWork implements PropertyChangedListener
* @var array
*/
private $collectionPersisters = array();
/**
* The EventManager used for dispatching events.
*
*
* @var EventManager
*/
private $evm;
/**
* Orphaned entities that are scheduled for removal.
*
*
* @var array
*/
private $orphanRemovals = array();
//private $_readOnlyObjects = array();
/**
* Read-Only objects are never evaluated
*
* @var array
*/
private $readOnlyObjects = array();
/**
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
*
*
* @var array
*/
private $eagerLoadingEntities = array();
@@ -240,15 +245,15 @@ class UnitOfWork implements PropertyChangedListener
* Commits the UnitOfWork, executing all operations that have been postponed
* up to this point. The state of all managed entities will be synchronized with
* the database.
*
*
* The operations are executed in the following order:
*
*
* 1) All entity insertions
* 2) All entity updates
* 3) All collection deletions
* 4) All collection updates
* 5) All entity deletions
*
*
*/
public function commit()
{
@@ -269,12 +274,12 @@ class UnitOfWork implements PropertyChangedListener
$this->remove($orphan);
}
}
// Raise onFlush
if ($this->evm->hasListeners(Events::onFlush)) {
$this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em));
}
// Now we need a commit order to maintain referential integrity
$commitOrder = $this->getCommitOrder();
@@ -341,7 +346,7 @@ class UnitOfWork implements PropertyChangedListener
$this->scheduledForDirtyCheck =
$this->orphanRemovals = array();
}
/**
* Executes any extra updates that have been scheduled.
*/
@@ -401,8 +406,13 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->em->getClassMetadata(get_class($entity));
}
$oid = spl_object_hash($entity);
if (isset($this->readOnlyObjects[$oid])) {
return;
}
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity);
@@ -415,16 +425,16 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $value instanceof Collection) {
$value = new ArrayCollection($value);
}
$assoc = $class->associationMappings[$name];
// Inject PersistentCollection
$coll = new PersistentCollection(
$this->em,
$this->em->getClassMetadata($assoc['targetEntity']),
$value
);
$coll->setOwner($entity, $assoc);
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($entity, $coll);
@@ -458,7 +468,15 @@ class UnitOfWork implements PropertyChangedListener
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (isset($originalData[$propName])) {
$orgValue = $originalData[$propName];
} else if (array_key_exists($propName, $originalData)) {
$orgValue = null;
} else {
// skip field, its a partially omitted one!
continue;
}
if (isset($class->associationMappings[$propName])) {
$assoc = $class->associationMappings[$propName];
if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) {
@@ -528,7 +546,7 @@ class UnitOfWork implements PropertyChangedListener
foreach ($entitiesToProcess as $entity) {
// Ignore uninitialized proxy objects
if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
continue;
}
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
@@ -620,16 +638,16 @@ class UnitOfWork implements PropertyChangedListener
$this->scheduleForInsert($entity);
}
/**
* INTERNAL:
* Computes the changeset of an individual entity, independently of the
* computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
*
*
* The passed entity must be a managed entity. If the entity already has a change set
* because this method is invoked during a commit cycle then the change sets are added.
* whereby changes detected in this method prevail.
*
*
* @ignore
* @param ClassMetadata $class The class descriptor of the entity.
* @param object $entity The entity for which to (re)calculate the change set.
@@ -638,11 +656,11 @@ class UnitOfWork implements PropertyChangedListener
public function recomputeSingleEntityChangeSet($class, $entity)
{
$oid = spl_object_hash($entity);
if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
throw new InvalidArgumentException('Entity must be managed.');
}
/* TODO: Just return if changetracking policy is NOTIFY?
if ($class->isChangeTrackingNotify()) {
return;
@@ -688,13 +706,13 @@ class UnitOfWork implements PropertyChangedListener
{
$className = $class->name;
$persister = $this->getEntityPersister($className);
$hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]);
$hasListeners = $this->evm->hasListeners(Events::postPersist);
if ($hasLifecycleCallbacks || $hasListeners) {
$entities = array();
}
foreach ($this->entityInsertions as $oid => $entity) {
if (get_class($entity) === $className) {
$persister->addInsert($entity);
@@ -719,7 +737,7 @@ class UnitOfWork implements PropertyChangedListener
$this->addToIdentityMap($entity);
}
}
if ($hasLifecycleCallbacks || $hasListeners) {
foreach ($entities as $entity) {
if ($hasLifecycleCallbacks) {
@@ -746,15 +764,15 @@ class UnitOfWork implements PropertyChangedListener
$hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate);
$hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]);
$hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate);
foreach ($this->entityUpdates as $oid => $entity) {
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
if ($hasPreUpdateLifecycleCallbacks) {
$class->invokeLifecycleCallbacks(Events::preUpdate, $entity);
$this->recomputeSingleEntityChangeSet($class, $entity);
}
if ($hasPreUpdateListeners) {
$this->evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs(
$entity, $this->em, $this->entityChangeSets[$oid])
@@ -765,7 +783,7 @@ class UnitOfWork implements PropertyChangedListener
$persister->update($entity);
}
unset($this->entityUpdates[$oid]);
if ($hasPostUpdateLifecycleCallbacks) {
$class->invokeLifecycleCallbacks(Events::postUpdate, $entity);
}
@@ -785,10 +803,10 @@ class UnitOfWork implements PropertyChangedListener
{
$className = $class->name;
$persister = $this->getEntityPersister($className);
$hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]);
$hasListeners = $this->evm->hasListeners(Events::postRemove);
foreach ($this->entityDeletions as $oid => $entity) {
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
$persister->delete($entity);
@@ -829,14 +847,16 @@ class UnitOfWork implements PropertyChangedListener
$this->entityDeletions
);
}
$calc = $this->getCommitOrderCalculator();
// See if there are any new classes in the changeset, that are not in the
// commit order graph yet (dont have a node).
// TODO: Can we know the know the possible $newNodes based on something more efficient? IdentityMap?
$newNodes = array();
foreach ($entityChangeSet as $oid => $entity) {
$className = get_class($entity);
$className = get_class($entity);
if ( ! $calc->hasClass($className)) {
$class = $this->em->getClassMetadata($className);
$calc->addClass($class);
@@ -845,12 +865,13 @@ class UnitOfWork implements PropertyChangedListener
}
// Calculate dependencies for new nodes
foreach ($newNodes as $class) {
while ($class = array_pop($newNodes)) {
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
if ( ! $calc->hasClass($targetClass->name)) {
$calc->addClass($targetClass);
$newNodes[] = $targetClass;
}
$calc->addDependency($targetClass, $class);
// If the target class has mapped subclasses,
@@ -860,6 +881,7 @@ class UnitOfWork implements PropertyChangedListener
$targetSubClass = $this->em->getClassMetadata($subClassName);
if ( ! $calc->hasClass($subClassName)) {
$calc->addClass($targetSubClass);
$newNodes[] = $targetSubClass;
}
$calc->addDependency($targetSubClass, $class);
}
@@ -928,14 +950,14 @@ class UnitOfWork implements PropertyChangedListener
$this->entityUpdates[$oid] = $entity;
}
}
/**
* INTERNAL:
* Schedules an extra update that will be executed immediately after the
* regular entity updates within the currently running commit cycle.
*
*
* Extra updates for entities are stored as (entity, changeset) tuples.
*
*
* @ignore
* @param object $entity The entity for which to schedule an extra update.
* @param array $changeset The changeset of the entity (what to update).
@@ -973,13 +995,13 @@ class UnitOfWork implements PropertyChangedListener
/**
* INTERNAL:
* Schedules an entity for deletion.
*
*
* @param object $entity
*/
public function scheduleForDelete($entity)
{
$oid = spl_object_hash($entity);
if (isset($this->entityInsertions[$oid])) {
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
@@ -1017,7 +1039,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Checks whether an entity is scheduled for insertion, update or deletion.
*
*
* @param $entity
* @return boolean
*/
@@ -1102,6 +1124,22 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
} else if (!$class->idGenerator->isPostInsertGenerator()) {
// if we have a pre insert generator we can't be sure that having an id
// really means that the entity exists. We have to verify this through
// the last resort: a db lookup
// Last try before db lookup: check the identity map.
if ($this->tryGetById($id, $class->rootEntityName)) {
return self::STATE_DETACHED;
} else {
// db lookup
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
return self::STATE_DETACHED;
} else {
return self::STATE_NEW;
}
}
} else {
return self::STATE_DETACHED;
}
@@ -1186,7 +1224,7 @@ class UnitOfWork implements PropertyChangedListener
if ($idHash === '') {
return false;
}
return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]);
}
@@ -1217,7 +1255,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Persists an entity as part of the current unit of work.
*
*
* This method is internally called during persist() cascades as it tracks
* the already visited entities to prevent infinite recursions.
*
@@ -1295,8 +1333,8 @@ class UnitOfWork implements PropertyChangedListener
}
$visited[$oid] = $entity; // mark visited
// Cascade first, because scheduleForDelete() removes the entity from the identity map, which
// Cascade first, because scheduleForDelete() removes the entity from the identity map, which
// can cause problems when a lazy proxy has to be initialized for the cascade operation.
$this->cascadeRemove($entity, $visited);
@@ -1368,6 +1406,10 @@ class UnitOfWork implements PropertyChangedListener
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
$managedCopy = $entity;
} else {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__load();
}
// Try to look the entity up in the identity map.
$id = $class->getIdentifierValues($entity);
@@ -1406,7 +1448,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
}
}
@@ -1426,15 +1468,18 @@ class UnitOfWork implements PropertyChangedListener
// do not merge fields marked lazy that have not been fetched.
continue;
} else if ( ! $assoc2['isCascadeMerge']) {
if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) {
$prop->setValue($managedCopy, $other);
} else {
if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) {
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
$id = $targetClass->getIdentifierValues($other);
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id);
$prop->setValue($managedCopy, $proxy);
$this->registerManaged($proxy, $id, array());
$relatedId = $targetClass->getIdentifierValues($other);
if ($targetClass->subClasses) {
$other = $this->em->find($targetClass->name, $relatedId);
} else {
$other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
$this->registerManaged($other, $relatedId, array());
}
}
$prop->setValue($managedCopy, $other);
}
} else {
$mergeCol = $prop->getValue($entity);
@@ -1456,7 +1501,8 @@ class UnitOfWork implements PropertyChangedListener
}
if ($assoc2['isCascadeMerge']) {
$managedCol->initialize();
if (!$managedCol->isEmpty()) {
// clear and set dirty a managed collection if its not also the same collection to merge from.
if (!$managedCol->isEmpty() && $managedCol != $mergeCol) {
$managedCol->unwrap()->clear();
$managedCol->setDirty(true);
if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
@@ -1496,7 +1542,7 @@ class UnitOfWork implements PropertyChangedListener
return $managedCopy;
}
/**
* Detaches an entity from the persistence management. It's persistence will
* no longer be managed by Doctrine.
@@ -1508,10 +1554,10 @@ class UnitOfWork implements PropertyChangedListener
$visited = array();
$this->doDetach($entity, $visited);
}
/**
* Executes a detach operation on the given entity.
*
*
* @param object $entity
* @param array $visited
*/
@@ -1523,7 +1569,7 @@ class UnitOfWork implements PropertyChangedListener
}
$visited[$oid] = $entity; // mark visited
switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
case self::STATE_MANAGED:
if ($this->isInIdentityMap($entity)) {
@@ -1537,14 +1583,14 @@ class UnitOfWork implements PropertyChangedListener
case self::STATE_DETACHED:
return;
}
$this->cascadeDetach($entity, $visited);
}
/**
* Refreshes the state of the given entity from the database, overwriting
* any local, unpersisted changes.
*
*
* @param object $entity The entity to refresh.
* @throws InvalidArgumentException If the entity is not MANAGED.
*/
@@ -1553,10 +1599,10 @@ class UnitOfWork implements PropertyChangedListener
$visited = array();
$this->doRefresh($entity, $visited);
}
/**
* Executes a refresh operation on an entity.
*
*
* @param object $entity The entity to refresh.
* @param array $visited The already visited entities during cascades.
* @throws InvalidArgumentException If the entity is not MANAGED.
@@ -1579,10 +1625,10 @@ class UnitOfWork implements PropertyChangedListener
} else {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
$this->cascadeRefresh($entity, $visited);
}
/**
* Cascades a refresh operation to associated entities.
*
@@ -1610,7 +1656,7 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
/**
* Cascades a detach operation to associated entities.
*
@@ -1655,6 +1701,10 @@ class UnitOfWork implements PropertyChangedListener
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
continue;
}
if ($relatedEntities instanceof PersistentCollection) {
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
@@ -1682,7 +1732,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $assoc['isCascadePersist']) {
continue;
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
if ($relatedEntities instanceof PersistentCollection) {
@@ -1711,11 +1761,11 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $assoc['isCascadeRemove']) {
continue;
}
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
$entity->__load();
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
// If its a PersistentCollection initialization is intended! No unwrap!
@@ -1737,10 +1787,10 @@ class UnitOfWork implements PropertyChangedListener
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
if ($this->getEntityState($entity) != self::STATE_MANAGED) {
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
$entityName = get_class($entity);
$class = $this->em->getClassMetadata($entityName);
@@ -1760,7 +1810,7 @@ class UnitOfWork implements PropertyChangedListener
if (!$this->em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
$oid = spl_object_hash($entity);
$this->getEntityPersister($class->name)->lock(
@@ -1809,13 +1859,13 @@ class UnitOfWork implements PropertyChangedListener
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em));
}
}
/**
* INTERNAL:
* Schedules an orphaned entity for removal. The remove() operation will be
* invoked on that entity at the beginning of the next commit of this
* UnitOfWork.
*
*
* @ignore
* @param object $entity
*/
@@ -1823,7 +1873,7 @@ class UnitOfWork implements PropertyChangedListener
{
$this->orphanRemovals[spl_object_hash($entity)] = $entity;
}
/**
* INTERNAL:
* Schedules a complete collection for removal when this UnitOfWork commits.
@@ -1852,7 +1902,7 @@ class UnitOfWork implements PropertyChangedListener
* @param array $hints Any hints to account for during reconstitution/lookup of the entity.
* @return object The managed entity instance.
* @internal Highly performance-sensitive method.
*
*
* @todo Rename: getOrCreateEntity
*/
public function createEntity($className, array $data, &$hints = array())
@@ -1878,8 +1928,8 @@ class UnitOfWork implements PropertyChangedListener
}
$id = array($class->identifier[0] => $idHash);
}
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
$entity = $this->identityMap[$class->rootEntityName][$idHash];
$oid = spl_object_hash($entity);
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
@@ -1890,6 +1940,11 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
$overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
// If only a specific entity is set to refresh, check that it's the one
if(isset($hints[Query::HINT_REFRESH_ENTITY])) {
$overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
}
}
if ($overrideLocalValues) {
@@ -1917,12 +1972,12 @@ class UnitOfWork implements PropertyChangedListener
// Loading the entity right here, if its in the eager loading map get rid of it there.
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
// Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
foreach ($class->associationMappings as $field => $assoc) {
// Check if the association is not among the fetch-joined associations already.
if (isset($hints['fetched'][$className][$field])) {
if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
continue;
}
@@ -1958,7 +2013,7 @@ class UnitOfWork implements PropertyChangedListener
$relatedIdHash = implode(' ', $associatedId);
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
// if this is an uninitialized proxy, we are deferring eager loads,
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
// then we cann append this entity for eager loading!
@@ -1967,7 +2022,7 @@ class UnitOfWork implements PropertyChangedListener
!$targetClass->isIdentifierComposite &&
$newValue instanceof Proxy &&
$newValue->__isInitialized__ === false) {
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
}
} else {
@@ -1976,7 +2031,7 @@ class UnitOfWork implements PropertyChangedListener
// a way to solve this with deferred eager loading, which means putting
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
$newValue = $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
->loadOneToOneEntity($assoc, $entity, $associatedId);
} else {
// Deferred eager load only works for single identifier classes
@@ -2003,7 +2058,7 @@ class UnitOfWork implements PropertyChangedListener
}
$this->originalEntityData[$oid][$field] = $newValue;
$class->reflFields[$field]->setValue($entity, $newValue);
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
@@ -2012,7 +2067,7 @@ class UnitOfWork implements PropertyChangedListener
} else {
// Inverse side of x-to-one can never be lazy
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null));
->loadOneToOneEntity($assoc, $entity));
}
} else {
// Inject collection
@@ -2033,7 +2088,7 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
@@ -2110,7 +2165,7 @@ class UnitOfWork implements PropertyChangedListener
}
return array();
}
/**
* @ignore
*/
@@ -2304,27 +2359,27 @@ class UnitOfWork implements PropertyChangedListener
/**
* Gets the currently scheduled entity insertions in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityInsertions()
{
return $this->entityInsertions;
}
/**
* Gets the currently scheduled entity updates in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityUpdates()
{
return $this->entityUpdates;
}
/**
* Gets the currently scheduled entity deletions in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityDeletions()
@@ -2351,10 +2406,10 @@ class UnitOfWork implements PropertyChangedListener
{
return $this->collectionUpdates;
}
/**
* Helper method to initialize a lazy loading proxy or persistent collection.
*
*
* @param object
* @return void
*/
@@ -2366,15 +2421,48 @@ class UnitOfWork implements PropertyChangedListener
$obj->initialize();
}
}
/**
* Helper method to show an object as string.
*
*
* @param object $obj
* @return string
* @return string
*/
private static function objToStr($obj)
{
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
}
/**
* Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
*
* This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
* on this object that might be necessary to perform a correct udpate.
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function markReadOnly($object)
{
if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
throw new InvalidArgumentException("Managed entity required");
}
$this->readOnlyObjects[spl_object_hash($object)] = true;
}
/**
* Is this entity read only?
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function isReadOnly($object)
{
if ( ! is_object($object) ) {
throw new InvalidArgumentException("Managed entity required");
}
return isset($this->readOnlyObjects[spl_object_hash($object)]);
}
}

View File

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

View File

@@ -31,7 +31,7 @@ class CmsUser
*/
public $name;
/**
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true)
*/
public $phonenumbers;
/**

View File

@@ -7,7 +7,11 @@ namespace Doctrine\Tests\Models\Company;
* @Table(name="company_contracts")
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"fix" = "CompanyFixContract", "flexible" = "CompanyFlexContract", "flexultra" = "CompanyFlexUltraContract"})
* @DiscriminatorMap({
* "fix" = "CompanyFixContract",
* "flexible" = "CompanyFlexContract",
* "flexultra" = "CompanyFlexUltraContract"
* })
*/
abstract class CompanyContract
{

View File

@@ -17,6 +17,11 @@ class CompanyEmployee extends CompanyPerson
* @Column(type="string", length=255)
*/
private $department;
/**
* @Column(type="datetime", nullable=true)
*/
private $startDate;
public function getSalary() {
return $this->salary;
@@ -33,4 +38,12 @@ class CompanyEmployee extends CompanyPerson
public function setDepartment($dep) {
$this->department = $dep;
}
public function getStartDate() {
return $this->startDate;
}
public function setStartDate($date) {
$this->startDate = $date;
}
}

View File

@@ -9,6 +9,7 @@ class DDC117Article
{
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
private $id;
/** @Column */
private $title;
@@ -27,6 +28,11 @@ class DDC117Article
*/
private $translations;
/**
* @OneToMany(targetEntity="DDC117Link", mappedBy="source")
*/
private $links;
public function __construct($title)
{
$this->title = $title;

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* Foreign Key Entity without additional fields!
*
* @Entity
*/
class DDC117Link
{
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article", inversedBy="links")
* @JoinColumn(name="source_id", referencedColumnName="article_id")
*/
public $source;
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article")
* @JoinColumn(name="target_id", referencedColumnName="article_id")
*/
public $target;
public function __construct($source, $target, $description)
{
$this->source = $source;
$this->target = $target;
}
}

View File

@@ -34,7 +34,7 @@ class ECommerceCustomer
* only one customer at the time, while a customer can choose only one
* mentor. Not properly appropriate but it works.
*
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"}, fetch="EAGER")
* @JoinColumn(name="mentor_id", referencedColumnName="id")
*/
private $mentor;

View File

@@ -56,6 +56,7 @@ class ECommerceProduct
private $related;
public $isCloned = false;
public $wakeUp = false;
public function __construct()
{
@@ -166,4 +167,12 @@ class ECommerceProduct
{
$this->isCloned = true;
}
/**
* Testing docblock contents here
*/
public function __wakeup()
{
$this->wakeUp = true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
/**
* @Entity
* @Table(name="legacy_articles")
*/
class LegacyArticle
{
/**
* @Id
* @Column(name="iArticleId", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $_id;
/**
* @Column(name="sTopic", type="string", length=255)
*/
public $_topic;
/**
* @Column(name="sText", type="text")
*/
public $_text;
/**
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_articles")
* @JoinColumn(name="iUserId", referencedColumnName="iUserId")
*/
public $_user;
public function setAuthor(LegacyUser $author) {
$this->_user = $author;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="legacy_cars")
*/
class LegacyCar
{
/**
* @Id
* @GeneratedValue
* @Column(name="iCarId", type="integer", nullable=false)
*/
public $_id;
/**
* @ManyToMany(targetEntity="LegacyUser", mappedBy="_cars")
*/
public $_users;
/**
* @Column(name="sDescription", type="string", length=255, unique=true)
*/
public $_description;
function getDescription()
{
return $this->_description;
}
public function addUser(LegacyUser $user) {
$this->_users[] = $user;
}
public function getUsers() {
return $this->_users;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="legacy_users")
*/
class LegacyUser
{
/**
* @Id
* @GeneratedValue
* @Column(name="iUserId", type="integer", nullable=false)
*/
public $_id;
/**
* @Column(name="sUsername", type="string", length=255, unique=true)
*/
public $_username;
/**
* @Column(type="string", length=255)
*/
public $_name;
/**
* @OneToMany(targetEntity="LegacyArticle", mappedBy="_user")
*/
public $_articles;
/**
* @OneToMany(targetEntity="LegacyUserReference", mappedBy="_source", cascade={"remove"})
*/
public $_references;
/**
* @ManyToMany(targetEntity="LegacyCar", inversedBy="_users", cascade={"persist", "merge"})
* @JoinTable(name="legacy_users_cars",
* joinColumns={@JoinColumn(name="iUserId", referencedColumnName="iUserId")},
* inverseJoinColumns={@JoinColumn(name="iCarId", referencedColumnName="iCarId")}
* )
*/
public $_cars;
public function __construct() {
$this->_articles = new ArrayCollection;
$this->_references = new ArrayCollection;
$this->_cars = new ArrayCollection;
}
public function getId() {
return $this->_id;
}
public function getUsername() {
return $this->_username;
}
public function addArticle(LegacyArticle $article) {
$this->_articles[] = $article;
$article->setAuthor($this);
}
public function addReference($reference)
{
$this->_references[] = $reference;
}
public function references()
{
return $this->_references;
}
public function addCar(LegacyCar $car) {
$this->_cars[] = $car;
$car->addUser($this);
}
public function getCars() {
return $this->_cars;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Doctrine\Tests\Models\Legacy;
/**
* @Entity
* @Table(name="legacy_users_reference")
*/
class LegacyUserReference
{
/**
* @Id
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_references")
* @JoinColumn(name="iUserIdSource", referencedColumnName="iUserId")
*/
private $_source;
/**
* @Id
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_references")
* @JoinColumn(name="iUserIdTarget", referencedColumnName="iUserId")
*/
private $_target;
/**
* @column(type="string")
*/
private $_description;
/**
* @column(type="datetime")
*/
private $_created;
public function __construct($source, $target, $description)
{
$source->addReference($this);
$target->addReference($this);
$this->_source = $source;
$this->_target = $target;
$this->_description = $description;
$this->_created = new \DateTime("now");
}
public function source()
{
return $this->_source;
}
public function target()
{
return $this->_target;
}
public function setDescription($desc)
{
$this->_description = $desc;
}
public function getDescription()
{
return $this->_description;
}
}

View File

@@ -150,15 +150,15 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->username = 'gblanco';
$user->status = 'developer';
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
$this->_em->persist($user);
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_MANAGED");
$this->_em->remove($user);
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
$this->_em->persist($user);
$this->_em->flush();
@@ -166,10 +166,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->remove($user);
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_REMOVED");
$this->_em->flush();
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
$this->assertNull($this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $id));
}
@@ -863,7 +863,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt()
{
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "Benjamin E.";
@@ -882,7 +881,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->_em->clear();
$this->assertEquals('Stephan', $this->_em->find(get_class($user), $userId)->name);
$this->assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name);
}
public function testMergePersistsNewEntities()

View File

@@ -291,8 +291,21 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue(count($this->_em->createQuery(
'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1')
->getResult()) > 0);
->getResult()) > 0);
}
/**
* @group DDC-1341
*/
public function testBulkUpdateNonScalarParameterDDC1341()
{
$dql = 'UPDATE Doctrine\Tests\Models\Company\CompanyEmployee AS p SET p.startDate = ?0 WHERE p.department = ?1';
$query = $this->_em->createQuery($dql)
->setParameter(0, new \DateTime())
->setParameter(1, 'IT');
$result = $query->execute();
}
/**

View File

@@ -45,7 +45,7 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$this->assertType('Doctrine\Tests\Models\Navigation\NavPointOfInterest', $poi);
$this->assertInstanceOf('Doctrine\Tests\Models\Navigation\NavPointOfInterest', $poi);
$this->assertEquals(100, $poi->getLat());
$this->assertEquals(200, $poi->getLong());
$this->assertEquals('Brandenburger Tor', $poi->getName());

View File

@@ -54,7 +54,30 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($userId, $a2->getUser()->getId());
$this->assertEquals('Poweruser', $a2->getUser()->type);
}
/**
* @group DDC-1386
*/
public function testGetPartialReferenceWithDefaultValueNotEvalutedInFlush()
{
$user = new DefaultValueUser;
$user->name = 'romanb';
$user->type = 'Normaluser';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->getPartialReference('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
$this->assertTrue($this->_em->getUnitOfWork()->isReadOnly($user));
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->find('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
$this->assertEquals('Normaluser', $user->type);
}
}

View File

@@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../TestInit.php';
@@ -192,5 +193,26 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($this->_em->contains($user));
$this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user));
}
/**
* @group DDC-1340
*/
public function testMergeArticleWrongVersion()
{
$article = new CmsArticle();
$article->topic = "test";
$article->text = "test";
$this->_em->persist($article);
$this->_em->flush();
$this->_em->detach($article);
$sql = "UPDATE cms_articles SET version = version+1 WHERE id = " . $article->id;
$this->_em->getConnection()->executeUpdate($sql);
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock failed, version 1 was expected, but is actually 2');
$this->_em->merge($article);
}
}

View File

@@ -244,7 +244,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$address = $repos->findOneBy(array('user' => $userId));
$this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertEquals($addressId, $address->id);
}
@@ -285,7 +285,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$address = $repos->findOneByUser($userId);
$this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertEquals($addressId, $address->id);
}
@@ -295,7 +295,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $repos->createNamedQuery('all');
$this->assertType('Doctrine\ORM\Query', $query);
$this->assertInstanceOf('Doctrine\ORM\Query', $query);
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', $query->getDQL());
}
@@ -355,5 +355,16 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSame($usersAsc[0], $usersDesc[1]);
$this->assertSame($usersAsc[1], $usersDesc[0]);
}
/**
* @group DDC-1500
*/
public function testInvalidOrientation()
{
$this->setExpectedException('Doctrine\ORM\ORMException', 'Invalid order by orientation specified for Doctrine\Tests\Models\CMS\CmsUser#username');
$repo = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$repo->findBy(array('status' => 'test'), array('username' => 'INVALID'));
}
}

View File

@@ -29,7 +29,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$this->loadFixture();
}
@@ -137,9 +137,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
$queryCount = $this->getCurrentQueryCount();
$someGroups = $user->groups->slice(0, 2);
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsGroup', $someGroups);
@@ -225,7 +225,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
$queryCount = $this->getCurrentQueryCount();
$this->assertTrue($user->articles->contains($article));
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
@@ -304,6 +304,49 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
}
/**
* @group DDC-1399
*/
public function testCountAfterAddThenFlush()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$this->assertFalse($user->groups->isInitialized());
$this->assertEquals(4, count($user->groups));
$this->assertFalse($user->groups->isInitialized());
$this->_em->flush();
$this->assertEquals(4, count($user->groups));
}
/**
* @group DDC-1462
*/
public function testSliceOnDirtyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$qc = $this->getCurrentQueryCount();
$groups = $user->groups->slice(0, 10);
$this->assertEquals(4, count($groups));
$this->assertEquals($qc + 1, $this->getCurrentQueryCount());
}
private function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();
@@ -364,7 +407,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($article1);
$this->_em->persist($article2);
$this->_em->flush();
$this->_em->clear();

View File

@@ -38,9 +38,10 @@ class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase
$cleanFile = $this->_em->find(get_class($file), $file->getId());
$this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent());
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $cleanFile->getParent());
$this->assertEquals($directory->getId(), $cleanFile->getParent()->getId());
$this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent());
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent());
$this->assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId());
}
}

View File

@@ -52,6 +52,48 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Roman', $users[0]->name);
}
public function testBasicNativeQueryWithMetaResult()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$addr = new CmsAddress;
$addr->country = 'germany';
$addr->zip = 10827;
$addr->city = 'Berlin';
$user->setAddress($addr);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsAddress', 'a');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('id'), 'id');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('country'), 'country');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('zip'), 'zip');
$rsm->addFieldResult('a', $this->platform->getSQLResultCasing('city'), 'city');
$rsm->addMetaResult('a', $this->platform->getSQLResultCasing('user_id'), 'user_id');
$query = $this->_em->createNativeQuery('SELECT a.id, a.country, a.zip, a.city, a.user_id FROM cms_addresses a WHERE a.id = ?', $rsm);
$query->setParameter(1, $addr->id);
$addresses = $query->getResult();
$this->assertEquals(1, count($addresses));
$this->assertTrue($addresses[0] instanceof CmsAddress);
$this->assertEquals($addr->country, $addresses[0]->country);
$this->assertEquals($addr->zip, $addresses[0]->zip);
$this->assertEquals($addr->city, $addresses[0]->city);
$this->assertEquals($addr->street, $addresses[0]->street);
$this->assertTrue($addresses[0]->user instanceof CmsUser);
}
public function testJoinedOneToManyNativeQuery()
{
@@ -193,6 +235,17 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$phones = $users[0]->getPhonenumbers();
$this->assertEquals(424242, $phones[0]->phonenumber);
$this->assertTrue($phones[0]->getUser() === $users[0]);
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p');
$query = $this->_em->createNativeQuery('SELECT p.* FROM cms_phonenumbers p WHERE p.phonenumber = ?', $rsm);
$query->setParameter(1, $phone->phonenumber);
$phone = $query->getSingleResult();
$this->assertNotNull($phone->getUser());
$this->assertEquals($user->name, $phone->getUser()->getName());
}
public function testJoinedOneToOneNativeQueryWithRSMBuilder()
@@ -235,6 +288,17 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('germany', $users[0]->getAddress()->getCountry());
$this->assertEquals(10827, $users[0]->getAddress()->getZipCode());
$this->assertEquals('Berlin', $users[0]->getAddress()->getCity());
$this->_em->clear();
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a');
$query = $this->_em->createNativeQuery('SELECT a.* FROM cms_addresses a WHERE a.id = ?', $rsm);
$query->setParameter(1, $addr->getId());
$address = $query->getSingleResult();
$this->assertNotNull($address->getUser());
$this->assertEquals($user->name, $address->getUser()->getName());
}
/**

View File

@@ -0,0 +1,75 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser,
Doctrine\Tests\Models\CMS\CmsAddress,
Doctrine\Tests\Models\CMS\CmsPhonenumber;
require_once __DIR__ . '/../../TestInit.php';
/**
* Tests a bidirectional one-to-many association mapping with orphan removal.
*/
class OneToManyOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected $userId;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user = new CmsUser;
$user->status = 'dev';
$user->username = 'romanb';
$user->name = 'Roman B.';
$phone = new CmsPhonenumber;
$phone->phonenumber = '123456';
$user->addPhonenumber($phone);
$this->_em->persist($user);
$this->_em->flush();
$this->userId = $user->getId();
$this->_em->clear();
}
public function testOrphanRemoval()
{
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->_em->remove($userProxy);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u');
$result = $query->getResult();
$this->assertEquals(0, count($result), 'CmsUser should be removed by EntityManager');
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p');
$result = $query->getResult();
$this->assertEquals(0, count($result), 'CmsPhonenumber should be removed by orphanRemoval');
}
/**
* @group DDC-1496
*/
public function testOrphanRemovalUnitializedCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$user->phonenumbers->clear();
$this->_em->flush();
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p');
$result = $query->getResult();
$this->assertEquals(0, count($result), 'CmsPhonenumber should be removed by orphanRemoval');
}
}

View File

@@ -73,7 +73,14 @@ class OneToManyUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunction
$this->_em->persist($routeA);
$this->_em->persist($routeB);
$this->setExpectedException('Exception'); // depends on the underyling Database Driver
$this->_em->flush(); // Exception
$exceptionThrown = false;
try {
// exception depending on the underyling Database Driver
$this->_em->flush();
} catch(\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser,
Doctrine\Tests\Models\CMS\CmsAddress,
Doctrine\Tests\Models\CMS\CmsPhonenumber;
require_once __DIR__ . '/../../TestInit.php';
/**
* Tests a bidirectional one-to-one association mapping with orphan removal.
*/
class OneToOneOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testOrphanRemoval()
{
$user = new CmsUser;
$user->status = 'dev';
$user->username = 'romanb';
$user->name = 'Roman B.';
$address = new CmsAddress;
$address->country = 'de';
$address->zip = 1234;
$address->city = 'Berlin';
$user->setAddress($address);
$this->_em->persist($user);
$this->_em->flush();
$userId = $user->getId();
$this->_em->clear();
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $userId);
$this->_em->remove($userProxy);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u');
$result = $query->getResult();
$this->assertEquals(0, count($result), 'CmsUser should be removed by EntityManager');
$query = $this->_em->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a');
$result = $query->getResult();
$this->assertEquals(0, count($result), 'CmsAddress should be removed by orphanRemoval');
}
}

View File

@@ -49,6 +49,14 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->assertForeignKeyIs(null);
}
public function testFind()
{
$id = $this->_createFixture();
$customer = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $id);
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $customer->getMentor());
}
public function testEagerLoadsAssociation()
{
$this->_createFixture();
@@ -127,6 +135,8 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
$this->_em->flush();
$this->_em->clear();
return $customer->getId();
}
}

View File

@@ -15,6 +15,10 @@ require_once __DIR__ . '/../../TestInit.php';
class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->markTestSkipped('Test not compatible with 2.2 common');
}
$this->useModelSet('cms');
parent::setUp();
}
@@ -86,7 +90,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->getConfiguration()->setQueryCacheImpl(new ArrayCache());
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$cache = $this->getMock('Doctrine\Common\Cache\AbstractCache', array('_doFetch', '_doContains', '_doSave', '_doDelete', 'getIds'));
$cache->expects($this->at(0))
->method('_doFetch')

View File

@@ -34,9 +34,9 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
$query = $this->_em->createQuery("select u, upper(u.name) from Doctrine\Tests\Models\CMS\CmsUser u where u.username = 'gblanco'");
$result = $query->getResult();
$this->assertEquals(1, count($result));
$this->assertTrue($result[0][0] instanceof CmsUser);
$this->assertEquals('Guilherme', $result[0][0]->name);
@@ -109,7 +109,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$q = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = ?0');
$q->setParameter(0, 'jwage');
$user = $q->getSingleResult();
$this->assertNotNull($user);
}
@@ -216,7 +216,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$identityMap = $this->_em->getUnitOfWork()->getIdentityMap();
$identityMapCount = count($identityMap['Doctrine\Tests\Models\CMS\CmsArticle']);
$this->assertTrue($identityMapCount>$iteratedCount);
$iteratedCount++;
}
@@ -235,7 +235,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles a");
$articles = $query->iterate();
}
/**
* @expectedException Doctrine\ORM\NoResultException
*/
@@ -366,7 +366,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($result[0]->user instanceof \Doctrine\ORM\Proxy\Proxy);
$this->assertFalse($result[0]->user->__isInitialized__);
}
/**
* @group DDC-952
*/
@@ -386,11 +386,11 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
}
$this->_em->flush();
$this->_em->clear();
$articles = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a')
->setFetchMode('Doctrine\Tests\Models\CMS\CmsArticle', 'user', ClassMetadata::FETCH_EAGER)
->getResult();
$this->assertEquals(10, count($articles));
foreach ($articles AS $article) {
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $article);
@@ -456,7 +456,43 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery("select u.username from Doctrine\Tests\Models\CMS\CmsUser u where u.username = 'gblanco'");
$this->assertNull($query->getOneOrNullResult(Query::HYDRATE_SCALAR));
}
/**
* @group DBAL-171
*/
public function testParameterOrder()
{
$user1 = new CmsUser;
$user1->name = 'Benjamin';
$user1->username = 'beberlei';
$user1->status = 'developer';
$this->_em->persist($user1);
$user2 = new CmsUser;
$user2->name = 'Roman';
$user2->username = 'romanb';
$user2->status = 'developer';
$this->_em->persist($user2);
$user3 = new CmsUser;
$user3->name = 'Jonathan';
$user3->username = 'jwage';
$user3->status = 'developer';
$this->_em->persist($user3);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :a AND u.id IN (:b)");
$query->setParameters(array(
'b' => array($user1->id, $user2->id, $user3->id),
'a' => 'developer',
));
$result = $query->getResult();
$this->assertEquals(3, count($result));
}
public function testDqlWithAutoInferOfParameters()
{
$user = new CmsUser;
@@ -464,30 +500,30 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->username = 'beberlei';
$user->status = 'developer';
$this->_em->persist($user);
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'developer';
$this->_em->persist($user);
$user = new CmsUser;
$user->name = 'Jonathan';
$user->username = 'jwage';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username IN (?0)");
$query->setParameter(0, array('beberlei', 'jwage'));
$users = $query->execute();
$this->assertEquals(2, count($users));
}
public function testQueryBuilderWithStringWhereClauseContainingOrAndConditionalPrimary()
{
$qb = $this->_em->createQueryBuilder();
@@ -495,10 +531,42 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->innerJoin('u.articles', 'a')
->where('(u.id = 0) OR (u.id IS NULL)');
$query = $qb->getQuery();
$users = $query->execute();
$this->assertEquals(0, count($users));
}
}
public function testQueryWithArrayOfEntitiesAsParameter()
{
$userA = new CmsUser;
$userA->name = 'Benjamin';
$userA->username = 'beberlei';
$userA->status = 'developer';
$this->_em->persist($userA);
$userB = new CmsUser;
$userB->name = 'Roman';
$userB->username = 'romanb';
$userB->status = 'developer';
$this->_em->persist($userB);
$userC = new CmsUser;
$userC->name = 'Jonathan';
$userC->username = 'jwage';
$userC->status = 'developer';
$this->_em->persist($userC);
$this->_em->flush();
$this->_em->clear();
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u IN (?0) OR u.username = ?1");
$query->setParameter(0, array($userA, $userC));
$query->setParameter(1, 'beberlei');
$users = $query->execute();
$this->assertEquals(2, count($users));
}
}

View File

@@ -130,4 +130,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertEquals('Doctrine 2 Cookbook', $entity->getName());
}
/**
* @group DDC-1022
*/
public function testWakeupCalledOnProxy()
{
$id = $this->createProduct();
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$this->assertFalse($entity->wakeUp);
$entity->setName('Doctrine 2 Cookbook');
$this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup().");
}
}

View File

@@ -16,6 +16,10 @@ require_once __DIR__ . '/../../TestInit.php';
class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->markTestSkipped('Test not compatible with 2.2 common');
}
$this->useModelSet('cms');
parent::setUp();
}
@@ -33,7 +37,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$cache = new ArrayCache();
$query->setResultCacheDriver($cache)->setResultCacheId('my_cache_id');
$this->assertFalse($cache->contains('my_cache_id'));
@@ -65,7 +69,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setResultCacheId('testing_result_cache_id');
$this->assertFalse($cache->contains('testing_result_cache_id'));
$users = $query->getResult();
$this->assertTrue($cache->contains('testing_result_cache_id'));
@@ -124,7 +128,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$cache = new ArrayCache();
$query->setResultCacheDriver($cache)->useResultCache(true);
$this->assertEquals(0, count($cache->getIds()));
$query->getResult();
$this->assertEquals(1, count($cache->getIds()));
@@ -218,7 +222,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query2 = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.user = ?1');
$query2->setParameter(1, $user1);
$query2->setResultCacheDriver($cache)->useResultCache(true);
$articles = $query2->getResult();

View File

@@ -30,10 +30,10 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]);
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]);
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups(id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id)", $sql[7]);
$this->assertEquals(8, count($sql));
}

View File

@@ -21,7 +21,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$address = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$this->assertEquals(1, $address->sequenceGeneratorDefinition['allocationSize']);
}
public function testGetCreateSchemaSql()
{
$classes = array(
@@ -32,7 +32,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", $sql[0]);
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_ACAC157BA76ED395 ON cms_addresses (user_id)", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[2]);
@@ -44,14 +44,14 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", $sql[8]);
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[9]);
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[10]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
$this->assertEquals(count($sql), 15);
}
public function testGetCreateSchemaSql2()
{
$classes = array(
@@ -62,11 +62,11 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals(2, count($sql));
$this->assertEquals('CREATE TABLE decimal_model (id INT NOT NULL, "decimal" NUMERIC(5, 2) NOT NULL, "high_scale" NUMERIC(14, 4) NOT NULL, PRIMARY KEY(id))', $sql[0]);
$this->assertEquals("CREATE SEQUENCE decimal_model_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[1]);
}
public function testGetCreateSchemaSql3()
{
$classes = array(
@@ -75,12 +75,12 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals(2, count($sql));
$this->assertEquals("CREATE TABLE boolean_model (id INT NOT NULL, booleanField BOOLEAN NOT NULL, PRIMARY KEY(id))", $sql[0]);
$this->assertEquals("CREATE SEQUENCE boolean_model_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[1]);
}
public function testGetDropSchemaSql()
{
$classes = array(
@@ -90,8 +90,13 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
);
$tool = new SchemaTool($this->_em);
try {
$tool->createSchema($classes);
} catch(\Exception $e) {
}
$sql = $tool->getDropSchemaSQL($classes);
$this->assertEquals(13, count($sql));
$dropSequenceSQLs = 0;
foreach ($sql AS $stmt) {

View File

@@ -0,0 +1,99 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1113
* @group DDC-1306
*/
class DDC1113Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Engine'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Vehicle'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Car'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Bus'),
));
} catch (\Exception $e) {
}
}
public function testIssue()
{
$car = new DDC1113Car();
$car->engine = new DDC1113Engine();
$bus = new DDC1113Bus();
$bus->engine = new DDC1113Engine();
$this->_em->persist($car);
$this->_em->flush();
$this->_em->persist($bus);
$this->_em->flush();
$this->_em->remove($bus);
$this->_em->remove($car);
$this->_em->flush();
}
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({"car" = "DDC1113Car", "bus" = "DDC1113Bus"})
*/
class DDC1113Vehicle
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/**
* @ManyToOne(targetEntity="DDC1113Vehicle")
*/
public $parent;
/** @OneToOne(targetEntity="DDC1113Engine", cascade={"persist", "remove"}) */
public $engine;
}
/**
* @Entity
*/
class DDC1113Car extends DDC1113Vehicle
{
}
/**
* @Entity
*/
class DDC1113Bus extends DDC1113Vehicle
{
}
/**
* @Entity
*/
class DDC1113Engine
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
}

View File

@@ -8,18 +8,18 @@ require_once __DIR__ . '/../../../TestInit.php';
* @group DDC-1151
*/
class DDC1151Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
{
public function testQuoteForeignKey()
{
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != 'postgresql') {
$this->markTestSkipped("This test is useful for all databases, but designed only for postgresql.");
}
$sql = $this->_schemaTool->getCreateSchemaSql(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1151User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1151Group'),
));
$this->assertEquals("CREATE TABLE \"User\" (id INT NOT NULL, PRIMARY KEY(id))", $sql[0]);
$this->assertEquals("CREATE TABLE ddc1151user_ddc1151group (ddc1151user_id INT NOT NULL, ddc1151group_id INT NOT NULL, PRIMARY KEY(ddc1151user_id, ddc1151group_id))", $sql[1]);
$this->assertEquals("CREATE INDEX IDX_88A3259AC5AD08A ON ddc1151user_ddc1151group (ddc1151user_id)", $sql[2]);
@@ -27,8 +27,8 @@ class DDC1151Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE TABLE \"Group\" (id INT NOT NULL, PRIMARY KEY(id))", $sql[4]);
$this->assertEquals("CREATE SEQUENCE User_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[5]);
$this->assertEquals("CREATE SEQUENCE Group_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[6]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151user_id) REFERENCES \"User\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A3259AC5AD08A FOREIGN KEY (ddc1151user_id) REFERENCES \"User\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A32597357E0B1 FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
}
}
@@ -40,7 +40,7 @@ class DDC1151User
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @ManyToMany(targetEntity="DDC1151Group") */
public $groups;
}

View File

@@ -209,8 +209,15 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->article1->addTranslation('en', 'Bar');
$this->article1->addTranslation('en', 'Baz');
$this->setExpectedException('Exception');
$this->_em->flush();
$exceptionThrown = false;
try {
// exception depending on the underyling Database Driver
$this->_em->flush();
} catch(\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
}
/**

View File

@@ -0,0 +1,85 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsEmployee;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1225
*/
class DDC1225Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1225_TestEntity1'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1225_TestEntity2'),
));
} catch(\PDOException $e) {
}
}
public function testIssue()
{
$qb = $this->_em->createQueryBuilder();
$qb->from('Doctrine\Tests\ORM\Functional\Ticket\DDC1225_TestEntity1', 'te1')
->select('te1')
->where('te1.testEntity2 = ?1')
->setParameter(1, 0);
$this->assertEquals(
'SELECT t0_.test_entity2_id AS test_entity2_id0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?',
$qb->getQuery()->getSQL()
);
}
}
/**
* @Entity
* @Table(name="te1")
*/
class DDC1225_TestEntity1
{
/**
* @Id
* @ManyToOne(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\DDC1225_TestEntity2")
* @JoinColumn(name="test_entity2_id", referencedColumnName="id", nullable=false)
*/
private $testEntity2;
/**
* @param DDC1225_TestEntity2 $testEntity2
*/
public function setTestEntity2(DDC1225_TestEntity2 $testEntity2)
{
$this->testEntity2 = $testEntity2;
}
/**
* @return DDC1225_TestEntity2
*/
public function getTestEntity2()
{
return $this->testEntity2;
}
}
/**
* @Entity
* @Table(name="te2")
*/
class DDC1225_TestEntity2
{
/**
* @Id
* @GeneratedValue(strategy="AUTO")
* @Column(type="integer")
*/
private $id;
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsEmployee;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1238
*/
class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1238User'),
));
} catch(\PDOException $e) {
}
}
public function testIssue()
{
$user = new DDC1238User;
$user->setName("test");
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$userId = $user->getId();
$this->_em->clear();
$user = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->_em->clear();
$userId2 = $user->getId();
$this->assertEquals($userId, $userId2, "This proxy can still be initialized.");
}
public function testIssueProxyClear()
{
$user = new DDC1238User;
$user->setName("test");
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$userId = $user->getId();
$this->_em->clear();
$user = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->_em->clear();
$user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
$this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type");
}
}
/**
* @Entity
*/
class DDC1238User
{
/** @Id @GeneratedValue @Column(type="integer") */
private $id;
/**
* @Column
* @var string
*/
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsEmployee;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1250
*/
class DDC1250Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1250ClientHistory'),
));
} catch(\PDOException $e) {
}
}
public function testIssue()
{
$c1 = new DDC1250ClientHistory;
$c2 = new DDC1250ClientHistory;
$c1->declinedClientsHistory = $c2;
$c1->declinedBy = $c2;
$c2->declinedBy = $c1;
$c2->declinedClientsHistory= $c1;
$this->_em->persist($c1);
$this->_em->persist($c2);
$this->_em->flush();
$this->_em->clear();
$history = $this->_em->createQuery('SELECT h FROM ' . __NAMESPACE__ . '\\DDC1250ClientHistory h WHERE h.id = ?1')
->setParameter(1, $c2->id)->getSingleResult();
$this->assertInstanceOf(__NAMESPACE__ . '\\DDC1250ClientHistory', $history);
}
}
/**
* @Entity
*/
class DDC1250ClientHistory
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/** @OneToOne(targetEntity="DDC1250ClientHistory", inversedBy="declinedBy")
* @JoinColumn(name="declined_clients_history_id", referencedColumnName="id")
*/
public $declinedClientsHistory;
/**
* @OneToOne(targetEntity="DDC1250ClientHistory", mappedBy="declinedClientsHistory")
* @var
*/
public $declinedBy;
}
/**
*
Entities\ClientsHistory:
type: entity
table: clients_history
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
[...skiped...]
oneToOne:
declinedClientsHistory:
targetEntity: Entities\ClientsHistory
joinColumn:
name: declined_clients_history_id
referencedColumnName: id
inversedBy: declinedBy
declinedBy:
targetEntity: Entities\ClientsHistory
mappedBy: declinedClientsHistory
lifecycleCallbacks: { }
repositoryClass: Entities\ClientsHistoryRepository
*/

View File

@@ -0,0 +1,50 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1276
*/
class DDC1276Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$user = new CmsUser();
$user->name = "Benjamin";
$user->username = "beberlei";
$user->status = "active";
$this->_em->persist($user);
for ($i = 0; $i < 2; $i++) {
$group = new CmsGroup();
$group->name = "group".$i;
$user->groups[] = $group;
$this->_em->persist($group);
}
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $user->id);
$cloned = clone $user;
$this->assertSame($user->groups, $cloned->groups);
$this->assertEquals(2, count($user->groups));
$this->_em->merge($cloned);
$this->assertEquals(2, count($user->groups));
$this->_em->flush();
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1300
*/
class DDC1300Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1300Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1300FooLocale'),
));
}
public function testIssue()
{
$foo = new DDC1300Foo();
$foo->_fooReference = "foo";
$this->_em->persist($foo);
$this->_em->flush();
$locale = new DDC1300FooLocale();
$locale->_foo = $foo;
$locale->_locale = "en";
$locale->_title = "blub";
$this->_em->persist($locale);
$this->_em->flush();
$query = $this->_em->createQuery('SELECT f, fl FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1300Foo f JOIN f._fooLocaleRefFoo fl');
$result = $query->getResult();
$this->assertEquals(1, count($result));
}
}
/**
* @Entity
*/
class DDC1300Foo
{
/**
* @var integer fooID
* @Column(name="fooID", type="integer", nullable=false)
* @GeneratedValue(strategy="AUTO")
* @Id
*/
public $_fooID = null;
/**
* @var string fooReference
* @Column(name="fooReference", type="string", nullable=true, length=45)
*/
public $_fooReference = null;
/**
* @OneToMany(targetEntity="DDC1300FooLocale", mappedBy="_foo",
* cascade={"persist"})
*/
public $_fooLocaleRefFoo = null;
/**
* Constructor
*
* @param array|Zend_Config|null $options
* @return Bug_Model_Foo
*/
public function __construct($options = null)
{
$this->_fooLocaleRefFoo = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/**
* @Entity
*/
class DDC1300FooLocale
{
/**
* @ManyToOne(targetEntity="DDC1300Foo")
* @JoinColumn(name="fooID", referencedColumnName="fooID")
* @Id
*/
public $_foo = null;
/**
* @var string locale
* @Column(name="locale", type="string", nullable=false, length=5)
* @Id
*/
public $_locale = null;
/**
* @var string title
* @Column(name="title", type="string", nullable=true, length=150)
*/
public $_title = null;
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @author asm89
*/
class DDC1301Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $userId;
public function setUp()
{
$this->useModelSet('legacy');
parent::setUp();
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Legacy\LegacyUser');
$class->associationMappings['_articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['_references']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['_cars']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$this->loadFixture();
}
public function tearDown()
{
parent::tearDown();
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\Legacy\LegacyUser');
$class->associationMappings['_articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['_references']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
$class->associationMappings['_cars']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
}
public function testCountNotInitializesLegacyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_articles->isInitialized());
$this->assertEquals(2, count($user->_articles));
$this->assertFalse($user->_articles->isInitialized());
foreach ($user->_articles AS $article) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function testCountNotInitializesLegacyCollectionWithForeignIdentifier()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_references->isInitialized());
$this->assertEquals(2, count($user->_references));
$this->assertFalse($user->_references->isInitialized());
foreach ($user->_references AS $reference) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function testCountNotInitializesLegacyManyToManyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\Legacy\LegacyUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->_cars->isInitialized());
$this->assertEquals(3, count($user->_cars));
$this->assertFalse($user->_cars->isInitialized());
foreach ($user->_cars AS $reference) { }
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
}
public function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user1->_username = "beberlei";
$user1->_name = "Benjamin";
$user1->_status = "active";
$user2 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user2->_username = "jwage";
$user2->_name = "Jonathan";
$user2->_status = "active";
$user3 = new \Doctrine\Tests\Models\Legacy\LegacyUser();
$user3->_username = "romanb";
$user3->_name = "Roman";
$user3->_status = "active";
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($user3);
$article1 = new \Doctrine\Tests\Models\Legacy\LegacyArticle();
$article1->_topic = "Test";
$article1->_text = "Test";
$article1->setAuthor($user1);
$article2 = new \Doctrine\Tests\Models\Legacy\LegacyArticle();
$article2->_topic = "Test";
$article2->_text = "Test";
$article2->setAuthor($user1);
$this->_em->persist($article1);
$this->_em->persist($article2);
$car1 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car1->_description = "Test1";
$car2 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car2->_description = "Test2";
$car3 = new \Doctrine\Tests\Models\Legacy\LegacyCar();
$car3->_description = "Test3";
$user1->addCar($car1);
$user1->addCar($car2);
$user1->addCar($car3);
$user2->addCar($car1);
$user3->addCar($car1);
$this->_em->persist($car1);
$this->_em->persist($car2);
$this->_em->persist($car3);
$this->_em->flush();
$detail1 = new \Doctrine\Tests\Models\Legacy\LegacyUserReference($user1, $user2, "foo");
$detail2 = new \Doctrine\Tests\Models\Legacy\LegacyUserReference($user1, $user3, "bar");
$this->_em->persist($detail1);
$this->_em->persist($detail2);
$this->_em->flush();
$this->_em->clear();
$this->userId = $user1->getId();
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1306
*/
class DDC1306Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$phone = new CmsPhonenumber();
$phone->phonenumber = "1234";
// puts user and phone into commit order calculator
$this->_em->persist($phone);
$this->_em->flush();
$address = new \Doctrine\Tests\Models\CMS\CmsAddress();
$address->city = "bonn";
$address->country = "Germany";
$address->street = "somestreet!";
$address->zip = 12345;
$this->_em->persist($address);
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "benjamin";
$user->status = "active";
$user->setAddress($address);
// puts user and address into commit order calculator, but does not calculate user dependencies new
$this->_em->persist($user);
$this->_em->flush();
$this->_em->remove($user->getAddress());
$this->_em->remove($user);
$this->_em->flush();
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1383
*/
class DDC1383Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383AbstractEntity'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383Entity'),
));
} catch(\Exception $ignored) {}
}
public function testFailingCase()
{
$parent = new DDC1383Entity();
$child = new DDC1383Entity();
$child->setReference($parent);
$this->_em->persist($parent);
$this->_em->persist($child);
$id = $child->getId();
$this->_em->flush();
$this->_em->clear();
// Try merging the parent entity
$child = $this->_em->merge($child);
$parent = $child->getReference();
// Parent is not instance of the abstract class
self::assertTrue($parent instanceof DDC1383AbstractEntity,
"Entity class is " . get_class($parent) . ', "DDC1383AbstractEntity" was expected');
// Parent is NOT instance of entity
self::assertTrue($parent instanceof DDC1383Entity,
"Entity class is " . get_class($parent) . ', "DDC1383Entity" was expected');
}
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="integer")
* @DiscriminatorMap({1 = "DDC1383Entity"})
*/
abstract class DDC1383AbstractEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $id;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
}
/**
* @Entity
*/
class DDC1383Entity extends DDC1383AbstractEntity
{
/**
* @ManyToOne(targetEntity="DDC1383AbstractEntity")
*/
protected $reference;
public function getReference()
{
return $this->reference;
}
public function setReference(DDC1383AbstractEntity $reference)
{
$this->reference = $reference;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1392
*/
class DDC1392Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$file = new DDC1392File;
$picture = new DDC1392Picture;
$picture->setFile($file);
$em = $this->_em;
$em->persist($picture);
$em->flush();
$em->clear();
$fileId = $file->getFileId();
$pictureId = $picture->getPictureId();
$this->assertTrue($fileId > 0);
$picture = $em->find(__NAMESPACE__ . '\DDC1392Picture', $pictureId);
$this->assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), "Lazy Proxy should be marked MANAGED.");
$file = $picture->getFile();
// With this activated there will be no problem
//$file->__load();
$picture->setFile(null);
$em->clear();
$em->merge($file);
$em->flush();
$q = $this->_em->createQuery("SELECT COUNT(e) FROM " . __NAMESPACE__ . '\DDC1392File e');
$result = $q->getSingleScalarResult();
self::assertEquals(1, $result);
}
}
/**
* @Entity
*/
class DDC1392Picture
{
/**
* @Column(name="picture_id", type="integer")
* @Id @GeneratedValue
*/
private $pictureId;
/**
* @ManyToOne(targetEntity="DDC1392File", cascade={"persist", "remove"})
* @JoinColumn(name="file_id", referencedColumnName="file_id")
*/
private $file;
/**
* Get pictureId
*/
public function getPictureId()
{
return $this->pictureId;
}
/**
* Set file
*/
public function setFile($value = null)
{
$this->file = $value;
}
/**
* Get file
*/
public function getFile()
{
return $this->file;
}
}
/**
* @Entity
*/
class DDC1392File
{
/**
* @Column(name="file_id", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $fileId;
/**
* Get fileId
*/
public function getFileId()
{
return $this->fileId;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1452
*/
class DDC1452Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1452EntityA'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1452EntityB'),
));
} catch (\Exception $ignored) {
}
}
public function testIssue()
{
$a1 = new DDC1452EntityA();
$a1->title = "foo";
$a2 = new DDC1452EntityA();
$a2->title = "bar";
$b = new DDC1452EntityB();
$b->entityAFrom = $a1;
$b->entityATo = $a2;
$this->_em->persist($a1);
$this->_em->persist($a2);
$this->_em->persist($b);
$this->_em->flush();
$this->_em->clear();
$dql = "SELECT a, b, ba FROM " . __NAMESPACE__ . "\DDC1452EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba";
$results = $this->_em->createQuery($dql)->setMaxResults(1)->getResult();
$this->assertSame($results[0], $results[0]->entitiesB[0]->entityAFrom);
$this->assertFalse( $results[0]->entitiesB[0]->entityATo instanceof \Doctrine\ORM\Proxy\Proxy );
$this->assertInstanceOf('Doctrine\Common\Collections\Collection', $results[0]->entitiesB[0]->entityATo->getEntitiesB());
}
public function testFetchJoinOneToOneFromInverse()
{
$address = new \Doctrine\Tests\Models\CMS\CmsAddress();
$address->city = "Bonn";
$address->country = "Germany";
$address->street = "Somestreet";
$address->zip = 12345;
$user = new \Doctrine\Tests\Models\CMS\CmsUser();
$user->name = "beberlei";
$user->username = "beberlei";
$user->status = "active";
$user->address = $address;
$address->user = $user;
$this->_em->persist($address);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$dql = "SELECT a, u FROM Doctrine\Tests\Models\CMS\CmsAddress a INNER JOIN a.user u";
$data = $this->_em->createQuery($dql)->getResult();
$this->_em->clear();
$this->assertFalse($data[0]->user instanceof \Doctrine\ORM\Proxy\Proxy);
$dql = "SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.address a";
$data = $this->_em->createQuery($dql)->getResult();
$this->assertFalse($data[0]->address instanceof \Doctrine\ORM\Proxy\Proxy);
}
}
/**
* @Entity
*/
class DDC1452EntityA
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @Column */
public $title;
/** @ManyToMany(targetEntity="DDC1452EntityB", mappedBy="entityAFrom") */
public $entitiesB;
public function __construct()
{
$this->entitiesB = new ArrayCollection();
}
public function getEntitiesB()
{
return $this->entitiesB;
}
}
/**
* @Entity
*/
class DDC1452EntityB
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/**
* @ManyToOne(targetEntity="DDC1452EntityA", inversedBy="entitiesB")
*/
public $entityAFrom;
/**
* @ManyToOne(targetEntity="DDC1452EntityA")
*/
public $entityATo;
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1454
*/
class DDC1454Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$pic = new DDC1454Picture();
$this->_em->getUnitOfWork()->getEntityState($pic);
}
}
/**
* @Entity
*/
class DDC1454Picture extends DDC1454File
{
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"picture" = "DDC1454Picture"})
*/
class DDC1454File
{
/**
* @Column(name="file_id", type="integer")
* @Id
*/
public $fileId;
public function __construct() {
$this->fileId = rand();
}
/**
* Get fileId
*/
public function getFileId() {
return $this->fileId;
}
}

View File

@@ -0,0 +1,131 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
require_once __DIR__ . '/../../../TestInit.php';
class DDC1258Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\TestEntity'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\TestAdditionalEntity')
));
}
public function testIssue()
{
$testEntity = new TestEntity();
$testEntity->setValue(3);
$testEntity->setAdditional(new TestAdditionalEntity());
$this->_em->persist($testEntity);
$this->_em->flush();
$this->_em->clear();
// So here the value is 3
$this->assertEquals(3, $testEntity->getValue());
$test = $this->_em->getRepository(__NAMESPACE__ . '\TestEntity')->find(1);
// New value is set
$test->setValue(5);
// So here the value is 5
$this->assertEquals(5, $test->getValue());
// Get the additional entity
$additional = $test->getAdditional();
// Still 5..
$this->assertEquals(5, $test->getValue());
// Force the proxy to load
$additional->getBool();
// The value should still be 5
$this->assertEquals(5, $test->getValue());
}
}
/**
* @Entity
*/
class TestEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @Column(type="integer")
*/
protected $value;
/**
* @OneToOne(targetEntity="TestAdditionalEntity", inversedBy="entity", orphanRemoval=true, cascade={"persist", "remove"})
*/
protected $additional;
public function getValue()
{
return $this->value;
}
public function setValue($value)
{
$this->value = $value;
}
public function getAdditional()
{
return $this->additional;
}
public function setAdditional($additional)
{
$this->additional = $additional;
}
}
/**
* @Entity
*/
class TestAdditionalEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @OneToOne(targetEntity="TestEntity", mappedBy="additional")
*/
protected $entity;
/**
* @Column(type="boolean")
*/
protected $bool;
public function __construct()
{
$this->bool = false;
}
public function getBool()
{
return $this->bool;
}
public function setBool($bool)
{
$this->bool = $bool;
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
/**
* @group DDC-1509
*/
class DDC1509Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509AbstractFile'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$file = new DDC1509File;
$thumbnail = new DDC1509File;
$picture = new DDC1509Picture;
$picture->setFile($file);
$picture->setThumbnail($thumbnail);
/* @var $em \Doctrine\ORM\EntityManager */
$em = $this->_em;
$em->persist($picture);
$em->flush();
$em->clear();
$id = $picture->getPictureId();
$pic = $em->merge($picture);
/* @var $pic DDC1509Picture */
$this->assertNotNull($pic->getThumbnail());
$this->assertNotNull($pic->getFile());
}
}
/**
* @Entity
*/
class DDC1509Picture
{
/**
* @Column(type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"})
*/
private $thumbnail;
/**
* @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"})
*/
private $file;
/**
* Get pictureId
*/
public function getPictureId()
{
return $this->id;
}
/**
* Set file
*/
public function setFile($value = null)
{
$this->file = $value;
}
/**
* Get file
*/
public function getFile()
{
return $this->file;
}
public function getThumbnail()
{
return $this->thumbnail;
}
public function setThumbnail($thumbnail)
{
$this->thumbnail = $thumbnail;
}
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"file" = "DDC1509File"})
*/
class DDC1509AbstractFile
{
/**
* @Column(type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* Get fileId
*/
public function getFileId()
{
return $this->id;
}
}
/**
* @Entity
*/
class DDC1509File extends DDC1509AbstractFile
{
}

View File

@@ -44,7 +44,7 @@ class DDC258Test extends \Doctrine\Tests\OrmFunctionalTestCase
$e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC258Super', $c2->id);
$this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC258Class2', $e2);
$this->assertInstanceOf('Doctrine\Tests\ORM\Functional\Ticket\DDC258Class2', $e2);
$this->assertEquals('Bar', $e2->title);
$this->assertEquals('Bar', $e2->description);
$this->assertEquals('Bar', $e2->text);

View File

@@ -64,13 +64,13 @@ class DDC440Test extends \Doctrine\Tests\OrmFunctionalTestCase
// Test the first phone. The assertion actually failed because original entity data is not set properly.
// This was because it is also set as MainPhone and that one is created as a proxy, not the
// original object when the find on Client is called. However loading proxies did not work correctly.
$this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC440Phone', $p1);
$this->assertInstanceOf('Doctrine\Tests\ORM\Functional\Ticket\DDC440Phone', $p1);
$originalData = $uw->getOriginalEntityData($p1);
$this->assertEquals($phone->getNumber(), $originalData['number']);
//If you comment out previous test, this one should pass
$this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC440Phone', $p2);
$this->assertInstanceOf('Doctrine\Tests\ORM\Functional\Ticket\DDC440Phone', $p2);
$originalData = $uw->getOriginalEntityData($p2);
$this->assertEquals($phone2->getNumber(), $originalData['number']);
}

View File

@@ -50,7 +50,7 @@ class DDC501Test extends OrmFunctionalTestCase
// freeze and unfreeze
$userClone = unserialize(serialize($userReloaded));
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $userClone);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $userClone);
// detached user can't know about his phonenumbers
$this->assertEquals(0, count($userClone->getPhonenumbers()));

View File

@@ -66,7 +66,7 @@ class DDC633Test extends \Doctrine\Tests\OrmFunctionalTestCase
$appointments = $this->_em->createQuery("SELECT a FROM " . __NAMESPACE__ . "\DDC633Appointment a")->getResult();
foreach ($appointments AS $eagerAppointment) {
$this->assertType('Doctrine\ORM\Proxy\Proxy', $eagerAppointment->patient);
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $eagerAppointment->patient);
$this->assertTrue($eagerAppointment->patient->__isInitialized__, "Proxy should already be initialized due to eager loading!");
}
}

View File

@@ -36,11 +36,11 @@ class DDC729Test extends \Doctrine\Tests\OrmFunctionalTestCase
$a = new DDC729A();
$a->id = $aId;
$this->assertType('Doctrine\Common\Collections\ArrayCollection', $a->related);
$this->assertInstanceOf('Doctrine\Common\Collections\ArrayCollection', $a->related);
$a = $this->_em->merge($a);
$this->assertType('Doctrine\ORM\PersistentCollection', $a->related);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $a->related);
$this->assertFalse($a->related->isInitialized(), "Collection should not be marked initialized.");
$this->assertFalse($a->related->isDirty(), "Collection should not be marked as dirty.");

View File

@@ -72,7 +72,7 @@ class DDC736Test extends \Doctrine\Tests\OrmFunctionalTestCase
/* @var $cart2 Doctrine\Tests\Models\ECommerce\ECommerceCart */
$cart2 = $result[0][0];
$this->assertType('Doctrine\ORM\Proxy\Proxy', $cart2->getCustomer());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $cart2->getCustomer());
}
}

View File

@@ -33,10 +33,10 @@ class DDC748Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($article);
$this->_em->flush();
$this->assertType('Doctrine\Common\Collections\Collection', $user->articles);
$this->assertInstanceOf('Doctrine\Common\Collections\Collection', $user->articles);
$this->_em->refresh($article);
$this->assertTrue($article !== $user->articles, "The article should not be replaced on the inverse side of the relation.");
$this->assertType('Doctrine\Common\Collections\Collection', $user->articles);
$this->assertInstanceOf('Doctrine\Common\Collections\Collection', $user->articles);
}
public function testRefreshOneToOne()

View File

@@ -52,20 +52,20 @@ class DDC837Test extends \Doctrine\Tests\OrmFunctionalTestCase
// Test Class1
$e1 = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC837Super', $c1->id);
$this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class1', $e1);
$this->assertInstanceOf('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class1', $e1);
$this->assertEquals('Foo', $e1->title);
$this->assertEquals('Foo', $e1->description);
$this->assertType(__NAMESPACE__ . '\DDC837Aggregate', $e1->aggregate);
$this->assertInstanceOf(__NAMESPACE__ . '\DDC837Aggregate', $e1->aggregate);
$this->assertEquals('test1', $e1->aggregate->getSysname());
// Test Class 2
$e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC837Super', $c2->id);
$this->assertType('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class2', $e2);
$this->assertInstanceOf('Doctrine\Tests\ORM\Functional\Ticket\DDC837Class2', $e2);
$this->assertEquals('Bar', $e2->title);
$this->assertEquals('Bar', $e2->description);
$this->assertEquals('Bar', $e2->text);
$this->assertType(__NAMESPACE__ . '\DDC837Aggregate', $e2->aggregate);
$this->assertInstanceOf(__NAMESPACE__ . '\DDC837Aggregate', $e2->aggregate);
$this->assertEquals('test2', $e2->aggregate->getSysname());
$all = $this->_em->getRepository(__NAMESPACE__.'\DDC837Super')->findAll();

View File

@@ -34,10 +34,10 @@ class DDC949Test extends \Doctrine\Tests\OrmFunctionalTestCase
$true = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => true));
$false = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => false));
$this->assertType('Doctrine\Tests\Models\Generic\BooleanModel', $true);
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $true);
$this->assertTrue($true->booleanField, "True Boolean Model should be true.");
$this->assertType('Doctrine\Tests\Models\Generic\BooleanModel', $false);
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $false);
$this->assertFalse($false->booleanField, "False Boolean Model should be false.");
}
}

View File

@@ -763,4 +763,58 @@ class ArrayHydratorTest extends HydrationTestCase
$this->assertEquals(1, count($result));
}
/**
* @group DDC-1358
*/
public function testMissingIdForRootEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(4, count($result), "Should hydrate four results.");
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertEquals(array('id' => 1, 'status' => 'developer'), $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertEquals(array('id' => 2, 'status' => 'developer'), $result[2][0]);
$this->assertNull($result[3][0]);
}
}

View File

@@ -1008,4 +1008,165 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals(4, count($result[1]->groups));
$this->assertEquals(2, count($result[1]->phonenumbers));
}
/**
* @group DDC-1358
*/
public function testMissingIdForRootEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
array(
'u__id' => null,
'u__status' => null,
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(4, count($result), "Should hydrate four results.");
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][0]);
$this->assertNull($result[3][0]);
}
/**
* @group DDC-1358
* @return void
*/
public function testMissingIdForCollectionValuedChildEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
'u',
'phonenumbers'
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => '42',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => null
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
'p__phonenumber' => '91'
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
'p__phonenumber' => null
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertEquals(1, $result[0][0]->phonenumbers->count());
$this->assertEquals(1, $result[1][0]->phonenumbers->count());
}
/**
* @group DDC-1358
*/
public function testMissingIdForSingleValuedChildEntity()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsAddress',
'a',
'u',
'address'
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addFieldResult('a', 'a__id', 'id');
$rsm->addFieldResult('a', 'a__city', 'city');
$rsm->addMetaResult('a', 'user_id', 'user_id');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'a__id' => 1,
'a__city' => 'Berlin',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'BENJAMIN',
'a__id' => null,
'a__city' => null,
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][0]->address);
$this->assertNull($result[1][0]->address);
}
}

View File

@@ -182,10 +182,11 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($class->associationMappings['phonenumbers']));
$this->assertFalse($class->associationMappings['phonenumbers']['isOwningSide']);
$this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRemove']);
$this->assertTrue($class->associationMappings['phonenumbers']['isCascadeRemove']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']);
$this->assertTrue($class->associationMappings['phonenumbers']['orphanRemoval']);
// Test Order By
$this->assertEquals(array('number' => 'ASC'), $class->associationMappings['phonenumbers']['orderBy']);
@@ -329,7 +330,7 @@ class User
public $address;
/**
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"})
* @OneToMany(targetEntity="Phonenumber", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
* @OrderBy({"number"="ASC"})
*/
public $phonenumbers;
@@ -425,7 +426,7 @@ class User
1 => 'persist',
),
'mappedBy' => 'user',
'orphanRemoval' => false,
'orphanRemoval' => true,
'orderBy' =>
array(
'number' => 'ASC',

View File

@@ -64,7 +64,7 @@ $metadata->mapOneToMany(array(
1 => 'persist',
),
'mappedBy' => 'user',
'orphanRemoval' => false,
'orphanRemoval' => true,
'orderBy' =>
array(
'number' => 'ASC',

Some files were not shown because too many files have changed in this diff Show More