Compare commits

..

187 Commits
2.1.2 ... 2.0.7

Author SHA1 Message Date
Benjamin Eberlei
b7a15316a0 Release 2.0.7 2011-08-17 21:47:50 +00:00
Benjamin Eberlei
2b878fc15d Bump dependency DBAL to 2.0.7 2011-08-17 21:46:43 +00:00
Benjamin Eberlei
4fea26c833 Fix: YamlDriver does not support orphanRemoval 2011-08-17 21:23:13 +02:00
Benjamin Eberlei
bf3ae5f2cf Merge branch 'DDC-1302' into 2.0.x 2011-07-31 11:36:02 +02:00
Benjamin Eberlei
2836d14c31 DDC-1302 - Fix bug in XmlDriver not handling orphan removal 2011-07-31 11:35:50 +02:00
Benjamin Eberlei
a342c87a32 Merge branch 'DDC-1276' into 2.0.x 2011-07-26 22:18:21 +02:00
Benjamin Eberlei
192eedc6a7 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:17:13 +02:00
Benjamin Eberlei
ed78cdb937 Merge remote-tracking branch 'origin/2.0.x' into 2.0.x 2011-07-12 22:52:35 +02:00
Benjamin Eberlei
793c383aa2 Merge branch 'DDC-1240' into 2.0.x 2011-07-12 22:52:18 +02:00
Benjamin Eberlei
7595b021cc DDC-1240 - Fix optimistic lock exception loosing the message 2011-07-12 22:52:09 +02:00
Benjamin Eberlei
f1bcb3cb80 Merge branch 'DDC-1257' into 2.0.x 2011-07-09 15:15:53 +02:00
Benjamin Eberlei
35ea4ed1fd DDC-1257 - Fix bug where validation callbacks are added multiple times in EntityGenerator 2011-07-09 15:15:34 +02:00
Benjamin Eberlei
ac8a36cda3 Merge branch 'DDC-1251' into 2.0.x 2011-07-09 14:55:12 +02:00
Benjamin Eberlei
0917ed5ced DDC-1251 - Fix bug in token parsing of EntityGenerator 2011-07-09 14:54:56 +02:00
Benjamin Eberlei
8068f34c66 DDC-1022 - Call __wakeup() with the same semantics then ClassMetadata::newInstance() does inside UnitOfWork 2011-07-09 14:34:21 +02:00
Benjamin Eberlei
91a9d4f27c Merge branch 'DDC-1230' into 2.0.x 2011-06-28 21:38:54 +02:00
Benjamin Eberlei
7c8590ea74 DDC-1230 - Fix bug where UnitOfWork does not set STATE_REMOVE when calling EntityManager#remove() on an entity 2011-06-28 21:38:44 +02:00
Benjamin Eberlei
d1cea4f77c Merge branch 'DDC-1224' into 2.0.x 2011-06-26 10:12:08 +02:00
Benjamin Eberlei
ae958e73d1 DDC-1224 - Bugfix with temporary table ids and tables in schema (in postgresql) 2011-06-26 10:11:57 +02:00
Benjamin Eberlei
3b841883d3 Merge branch 'DDC-1211' into 2.0.x 2011-06-19 10:27:21 +02:00
Benjamin Eberlei
cfe36f9a31 DDC-1211 - Fix bug with empty numeric literal 2011-06-19 10:26:36 +02:00
Benjamin Eberlei
d2eea18977 Merge branch 'DDC-1189' into 2.0.x 2011-06-19 09:44:21 +02:00
Benjamin Eberlei
247887ec0e DDC-1189 - Bugfix with PersistentCollection#clear() in combination with lazy loading 2011-06-19 09:43:33 +02:00
Benjamin Eberlei
2b309a3682 Merge remote-tracking branch 'origin/2.0.x' into 2.0.x 2011-06-16 22:47:28 +02:00
Benjamin Eberlei
5ebe7ca89e Merge branch 'DDC-1172' into 2.0.x 2011-06-16 22:47:08 +02:00
Benjamin Eberlei
c582b7790a DDC-1172 - Adjust patch to 2.0.x code base 2011-06-16 22:43:06 +02:00
Benjamin Eberlei
a98a21b269 DDC-1172 - Handle sequence dropping in SchemaTool. 2011-06-16 22:34:56 +02:00
Benjamin Eberlei
5c9b3d0b92 Bump Dev Version to 2.0.7-DEV 2011-06-15 21:45:09 +00:00
Benjamin Eberlei
7f3bdab34c Release 2.0.6 2011-06-15 21:44:59 +00:00
Benjamin Eberlei
e075ab6958 Update dependency of DBAL to 2.0.6 2011-06-15 21:43:17 +00:00
Benjamin Eberlei
a1bc743568 Merge branch 'DDC-1208' into 2.0.x 2011-06-15 18:33:24 +02:00
Benjamin Eberlei
20d8ebfd43 DDC-1208 - Allow namespace separator in <discriminator-mapping /> 2011-06-15 18:33:12 +02:00
Fabien Potencier
3587f3bcb3 Update Symfony2 vendors 2011-06-15 11:44:04 +02:00
Benjamin Eberlei
ec87cb779c Merge branch 'DDC-1163' into 2.0.x 2011-06-05 16:20:56 +02:00
Benjamin Eberlei
ac40ce8042 DDC-1163 - Fix nasty bug with inheritance in UnitOfWork::executeUpdates() and executeRemovals() 2011-06-05 16:20:41 +02:00
Benjamin Eberlei
d4302b32e1 Merge branch 'DDC-1192' into 2.0.x 2011-06-05 14:50:35 +02:00
Benjamin Eberlei
3c95b0ec35 DDC-1192 - Fix notice in XmlDriver, removed unnecessary code. 2011-06-05 14:50:25 +02:00
Benjamin Eberlei
c12c6e82b6 Merge remote-tracking branch 'origin/2.0.x' into 2.0.x 2011-06-05 10:09:26 +02:00
Benjamin Eberlei
505f7bd252 Merge branch 'DDC-1193' into 2.0.x 2011-06-05 10:08:29 +02:00
Benjamin Eberlei
d5459616d2 DDC-733, DDC-1193 - Proxy has only _load() in 2.0.x - Change to __load() for consinstent code. 2011-06-05 10:08:05 +02:00
Benjamin Eberlei
97d9fb7efa DDC-733, DDC-1193 - Proxy has only _load() in 2.0.x 2011-06-05 10:06:56 +02:00
Benjamin Eberlei
2da1fadaf1 DDC-1193 - Fix previous commit. 2011-06-05 10:05:25 +02:00
Benjamin Eberlei
a612ca7231 DDC-1193 - Fix bug with cascade remove and proxy classes. 2011-06-05 10:05:12 +02:00
Benjamin Eberlei
bd4faa6a7d DDC-733 - Add UnitOfWork::initializeObject() method. 2011-06-05 10:04:23 +02:00
Benjamin Eberlei
be03811753 Merge pull request #66 from fabpot/feat-doctrine-mapping-configuration
Feat doctrine mapping configuration
2011-06-04 23:18:52 -07:00
Fabien Potencier
636fa34136 Made orm:convert-mapping command more configurable (allow to change the extension of the generated files for instance) 2011-06-05 08:03:41 +02:00
Benjamin Eberlei
016810af7f [DDC-952] Add Persister hydration performance tests. 2011-05-14 16:09:40 +02:00
Benjamin Eberlei
178fe38a39 Bump Dev Version to 2.0.6-DEV 2011-05-14 13:36:43 +00:00
Benjamin Eberlei
35a318148c Release 2.0.5 2011-05-14 13:36:31 +00:00
Benjamin Eberlei
c63115efa9 Bump DBAL dependency to 2.0.5 2011-05-14 15:32:31 +02:00
Benjamin Eberlei
7cba650aff DDC-1136 - trailing character for backup files. 2011-05-14 11:58:27 +02:00
Benjamin Eberlei
d9475cbfd0 Merge remote-tracking branch 'origin/2.0.x' into 2.0.x 2011-05-14 08:49:01 +02:00
Benjamin Eberlei
ee1708b04a Merge branch 'DDC-1151' into 2.0.x 2011-05-14 08:48:32 +02:00
Benjamin Eberlei
a8057dfe26 DDC-1151 - Slight modification necessary for 2.0.x 2011-05-14 08:46:51 +02:00
Benjamin Eberlei
60e7b8eff4 DDC-1151 - Fix missing table quotes when adding foreign keys in SchemaTool 2011-05-14 08:45:50 +02:00
Guilherme Blanco
28291f0fa7 Merge pull request #56 from stof/DDC-1146
DDC 1146
2011-05-11 15:47:20 -07:00
Christophe Coevoet
eecd9afc09 [DDC-1146] Updated the Symfony2 vendors 2011-05-12 00:28:36 +02:00
Benjamin Eberlei
8f9f85da39 Merge branch 'DDC-1129' into 2.0.x 2011-05-01 11:46:16 +02:00
Benjamin Eberlei
5d7063b2e1 [DDC-1129] Fix bug in version changeset computation aswell as inline ClassMetadata::isCollectionValuedAssociation to increase performance by 2-5% 2011-05-01 11:46:00 +02:00
Benjamin Eberlei
cc26080a00 Merge branch 'DDC-1091' into 2.0.x 2011-05-01 11:02:21 +02:00
Benjamin Eberlei
2b900e2c26 [DDC-1091] Fix bug with custom string functions in StringPrimary 2011-05-01 11:02:08 +02:00
Benjamin Eberlei
aedda4a21d Merge branch 'DDC-1102' into 2.0.x 2011-05-01 10:02:12 +02:00
Benjamin Eberlei
de6f0e4594 DDC-1102 - Typo in EntityGenerator 2011-05-01 10:02:03 +02:00
Benjamin Eberlei
d1191c3f74 Merge pull request #39 from francisbesset/parent_object_not_found.
[2.0.x] Fixed an undefined index by a HydrationException
2011-04-30 15:08:10 -07:00
Guilherme Blanco
bd117fc9eb Merge pull request #49 from fabpot/namespace-parsing-fix.
[DDC-1134] Entity generator: Namespace parsing fix
2011-04-30 12:49:36 -07:00
Benjamin Eberlei
ac04ce4114 Merge branch 'DBAL-115' into 2.0.x 2011-04-30 17:19:31 +02:00
Benjamin Eberlei
8d39f2a36d [DBAL-115] REALLY fix issues with SchemaTool::getDropSchemaSQL(). 2011-04-30 17:19:15 +02:00
Benjamin Eberlei
e86cbd65fa [DBAL-115] Bugfix in SchemaTool not quoting table names when dropping schema. 2011-04-30 17:17:34 +02:00
Benjamin Eberlei
410dd69c5b Merge branch 'DDC-1133' into 2.0.x 2011-04-30 14:24:29 +02:00
Benjamin Eberlei
222a90fc8a DDC-1133 - Ducktype AnnotationReader in AnnotationDriver 2011-04-30 14:24:20 +02:00
Benjamin Eberlei
c8e2ee05d1 Merge branch 'DDC-1109' into 2.0.x 2011-04-30 12:51:30 +02:00
Benjamin Eberlei
7c9c7369fb [DDC-1109] ltrim discriminator map for convenience. 2011-04-30 12:51:10 +02:00
Benjamin Eberlei
8ec9ee82f9 Merge branch '2.0.x' of github.com:doctrine/doctrine2 into 2.0.x 2011-04-30 12:28:29 +02:00
Benjamin Eberlei
33c896c31f Merge branch 'DDC-1108' into 2.0.x 2011-04-30 12:28:14 +02:00
Benjamin Eberlei
9ee3cc98ae [DDC-1108] Fix bug with single char named input parameters in DQL lexer. 2011-04-30 12:27:55 +02:00
Benjamin Eberlei
12bde174c5 Merge branch 'DDC-1132' into 2.0.x 2011-04-30 11:23:30 +02:00
Benjamin Eberlei
7634d45a29 [DDC-1132] Fix many to many table detection. 2011-04-30 11:20:25 +02:00
Benjamin Eberlei
b2873ab236 [DDC-1132] Fix many to many table detection. 2011-04-30 11:20:25 +02:00
Fabien Potencier
2978cad198 Fix namespace/class parsing in the entity generator 2011-04-30 10:42:38 +02:00
Benjamin Eberlei
c49467b7fa build.xml: Cleanup .git directory in Symfony Components 2011-04-07 15:11:52 -04:00
Benjamin Eberlei
095b8fa930 Bump Dev Version to 2.0.5-DEV 2011-04-07 15:04:05 -04:00
Benjamin Eberlei
8a2b1058c3 Release 2.0.4 2011-04-07 15:03:49 -04:00
Benjamin Eberlei
2bdceefb2d Bump dependencies, Doctrine Common to 2.0.2 and DBAL to 2.0.4 2011-04-07 20:53:19 +02:00
Benjamin Eberlei
71bcdce031 Merge branch 'DDC-1040' into 2.0.x 2011-04-03 23:08:24 +02:00
Benjamin Eberlei
f6a700062d [DDC-1040] Bugfix with named parameters and multiple entities passed as parameter. 2011-04-03 23:08:07 +02:00
Benjamin Eberlei
fb4db2782c [DDC-1040] Add regression tests for entity as multiple named/positional parameters. 2011-04-03 23:06:47 +02:00
Benjamin Eberlei
39ec8de08b Merge branch 'DDC-1093' into 2.0.x 2011-04-03 20:29:41 +02:00
Benjamin Eberlei
7a469581e1 [DDC-1093] Fix docblock type hint 2011-04-03 20:29:32 +02:00
Benjamin Eberlei
1f8cdb77be Merge branch 'DDC-1087' into 2.0.x 2011-04-03 09:12:10 +02:00
Benjamin Eberlei
af5bcc148d [DDC-1087] Add missing resolution to IS NULL in EntityRepository when passing a null value as a criteria. 2011-04-03 09:11:54 +02:00
Benjamin Eberlei
06870fd87f Merge branch 'DDC-1077' into 2.0.x 2011-03-27 21:12:24 +02:00
Benjamin Eberlei
26af92f452 [DDC-1077] Bugfix in not handling literals in Select Expressions. 2011-03-27 21:12:05 +02:00
Benjamin Eberlei
97d97a8a89 Merge branch 'DDC-1079' into 2.0.x 2011-03-27 14:06:05 +02:00
Benjamin Eberlei
3df673cb6a [DDC-1079] Bugfix for shortcut for ArithmeticExpressions in SimpleSelectExpression that lead to literals not being valid. Problem was that ScalarExpression() did not handle AggregateExpressions() at all, which is now fixed. 2011-03-27 14:05:33 +02:00
Francis Besset
85474a0c7a Fixed an undefined index by a HydrationException 2011-03-21 20:33:32 +01:00
Benjamin Eberlei
1c05831b20 Merge ModifyLimitQueryFix into 2.0.x 2011-03-20 16:06:36 -04:00
Benjamin Eberlei
26126a10cd Bump Dev Version to 2.0.4-DEV 2011-03-20 16:04:07 -04:00
Benjamin Eberlei
b6c5de3ef5 Release 2.0.3 2011-03-20 16:03:54 -04:00
Benjamin Eberlei
48fe910978 Update DBAL dependency to 2.0.3 2011-03-20 16:03:11 -04:00
Benjamin Eberlei
0df6086960 Bump Version to 2.0.3-DEV 2011-03-20 15:51:51 -04:00
Benjamin Eberlei
3d406afa38 Merge branch 'DDC-992' into 2.0.x 2011-03-20 17:14:17 +01:00
Benjamin Eberlei
526f21ad3d [DDC-992] Fix criteria usage of column names clashing with field or associations by prefixing with table names or alias. 2011-03-20 17:14:04 +01:00
Benjamin Eberlei
31722cf8f6 [DDC-1053] Fix rebase/merge mistake in tests. 2011-03-20 14:10:33 +01:00
Benjamin Eberlei
758a09dfe8 Merge branch 'DDC-1053' into 2.0.x 2011-03-20 14:09:15 +01:00
Benjamin Eberlei
ba00dd13b6 [DDC-1053] Fix bug with usage of identification variables in GroupByItem. 2011-03-20 14:08:57 +01:00
Benjamin Eberlei
ff65aeed60 Merge branch 'DDC-1052' into 2.0.x 2011-03-20 13:08:48 +01:00
Benjamin Eberlei
3a5d70fbf3 [DDC-1052] Fix bug with versioning and inheritance 2011-03-20 13:08:16 +01:00
Benjamin Eberlei
68257a8c93 Merge branch 'DDC-1070' into 2.0.x 2011-03-20 12:23:43 +01:00
Benjamin Eberlei
3b4596691f [DDC-1070] Fix in AbstractQuery::iterate() method not respecting hydrator and parameters. 2011-03-20 12:23:21 +01:00
Benjamin Eberlei
ecb409412f Bump Dev Version to 2.0.3-DEV 2011-03-05 04:12:43 -05:00
Benjamin Eberlei
91ea598eb7 Release 2.0.2 2011-03-05 04:12:35 -05:00
Benjamin Eberlei
75e42f8145 Adjust tests to changes in DBAL dependency with regard to automatic foreign key and index naming. 2011-03-05 10:08:12 +01:00
Benjamin Eberlei
c6cc39ddee Remove Test for DDC-1050 which was merged in error during rebase of DDC-1041 2011-03-05 09:22:10 +01:00
Benjamin Eberlei
211fd2a69e Add symfony components deploy mechanism to build.xml 2011-03-05 08:59:54 +01:00
Benjamin Eberlei
8eef747aba Merge branch 'DDC-1034' into 2.0.x 2011-03-04 23:02:41 +01:00
Benjamin Eberlei
ceb6924aae DDC-1034 - Fix bug where callbacks where registered multiple times in inheritance hierachies. 2011-03-04 23:02:18 +01:00
Benjamin Eberlei
af4e836f4f Merge branch 'DDC-1056' into 2.0.x 2011-03-04 22:22:48 +01:00
Benjamin Eberlei
d650ade2b1 DDC-1056 - Fixed notice in StaticPHPDriver. 2011-03-04 22:22:31 +01:00
Benjamin Eberlei
521e4880aa Merge branch 'DDC-1041' into 2.0.x 2011-03-04 21:55:48 +01:00
Benjamin Eberlei
ee951a19aa [DDC-1041] You could retrieve instances of the wrong type in inheritance hierachies because the identity map aggregates them by rootEntityName. 2011-03-04 21:55:39 +01:00
Benjamin Eberlei
b1a616dfa5 Remove copied Symfony Component code 2011-03-04 21:51:07 +01:00
Benjamin Eberlei
859ca72426 Merge remote branch 'origin/2.0.x' into 2.0.x 2011-02-26 12:49:48 +01:00
Benjamin Eberlei
a655188e55 Merge branch 'DDC-1033' into 2.0.x 2011-02-26 12:48:53 +01:00
Benjamin Eberlei
5be16db9ec DDC-1033 - Fix cloning of not initialized proxies. 2011-02-26 12:48:39 +01:00
Benjamin Eberlei
f6aca7c6ef Merge branch 'DDC-1026' into 2.0.x 2011-02-26 00:41:03 +01:00
Benjamin Eberlei
4271706cab DDC-1026 - Fix Result Cache Seperate chaining implementation that was wrong since DDC-892 was applied. 2011-02-26 00:40:26 +01:00
Romain Pouclet
98d4075de1 Fixed typo in AbstractQuery::execute() doc 2011-02-25 10:22:02 -06:00
Benjamin Eberlei
81cff239f8 Merge branch 'DDC-1036' into 2.0.x 2011-02-20 09:15:37 +01:00
Guilherme Blanco
46dbce9b6c [DDC-1036] Modified the AggregateExpressions to support SimpleArithmeticExpression instead of StateFieldPathExpression. 2011-02-20 09:15:01 +01:00
Benjamin Eberlei
64129c66e0 Merge branch 'DDC-1030' into 2.0.x 2011-02-13 10:03:00 +01:00
Benjamin Eberlei
479ae3e22a DDC-1030 - Fix Static Reflection with namespace levels deeper than one. 2011-02-13 10:02:49 +01:00
Benjamin Eberlei
0d42c686e1 Merge branch 'DDC-1024' into 2.0.x 2011-02-12 17:44:54 +01:00
Benjamin Eberlei
2ba48d58e7 DDC-1024 - Do not generate setter/getter for inherited fields. 2011-02-12 17:44:39 +01:00
Benjamin Eberlei
c3ab35a8f5 Merge branch 'DDC-1018' into 2.0.x 2011-02-05 10:03:48 +01:00
Benjamin Eberlei
6dcbbdae4d [DDC-1018] Bugfix: INDEX BY was not working in JOIN Declarations, only in FROM. 2011-02-05 10:03:22 +01:00
Benjamin Eberlei
4a020f2c48 Merge branch 'DDC-1006' into 2.0.x 2011-02-02 23:31:16 +01:00
Benjamin Eberlei
5e4811a3cd DDC-1008, DDC-1002 - Create constructor and id setter if necessary. 2011-02-02 23:30:57 +01:00
Benjamin Eberlei
1dd85fe19e DDC-1006, DDC-953 - Fix EntityGenerator creating empty classes 2011-02-02 23:30:57 +01:00
Benjamin Eberlei
fd7442a056 Merge remote branch 'origin/2.0.x' into 2.0.x 2011-02-02 19:48:54 +01:00
Benjamin Eberlei
1d4b188a52 Fix build.xml 2011-02-02 19:48:08 +01:00
Benjamin Eberlei
6c57ec12ee Bump Dev Version to 2.0.2-DEV 2011-01-30 11:23:07 -05:00
Benjamin Eberlei
7a3ee2a8da Release 2.0.1 2011-01-30 11:22:59 -05:00
Benjamin Eberlei
5e61b73619 Fix version of 2.0.x branch to 2.0.1-DEV 2011-01-30 17:16:30 +01:00
Benjamin Eberlei
23ac0026a0 Merge remote branch 'origin/2.0.x' into 2.0.x 2011-01-30 17:14:35 +01:00
Benjamin Eberlei
65e85aa471 Update dependencies to Common 2.0.1 and DBAL 2.0.1 2011-01-30 17:14:07 +01:00
Benjamin Eberlei
d5b61ec897 Update build.xml workflow 2011-01-30 17:11:56 +01:00
Benjamin Eberlei
adc2f24c9a Merge branch 'DDC-892' into 2.0.x 2011-01-23 20:54:03 +01:00
Benjamin Eberlei
8423c0c608 DDC-892 - Implement separate chaining approach for result caches to prevent hash colissions. 2011-01-23 20:53:20 +01:00
Benjamin Eberlei
44aa098b37 Merge branch 'GenerationTools' into 2.0.x 2011-01-23 20:27:02 +01:00
Benjamin Eberlei
839ec8f25a Significantly updated the Help of the ConvertMapping and GenerateEntities Commands to help people using and understanding their scope. Added an additional --force flag to ConvertMapping command. 2011-01-23 20:26:56 +01:00
Benjamin Eberlei
0893d2eb00 Merge branch 'DDC-958' into 2.0.x 2011-01-23 17:28:24 +01:00
Benjamin Eberlei
b63babc7b8 DDC-958 - Fire postLoad event when calling refresh(). 2011-01-23 17:28:16 +01:00
Benjamin Eberlei
7350d85a4b Merge branch 'DDC-968' into 2.0.x 2011-01-23 16:48:08 +01:00
Benjamin Eberlei
a6b81d684a DDC-968 - Add AbstractQuery::getHints() method 2011-01-23 16:47:48 +01:00
Benjamin Eberlei
9e864c3662 Merge branch 'DDC-969' into 2.0.x 2011-01-23 16:13:12 +01:00
Benjamin Eberlei
024cc43189 DDC-969 - Use of field instead of column when accessing a table leads to error when both differ. 2011-01-23 16:12:59 +01:00
Benjamin Eberlei
1e79672147 Merge branch 'DDC-978' into 2.0.x 2011-01-23 15:41:02 +01:00
Benjamin Eberlei
3b7448bcf4 DDC-978 - Fix bug where Collection gets cleared (again) when calling flush multiple times and replacing a PersistentCollection with a new one. 2011-01-23 15:40:54 +01:00
Benjamin Eberlei
d5025235f3 Merge branch 'DDC-996' into 2.0.x 2011-01-23 14:24:13 +01:00
Benjamin Eberlei
dbc1a4da4b DDC-996 - Throw more useful exception if fieldName is empty in a mapped field or association. 2011-01-23 14:23:25 +01:00
Benjamin Eberlei
837d3f0f80 Merge branch 'DDc-960' into 2.0.x 2011-01-23 12:58:55 +01:00
Benjamin Eberlei
203fb59115 DDC-960 - Bugfix in how Persisters generate Fetch last version of Entity SQL. 2011-01-23 12:58:27 +01:00
Benjamin Eberlei
ecdc0fad64 Merge branch 'DDC-975' into 2.0.x 2011-01-13 21:45:44 +01:00
Benjamin Eberlei
e1fd2c158b DDC-975 - Fix notice in SchemaTool in combination with XML mapping driver. 2011-01-13 21:45:30 +01:00
Benjamin Eberlei
479dae2a99 Merge branch 'DDC-980' into 2.0.x 2011-01-13 21:18:46 +01:00
Benjamin Eberlei
d94c2a11e8 DDC-980 - Fix Update and Delete statements reference of the root table when doing subselects. 2011-01-13 21:18:23 +01:00
Benjamin Eberlei
d44b9fb05c Merge branch 'DDC-965' into 2.0.x 2011-01-02 10:25:37 +01:00
Benjamin Eberlei
fc16ecc6d7 DDC-965 - Defer ID check after loadMetata event is fired. 2011-01-02 10:25:23 +01:00
Benjamin Eberlei
1c9007fb0e Merge branch 'DDC-966' into 2.0.x 2011-01-02 10:19:06 +01:00
Benjamin Eberlei
79dc69b920 DDC-966 - Fix NOT NULL constraint SingleTableInheritance Generation using SchemaTool. 2011-01-02 10:18:55 +01:00
Benjamin Eberlei
5e22a08ee1 Merge branch 'DDC-949' into 2.0.x 2011-01-02 09:43:21 +01:00
Benjamin Eberlei
3d43049426 DDC-949 - Bugfix for BasicEntityPersister not using $types for select clauses. This fixes the issue for PostgreSQL however it still occurs on Oracle. DBAL change is necessary for this. 2011-01-02 09:43:04 +01:00
Benjamin Eberlei
95464c95fa Merge branch 'DDC-945-2' into 2.0.x 2010-12-31 14:39:49 +01:00
Benjamin Eberlei
392d23e401 DDC-945 - Fix regression, ManyToMany unidirectional owning side assocations should be allowed. 2010-12-31 14:39:38 +01:00
Benjamin Eberlei
19db4f304d Merge branch 'DDC-929' into 2.0.x 2010-12-30 23:18:49 +01:00
Benjamin Eberlei
aeaeff9002 DDC-929 - Fix bug with DatabaseDriver not detecting indexes that are not called primary. 2010-12-30 23:18:36 +01:00
Benjamin Eberlei
9bc236b4fa Merge branch 'DDC-961' into 2.0.x 2010-12-30 22:31:39 +01:00
Benjamin Eberlei
cd6611ad3e DDC-961 - Bugfix with missing first letter in automatic join table names in global namespace entities. 2010-12-30 22:31:24 +01:00
Benjamin Eberlei
41e5fc6b34 Merge branch 'DDC-837' into 2.0.x 2010-12-28 14:58:56 +01:00
Benjamin Eberlei
615a1159a7 DDC-837 - Fix bug with associations of the same name not being possible in inheritance hierachies. 2010-12-28 14:58:30 +01:00
Benjamin Eberlei
1059991865 Merge branch 'DDC-928' into 2.0.x 2010-12-28 12:20:28 +01:00
Benjamin Eberlei
18fc94b395 DDC-928 - Fix undefined variable notice. 2010-12-28 12:20:10 +01:00
Benjamin Eberlei
b288eac530 Merge branch 'DDC-945' into 2.0.x 2010-12-28 12:01:17 +01:00
Benjamin Eberlei
699cd2cf4b DDC-945 - Throw exception in ClassMetadataFactory when mapped superclass has to many associations. 2010-12-28 12:00:42 +01:00
Benjamin Eberlei
c1f10b4673 DDC-617 - Throw error if selecting identification variables without picking at least one root entity alias. 2010-12-28 10:21:25 +01:00
Benjamin Eberlei
6bff8c338f DDC-931 - SchemaTool#dropSchema() should not stop on failure of a single query (as stated in docblocks). 2010-12-22 23:07:27 +01:00
Benjamin Eberlei
1ca924d021 Fix for DDC-944 2010-12-22 00:19:03 +01:00
193 changed files with 1565 additions and 9396 deletions

6
.gitmodules vendored
View File

@@ -4,9 +4,9 @@
[submodule "lib/vendor/doctrine-dbal"]
path = lib/vendor/doctrine-dbal
url = git://github.com/doctrine/dbal.git
[submodule "lib/vendor/Symfony/Component/Console"]
path = lib/vendor/Symfony/Component/Console
url = git://github.com/symfony/Console.git
[submodule "lib/vendor/Symfony/Component/Yaml"]
path = lib/vendor/Symfony/Component/Yaml
url = git://github.com/symfony/Yaml.git
[submodule "lib/vendor/Symfony/Component/Console"]
path = lib/vendor/Symfony/Component/Console
url = git://github.com/symfony/Console.git

View File

@@ -1,24 +0,0 @@
This document details all the possible changes that you should investigate when updating
your project from Doctrine 2.0.x to 2.1
## Interface for EntityRepository
The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface.
## AnnotationReader changes
The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way:
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
// new code necessary starting here
$reader->setIgnoreNotImportedAnnotations(true);
$reader->setEnableParsePhpImports(false);
$reader = new \Doctrine\Common\Annotations\CachedReader(
new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache()
);
This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory.

View File

@@ -1,6 +1,6 @@
version=2.0.0BETA2
dependencies.common=2.0.0BETA4
dependencies.dbal=2.0.0BETA4
version=2.0.1
dependencies.common=2.0.1
dependencies.dbal=2.0.1
stability=beta
build.dir=build
dist.dir=dist

View File

@@ -107,23 +107,6 @@
<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.
-->
@@ -186,8 +169,6 @@
<dirroles key="bin">script</dirroles>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Symfony/Component/Yaml/</ignore>
<ignore>Symfony/Component/Console/</ignore>
<release>
<install as="doctrine" name="bin/doctrine" />
<install as="doctrine.php" name="bin/doctrine.php" />
@@ -223,7 +204,6 @@
<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">
@@ -235,7 +215,7 @@
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,package-phar,distribute-download,pirum-release,update-dev-version" />
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
<!--
Builds distributable PEAR packages for the Symfony Dependencies
@@ -255,7 +235,6 @@
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
</dependencies>
<ignore>bin/</ignore>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Doctrine/ORM/</ignore>
@@ -278,7 +257,6 @@
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
</dependencies>
<ignore>bin/</ignore>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Doctrine/ORM/</ignore>
@@ -292,4 +270,3 @@
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
</project>

View File

@@ -17,18 +17,11 @@
<xs:sequence>
<xs:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
</xs:element>
<xs:complexType name="emptyType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="emptyType"/>
<xs:complexType name="cascade-type">
<xs:sequence>
@@ -37,9 +30,7 @@
<xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/>
<xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:simpleType name="lifecycle-callback-type">
@@ -55,32 +46,13 @@
</xs:simpleType>
<xs:complexType name="lifecycle-callback">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="type" type="orm:lifecycle-callback-type" use="required" />
<xs:attribute name="method" type="xs:NMTOKEN" use="required" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="lifecycle-callbacks">
<xs:sequence>
<xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="named-query">
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="query" type="xs:string" use="required" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="named-queries">
<xs:sequence>
<xs:element name="named-query" type="orm:named-query" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
</xs:complexType>
@@ -91,14 +63,12 @@
<xs:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
<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="unbounded" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<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" />
<xs:element name="many-to-one" type="orm:many-to-one" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="many-to-many" type="orm:many-to-many" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="table" type="xs:NMTOKEN" />
@@ -106,18 +76,11 @@
<xs:attribute name="repository-class" type="xs:string"/>
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
<xs:attribute name="read-only" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="mapped-superclass" >
<xs:complexContent>
<xs:extension base="orm:entity">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:extension>
<xs:extension base="orm:entity"/>
</xs:complexContent>
</xs:complexType>
@@ -158,14 +121,10 @@
<xs:restriction base="xs:token">
<xs:enumeration value="EAGER"/>
<xs:enumeration value="LAZY"/>
<xs:enumeration value="EXTRALAZY"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="field">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
<xs:attribute name="column" type="xs:NMTOKEN" />
@@ -176,114 +135,75 @@
<xs:attribute name="column-definition" type="xs:string" />
<xs:attribute name="precision" type="xs:integer" use="optional" />
<xs:attribute name="scale" type="xs:integer" use="optional" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="discriminator-column">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field-name" type="xs:NMTOKEN" />
<xs:attribute name="length" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="unique-constraint">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="unique-constraints">
<xs:sequence>
<xs:element name="unique-constraint" type="orm:unique-constraint" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="index">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/>
<xs:attribute name="columns" type="xs:NMTOKENS" use="required"/>
</xs:complexType>
<xs:complexType name="indexes">
<xs:sequence>
<xs:element name="index" type="orm:index" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="discriminator-mapping">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="value" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="class" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="discriminator-map">
<xs:sequence>
<xs:element name="discriminator-mapping" type="orm:discriminator-mapping" minOccurs="1" maxOccurs="unbounded"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="generator">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="strategy" type="orm:generator-strategy" use="optional" default="AUTO" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="id">
<xs:sequence>
<xs:element name="generator" type="orm:generator" minOccurs="0" />
<xs:element name="sequence-generator" type="orm:sequence-generator" minOccurs="0" maxOccurs="1" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" />
<xs:attribute name="type" type="xs:NMTOKEN" use="required" />
<xs:attribute name="column" type="xs:NMTOKEN" />
<xs:attribute name="association-key" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="sequence-generator">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="sequence-name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="allocation-size" type="xs:integer" use="optional" default="1" />
<xs:attribute name="initial-value" type="xs:integer" use="optional" default="1" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="inverse-join-columns">
<xs:sequence>
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-column">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="referenced-column-name" type="xs:NMTOKEN" use="optional" default="id" />
<xs:attribute name="unique" type="xs:boolean" default="false" />
@@ -291,43 +211,32 @@
<xs:attribute name="on-delete" type="orm:fk-action" />
<xs:attribute name="on-update" type="orm:fk-action" />
<xs:attribute name="column-definition" type="xs:string" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-columns">
<xs:sequence>
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="join-table">
<xs:sequence>
<xs:element name="join-columns" type="orm:join-columns" />
<xs:element name="inverse-join-columns" type="orm:join-columns" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="schema" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="order-by">
<xs:sequence>
<xs:element name="order-by-field" type="orm:order-by-field" minOccurs="1" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="order-by-field">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
<xs:anyAttribute namespace="##other"/>
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
</xs:complexType>
<xs:simpleType name="order-by-direction">
@@ -342,30 +251,24 @@
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="one-to-many">
<xs:sequence>
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="index-by" type="xs:NMTOKEN" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="many-to-one">
@@ -374,16 +277,13 @@
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="join-column" type="orm:join-column"/>
<xs:element name="join-columns" type="orm:join-columns"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="one-to-one">
@@ -392,9 +292,7 @@
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="join-column" type="orm:join-column"/>
<xs:element name="join-columns" type="orm:join-columns"/>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:choice>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
@@ -402,7 +300,6 @@
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
</xs:schema>

View File

@@ -1,5 +1,7 @@
<?php
/*
* $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -55,11 +57,6 @@ abstract class AbstractQuery
*/
const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
const HYDRATE_SIMPLEOBJECT = 5;
/**
* @var array The parameter map of this query.
*/
@@ -162,16 +159,6 @@ 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.
@@ -184,17 +171,6 @@ 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
@@ -216,13 +192,10 @@ abstract class AbstractQuery
*/
public function setParameter($key, $value, $type = null)
{
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
if ($type !== null) {
$this->_paramTypes[$key] = $type;
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
return $this;
}
@@ -296,7 +269,7 @@ abstract class AbstractQuery
* @param boolean $bool
* @param integer $timeToLive
* @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return This query instance.
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
{
@@ -358,26 +331,6 @@ abstract class AbstractQuery
return $this->_expireResultCache;
}
/**
* Change the default fetch mode of an association for this query.
*
* $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
*
* @param string $class
* @param string $assocName
* @param int $fetchMode
* @return AbstractQuery
*/
public function setFetchMode($class, $assocName, $fetchMode)
{
if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
$fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
}
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
return $this;
}
/**
* Defines the processing mode to be used during hydration / result set transformation.
*
@@ -437,31 +390,6 @@ abstract class AbstractQuery
return $this->execute(array(), self::HYDRATE_SCALAR);
}
/**
* Get exactly one result or null.
*
* @throws NonUniqueResultException
* @param int $hydrationMode
* @return mixed
*/
public function getOneOrNullResult($hydrationMode = null)
{
$result = $this->execute(array(), $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
return null;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
return $result;
}
/**
* Gets the single result of the query.
*
@@ -582,6 +510,10 @@ abstract class AbstractQuery
$this->setParameters($params);
}
if (isset($this->_params[0])) {
throw QueryException::invalidParameterPosition(0);
}
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
@@ -592,8 +524,8 @@ abstract class AbstractQuery
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);

View File

@@ -20,11 +20,7 @@
namespace Doctrine\ORM;
use Doctrine\Common\Cache\Cache,
Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\Common\Annotations\AnnotationReader,
Doctrine\ORM\Mapping\Driver\Driver,
Doctrine\ORM\Mapping\Driver\AnnotationDriver;
Doctrine\ORM\Mapping\Driver\Driver;
/**
* Configuration container for all configuration options of Doctrine.
@@ -124,28 +120,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function newDefaultAnnotationDriver($paths = array())
{
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
$reader = new AnnotationReader();
$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);
$reader->setEnableParsePhpImports(false);
$reader = new \Doctrine\Common\Annotations\CachedReader(
new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache()
);
} else {
$reader = new AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
}
return new AnnotationDriver($reader, (array)$paths);
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
}
/**
@@ -185,16 +163,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
$this->_attributes['entityNamespaces'] = $entityNamespaces;
}
/**
* Retrieves the list of registered entity namespace aliases.
*
* @return array
*/
public function getEntityNamespaces()
{
return $this->_attributes['entityNamespaces'];
}
/**
* Gets the cache driver implementation that is used for the mapping metadata.

View File

@@ -21,7 +21,6 @@ namespace Doctrine\ORM;
use Closure, Exception,
Doctrine\Common\EventManager,
Doctrine\Common\Persistence\ObjectManager,
Doctrine\DBAL\Connection,
Doctrine\DBAL\LockMode,
Doctrine\ORM\Mapping\ClassMetadata,
@@ -38,7 +37,7 @@ use Closure, Exception,
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityManager implements ObjectManager
class EntityManager
{
/**
* The used Configuration.
@@ -203,18 +202,13 @@ class EntityManager implements ObjectManager
public function transactional(Closure $func)
{
$this->conn->beginTransaction();
try {
$return = $func($this);
$func($this);
$this->flush();
$this->conn->commit();
return $return ?: true;
} catch (Exception $e) {
$this->close();
$this->conn->rollback();
throw $e;
}
}
@@ -688,9 +682,6 @@ class EntityManager implements ObjectManager
case Query::HYDRATE_SINGLE_SCALAR:
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
break;
case Query::HYDRATE_SIMPLEOBJECT:
$hydrator = new Internal\Hydration\SimpleObjectHydrator($this);
break;
default:
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
$hydrator = new $class($this);

View File

@@ -20,7 +20,6 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\LockMode;
use Doctrine\Common\Persistence\ObjectRepository;
/**
* An EntityRepository serves as a repository for entities with generic as well as
@@ -35,7 +34,7 @@ use Doctrine\Common\Persistence\ObjectRepository;
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityRepository implements ObjectRepository
class EntityRepository
{
/**
* @var string
@@ -78,17 +77,6 @@ class EntityRepository implements ObjectRepository
->from($this->_entityName, $alias);
}
/**
* Create a new Query instance based on a predefined metadata named query.
*
* @param string $queryName
* @return Query
*/
public function createNamedQuery($queryName)
{
return $this->_em->createQuery($this->_class->getNamedQuery($queryName));
}
/**
* Clears the repository, causing all managed entities to become detached.
*/
@@ -160,14 +148,11 @@ class EntityRepository implements ObjectRepository
* Finds entities by a set of criteria.
*
* @param array $criteria
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
* @return array The objects.
* @return array
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
public function findBy(array $criteria)
{
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
}
/**

View File

@@ -1,54 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Event;
/**
* Provides event arguments for the onClear event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class OnClearEventArgs extends \Doctrine\Common\EventArgs
{
/**
* @var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct($em)
{
$this->em = $em;
}
/**
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
}

View File

@@ -119,12 +119,4 @@ final class Events
* @var string
*/
const onFlush = 'onFlush';
/**
* The onClear event occurs when the EntityManager#clear() operation is invoked,
* after all references to entities have been removed from the unit of work.
*
* @var string
*/
const onClear = 'onClear';
}

View File

@@ -47,18 +47,9 @@ class AssignedGenerator extends AbstractIdGenerator
if ($class->isIdentifierComposite) {
$idFields = $class->getIdentifierFieldNames();
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
$value = $class->getReflectionProperty($idField)->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
$identifier[$idField] = $value;
} else {
throw ORMException::entityMissingAssignedId($entity);
}
@@ -67,16 +58,7 @@ class AssignedGenerator extends AbstractIdGenerator
$idField = $class->identifier[0];
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
$identifier[$idField] = $value;
} else {
throw ORMException::entityMissingAssignedId($entity);
}

View File

@@ -190,12 +190,9 @@ abstract class AbstractHydrator
continue;
} else {
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$fieldName = $this->_rsm->metaMappings[$key];
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
}
}
@@ -206,22 +203,13 @@ abstract class AbstractHydrator
$dqlAlias = $cache[$key]['dqlAlias'];
if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value;
if (isset($cache[$key]['isMetaColumn'])) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
continue;
}
if (isset($cache[$key]['isMetaColumn'])) {
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
}
continue;
}
// in an inheritance hierachy the same field could be defined several times.
// We overwrite this value so long we dont have a non-null value, that value we keep.
// Per definition it cannot be that a field is defined several times and has several values.
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
continue;
if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value;
}
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
@@ -287,25 +275,4 @@ 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

@@ -59,10 +59,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_resultPointers =
$this->_idTemplate = array();
$this->_resultCounter = 0;
if (!isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
@@ -115,17 +112,11 @@ 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();
}
}
/**
@@ -205,32 +196,18 @@ 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);
}
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) {
if (isset($class->associationMappings[$fieldName])) {
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
} else {
$idHash .= $data[$fieldName] . ' ';
}
$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);
}
@@ -422,10 +399,6 @@ class ObjectHydrator extends AbstractHydrator
$result[$key] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
}
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateSet($key, $element);
}
} else {
if ($this->_rsm->isMixed) {
$element = array(0 => $element);
@@ -433,10 +406,6 @@ class ObjectHydrator extends AbstractHydrator
$result[] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
++$this->_resultCounter;
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateAdd($element);
}
}
// Update result pointer

View File

@@ -1,131 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
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
{
/**
* @var ClassMetadata
*/
private $class;
private $declaringClasses = array();
protected function _hydrateAll()
{
$result = array();
$cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result);
}
$this->_em->getUnitOfWork()->triggerEagerLoads();
return $result;
}
protected function _prepare()
{
if (count($this->_rsm->aliasMap) == 1) {
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
}
} else {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
}
if ($this->_rsm->scalarMappings) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
}
}
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
{
$data = array();
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
$cache[$column]['field'] = true;
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
}
if (isset($cache[$column]['field'])) {
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
$data[$cache[$column]['name']] = $value;
}
$entityName = $this->class->name;
} else {
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$field = $this->_rsm->fieldMappings[$column];
$class = $this->declaringClasses[$column];
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$cache[$column]['name'] = $field;
$cache[$column]['class'] = $class;
}
} else if (isset($this->_rsm->relationMap[$column])) {
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
$cache[$column]['name'] = $field;
}
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
}
if (isset($cache[$column]['class'])) {
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
// the second and part is to prevent overwrites in case of multiple
// inheritance classes using the same property name (See AbstractHydrator)
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
$data[$cache[$column]['name']] = $value;
}
}
}
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

@@ -63,10 +63,10 @@ class ClassMetadata extends ClassMetadataInfo
*/
public function __construct($entityName)
{
parent::__construct($entityName);
$this->reflClass = new ReflectionClass($entityName);
$this->namespace = $this->reflClass->getNamespaceName();
$this->table['name'] = $this->reflClass->getShortName();
parent::__construct($this->reflClass->getName()); // do not use $entityName, possible case-problems
}
/**
@@ -205,6 +205,48 @@ class ClassMetadata extends ClassMetadataInfo
$this->reflFields[$sourceFieldName] = $refProp;
}
/**
* 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
*/
public function getQuotedColumnName($field, $platform)
{
return isset($this->fieldMappings[$field]['quoted']) ?
$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
*/
public function getQuotedTableName($platform)
{
return isset($this->table['quoted']) ?
$platform->quoteIdentifier($this->table['name']) :
$this->table['name'];
}
/**
* Gets the (possibly quoted) name of the join table.
*
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedJoinTableName(array $assoc, $platform)
{
return isset($assoc['joinTable']['quoted'])
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
}
/**
* Creates a string representation of this instance.
*
@@ -275,10 +317,6 @@ class ClassMetadata extends ClassMetadataInfo
$serialized[] = 'isMappedSuperclass';
}
if ($this->containsForeignIdentifier) {
$serialized[] = 'containsForeignIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
@@ -288,14 +326,6 @@ class ClassMetadata extends ClassMetadataInfo
$serialized[] = 'lifecycleCallbacks';
}
if ($this->namedQueries) {
$serialized[] = 'namedQueries';
}
if ($this->isReadOnly) {
$serialized[] = 'isReadOnly';
}
return $serialized;
}

View File

@@ -23,8 +23,7 @@ use ReflectionException,
Doctrine\ORM\ORMException,
Doctrine\ORM\EntityManager,
Doctrine\DBAL\Platforms,
Doctrine\ORM\Events,
Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface;
Doctrine\ORM\Events;
/**
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
@@ -37,7 +36,7 @@ use ReflectionException,
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ClassMetadataFactory implements ClassMetadataFactoryInterface
class ClassMetadataFactory
{
/**
* @var EntityManager
@@ -248,13 +247,11 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
// Move down the hierarchy of parent classes, starting from the topmost class
$parent = null;
$rootEntityFound = false;
$visited = array();
foreach ($parentClasses as $className) {
if (isset($this->loadedMetadata[$className])) {
$parent = $this->loadedMetadata[$className];
if ( ! $parent->isMappedSuperclass) {
$rootEntityFound = true;
array_unshift($visited, $className);
}
continue;
@@ -263,15 +260,19 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class = $this->newClassMetadataInstance($className);
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
if (!$parent->isMappedSuperclass) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
}
$class->setIdGeneratorType($parent->generatorType);
$this->addInheritedFields($class, $parent);
$this->addInheritedRelations($class, $parent);
$class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField);
$class->setDiscriminatorMap($parent->discriminatorMap);
if (!$parent->isMappedSuperclass) {
$class->setDiscriminatorMap($parent->discriminatorMap);
}
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
}
@@ -283,10 +284,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
throw MappingException::reflectionFailure($className, $e);
}
// If this class has a parent the id generator strategy is inherited.
// However this is only true if the hierachy of parents contains the root entity,
// if it consinsts of mapped superclasses these don't necessarily include the id field.
if ($parent && $rootEntityFound) {
if ($parent && ! $parent->isMappedSuperclass) {
if ($parent->isIdGeneratorSequence()) {
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
} else if ($parent->isIdGeneratorTable()) {
@@ -313,14 +311,26 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->validateRuntimeMetadata($class, $parent);
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
// verify inheritance
if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
}
$this->loadedMetadata[$className] = $class;
$parent = $class;
if ( ! $class->isMappedSuperclass) {
$rootEntityFound = true;
array_unshift($visited, $className);
}
@@ -330,38 +340,6 @@ 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.
*

View File

@@ -19,7 +19,6 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use ReflectionClass;
/**
@@ -40,7 +39,7 @@ use ReflectionClass;
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
*/
class ClassMetadataInfo implements ClassMetadata
class ClassMetadataInfo
{
/* The inheritance mapping types */
/**
@@ -122,12 +121,6 @@ class ClassMetadataInfo implements ClassMetadata
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
* Specifies that an association is to be fetched lazy (on first access) and that
* commands such as Collection#count, Collection#slice are issued directly against
* the database if the collection is not yet initialized.
*/
const FETCH_EXTRA_LAZY = 4;
/**
* Identifies a one-to-one association.
*/
@@ -204,13 +197,6 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $subClasses = array();
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
* @var array
*/
public $namedQueries = array();
/**
* READ-ONLY: The field names of all fields that are part of the identifier/primary key
* of the mapped entity class.
@@ -269,7 +255,7 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>scale</b> (integer, optional, schema-only)
* The scale of a decimal column. Only valid if the column type is decimal.
*
[* - <b>'unique'] (string, optional, schema-only)</b>
* - <b>unique (string, optional, schema-only)</b>
* Whether a unique constraint should be generated for the column.
*
* @var array
@@ -335,6 +321,7 @@ class ClassMetadataInfo implements ClassMetadata
* uniqueConstraints => array
*
* @var array
* @todo Rename to just $table
*/
public $table;
@@ -383,11 +370,6 @@ class ClassMetadataInfo implements ClassMetadata
* Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
* through a join table by simply mapping the association as many-to-many with a unique
* constraint on the join table.
*
* - <b>indexBy</b> (string, optional, to-many only)
* 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>
@@ -410,15 +392,6 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $isIdentifierComposite = false;
/**
* READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
*
* This flag is necessary because some code blocks require special treatment of this cases.
*
* @var boolean
*/
public $containsForeignIdentifier = false;
/**
* READ-ONLY: The ID generator used for generating IDs for this class.
*
@@ -483,17 +456,6 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $reflClass;
/**
* Is this entity marked as "read-only"?
*
* That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
* optimization for entities that are immutable, either in your domain or through the relation database
* (coming from a view, or a history table for example).
*
* @var bool
*/
public $isReadOnly = false;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
@@ -672,32 +634,6 @@ class ClassMetadataInfo implements ClassMetadata
$this->fieldNames[$columnName] : $columnName;
}
/**
* Gets the named query.
*
* @see ClassMetadataInfo::$namedQueries
* @throws MappingException
* @param string $queryName The query name
* @return string
*/
public function getNamedQuery($queryName)
{
if ( ! isset($this->namedQueries[$queryName])) {
throw MappingException::queryNotFound($this->name, $queryName);
}
return $this->namedQueries[$queryName];
}
/**
* Gets all named queries of the class.
*
* @return array
*/
public function getNamedQueries()
{
return $this->namedQueries;
}
/**
* Validates & completes the given field mapping.
*
@@ -766,46 +702,14 @@ class ClassMetadataInfo implements ClassMetadata
}
$mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
// unset optional indexBy attribute if its empty
if (!isset($mapping['indexBy']) || !$mapping['indexBy']) {
unset($mapping['indexBy']);
}
// If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity.
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity'])) {
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
&& strlen($this->namespace) > 0) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
// Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) {
if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
}
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
if (count($mapping['joinColumns']) >= 2) {
throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
$mapping['targetEntity'], $this->name, $mapping['fieldName']
);
}
$this->identifier[] = $mapping['fieldName'];
$this->containsForeignIdentifier = true;
}
// Check for composite key
if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
$this->isIdentifierComposite = true;
}
}
// Mandatory attributes for both sides
// Mandatory: fieldName, targetEntity
if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
throw MappingException::missingFieldName($this->name);
@@ -825,10 +729,6 @@ class ClassMetadataInfo implements ClassMetadata
} else {
$mapping['isOwningSide'] = false;
}
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'])) {
@@ -836,7 +736,7 @@ class ClassMetadataInfo implements ClassMetadata
}
// Cascades
$cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
$cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
if (in_array('all', $cascades)) {
$cascades = array(
'remove',
@@ -879,15 +779,9 @@ class ClassMetadataInfo implements ClassMetadata
'referencedColumnName' => 'id'
));
}
$uniqueContraintColumns = array();
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE) {
if (count($mapping['joinColumns']) == 1) {
$joinColumn['unique'] = true;
} else {
$uniqueContraintColumns[] = $joinColumn['name'];
}
$joinColumn['unique'] = true;
}
if (empty($joinColumn['name'])) {
$joinColumn['name'] = $mapping['fieldName'] . '_id';
@@ -899,25 +793,12 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
? $joinColumn['fieldName'] : $joinColumn['name'];
}
if ($uniqueContraintColumns) {
if (!$this->table) {
throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
}
$this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
'columns' => $uniqueContraintColumns
);
}
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
}
$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']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
return $mapping;
}
@@ -938,8 +819,9 @@ class ClassMetadataInfo implements ClassMetadata
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
@@ -1070,16 +952,6 @@ class ClassMetadataInfo implements ClassMetadata
$this->isIdentifierComposite = (count($this->identifier) > 1);
}
/**
* Gets the mapped identifier field of this class.
*
* @return string $identifier
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* Checks whether the class has a (mapped) field with a certain name.
*
@@ -1118,19 +990,11 @@ class ClassMetadataInfo implements ClassMetadata
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[] = $this->fieldMappings[$idField]['columnName'];
}
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']);
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
}
}
@@ -1414,7 +1278,8 @@ class ClassMetadataInfo implements ClassMetadata
* Adds an association mapping without completing/validating it.
* This is mainly used to add inherited association mappings to derived classes.
*
* @param array $mapping
* @param AssociationMapping $mapping
* @param string $owningClassName The name of the class that defined this mapping.
*/
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
{
@@ -1430,6 +1295,7 @@ class ClassMetadataInfo implements ClassMetadata
* This is mainly used to add inherited field mappings to derived classes.
*
* @param array $mapping
* @todo Rename: addInheritedFieldMapping
*/
public function addInheritedFieldMapping(array $fieldMapping)
{
@@ -1438,22 +1304,6 @@ class ClassMetadataInfo implements ClassMetadata
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
}
/**
* INTERNAL:
* Adds a named query to this class.
*
* @throws MappingException
* @param array $queryMapping
*/
public function addNamedQuery(array $queryMapping)
{
if (isset($this->namedQueries[$queryMapping['name']])) {
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
}
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
$this->namedQueries[$queryMapping['name']] = $query;
}
/**
* Adds a one-to-one mapping.
*
@@ -1627,13 +1477,11 @@ class ClassMetadataInfo implements ClassMetadata
public function setDiscriminatorMap(array $map)
{
foreach ($map as $value => $className) {
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
if (strpos($className, '\\') === false && strlen($this->namespace)) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$value] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;
} else {
@@ -1647,17 +1495,6 @@ class ClassMetadataInfo implements ClassMetadata
}
}
/**
* Checks whether the class has a named query with the given query name.
*
* @param string $fieldName
* @return boolean
*/
public function hasNamedQuery($queryName)
{
return isset($this->namedQueries[$queryName]);
}
/**
* Checks whether the class has a mapped association with the given field name.
*
@@ -1695,74 +1532,6 @@ class ClassMetadataInfo implements ClassMetadata
! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
}
/**
* Is this an association that only has a single join column?
*
* @param string $fieldName
* @return bool
*/
public function isAssociationWithSingleJoinColumn($fieldName)
{
return (
isset($this->associationMappings[$fieldName]) &&
isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
!isset($this->associationMappings[$fieldName]['joinColumns'][1])
);
}
/**
* Return the single association join column (if any).
*
* @param string $fieldName
* @return string
*/
public function getSingleAssociationJoinColumnName($fieldName)
{
if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
}
return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
}
/**
* Return the single association referenced join column name (if any).
*
* @param string $fieldName
* @return string
*/
public function getSingleAssociationReferencedJoinColumnName($fieldName)
{
if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
}
return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
}
/**
* Used to retrieve a fieldname for either field or association from a given column,
*
* This method is used in foreign-key as primary-key contexts.
*
* @param string $columnName
* @return string
*/
public function getFieldForColumn($columnName)
{
if (isset($this->fieldNames[$columnName])) {
return $this->fieldNames[$columnName];
} else {
foreach ($this->associationMappings AS $assocName => $mapping) {
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
return $assocName;
}
}
throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
}
}
/**
* Sets the ID generator used to generate IDs for instances of this class.
*
@@ -1834,104 +1603,4 @@ class ClassMetadataInfo implements ClassMetadata
{
$this->versionField = $versionField;
}
/**
* Mark this class as read only, no change tracking is applied to it.
*
* @return void
*/
public function markReadOnly()
{
$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
*/
public function getAssociationTargetClass($assocName)
{
if (!isset($this->associationMappings[$assocName])) {
throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
}
return $this->associationMappings[$assocName]['targetEntity'];
}
/**
* Get fully-qualified class name of this persistent class.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 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
*/
public function getQuotedColumnName($field, $platform)
{
return isset($this->fieldMappings[$field]['quoted']) ?
$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
*/
public function getQuotedTableName($platform)
{
return isset($this->table['quoted']) ?
$platform->quoteIdentifier($this->table['name']) :
$this->table['name'];
}
/**
* Gets the (possibly quoted) name of the join table.
*
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedJoinTableName(array $assoc, $platform)
{
return isset($assoc['joinTable']['quoted'])
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
}
}
}

View File

@@ -21,10 +21,11 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Annotations\AnnotationReader,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\MappingException;
require __DIR__ . '/DoctrineAnnotations.php';
/**
* The AnnotationDriver reads the mapping metadata from docblock annotations.
*
@@ -41,7 +42,7 @@ class AnnotationDriver implements Driver
*
* @var AnnotationReader
*/
protected $_reader;
private $_reader;
/**
* The paths where to look for mapping files.
@@ -61,13 +62,13 @@ class AnnotationDriver implements Driver
* @param array
*/
protected $_classNames;
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
*
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed.
* @param string|array $paths One or multiple paths where mapping classes can be found.
* @param string|array $paths One or multiple paths where mapping classes can be found.
*/
public function __construct($reader, $paths = null)
{
@@ -76,7 +77,7 @@ class AnnotationDriver implements Driver
$this->addPaths((array) $paths);
}
}
/**
* Append lookup paths to metadata driver.
*
@@ -127,21 +128,10 @@ class AnnotationDriver implements Driver
$classAnnotations = $this->_reader->getClassAnnotations($class);
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
$classAnnotations[get_class($annot)] = $annot;
}
}
// Evaluate Entity annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
if ($entityAnnot->readOnly) {
$metadata->markReadOnly();
}
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
$metadata->isMappedSuperclass = true;
} else {
@@ -175,18 +165,6 @@ class AnnotationDriver implements Driver
$metadata->setPrimaryTable($primaryTable);
}
// Evaluate NamedQueries annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
$namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
foreach ($namedQueriesAnnot->value as $namedQuery) {
$metadata->addNamedQuery(array(
'name' => $namedQuery->name,
'query' => $namedQuery->query
));
}
}
// Evaluate InheritanceType annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
$inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
@@ -310,10 +288,6 @@ class AnnotationDriver implements Driver
throw MappingException::tableIdGeneratorNotImplemented($className);
}
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
$mapping['id'] = true;
}
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
$mapping['joinColumns'] = $joinColumns;
$mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
@@ -326,7 +300,6 @@ class AnnotationDriver implements Driver
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
$mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
$mapping['cascade'] = $oneToManyAnnot->cascade;
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
@@ -336,10 +309,6 @@ class AnnotationDriver implements Driver
$metadata->mapOneToMany($mapping);
} else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
$mapping['id'] = true;
}
$mapping['joinColumns'] = $joinColumns;
$mapping['cascade'] = $manyToOneAnnot->cascade;
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
@@ -385,7 +354,6 @@ class AnnotationDriver implements Driver
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
@@ -403,13 +371,6 @@ class AnnotationDriver implements Driver
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->_reader->getMethodAnnotations($method);
// Compatibility with Doctrine Common 3.x
if ($annotations && is_int(key($annotations))) {
foreach ($annotations as $annot) {
$annotations[get_class($annot)] = $annot;
}
}
if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist);
}
@@ -455,20 +416,6 @@ class AnnotationDriver implements Driver
{
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
if ($annot instanceof \Doctrine\ORM\Mapping\Entity) {
return false;
}
if ($annot instanceof \Doctrine\ORM\Mapping\MappedSuperclass) {
return false;
}
}
return true;
}
return ! isset($classAnnotations['Doctrine\ORM\Mapping\Entity']) &&
! isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']);
}
@@ -494,20 +441,18 @@ class AnnotationDriver implements Driver
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RegexIterator(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
),
'/^.+\\' . $this->_fileExtension . '$/i',
\RecursiveRegexIterator::GET_MATCH
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
$sourceFile = realpath($file[0]);
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
continue;
}
$sourceFile = realpath($file->getPathName());
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
}
@@ -529,7 +474,7 @@ class AnnotationDriver implements Driver
/**
* Factory method for the Annotation Driver
*
*
* @param array|string $paths
* @param AnnotationReader $reader
* @return AnnotationDriver

View File

@@ -55,24 +55,7 @@ class DatabaseDriver implements Driver
* @var array
*/
private $manyToManyTables = array();
/**
* @var array
*/
private $classNamesForTables = array();
/**
* @var array
*/
private $fieldNamesForColumns = array();
/**
* The namespace for the generated entities.
*
* @var string
*/
private $namespace;
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
@@ -95,7 +78,7 @@ class DatabaseDriver implements Driver
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables AS $table) {
$className = $this->getClassNameForTable($table->getName());
$className = Inflector::classify(strtolower($table->getName()));
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
@@ -110,8 +93,6 @@ class DatabaseDriver implements Driver
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
@@ -139,7 +120,7 @@ class DatabaseDriver implements Driver
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$className = Inflector::classify(strtolower($tableName));
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
@@ -191,7 +172,7 @@ class DatabaseDriver implements Driver
continue;
}
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['fieldName'] = Inflector::camelize(strtolower($column->getName()));
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
@@ -245,10 +226,10 @@ class DatabaseDriver implements Driver
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower(current($otherFk->getColumns()))));
$associationMapping['targetEntity'] = Inflector::classify(strtolower($otherFk->getForeignTableName()));
if (current($manyTable->getColumns())->getName() == $localColumn) {
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['inversedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
$associationMapping['joinTable'] = array(
'name' => strtolower($manyTable->getName()),
'joinColumns' => array(),
@@ -273,7 +254,7 @@ class DatabaseDriver implements Driver
);
}
} else {
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['mappedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
}
$metadata->mapManyToMany($associationMapping);
break;
@@ -288,8 +269,8 @@ class DatabaseDriver implements Driver
$localColumn = current($cols);
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower($localColumn)));
$associationMapping['targetEntity'] = Inflector::classify($foreignTable);
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
@@ -322,78 +303,4 @@ class DatabaseDriver implements Driver
return array_keys($this->classToTableNames);
}
/**
* Set class name for a table.
*
* @param string $tableName
* @param string $className
* @return void
*/
public function setClassNameForTable($tableName, $className)
{
$this->classNamesForTables[$tableName] = $className;
}
/**
* Set field name for a column on a specific table.
*
* @param string $tableName
* @param string $columnName
* @param string $fieldName
* @return void
*/
public function setFieldNameForColumn($tableName, $columnName, $fieldName)
{
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
}
/**
* Return the mapped class name for a table if it exists. Otherwise return "classified" version.
*
* @param string $tableName
* @return string
*/
private function getClassNameForTable($tableName)
{
if (isset($this->classNamesForTables[$tableName])) {
return $this->namespace . $this->classNamesForTables[$tableName];
}
return $this->namespace . Inflector::classify(strtolower($tableName));
}
/**
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
*
* @param string $tableName
* @param string $columnName
* @param boolean $fk Whether the column is a foreignkey or not.
* @return string
*/
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
{
if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) {
return $this->fieldNamesForColumns[$tableName][$columnName];
}
$columnName = strtolower($columnName);
// Replace _id if it is a foreignkey column
if ($fk) {
$columnName = str_replace('_id', '', $columnName);
}
return Inflector::camelize($columnName);
}
/**
* Set the namespace for the generated entities.
*
* @param string $namespace
* @return void
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
}
}

View File

@@ -1,5 +1,7 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -23,41 +25,23 @@ use Doctrine\Common\Annotations\Annotation;
/* Annotations */
/** @Annotation */
final class Entity extends Annotation {
public $repositoryClass;
public $readOnly = false;
}
/** @Annotation */
final class MappedSuperclass extends Annotation {}
/** @Annotation */
final class InheritanceType extends Annotation {}
/** @Annotation */
final class DiscriminatorColumn extends Annotation {
public $name;
public $fieldName; // field name used in non-object hydration (array/scalar)
public $type;
public $length;
}
/** @Annotation */
final class DiscriminatorMap extends Annotation {}
/** @Annotation */
final class Id extends Annotation {}
/** @Annotation */
final class GeneratedValue extends Annotation {
public $strategy = 'AUTO';
}
/** @Annotation */
final class Version extends Annotation {}
/** @Annotation */
final class JoinColumn extends Annotation {
public $name;
public $fieldName; // field name used in non-object hydration (array/scalar)
@@ -68,11 +52,7 @@ final class JoinColumn extends Annotation {
public $onUpdate;
public $columnDefinition;
}
/** @Annotation */
final class JoinColumns extends Annotation {}
/** @Annotation */
final class Column extends Annotation {
public $type = 'string';
public $length;
@@ -86,8 +66,6 @@ final class Column extends Annotation {
public $options = array();
public $columnDefinition;
}
/** @Annotation */
final class OneToOne extends Annotation {
public $targetEntity;
public $mappedBy;
@@ -96,111 +74,64 @@ final class OneToOne extends Annotation {
public $fetch = 'LAZY';
public $orphanRemoval = false;
}
/** @Annotation */
final class OneToMany extends Annotation {
public $mappedBy;
public $targetEntity;
public $cascade;
public $fetch = 'LAZY';
public $orphanRemoval = false;
public $indexBy;
}
/** @Annotation */
final class ManyToOne extends Annotation {
public $targetEntity;
public $cascade;
public $fetch = 'LAZY';
public $inversedBy;
}
/** @Annotation */
final class ManyToMany extends Annotation {
public $targetEntity;
public $mappedBy;
public $inversedBy;
public $cascade;
public $fetch = 'LAZY';
public $indexBy;
}
/** @Annotation */
final class ElementCollection extends Annotation {
public $tableName;
}
/** @Annotation */
final class Table extends Annotation {
public $name;
public $schema;
public $indexes;
public $uniqueConstraints;
}
/** @Annotation */
final class UniqueConstraint extends Annotation {
public $name;
public $columns;
}
/** @Annotation */
final class Index extends Annotation {
public $name;
public $columns;
}
/** @Annotation */
final class JoinTable extends Annotation {
public $name;
public $schema;
public $joinColumns = array();
public $inverseJoinColumns = array();
}
/** @Annotation */
final class SequenceGenerator extends Annotation {
public $sequenceName;
public $allocationSize = 1;
public $initialValue = 1;
}
/** @Annotation */
final class ChangeTrackingPolicy extends Annotation {}
/** @Annotation */
final class OrderBy extends Annotation {}
/** @Annotation */
final class NamedQueries extends Annotation {}
/** @Annotation */
final class NamedQuery extends Annotation {
public $name;
public $query;
}
/* Annotations for lifecycle callbacks */
/** @Annotation */
final class HasLifecycleCallbacks extends Annotation {}
/** @Annotation */
final class PrePersist extends Annotation {}
/** @Annotation */
final class PostPersist extends Annotation {}
/** @Annotation */
final class PreUpdate extends Annotation {}
/** @Annotation */
final class PostUpdate extends Annotation {}
/** @Annotation */
final class PreRemove extends Annotation {}
/** @Annotation */
final class PostRemove extends Annotation {}
/** @Annotation */
final class PostLoad extends Annotation {}

View File

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

View File

@@ -55,9 +55,6 @@ class XmlDriver extends AbstractFileDriver
$metadata->setCustomRepositoryClass(
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
);
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
$metadata->markReadOnly();
}
} else if ($xmlRoot->getName() == 'mapped-superclass') {
$metadata->isMappedSuperclass = true;
} else {
@@ -72,16 +69,6 @@ class XmlDriver extends AbstractFileDriver
$metadata->setPrimaryTable($table);
// Evaluate named queries
if (isset($xmlRoot['named-queries'])) {
foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
$metadata->addNamedQuery(array(
'name' => (string)$namedQueryElement['name'],
'query' => (string)$namedQueryElement['query']
));
}
}
/* not implemented specially anyway. use table = schema.table
if (isset($xmlRoot['schema'])) {
$metadata->table['schema'] = (string)$xmlRoot['schema'];
@@ -207,13 +194,7 @@ class XmlDriver extends AbstractFileDriver
}
// Evaluate <id ...> mappings
$associationIds = array();
foreach ($xmlRoot->id as $idElement) {
if ((bool)$idElement['association-key'] == true) {
$associationIds[(string)$idElement['fieldName']] = true;
continue;
}
$mapping = array(
'id' => true,
'fieldName' => (string)$idElement['name'],
@@ -254,10 +235,6 @@ class XmlDriver extends AbstractFileDriver
'targetEntity' => (string)$oneToOneElement['target-entity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($oneToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']);
}
@@ -322,10 +299,6 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($oneToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$oneToManyElement->{'index-by'};
}
$metadata->mapOneToMany($mapping);
}
}
@@ -338,10 +311,6 @@ class XmlDriver extends AbstractFileDriver
'targetEntity' => (string)$manyToOneElement['target-entity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($manyToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']);
}
@@ -429,10 +398,6 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
}
$metadata->mapManyToMany($mapping);
}
}

View File

@@ -49,9 +49,6 @@ class YamlDriver extends AbstractFileDriver
$metadata->setCustomRepositoryClass(
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
);
if (isset($element['readOnly']) && $element['readOnly'] == true) {
$metadata->markReadOnly();
}
} else if ($element['type'] == 'mappedSuperclass') {
$metadata->isMappedSuperclass = true;
} else {
@@ -65,21 +62,6 @@ class YamlDriver extends AbstractFileDriver
}
$metadata->setPrimaryTable($table);
// Evaluate named queries
if (isset($element['namedQueries'])) {
foreach ($element['namedQueries'] as $name => $queryMapping) {
if (is_string($queryMapping)) {
$queryMapping = array('query' => $queryMapping);
}
if ( ! isset($queryMapping['name'])) {
$queryMapping['name'] = $name;
}
$metadata->addNamedQuery($queryMapping);
}
}
/* not implemented specially anyway. use table = schema.table
if (isset($element['schema'])) {
$metadata->table['schema'] = $element['schema'];
@@ -153,15 +135,9 @@ class YamlDriver extends AbstractFileDriver
}
}
$associationIds = array();
if (isset($element['id'])) {
// Evaluate identifier settings
foreach ($element['id'] as $name => $idElement) {
if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) {
$associationIds[$name] = true;
continue;
}
if (!isset($idElement['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
}
@@ -258,10 +234,6 @@ class YamlDriver extends AbstractFileDriver
'targetEntity' => $oneToOneElement['targetEntity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($oneToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']);
}
@@ -315,20 +287,16 @@ class YamlDriver extends AbstractFileDriver
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']);
}
if (isset($oneToManyElement['cascade'])) {
$mapping['cascade'] = $oneToManyElement['cascade'];
}
if (isset($oneToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval'];
}
if (isset($oneToManyElement['orderBy'])) {
$mapping['orderBy'] = $oneToManyElement['orderBy'];
if (isset($oneToManyElement['cascade'])) {
$mapping['cascade'] = $oneToManyElement['cascade'];
}
if (isset($oneToManyElement['indexBy'])) {
$mapping['indexBy'] = $oneToManyElement['indexBy'];
if (isset($oneToManyElement['orderBy'])) {
$mapping['orderBy'] = $oneToManyElement['orderBy'];
}
$metadata->mapOneToMany($mapping);
@@ -343,10 +311,6 @@ class YamlDriver extends AbstractFileDriver
'targetEntity' => $manyToOneElement['targetEntity']
);
if (isset($associationIds[$mapping['fieldName']])) {
$mapping['id'] = true;
}
if (isset($manyToOneElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']);
}
@@ -375,10 +339,6 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToOneElement['cascade'];
}
if (isset($manyToOneElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToOneElement['orphanRemoval'];
}
$metadata->mapManyToOne($mapping);
}
}
@@ -434,18 +394,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToManyElement['cascade'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
if (isset($manyToManyElement['orderBy'])) {
$mapping['orderBy'] = $manyToManyElement['orderBy'];
}
if (isset($manyToManyElement['indexBy'])) {
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
$metadata->mapManyToMany($mapping);
}
}

View File

@@ -73,11 +73,6 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("No mapping found for field '$fieldName' on class '$className'.");
}
public static function queryNotFound($className, $queryName)
{
return new self("No query found named '$queryName' on class '$className'.");
}
public static function oneToManyRequiresMappedBy($fieldName)
{
return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute.");
@@ -165,10 +160,6 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function duplicateQueryMapping($entity, $queryName) {
return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once');
}
public static function singleIdNotAllowedOnCompositePrimaryKey($entity) {
return new self('Single id is not allowed on composite primary key in entity '.$entity);
}
@@ -240,57 +231,4 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
}
/**
* @param string $className
* @param string $targetEntity
* @param string $targetField
* @return self
*/
public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField)
{
return new self("It is not possible to map entity '".$className."' with a composite primary key ".
"as part of the primary key of another entity '".$targetEntity."#".$targetField."'.");
}
public static function noSingleAssociationJoinColumnFound($className, $field)
{
return new self("'$className#$field' is not an association with a single join column.");
}
public static function noFieldNameFoundForColumn($className, $column)
{
return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ".
"field does not exist or an association exists but it has multiple join columns.");
}
public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field)
{
return new self("The orphan removal option is not allowed on an association that is ".
"part of the identifier in '$className#$field'.");
}
public static function illegalInverseIdentifierAssocation($className, $field)
{
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
}
public static function illegalToManyIdentifierAssoaction($className, $field)
{
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
}
public static function noInheritanceOnMappedSuperClass($className)
{
return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'.");
}
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
{
return new self(
"Entity '" . $className . "' has to be part of the descriminator map of '" . $rootClassName . "' " .
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
"to avoid this exception from occuring."
);
}
}

View File

@@ -34,25 +34,10 @@ class ORMException extends Exception
return new self("It's a requirement to specify a Metadata Driver and pass it ".
"to Doctrine\ORM\Configuration::setMetadataDriverImpl().");
}
public static function entityMissingForeignAssignedId($entity, $relatedEntity)
{
return new self(
"Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " .
"however this entity has no ientity itself. You have to call EntityManager#persist() on the related entity " .
"and make sure it an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " .
"of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call " .
"EntityManager#flush() between both persist operations."
);
}
public static function entityMissingAssignedId($entity)
{
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " .
"The identifier generation strategy for this entity requires the ID field to be populated before ".
"EntityManager#persist() is called. If you want automatically generated identifiers instead " .
"you need to adjust the metadata mapping accordingly."
);
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID.");
}
public static function unrecognizedField($field)

View File

@@ -59,7 +59,7 @@ final class PersistentCollection implements Collection
* The association mapping the collection belongs to.
* This is currently either a OneToManyMapping or a ManyToManyMapping.
*
* @var array
* @var Doctrine\ORM\Mapping\AssociationMapping
*/
private $association;
@@ -404,12 +404,22 @@ final class PersistentCollection implements Collection
*/
public function contains($element)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->coll->contains($element) ||
$this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->contains($this, $element);
}
/* DRAFT
if ($this->initialized) {
return $this->coll->contains($element);
} else {
if ($element is MANAGED) {
if ($this->coll->contains($element)) {
return true;
}
$exists = check db for existence;
if ($exists) {
$this->coll->add($element);
}
return $exists;
}
return false;
}*/
$this->initialize();
return $this->coll->contains($element);
@@ -465,12 +475,6 @@ final class PersistentCollection implements Collection
*/
public function count()
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->count($this) + $this->coll->count();
}
$this->initialize();
return $this->coll->count();
}
@@ -672,12 +676,6 @@ final class PersistentCollection implements Collection
*/
public function slice($offset, $length = null)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->slice($this, $offset, $length);
}
$this->initialize();
return $this->coll->slice($offset, $length);
}

View File

@@ -125,31 +125,6 @@ abstract class AbstractCollectionPersister
}
}
public function count(PersistentCollection $coll)
{
throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister.");
}
public function slice(PersistentCollection $coll, $offset, $length = null)
{
throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister.");
}
public function contains(PersistentCollection $coll, $element)
{
throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister.");
}
public function containsKey(PersistentCollection $coll, $key)
{
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
}
public function get(PersistentCollection $coll, $index)
{
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
}
/**
* Gets the SQL statement used for deleting a row from the collection.
*

View File

@@ -28,11 +28,24 @@ use Doctrine\ORM\Mapping\ClassMetadata,
* types in the hierarchy.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
*/
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
/**
* Map from column names to class metadata instances that declare the field the column is mapped to.
*
* @var array
*/
private $declaringClassMap = array();
/**
* Map from column names to class names that declare the field the association with join column is mapped to.
*
* @var array
*/
private $declaringJoinColumnMap = array();
/**
* {@inheritdoc}
*/
@@ -56,12 +69,49 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
/**
* {@inheritdoc}
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
protected function _processSQLResult(array $sqlResult)
{
$data = array();
$discrColumnName = $this->_platform->getSQLResultCasing($this->_class->discriminatorColumn['name']);
$entityName = $this->_class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
foreach ($sqlResult as $column => $value) {
$realColumnName = $this->_resultColumnNames[$column];
if (isset($this->declaringClassMap[$column])) {
$class = $this->declaringClassMap[$column];
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$field = $class->fieldNames[$realColumnName];
if (isset($data[$field])) {
$data[$realColumnName] = $value;
} else {
$data[$field] = Type::getType($class->fieldMappings[$field]['type'])
->convertToPHPValue($value, $this->_platform);
}
}
} else if (isset($this->declaringJoinColumnMap[$column])) {
if ($this->declaringJoinColumnMap[$column] == $entityName || is_subclass_of($entityName, $this->declaringJoinColumnMap[$column])) {
$data[$realColumnName] = $value;
}
} else {
$data[$realColumnName] = $value;
}
}
return array($entityName, $data);
}
/**
* {@inheritdoc}
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class)
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
$this->declaringClassMap[$columnAlias] = $class;
}
return "$sql AS $columnAlias";
}
@@ -70,7 +120,10 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $joinColumnName;
$this->declaringJoinColumnMap[$resultColumnName] = $className;
}
return $tableAlias . ".$joinColumnName AS $columnAlias";
}

View File

@@ -22,7 +22,6 @@ namespace Doctrine\ORM\Persisters;
use PDO,
Doctrine\DBAL\LockMode,
Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Connection,
Doctrine\ORM\ORMException,
Doctrine\ORM\OptimisticLockException,
Doctrine\ORM\EntityManager,
@@ -70,7 +69,6 @@ use PDO,
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
*/
class BasicEntityPersister
@@ -109,15 +107,15 @@ class BasicEntityPersister
* @var array
*/
protected $_queuedInserts = array();
/**
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
* Case-sensitive mappings of column names as they appear in an SQL result set
* to column names as they are defined in the mapping. This is necessary because different
* RDBMS vendors return column names in result sets in different casings.
*
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
*
* @var Query\ResultSetMapping
* @var array
*/
protected $_rsm;
protected $_resultColumnNames = array();
/**
* The map of column names to DBAL mapping types of all prepared columns used
@@ -144,14 +142,6 @@ class BasicEntityPersister
* @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.
@@ -344,16 +334,9 @@ class BasicEntityPersister
$where = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($this->_class->identifier as $idField) {
if (isset($this->_class->associationMappings[$idField])) {
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
$where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name'];
$params[] = $id[$idField];
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
} else {
$where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
$params[] = $id[$idField];
$types[] = $this->_class->fieldMappings[$idField]['type'];
}
$where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
$params[] = $id[$idField];
$types[] = $this->_class->fieldMappings[$idField]['type'];
}
if ($versioned) {
@@ -504,8 +487,6 @@ class BasicEntityPersister
foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
if ($newVal === null) {
$result[$owningTable][$sourceColumn] = null;
} else if ($targetClass->containsForeignIdentifier) {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->getFieldForColumn($targetColumn)];
} else {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
}
@@ -567,19 +548,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;
}
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ($this->_selectJoinSql) {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
} else {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
}
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
return $entities ? $entities[0] : null;
return $this->_createEntity($result, $entity, $hints);
}
/**
@@ -588,17 +560,14 @@ 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, array $identifier = array())
public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array())
{
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
return $foundEntity;
}
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($assoc['isOwningSide']) {
@@ -621,7 +590,7 @@ class BasicEntityPersister
}
*/
$targetEntity = $this->load($identifier, null, $assoc, $hints);
$targetEntity = $this->load($identifier, $targetEntity, $assoc, $hints);
// Complete bidirectional association, if necessary
if ($targetEntity !== null && $isInverseSingleValued) {
@@ -633,11 +602,7 @@ class BasicEntityPersister
// TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
// 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]);
$identifier[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn
@@ -645,7 +610,7 @@ class BasicEntityPersister
}
}
$targetEntity = $this->load($identifier, null, $assoc);
$targetEntity = $this->load($identifier, $targetEntity, $assoc);
if ($targetEntity !== null) {
$targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity);
@@ -667,9 +632,79 @@ 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));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$metaColumns = array();
$newData = array();
// Refresh simple state
foreach ($result as $column => $value) {
$column = $this->_resultColumnNames[$column];
if (isset($this->_class->fieldNames[$column])) {
$fieldName = $this->_class->fieldNames[$column];
$newValue = $this->_conn->convertToPHPValue($value, $this->_class->fieldMappings[$fieldName]['type']);
$this->_class->reflFields[$fieldName]->setValue($entity, $newValue);
$newData[$fieldName] = $newValue;
} else {
$metaColumns[$column] = $value;
}
}
// Refresh associations
foreach ($this->_class->associationMappings as $field => $assoc) {
$value = $this->_class->reflFields[$field]->getValue($entity);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($value instanceof Proxy && ! $value->__isInitialized__) {
continue; // skip uninitialized proxies
}
if ($assoc['isOwningSide']) {
$joinColumnValues = array();
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
if ($metaColumns[$srcColumn] !== null) {
$joinColumnValues[$targetColumn] = $metaColumns[$srcColumn];
}
}
if ( ! $joinColumnValues && $value !== null) {
$this->_class->reflFields[$field]->setValue($entity, null);
$newData[$field] = null;
} else if ($value !== null) {
// Check identity map first, if the entity is not there,
// place a proxy in there instead.
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) {
$this->_class->reflFields[$field]->setValue($entity, $found);
// Complete inverse side, if necessary.
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity);
}
$newData[$field] = $found;
} else {
// FIXME: What is happening with subClassees here?
$proxy = $this->_em->getProxyFactory()->getProxy($assoc['targetEntity'], $joinColumnValues);
$this->_class->reflFields[$field]->setValue($entity, $proxy);
$newData[$field] = $proxy;
$this->_em->getUnitOfWork()->registerManaged($proxy, $joinColumnValues, array());
}
}
} else {
// Inverse side of 1-1/1-x can never be lazy.
//$newData[$field] = $assoc->load($entity, null, $this->_em);
$newData[$field] = $this->_em->getUnitOfWork()->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null);
}
} else if ($value instanceof PersistentCollection && $value->isInitialized()) {
$value->setInitialized(false);
// no matter if dirty or non-dirty entities are already loaded, smoke them out!
// the beauty of it being, they are still in the identity map
$value->unwrap()->clear();
$newData[$field] = $value;
}
}
$this->_em->getUnitOfWork()->setOriginalEntityData($entity, $newData);
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
@@ -684,83 +719,22 @@ class BasicEntityPersister
* Loads a list of entities by a list of field criteria.
*
* @param array $criteria
* @param array $orderBy
* @param int $limit
* @param int $offset
* @return array
*/
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
public function loadAll(array $criteria = array())
{
$entities = array();
$sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy);
$sql = $this->_getSelectEntitiesSQL($criteria);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ($this->_selectJoinSql) {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
} else {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
}
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
}
/**
* Get (sliced or full) elements of the given collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @return array
*/
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt);
}
/**
* Load an array of entities from a given dbal statement.
*
* @param array $assoc
* @param Doctrine\DBAL\Statement $stmt
* @return array
*/
private function loadArrayFromStatement($assoc, $stmt)
{
$hints = array('deferEagerLoads' => true);
if (isset($assoc['indexBy'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
} else {
$rsm = $this->_rsm;
foreach ($result as $row) {
$entities[] = $this->_createEntity($row);
}
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints);
}
/**
* 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'])) {
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
} else {
$rsm = $this->_rsm;
}
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $rsm, $hints);
return $entities;
}
/**
@@ -769,33 +743,16 @@ class BasicEntityPersister
* @param ManyToManyMapping $assoc The association mapping of the association being loaded.
* @param object $sourceEntity The entity that owns the collection.
* @param PersistentCollection $coll The collection to fill.
* @param int|null $offset
* @param int|null $limit
* @return array
*/
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
}
private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$criteria = array();
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$joinTableConditions = array();
if ($assoc['isOwningSide']) {
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$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]];
}
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
@@ -808,15 +765,7 @@ class BasicEntityPersister
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform);
// TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$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]];
}
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
} else {
throw MappingException::joinColumnMustPointToMappedField(
@@ -826,9 +775,79 @@ class BasicEntityPersister
}
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$coll->hydrateAdd($this->_createEntity($result));
}
$stmt->closeCursor();
}
/**
* Creates or fills a single entity object from an SQL result.
*
* @param $result The SQL result.
* @param object $entity The entity object to fill, if any.
* @param array $hints Hints for entity creation.
* @return object The filled and managed entity object or NULL, if the SQL result is empty.
*/
private function _createEntity($result, $entity = null, array $hints = array())
{
if ($result === false) {
return null;
}
list($entityName, $data) = $this->_processSQLResult($result);
if ($entity !== null) {
$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($entity, $id, $data);
}
return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
}
/**
* Processes an SQL result set row that contains data for an entity of the type
* this persister is responsible for.
*
* Subclasses are supposed to override this method if they need to change the
* hydration procedure for entities loaded through basic find operations or
* lazy-loading (not DQL).
*
* @param array $sqlResult The SQL result set row to process.
* @return array A tuple where the first value is the actual type of the entity and
* the second value the prepared data of the entity (a map from field
* names to values).
*/
protected function _processSQLResult(array $sqlResult)
{
$data = array();
foreach ($sqlResult as $column => $value) {
$column = $this->_resultColumnNames[$column];
if (isset($this->_class->fieldNames[$column])) {
$field = $this->_class->fieldNames[$column];
if (isset($data[$field])) {
$data[$column] = $value;
} else {
$data[$field] = Type::getType($this->_class->fieldMappings[$field]['type'])
->convertToPHPValue($value, $this->_platform);
}
} else {
$data[$column] = $value;
}
}
return array($this->_class->name, $data);
}
/**
@@ -838,21 +857,19 @@ class BasicEntityPersister
* @param AssociationMapping $assoc
* @param string $orderBy
* @param int $lockMode
* @param int $limit
* @param int $offset
* @param array $orderBy
* @return string
* @todo Refactor: _getSelectSQL(...)
*/
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
{
$joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
$orderBySql = $assoc !== null && isset($assoc['orderBy']) ?
$this->_getCollectionOrderBySQL($assoc['orderBy'], $this->_getSQLTableAlias($this->_class->name))
: '';
$lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) {
@@ -861,12 +878,12 @@ class BasicEntityPersister
$lockSql = ' ' . $this->_platform->getWriteLockSql();
}
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL()
return 'SELECT ' . $this->_getSelectColumnListSQL()
. $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class->name), $lockMode)
. $this->_selectJoinSql . $joinSql
. $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
. $orderBySql, $limit, $offset)
. $orderBySql
. $lockSql;
}
@@ -878,12 +895,12 @@ class BasicEntityPersister
* @return string
* @todo Rename: _getOrderBySQL
*/
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
protected final function _getCollectionOrderBySQL(array $orderBy, $baseTableAlias)
{
$orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) {
if ( ! isset($this->_class->fieldMappings[$fieldName])) {
throw ORMException::unrecognizedField($fieldName);
ORMException::unrecognizedField($fieldName);
}
$tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ?
@@ -917,8 +934,6 @@ class BasicEntityPersister
}
$columnList = '';
$this->_rsm = new Query\ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
@@ -926,87 +941,10 @@ class BasicEntityPersister
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
}
$this->_selectJoinSql = '';
$eagerAliasCounter = 0;
foreach ($this->_class->associationMappings as $assocField => $assoc) {
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
if ($assocColumnSQL) {
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) {
if ($columnList) $columnList .= ', ';
$columnList .= $assoc2ColumnSQL;
}
}
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
$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 ';
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.'.$sourceCol.' = ' .
$this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.'.$targetCol.' ';
$first = false;
}
} 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) {
if (!$first) {
$this->_selectJoinSql .= ' AND ';
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.'.$sourceCol.' = ' .
$this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
$first = false;
}
}
}
}
$this->_selectColumnListSql = $columnList;
$this->_selectColumnListSql = $columnList . $this->_getSelectJoinColumnsSQL($this->_class);
return $this->_selectColumnListSql;
}
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
{
$columnList = '';
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
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);
}
}
return $columnList;
}
/**
* Gets the SQL join fragment used when selecting entities from a
@@ -1030,15 +968,8 @@ class BasicEntityPersister
$joinSql = '';
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND ';
if ($this->_class->containsForeignIdentifier && !isset($this->_class->fieldNames[$sourceColumn])) {
$quotedColumn = $sourceColumn; // join columns cannot be quoted
} else {
$quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform);
}
$joinSql .= $this->_getSQLTableAlias($this->_class->name) .
'.' . $quotedColumn . ' = '
'.' . $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform) . ' = '
. $joinTableName . '.' . $joinTableColumn;
}
@@ -1110,18 +1041,46 @@ class BasicEntityPersister
* @param string $field The field name.
* @param ClassMetadata $class The class that declares this field. The table this class is
* mapped to must own the column for the given field.
* @param string $alias
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
protected function _getSelectColumnSQL($field, ClassMetadata $class)
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
}
return "$sql AS $columnAlias";
}
/**
* Gets the SQL snippet for all join columns of the given class that are to be
* placed in an SQL SELECT statement.
*
* @param $class
* @return string
* @todo Not reused... inline?
*/
private function _getSelectJoinColumnsSQL(ClassMetadata $class)
{
$sql = '';
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$sql .= ', ' . $this->_getSQLTableAlias($this->_class->name) . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
}
}
}
return $sql;
}
/**
* Gets the SQL table alias for the given class name.
*
@@ -1129,18 +1088,14 @@ class BasicEntityPersister
* @return string The SQL table alias.
* @todo Reconsider. Binding table aliases to class names is not such a good idea.
*/
protected function _getSQLTableAlias($className, $assocName = '')
protected function _getSQLTableAlias($className)
{
if ($assocName) {
$className .= '#'.$assocName;
}
if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className];
}
$tableAlias = 't' . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$className] = $tableAlias;
return $tableAlias;
}
@@ -1213,62 +1168,31 @@ class BasicEntityPersister
} else {
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
}
$conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
} 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 {
throw ORMException::unrecognizedField($field);
}
$conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?');
$conditionSql .= ($value === null) ? ' IS NULL' : ' = ?';
}
return $conditionSql;
}
/**
* Return an array with (sliced or full list) of elements in the specified collection.
*
* @param array $assoc
* @param object $sourceEntity
* @param int $offset
* @param int $limit
* @return array
*/
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt);
}
/**
* Loads a collection of entities in a one-to-many association.
*
* @param array $assoc
* @param object $sourceEntity
* @param PersistentCollection $coll The collection to load/fill.
* @param int|null $offset
* @param int|null $limit
* @param OneToManyMapping $assoc
* @param array $criteria The criteria by which to select the entities.
* @param PersistentCollection The collection to load/fill.
*/
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
$this->loadCollectionFromStatement($assoc, $stmt, $coll);
}
/**
* Build criteria and execute SQL statement to fetch the one to many entities from.
*
* @param array $assoc
* @param object $sourceEntity
* @param int|null $offset
* @param int|null $limit
* @return Doctrine\DBAL\Statement
*/
private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
{
$criteria = array();
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
@@ -1279,23 +1203,16 @@ class BasicEntityPersister
: $this->_getSQLTableAlias($this->_class->name);
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$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]];
}
$criteria[$tableAlias . "." . $targetKeyColumn] = $value;
} else {
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
}
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
}
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
$coll->hydrateAdd($this->_createEntity($result));
}
$stmt->closeCursor();
}
/**
@@ -1317,10 +1234,6 @@ class BasicEntityPersister
if (isset($this->_class->fieldMappings[$field])) {
$type = Type::getType($this->_class->fieldMappings[$field]['type'])->getBindingType();
}
if (is_array($value)) {
$type += Connection::ARRAY_PARAM_OFFSET;
}
$params[] = $value;
$types[] = $type;
}
@@ -1333,17 +1246,19 @@ class BasicEntityPersister
* @param object $entity
* @return boolean TRUE if the entity exists in the database, FALSE otherwise.
*/
public function exists($entity, array $extraConditions = array())
public function exists($entity)
{
$criteria = $this->_class->getIdentifierValues($entity);
if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions);
}
$sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform)
. ' ' . $this->_getSQLTableAlias($this->_class->name)
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria));
}
//TODO
/*protected function _getOneToOneEagerFetchSQL()
{
}*/
}

View File

@@ -20,16 +20,13 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\LockMode,
Doctrine\ORM\Query\ResultSetMapping;
Doctrine\ORM\Mapping\ClassMetadata;
/**
* The joined subclass persister maps a single entity instance to several tables in the
* database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
*/
@@ -238,17 +235,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* {@inheritdoc}
*/
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// Create the column list fragment only once
if ($this->_selectColumnListSql === null) {
$this->_rsm = new ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r');
// Add regular columns
$columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
@@ -284,8 +277,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
}
// INNER JOIN parent tables
@@ -343,25 +335,19 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : '';
$orderBySql = '';
if ($assoc != null && isset($assoc['orderBy'])) {
$orderBySql = $this->_getCollectionOrderBySQL($assoc['orderBy'], $baseTableAlias);
}
if ($this->_selectColumnListSql === null) {
$this->_selectColumnListSql = $columnList;
}
$lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
$lockSql = ' ' . $this->_platform->getWriteLockSql();
}
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql
return 'SELECT ' . $this->_selectColumnListSql
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias
. $joinSql
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset)
. $lockSql;
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
}
/**

View File

@@ -21,8 +21,7 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\PersistentCollection;
/**
* Persister for many-to-many collections.
@@ -118,21 +117,13 @@ class ManyToManyPersister extends AbstractCollectionPersister
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
if ($isComposite) {
if ($class1->containsForeignIdentifier) {
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
} else {
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
}
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
} else {
$params[] = array_pop($identifier1);
}
} else {
if ($isComposite) {
if ($class2->containsForeignIdentifier) {
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
} else {
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
}
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
} else {
$params[] = array_pop($identifier2);
}
@@ -182,119 +173,4 @@ class ManyToManyPersister extends AbstractCollectionPersister
return $params;
}
/**
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
$params = array();
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
if ($mapping['isOwningSide']) {
$joinTable = $mapping['joinTable'];
$joinColumns = $mapping['relationToSourceKeyColumns'];
} else {
$mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']];
$joinTable = $mapping['joinTable'];
$joinColumns = $mapping['relationToTargetKeyColumns'];
}
$whereClause = '';
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($joinColumns[$joinTableColumn])) {
if ($whereClause !== '') {
$whereClause .= ' AND ';
}
$whereClause .= "$joinTableColumn = ?";
if ($class->containsForeignIdentifier) {
$params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])];
} else {
$params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
}
}
}
$sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
return $this->_conn->fetchColumn($sql, $params);
}
/**
* @param PersistentCollection $coll
* @param int $offset
* @param int $length
* @return array
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
return $this->_em->getUnitOfWork()
->getEntityPersister($mapping['targetEntity'])
->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* @param PersistentCollection $coll
* @param object $element
*/
public function contains(PersistentCollection $coll, $element)
{
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
return false;
}
$params = array();
$mapping = $coll->getMapping();
if (!$mapping['isOwningSide']) {
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$sourceId = $uow->getEntityIdentifier($element);
$targetId = $uow->getEntityIdentifier($coll->getOwner());
$mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
} else {
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceId = $uow->getEntityIdentifier($coll->getOwner());
$targetId = $uow->getEntityIdentifier($element);
}
$joinTable = $mapping['joinTable'];
$whereClause = '';
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
if ($whereClause !== '') {
$whereClause .= ' AND ';
}
$whereClause .= "$joinTableColumn = ?";
if ($targetClass->containsForeignIdentifier) {
$params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
} else {
$params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
}
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
if ($whereClause !== '') {
$whereClause .= ' AND ';
}
$whereClause .= "$joinTableColumn = ?";
if ($sourceClass->containsForeignIdentifier) {
$params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
} else {
$params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
}
}
}
$sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
return (bool)$this->_conn->fetchColumn($sql, $params);
}
}

View File

@@ -21,8 +21,7 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\PersistentCollection;
/**
* Persister for one-to-many collections.
@@ -117,69 +116,4 @@ class OneToManyPersister extends AbstractCollectionPersister
*/
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
{}
/**
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$params = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
$where = '';
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
if ($where != '') {
$where .= ' AND ';
}
$where .= $joinColumn['name'] . " = ?";
if ($targetClass->containsForeignIdentifier) {
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
} else {
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
}
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
return $this->_conn->fetchColumn($sql, $params);
}
/**
* @param PersistentCollection $coll
* @param int $offset
* @param int $length
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
return $this->_em->getUnitOfWork()
->getEntityPersister($mapping['targetEntity'])
->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* @param PersistentCollection $coll
* @param object $element
*/
public function contains(PersistentCollection $coll, $element)
{
$mapping = $coll->getMapping();
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
return false;
}
// only works with single id identifier entities. Will throw an exception in Entity Persisters
// if that is not the case for the 'mappedBy' field.
$id = current( $uow->getEntityIdentifier($coll->getOwner()) );
return $uow->getEntityPersister($mapping['targetEntity'])
->exists($element, array($mapping['mappedBy'] => $id));
}
}
}

View File

@@ -26,7 +26,6 @@ use Doctrine\ORM\Mapping\ClassMetadata;
* SINGLE_TABLE strategy.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
*/
@@ -49,8 +48,7 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
// Append subclass columns
foreach ($this->_class->subClasses as $subClassName) {
@@ -88,9 +86,9 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
}
/** {@inheritdoc} */
protected function _getSQLTableAlias($className, $assocName = '')
protected function _getSQLTableAlias($className)
{
return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName);
return parent::_getSQLTableAlias($this->_class->rootEntityName);
}
/** {@inheritdoc} */

View File

@@ -172,7 +172,7 @@ class ProxyFactory
}
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= "\n" . ' public function ';
$methods .= PHP_EOL . ' public function ';
if ($method->returnsReference()) {
$methods .= '&';
}
@@ -208,10 +208,10 @@ class ProxyFactory
}
$methods .= $parameterString . ')';
$methods .= "\n" . ' {' . "\n";
$methods .= ' $this->__load();' . "\n";
$methods .= PHP_EOL . ' {' . PHP_EOL;
$methods .= ' $this->__load();' . PHP_EOL;
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= "\n" . ' }' . "\n";
$methods .= PHP_EOL . ' }' . PHP_EOL;
}
}

View File

@@ -53,15 +53,6 @@ final class Query extends AbstractQuery
* @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.
@@ -238,83 +229,49 @@ final class Query extends AbstractQuery
throw QueryException::invalidParameterNumber();
}
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
if ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
}
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 (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);
}
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 ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
}
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));
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
}
/**

View File

@@ -1,47 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @since 2.1
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class CoalesceExpression extends Node
{
public $scalarExpressions = array();
public function __construct(array $scalarExpressions)
{
$this->scalarExpressions = $scalarExpressions;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkCoalesceExpression($this);
}
}

View File

@@ -1,5 +1,7 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -25,6 +27,7 @@ namespace Doctrine\ORM\Query\AST;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>

View File

@@ -1,71 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
/**
* "DATE_ADD(date1, interval, unit)"
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class DateAddFunction extends FunctionNode
{
public $firstDateExpression = null;
public $intervalExpression = null;
public $unit = null;
public function getSql(SqlWalker $sqlWalker)
{
$unit = strtolower($this->unit);
if ($unit == "day") {
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
} else if ($unit == "month") {
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
} else {
throw QueryException::semanticalError('DATE_ADD() only supports units of type day and month.');
}
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->unit = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\Parser;
/**
* "DATE_DIFF(date1, date2)"
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class DateDiffFunction extends FunctionNode
{
public $date1;
public $date2;
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression(
$this->date1->dispatch($sqlWalker),
$this->date2->dispatch($sqlWalker)
);
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->date1 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->date2 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
/**
* "DATE_ADD(date1, interval, unit)"
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class DateSubFunction extends DateAddFunction
{
public $firstDateExpression = null;
public $intervalExpression = null;
public $unit = null;
public function getSql(SqlWalker $sqlWalker)
{
$unit = strtolower($this->unit);
if ($unit == "day") {
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
} else if ($unit == "month") {
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
} else {
throw QueryException::semanticalError('DATE_SUB() only supports units of type day and month.');
}
}
}

View File

@@ -53,8 +53,8 @@ class SizeFunction extends FunctionNode
if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) {
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->table['name']);
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
$targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->table['name']);
$sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias);
$sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
@@ -76,8 +76,8 @@ class SizeFunction extends FunctionNode
$joinTable = $owningAssoc['joinTable'];
// SQL table aliases
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
$joinTableAlias = $sqlWalker->getSqlTableAlias($joinTable['name']);
$sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias);
// join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';

View File

@@ -1,49 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\AST;
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @since 2.1
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class NullIfExpression extends Node
{
public $firstExpression;
public $secondExpression;
public function __construct($firstExpression, $secondExpression)
{
$this->firstExpression = $firstExpression;
$this->secondExpression = $secondExpression;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkNullIfExpression($this);
}
}

View File

@@ -63,11 +63,9 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
$idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $primaryDqlAlias);
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
$sqlWalker->setSqlTableAlias($primaryClass->table['name'] . $primaryDqlAlias, 't0');
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias);
$fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array())));
$this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
@@ -98,7 +96,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
}
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
}
/**

View File

@@ -51,7 +51,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$em = $sqlWalker->getEntityManager();
$conn = $em->getConnection();
$platform = $conn->getDatabasePlatform();
$updateClause = $AST->updateClause;
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);
@@ -64,14 +64,11 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $updateClause->aliasIdentificationVariable);
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
$sqlWalker->setSqlTableAlias($primaryClass->table['name'] . $updateClause->aliasIdentificationVariable, 't0');
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable);
$fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array())));
$this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
// 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect)
@@ -88,9 +85,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
foreach ($updateItems as $updateItem) {
$field = $updateItem->pathExpression->field;
if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) ||
isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) {
isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) {
$newValue = $updateItem->newValue;
if ( ! $affected) {
@@ -106,9 +102,7 @@ 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]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey);
$this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
$this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey);
++$this->_numParametersInUpdateClause;
}
}
@@ -126,18 +120,15 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
// 4. Store DDL for temporary identifier table.
$columnDefinitions = array();
foreach ($idColumnNames as $idColumnName) {
$columnDefinitions[$idColumnName] = array(
'notnull' => true,
'type' => Type::getType($rootClass->getTypeOfColumn($idColumnName))
);
}
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
}
/**
@@ -155,23 +146,11 @@ 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),
array_slice($types, $this->_numParametersInUpdateClause)
);
$numUpdated = $conn->executeUpdate($this->_insertSql, array_slice($params, $this->_numParametersInUpdateClause), $types);
// Execute UPDATE statements
for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) {
$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);
$conn->executeUpdate($this->_sqlStatements[$i], isset($this->_sqlParameters[$i]) ? $this->_sqlParameters[$i] : array());
}
// Drop temporary table

View File

@@ -443,28 +443,6 @@ class Expr
return new Expr\Func($x . ' NOT IN', (array) $y);
}
/**
* Creates an IS NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NULL
* @return string
*/
public function isNull($x)
{
return $x . ' IS NULL';
}
/**
* Creates an IS NOT NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NOT NULL
* @return string
*/
public function isNotNull($x)
{
return $x . ' IS NOT NULL';
}
/**
* Creates a LIKE() comparison expression with the given arguments.
*

View File

@@ -32,9 +32,9 @@ namespace Doctrine\ORM\Query\Expr;
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Andx extends Composite
class Andx extends Base
{
protected $_separator = ' AND ';
protected $_separator = ') AND (';
protected $_allowedClasses = array(
'Doctrine\ORM\Query\Expr\Comparison',
'Doctrine\ORM\Query\Expr\Func',

View File

@@ -39,7 +39,7 @@ abstract class Base
protected $_postSeparator = ')';
protected $_allowedClasses = array();
protected $_parts = array();
private $_parts = array();
public function __construct($args = array())
{

View File

@@ -1,68 +0,0 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query\Expr;
/**
* Expression class for building DQL and parts
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Composite extends Base
{
public function __toString()
{
if ($this->count() === 1) {
return (string) $this->_parts[0];
}
$components = array();
foreach ($this->_parts as $part) {
$components[] = $this->processQueryPart($part);
}
return implode($this->_separator, $components);
}
private function processQueryPart($part)
{
$queryPart = (string) $part;
if (is_object($part) && $part instanceof self && $part->count() > 1) {
return $this->_preSeparator . $queryPart . $this->_postSeparator;
}
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
if (mb_stripos($queryPart, ' OR ') !== false || mb_stripos($queryPart, ' AND ') !== false) {
return $this->_preSeparator . $queryPart . $this->_postSeparator;
}
return $queryPart;
}
}

View File

@@ -45,23 +45,20 @@ class Join
private $_alias;
private $_conditionType;
private $_condition;
private $_indexBy;
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null)
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null)
{
$this->_joinType = $joinType;
$this->_join = $join;
$this->_alias = $alias;
$this->_joinType = $joinType;
$this->_join = $join;
$this->_alias = $alias;
$this->_conditionType = $conditionType;
$this->_condition = $condition;
$this->_indexBy = $indexBy;
$this->_condition = $condition;
}
public function __toString()
{
return strtoupper($this->_joinType) . ' JOIN ' . $this->_join
. ($this->_alias ? ' ' . $this->_alias : '')
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '')
. ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '');
}
}

View File

@@ -32,9 +32,9 @@ namespace Doctrine\ORM\Query\Expr;
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Orx extends Composite
class Orx extends Base
{
protected $_separator = ' OR ';
protected $_separator = ') OR (';
protected $_allowedClasses = array(
'Doctrine\ORM\Query\Expr\Andx',
'Doctrine\ORM\Query\Expr\Comparison',

View File

@@ -126,7 +126,7 @@ class Lexer extends \Doctrine\Common\Lexer
'[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
"'(?:[^']|'')*'",
'\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
'\?[1-9][0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
);
}

View File

@@ -1,72 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
use Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type;
/**
* Provides an enclosed support for parameter infering.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ParameterTypeInferer
{
/**
* Infer type of a given value, returning a compatible constant:
* - Type (Doctrine\DBAL\Types\Type::*)
* - Connection (Doctrine\DBAL\Connection::PARAM_*)
*
* @param mixed $value Parameter value
*
* @return mixed Parameter type constant
*/
public static function inferType($value)
{
switch (true) {
case is_integer($value):
return Type::INTEGER;
case ($value instanceof \DateTime):
return Type::DATETIME;
case is_array($value):
$key = key($value);
if (is_integer($value[$key])) {
return Connection::PARAM_INT_ARRAY;
}
return Connection::PARAM_STR_ARRAY;
default:
// Do nothing
break;
}
return \PDO::PARAM_STR;
}
}

View File

@@ -45,22 +45,19 @@ class Parser
/** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
private static $_NUMERIC_FUNCTIONS = array(
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction',
'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction',
'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction'
);
/** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */
private static $_DATETIME_FUNCTIONS = array(
'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction'
);
/**
@@ -234,7 +231,7 @@ class Parser
* If they match, updates the lookahead token; otherwise raises a syntax
* error.
*
* @param int token type
* @param int|string token type or value
* @return void
* @throws QueryException If the tokens dont match.
*/
@@ -926,10 +923,6 @@ class Parser
$token = $this->_lexer->lookahead;
$identVariable = $this->IdentificationVariable();
if (!isset($this->_queryComponents[$identVariable])) {
$this->semanticalError('Identification Variable ' . $identVariable .' used in join path expression but was not defined before.');
}
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
@@ -1644,10 +1637,6 @@ class Parser
return $this->StateFieldPathExpression();
} else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) {
return $this->SimpleArithmeticExpression();
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
// Since NULLIF and COALESCE can be identified as a function,
// we need to check if before check for FunctionDeclaration
return $this->CaseExpression();
} else if ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
// We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
$this->_lexer->peek(); // "("
@@ -1669,6 +1658,8 @@ class Parser
} else if ($lookahead == Lexer::T_TRUE || $lookahead == Lexer::T_FALSE) {
$this->match($lookahead);
return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
return $this->CaseExpression();
} else {
$this->syntaxError();
}
@@ -1676,66 +1667,11 @@ class Parser
public function CaseExpression()
{
$lookahead = $this->_lexer->lookahead['type'];
// if "CASE" "WHEN" => GeneralCaseExpression
// else if "CASE" => SimpleCaseExpression
// [DONE] else if "COALESCE" => CoalesceExpression
// [DONE] else if "NULLIF" => NullifExpression
switch ($lookahead) {
case Lexer::T_NULLIF:
return $this->NullIfExpression();
case Lexer::T_COALESCE:
return $this->CoalesceExpression();
default:
$this->semanticalError('CaseExpression not yet supported.');
return null;
}
}
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @return Doctrine\ORM\Query\AST\CoalesceExpression
*/
public function CoalesceExpression()
{
$this->match(Lexer::T_COALESCE);
$this->match(Lexer::T_OPEN_PARENTHESIS);
// Process ScalarExpressions (1..N)
$scalarExpressions = array();
$scalarExpressions[] = $this->ScalarExpression();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$scalarExpressions[] = $this->ScalarExpression();
}
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\CoalesceExpression($scalarExpressions);
}
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @return Doctrine\ORM\Query\AST\ExistsExpression
*/
public function NullIfExpression()
{
$this->match(Lexer::T_NULLIF);
$this->match(Lexer::T_OPEN_PARENTHESIS);
$firstExpression = $this->ScalarExpression();
$this->match(Lexer::T_COMMA);
$secondExpression = $this->ScalarExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\NullIfExpression($firstExpression, $secondExpression);
// else if "COALESCE" => CoalesceExpression
// else if "NULLIF" => NullifExpression
$this->semanticalError('CaseExpression not yet supported.');
}
/**
@@ -1774,16 +1710,12 @@ class Parser
}
} else if ($this->_isFunction()) {
$this->_lexer->peek(); // "("
$lookaheadType = $this->_lexer->lookahead['type'];
$beyond = $this->_peekBeyondClosingParenthesis();
$beyond = $this->_peekBeyondClosingParenthesis();
if ($this->_isMathOperator($beyond)) {
$expression = $this->ScalarExpression();
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
$expression = $this->AggregateExpression();
} else if (in_array ($lookaheadType, array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) {
$expression = $this->CaseExpression();
} else {
// Shortcut: ScalarExpression => Function
$expression = $this->FunctionDeclaration();
@@ -2290,7 +2222,7 @@ class Parser
/**
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
* | FunctionsReturningDatetime | IdentificationVariable
*/
public function ArithmeticPrimary()
{
@@ -2314,11 +2246,7 @@ 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

@@ -135,10 +135,4 @@ 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 . "', " .
"inheritance hierachy exists between these two classes.");
}
}

View File

@@ -1,5 +1,7 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -112,13 +114,6 @@ class ResultSetMapping
* @var array
*/
public $declaringClasses = array();
/**
* This is necessary to hydrate derivate foreign keys correctly.
*
* @var array
*/
public $isIdentifierColumn = array();
/**
* Adds an entity result to this ResultSetMapping.
@@ -388,17 +383,14 @@ class ResultSetMapping
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
* @param string $alias
* @param string $columnName
* @param string $fieldName
* @param bool
* @param $alias
* @param $columnName
* @param $fieldName
*/
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
public function addMetaResult($alias, $columnName, $fieldName)
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
if ($isIdentifierColumn) {
$this->isIdentifierColumn[$alias][$columnName] = true;
}
}
}
}

View File

@@ -1,107 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields
*
* @author Michael Ridgway <mcridgway@gmail.com>
* @since 2.1
*/
class ResultSetMappingBuilder extends ResultSetMapping
{
/**
* @var EntityManager
*/
private $em;
/**
* @param EntityManager
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Adds a root entity and all of its fields to the result set.
*
* @param string $class The class name of the root entity.
* @param string $alias The unique alias to use for the root entity.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array())
{
$this->addEntityResult($class, $alias);
$this->addAllClassFields($class, $alias, $renamedColumns);
}
/**
* Adds a joined entity and all of its fields to the result set.
*
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result with the joined entity result.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName)
*/
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.');
}
$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);
}
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

@@ -44,7 +44,7 @@ class SqlWalker implements TreeWalker
private $_aliasCounter = 0;
private $_tableAliasCounter = 0;
private $_scalarResultCounter = 1;
private $_sqlParamIndex = 0;
private $_sqlParamIndex = 1;
/**
* @var ParserResult
@@ -192,9 +192,9 @@ class SqlWalker implements TreeWalker
* @param string $dqlAlias The DQL alias.
* @return string Generated table alias.
*/
public function getSQLTableAlias($tableName, $dqlAlias = '')
public function getSqlTableAlias($tableName, $dqlAlias = '')
{
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
$tableName .= $dqlAlias;
if ( ! isset($this->_tableAliasMap[$tableName])) {
$this->_tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->_tableAliasCounter++ . '_';
@@ -212,9 +212,9 @@ class SqlWalker implements TreeWalker
* @param string $dqlAlias
* @return string
*/
public function setSQLTableAlias($tableName, $alias, $dqlAlias = '')
public function setSqlTableAlias($tableName, $alias, $dqlAlias = '')
{
$tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : '';
$tableName .= $dqlAlias;
$this->_tableAliasMap[$tableName] = $alias;
@@ -227,7 +227,7 @@ class SqlWalker implements TreeWalker
* @param string $columnName
* @return string
*/
public function getSQLColumnAlias($columnName)
public function getSqlColumnAlias($columnName)
{
return $columnName . $this->_aliasCounter++;
}
@@ -303,7 +303,7 @@ class SqlWalker implements TreeWalker
if ($sql != '') {
$sql .= ', ';
}
$sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.' .
$sql .= $this->getSqlTableAlias($tableName, $dqlAlias) . '.' .
$qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " $orientation";
}
}
@@ -342,7 +342,7 @@ class SqlWalker implements TreeWalker
}
$sql .= ($sql != '' ? ' AND ' : '')
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '')
. (($this->_useSqlTableAliases) ? $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.' : '')
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
}
}
@@ -502,7 +502,7 @@ class SqlWalker implements TreeWalker
}
if ($this->_useSqlTableAliases) {
$sql .= $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.';
$sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.';
}
$sql .= reset($assoc['targetToSourceKeyColumns']);
@@ -526,8 +526,9 @@ class SqlWalker implements TreeWalker
*/
public function walkSelectClause($selectClause)
{
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode(
', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)
);
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
@@ -550,15 +551,14 @@ class SqlWalker implements TreeWalker
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
// Add discriminator columns to SQL
$rootClass = $this->_em->getClassMetadata($class->rootEntityName);
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias);
$tblAlias = $this->getSqlTableAlias($rootClass->table['name'], $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->getSqlColumnAlias($discrColumn['name']);
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $discrColumn['fieldName']);
// Add foreign key columns to SQL, if necessary
if ($addMetaColumns) {
@@ -567,18 +567,16 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
if (isset($assoc['inherited'])) {
$owningClass = $this->_em->getClassMetadata($assoc['inherited']);
$sqlTableAlias = $this->getSQLTableAlias($owningClass->table['name'], $dqlAlias);
$sqlTableAlias = $this->getSqlTableAlias($owningClass->table['name'], $dqlAlias);
} else {
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
}
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->getSqlColumnAlias($srcColumn);
$sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
}
}
}
@@ -586,24 +584,20 @@ class SqlWalker implements TreeWalker
} else {
// Add foreign key columns to SQL, if necessary
if ($addMetaColumns) {
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$sqlTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->getSqlColumnAlias($srcColumn);
$sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
}
}
}
}
}
}
$sql .= implode(', ', $sqlSelectExpressions);
return $sql;
}
@@ -628,7 +622,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform) . ' '
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
. $this->getSqlTableAlias($class->table['name'], $dqlAlias);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
@@ -744,8 +738,8 @@ class SqlWalker implements TreeWalker
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name'], $joinedDqlAlias);
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name'], $joinedDqlAlias);
$sourceTableAlias = $this->getSqlTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
@@ -756,21 +750,7 @@ class SqlWalker implements TreeWalker
}
}
if ($joinVarDecl->indexBy) {
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
$this->_rsm->addIndexBy(
$joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
$joinVarDecl->indexBy->simpleStateFieldPathExpression->field
);
} else if (isset($relation['indexBy'])) {
$this->_rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
}
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
// The owning side is necessary at this point because only it contains the JoinColumn information.
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
$first = true;
@@ -778,25 +758,21 @@ class SqlWalker implements TreeWalker
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($relation['isOwningSide']) {
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
}
$sql .= $sourceTableAlias . '.' . $sourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
$sql .= $sourceTableAlias . '.' . $sourceColumn
. ' = '
. $targetTableAlias . '.' . $quotedTargetColumn;
} else {
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
}
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn
. ' = '
. $targetTableAlias . '.' . $sourceColumn;
}
}
} else if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) {
// Join relation table
$joinTable = $assoc['joinTable'];
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
$joinTableAlias = $this->getSqlTableAlias($joinTable['name'], $joinedDqlAlias);
$sql .= $sourceClass->getQuotedJoinTableName($assoc, $this->_platform) . ' ' . $joinTableAlias . ' ON ';
$first = true;
@@ -804,25 +780,17 @@ class SqlWalker implements TreeWalker
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn])) {
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform);
}
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $relationColumn;
}
} else {
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
}
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
$sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $relationColumn;
}
}
@@ -836,25 +804,17 @@ class SqlWalker implements TreeWalker
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
}
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $relationColumn;
}
} else {
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$sourceColumn])) {
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
} else {
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform);
}
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
$sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform)
. ' = '
. $joinTableAlias . '.' . $relationColumn;
}
}
}
@@ -879,60 +839,6 @@ class SqlWalker implements TreeWalker
return $sql;
}
/**
* Walks down a CoalesceExpression AST node and generates the corresponding SQL.
*
* @param CoalesceExpression $coalesceExpression
* @return string The SQL.
*/
public function walkCoalesceExpression($coalesceExpression)
{
$sql = 'COALESCE(';
$scalarExpressions = array();
foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
$scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
}
$sql .= implode(', ', $scalarExpressions) . ')';
return $sql;
}
public function walkCaseExpression($expression)
{
switch (true) {
case ($expression instanceof AST\CoalesceExpression):
return $this->walkCoalesceExpression($expression);
case ($expression instanceof AST\NullIfExpression):
return $this->walkNullIfExpression($expression);
default:
return '';
}
}
/**
* Walks down a NullIfExpression AST node and generates the corresponding SQL.
*
* @param NullIfExpression $nullIfExpression
* @return string The SQL.
*/
public function walkNullIfExpression($nullIfExpression)
{
$firstExpression = is_string($nullIfExpression->firstExpression)
? $this->_conn->quote($nullIfExpression->firstExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
$secondExpression = is_string($nullIfExpression->secondExpression)
? $this->_conn->quote($nullIfExpression->secondExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
}
/**
* Walks down a SelectExpression AST node and generates the corresponding SQL.
@@ -964,10 +870,10 @@ class SqlWalker implements TreeWalker
$tableName = $class->getTableName();
}
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$columnAlias = $this->getSqlColumnAlias($columnName);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
@@ -1016,7 +922,8 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
} else if (
}
else if (
$expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor ||
@@ -1030,32 +937,11 @@ class SqlWalker implements TreeWalker
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
if ($expr instanceof AST\Literal) {
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
} else {
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
}
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
} else if (
$expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression ||
$expr instanceof AST\CaseExpression
) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
@@ -1092,8 +978,8 @@ class SqlWalker implements TreeWalker
if ($beginning) $beginning = false; else $sql .= ', ';
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias);
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias;
@@ -1108,7 +994,7 @@ class SqlWalker implements TreeWalker
if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$sqlTableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias);
$sqlTableAlias = $this->getSqlTableAlias($subClass->table['name'], $dqlAlias);
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
continue;
@@ -1116,7 +1002,7 @@ class SqlWalker implements TreeWalker
if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias;
@@ -1130,7 +1016,7 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$columnAlias = $this->getSqlColumnAlias($srcColumn);
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
}
@@ -1197,7 +1083,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform) . ' '
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
. $this->getSqlTableAlias($class->table['name'], $dqlAlias);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
@@ -1284,7 +1170,7 @@ class SqlWalker implements TreeWalker
} else {
// IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$tableAlias = $this->getSqlTableAlias($class->getTableName(), $expr);
$first = true;
foreach ($class->identifier as $identifier) {
@@ -1360,7 +1246,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
$this->setSqlTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
@@ -1379,7 +1265,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
$this->setSqlTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
@@ -1543,8 +1429,8 @@ class SqlWalker implements TreeWalker
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
$sql .= $targetClass->getQuotedTableName($this->_platform)
. ' ' . $targetTableAlias . ' WHERE ';
@@ -1578,9 +1464,9 @@ class SqlWalker implements TreeWalker
$joinTable = $owningAssoc['joinTable'];
// SQL table aliases
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$joinTableAlias = $this->getSqlTableAlias($joinTable['name']);
$targetTableAlias = $this->getSqlTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias);
// join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform)
@@ -1737,10 +1623,6 @@ class SqlWalker implements TreeWalker
$sql .= $this->_conn->quote($class->discriminatorValue);
} else {
$discrMap = array_flip($class->discriminatorMap);
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$sql .= $this->_conn->quote($discrMap[$entityClassName]);
}
@@ -1917,12 +1799,6 @@ 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;
}

View File

@@ -217,92 +217,25 @@ class QueryBuilder
->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults);
}
/**
* Gets the FIRST root alias of the query. This is the first entity alias involved
* Gets the root alias of the query. This is the first entity alias involved
* in the construction of the query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u');
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u');
*
* echo $qb->getRootAlias(); // u
* echo $qb->getRootAlias(); // u
* </code>
*
* @deprecated Please use $qb->getRootAliases() instead.
* @return string $rootAlias
* @todo Rename/Refactor: getRootAliases(), there can be multiple roots!
*/
public function getRootAlias()
{
$aliases = $this->getRootAliases();
return $aliases[0];
}
/**
* Gets the root aliases of the query. This is the entity aliases involved
* in the construction of the query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u');
*
* $qb->getRootAliases(); // array('u')
* </code>
*
* @return array $rootAliases
*/
public function getRootAliases()
{
$aliases = array();
foreach ($this->_dqlParts['from'] as &$fromClause) {
if (is_string($fromClause)) {
$spacePos = strrpos($fromClause, ' ');
$from = substr($fromClause, 0, $spacePos);
$alias = substr($fromClause, $spacePos + 1);
$fromClause = new Query\Expr\From($from, $alias);
}
$aliases[] = $fromClause->getAlias();
}
return $aliases;
}
/**
* Gets the root entities of the query. This is the entity aliases involved
* in the construction of the query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u');
*
* $qb->getRootEntities(); // array('User')
* </code>
*
* @return array $rootEntities
*/
public function getRootEntities()
{
$entities = array();
foreach ($this->_dqlParts['from'] as &$fromClause) {
if (is_string($fromClause)) {
$spacePos = strrpos($fromClause, ' ');
$from = substr($fromClause, 0, $spacePos);
$alias = substr($fromClause, $spacePos + 1);
$fromClause = new Query\Expr\From($from, $alias);
}
$entities[] = $fromClause->getFrom();
}
return $entities;
return $this->_dqlParts['from'][0]->getAlias();
}
/**
@@ -323,13 +256,10 @@ class QueryBuilder
*/
public function setParameter($key, $value, $type = null)
{
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
if ($type !== null) {
$this->_paramTypes[$key] = $type;
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
return $this;
}
@@ -352,13 +282,8 @@ class QueryBuilder
*/
public function setParameters(array $params, array $types = array())
{
foreach ($params as $key => $value) {
if (isset($types[$key])) {
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
}
$this->_paramTypes = $types;
$this->_params = $params;
return $this;
}
@@ -443,29 +368,9 @@ class QueryBuilder
public function add($dqlPartName, $dqlPart, $append = false)
{
$isMultiple = is_array($this->_dqlParts[$dqlPartName]);
// This is introduced for backwards compatibility reasons.
// TODO: Remove for 3.0
if ($dqlPartName == 'join') {
$newDqlPart = array();
foreach ($dqlPart AS $k => $v) {
if (is_numeric($k)) {
$newDqlPart[$this->getRootAlias()] = $v;
} else {
$newDqlPart[$k] = $v;
}
}
$dqlPart = $newDqlPart;
}
if ($append && $isMultiple) {
if (is_array($dqlPart)) {
$key = key($dqlPart);
$this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
} else {
$this->_dqlParts[$dqlPartName][] = $dqlPart;
}
$this->_dqlParts[$dqlPartName][] = $dqlPart;
} else {
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
}
@@ -618,12 +523,11 @@ class QueryBuilder
* @param string $alias The alias of the join
* @param string $conditionType The condition type constant. Either ON or WITH.
* @param string $condition The condition for the join
* @param string $indexBy The index for the join
* @return QueryBuilder This QueryBuilder instance.
*/
public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
public function join($join, $alias, $conditionType = null, $condition = null)
{
return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
return $this->innerJoin($join, $alias, $conditionType, $condition);
}
/**
@@ -643,18 +547,12 @@ class QueryBuilder
* @param string $alias The alias of the join
* @param string $conditionType The condition type constant. Either ON or WITH.
* @param string $condition The condition for the join
* @param string $indexBy The index for the join
* @return QueryBuilder This QueryBuilder instance.
*/
public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
{
$rootAlias = substr($join, 0, strpos($join, '.'));
if (!in_array($rootAlias, $this->getRootAliases())) {
$rootAlias = $this->getRootAlias();
}
return $this->add('join', array(
$rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
return $this->add('join', new Expr\Join(
Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition
), true);
}
@@ -676,18 +574,12 @@ class QueryBuilder
* @param string $alias The alias of the join
* @param string $conditionType The condition type constant. Either ON or WITH.
* @param string $condition The condition for the join
* @param string $indexBy The index for the join
* @return QueryBuilder This QueryBuilder instance.
*/
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
public function leftJoin($join, $alias, $conditionType = null, $condition = null)
{
$rootAlias = substr($join, 0, strpos($join, '.'));
if (!in_array($rootAlias, $this->getRootAliases())) {
$rootAlias = $this->getRootAlias();
}
return $this->add('join', array(
$rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
return $this->add('join', new Expr\Join(
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition
), true);
}
@@ -737,7 +629,7 @@ class QueryBuilder
*/
public function where($predicates)
{
if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) {
if ( ! (func_num_args() == 1 && ($predicates instanceof Expr\Andx || $predicates instanceof Expr\Orx))) {
$predicates = new Expr\Andx(func_get_args());
}
@@ -973,36 +865,14 @@ class QueryBuilder
private function _getDQLForSelect()
{
$dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$fromParts = $this->getDQLPart('from');
$joinParts = $this->getDQLPart('join');
$fromClauses = array();
// Loop through all FROM clauses
if ( ! empty($fromParts)) {
$dql .= ' FROM ';
foreach ($fromParts as $from) {
$fromClause = (string) $from;
if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) {
foreach ($joinParts[$from->getAlias()] as $join) {
$fromClause .= ' ' . ((string) $join);
}
}
$fromClauses[] = $fromClause;
}
}
$dql .= implode(', ', $fromClauses)
return 'SELECT'
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('from', array('pre' => ' FROM ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('join', array('pre' => ' ', 'separator' => ' '))
. $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE '))
. $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING '))
. $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '));
return $dql;
}
private function _getReducedDQLQueryPart($queryPartName, $options = array())

View File

@@ -78,10 +78,6 @@ class ConvertMappingCommand extends Console\Command\Command
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
),
new InputOption(
'namespace', null, InputOption::VALUE_OPTIONAL,
'Defines a namespace for the generated entity classes, if converted from database.'
),
))
->setHelp(<<<EOT
Convert mapping information between supported formats.
@@ -111,17 +107,11 @@ EOT
$em = $this->getHelper('em')->getEntityManager();
if ($input->getOption('from-database') === true) {
$databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
);
$em->getConfiguration()->setMetadataDriverImpl(
$databaseDriver
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
);
if (($namespace = $input->getOption('namespace')) !== null) {
$databaseDriver->setNamespace($namespace);
}
}
$cmf = new DisconnectedClassMetadataFactory();

View File

@@ -1,80 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console\Command;
use Doctrine\ORM\Mapping\MappingException;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
/**
* Show information about mapped entities
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.1
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class InfoCommand extends Command
{
protected function configure()
{
$this
->setName('orm:info')
->setDescription('Show basic information about all mapped entities')
->setHelp(<<<EOT
The <info>doctrine:mapping:info</info> shows basic information about which
entities exist and possibly if their mapping information contains errors or
not.
EOT
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/* @var $entityManager Doctrine\ORM\EntityManager */
$entityManager = $this->getHelper('em')->getEntityManager();
$entityClassNames = $entityManager->getConfiguration()
->getMetadataDriverImpl()
->getAllClassNames();
if (!$entityClassNames) {
throw new \Exception(
'You do not have any mapped Doctrine ORM entities according to the current configuration. '.
'If you have entities or mapping files you should check your mapping configuration for errors.'
);
}
$output->writeln(sprintf("Found <info>%d</info> mapped entities:", count($entityClassNames)));
foreach ($entityClassNames as $entityClassName) {
try {
$cm = $entityManager->getClassMetadata($entityClassName);
$output->writeln(sprintf("<info>[OK]</info> %s", $entityClassName));
} catch (MappingException $e) {
$output->writeln("<error>[FAIL]</error> ".$entityClassName);
$output->writeln(sprintf("<comment>%s</comment>", $e->getMessage()));
$output->writeln('');
}
}
}
}

View File

@@ -65,7 +65,7 @@ EOT
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getCreateSchemaSql($metadatas);

View File

@@ -92,7 +92,7 @@ EOT
}
$output->write('Database schema dropped successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();

View File

@@ -28,8 +28,7 @@ use Symfony\Component\Console\Input\InputArgument,
Doctrine\ORM\Tools\SchemaTool;
/**
* Command to generate the SQL needed to update the database schema to match
* the current mapping information.
* Command to update the database schema for a set of classes based on their mappings.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
@@ -39,58 +38,37 @@ use Symfony\Component\Console\Input\InputArgument,
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Ryan Weaver <ryan@thatsquality.com>
*/
class UpdateCommand extends AbstractCommand
{
protected $name = 'orm:schema-tool:update';
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName($this->name)
->setName('orm:schema-tool:update')
->setDescription(
'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.'
'Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.'
)
->setDefinition(array(
new InputOption(
'complete', null, InputOption::VALUE_NONE,
'If defined, all assets of the database which are not relevant to the current metadata will be dropped.'
),
new InputOption(
'dump-sql', null, InputOption::VALUE_NONE,
'Dumps the generated SQL statements to the screen (does not execute them).'
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
'Causes the generated SQL statements to be physically executed against your database.'
"Don't ask for the incremental update of the database, but force the operation to run."
),
));
$fullName = $this->getName();
$this->setHelp(<<<EOT
The <info>$fullName</info> command generates the SQL needed to
synchronize the database schema with the current mapping metadata of the
default entity manager.
For example, if you add metadata for a new column to an entity, this command
would generate and output the SQL needed to add the new column to the database:
<info>$fullName --dump-sql</info>
Alternatively, you can execute the generated queries:
<info>$fullName --force</info>
Finally, be aware that if the <info>--complete</info> option is passed, this
task will drop all database assets (e.g. tables, etc) that are *not* described
by the current metadata. In other words, without this option, this task leaves
untouched any "extra" tables that exist in the database, but which aren't
described by any metadata.
))
->setHelp(<<<EOT
Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
Beware that if --complete is not defined, it will do a save update, which does not delete any tables, sequences or affected foreign keys.
If defined, all assets of the database which are not relevant to the current metadata are dropped by this command.
EOT
);
}
@@ -100,36 +78,26 @@ EOT
// Defining if update is complete or not (--complete not defined means $saveMode = true)
$saveMode = ($input->getOption('complete') !== true);
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
if (0 == count($sqls)) {
$output->writeln('Nothing to update - your database is already in sync with the current entity metadata.');
return;
}
$dumpSql = (true === $input->getOption('dump-sql'));
$force = (true === $input->getOption('force'));
if ($dumpSql && $force) {
throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).');
}
if ($dumpSql) {
$output->writeln(implode(';' . PHP_EOL, $sqls));
} else if ($force) {
$output->writeln('Updating database schema...');
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
} else if ($input->getOption('force') === true) {
$output->write('Updating database schema...' . PHP_EOL);
$schemaTool->updateSchema($metadatas, $saveMode);
$output->writeln(sprintf('Database schema updated successfully! "<info>%s</info>" queries were executed', count($sqls)));
$output->write('Database schema updated successfully!' . PHP_EOL);
} else {
$output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
$output->writeln(' Use the incremental update to detect changes during development and use');
$output->writeln(' the SQL DDL provided to manually update your database in production.');
$output->writeln('');
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL);
$output->write('Use the incremental update to detect changes during development and use' . PHP_EOL);
$output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL);
$output->writeln(sprintf('The Schema-Tool would execute <info>"%s"</info> queries to update the database.', count($sqls)));
$output->writeln('Please run the operation by passing one of the following options:');
$output->writeln(sprintf(' <info>%s --force</info> to execute the command', $this->getName()));
$output->writeln(sprintf(' <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()));
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
if (count($sqls)) {
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to update the database.' . PHP_EOL);
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
} else {
$output->write('Nothing to update. The database is in sync with the current entity metadata.' . PHP_EOL);
}
}
}
}

View File

@@ -64,7 +64,6 @@ class ConsoleRunner
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\InfoCommand()
));
}
}

View File

@@ -52,17 +52,6 @@ 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

@@ -91,8 +91,6 @@ class EntityGenerator
<namespace>
use Doctrine\ORM\Mapping as ORM;
<entityAnnotation>
<entityClassName>
{
@@ -103,7 +101,7 @@ use Doctrine\ORM\Mapping as ORM;
'/**
* <description>
*
* @return <variableType>
* @return <variableType>$<variableName>
*/
public function <methodName>()
{
@@ -148,18 +146,11 @@ public function <methodName>()
}
';
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
$this->_annotationsPrefix = 'ORM\\';
}
}
/**
* Generate and write entity classes for the given array of ClassMetadataInfo instances
*
* @param array $metadatas
* @param string $outputDirectory
* @param string $outputDirectory
* @return void
*/
public function generate(array $metadatas, $outputDirectory)
@@ -173,7 +164,7 @@ public function <methodName>()
* Generated and write entity class to disk for the given ClassMetadataInfo instance
*
* @param ClassMetadataInfo $metadata
* @param string $outputDirectory
* @param string $outputDirectory
* @return void
*/
public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
@@ -210,7 +201,7 @@ public function <methodName>()
/**
* Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
*
* @param ClassMetadataInfo $metadata
* @param ClassMetadataInfo $metadata
* @return string $code
*/
public function generateEntityClass(ClassMetadataInfo $metadata)
@@ -236,8 +227,8 @@ public function <methodName>()
/**
* Generate the updated code for the given ClassMetadataInfo and entity at path
*
* @param ClassMetadataInfo $metadata
* @param string $path
* @param ClassMetadataInfo $metadata
* @param string $path
* @return string $code;
*/
public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
@@ -254,7 +245,7 @@ public function <methodName>()
/**
* Set the number of spaces the exported class should have
*
* @param integer $numSpaces
* @param integer $numSpaces
* @return void
*/
public function setNumSpaces($numSpaces)
@@ -266,7 +257,7 @@ public function <methodName>()
/**
* Set the extension to use when writing php files to disk
*
* @param string $extension
* @param string $extension
* @return void
*/
public function setExtension($extension)
@@ -287,7 +278,7 @@ public function <methodName>()
/**
* Set whether or not to generate annotations for the entity
*
* @param bool $bool
* @param bool $bool
* @return void
*/
public function setGenerateAnnotations($bool)
@@ -302,16 +293,13 @@ public function <methodName>()
*/
public function setAnnotationPrefix($prefix)
{
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
return;
}
$this->_annotationsPrefix = $prefix;
}
/**
* Set whether or not to try and update the entity if it already exists
*
* @param bool $bool
* @param bool $bool
* @return void
*/
public function setUpdateEntityIfExists($bool)
@@ -419,7 +407,7 @@ public function <methodName>()
$tokens = token_get_all($src);
$lastSeenNamespace = "";
$lastSeenClass = false;
$inNamespace = false;
$inClass = false;
for ($i = 0; $i < count($tokens); $i++) {
@@ -438,7 +426,7 @@ public function <methodName>()
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
$lastSeenClass = $lastSeenNamespace . '\\' . $token[1];
$this->_staticReflection[$lastSeenClass]['properties'] = array();
$this->_staticReflection[$lastSeenClass]['methods'] = array();
}
@@ -462,14 +450,6 @@ 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'])
@@ -478,14 +458,6 @@ 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'])
@@ -702,13 +674,7 @@ public function <methodName>()
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
{
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
$addMethod = end($addMethod);
$methodName = $type . $addMethod;
} else {
$methodName = $type . Inflector::classify($fieldName);
}
$methodName = $type . Inflector::classify($fieldName);
if ($this->_hasMethod($methodName, $metadata)) {
return;
@@ -844,7 +810,7 @@ public function <methodName>()
if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
}
if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
@@ -898,7 +864,7 @@ public function <methodName>()
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({';
foreach ($associationMapping['orderBy'] as $name => $direction) {
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
}
$lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);

View File

@@ -120,30 +120,22 @@ class PhpExporter extends AbstractExporter
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$method = 'mapOneToMany';
$potentialAssociationMappingIndexes = array(
'mappedBy',
'orphanRemoval',
'orderBy',
$method = 'mapOneToMany';
$oneToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'orphanRemoval' => $associationMapping['orphanRemoval'],
'orderBy' => $associationMapping['orderBy']
);
foreach ($potentialAssociationMappingIndexes as $index) {
if (isset($associationMapping[$index])) {
$oneToManyMappingArray[$index] = $associationMapping[$index];
}
}
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
} else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$method = 'mapManyToMany';
$potentialAssociationMappingIndexes = array(
'mappedBy',
'joinTable',
'orderBy',
$method = 'mapManyToMany';
$manyToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'joinTable' => $associationMapping['joinTable'],
'orderBy' => $associationMapping['orderBy']
);
foreach ($potentialAssociationMappingIndexes as $index) {
if (isset($associationMapping[$index])) {
$manyToManyMappingArray[$index] = $associationMapping[$index];
}
}
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
}

View File

@@ -43,7 +43,7 @@ class YamlExporter extends AbstractExporter
*
* TODO: Should this code be pulled out in to a toArray() method in ClassMetadata
*
* @param ClassMetadataInfo $metadata
* @param ClassMetadataInfo $metadata
* @return mixed $exported
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
@@ -84,9 +84,9 @@ class YamlExporter extends AbstractExporter
if (isset($metadata->table['uniqueConstraints'])) {
$array['uniqueConstraints'] = $metadata->table['uniqueConstraints'];
}
$fieldMappings = $metadata->fieldMappings;
$ids = array();
foreach ($fieldMappings as $name => $fieldMapping) {
$fieldMapping['column'] = $fieldMapping['columnName'];
@@ -94,7 +94,7 @@ class YamlExporter extends AbstractExporter
$fieldMapping['columnName'],
$fieldMapping['fieldName']
);
if ($fieldMapping['column'] == $name) {
unset($fieldMapping['column']);
}
@@ -111,7 +111,7 @@ class YamlExporter extends AbstractExporter
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $this->_getIdGeneratorTypeString($metadata->generatorType);
}
if ($ids) {
$array['fields'] = $ids;
}
@@ -145,7 +145,7 @@ class YamlExporter extends AbstractExporter
'targetEntity' => $associationMapping['targetEntity'],
'cascade' => $cascade,
);
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
$joinColumns = $associationMapping['joinColumns'];
$newJoinColumns = array();
@@ -164,7 +164,7 @@ class YamlExporter extends AbstractExporter
'joinColumns' => $newJoinColumns,
'orphanRemoval' => $associationMapping['orphanRemoval'],
);
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
$array['oneToOne'][$name] = $associationMappingArray;
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
@@ -172,7 +172,7 @@ class YamlExporter extends AbstractExporter
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'orphanRemoval' => $associationMapping['orphanRemoval'],
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
'orderBy' => $associationMapping['orderBy']
);
$associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray);
@@ -181,10 +181,10 @@ class YamlExporter extends AbstractExporter
$manyToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null,
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
'joinTable' => $associationMapping['joinTable'],
'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null
);
$associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray);
$array['manyToMany'][$name] = $associationMappingArray;
}

View File

@@ -199,22 +199,6 @@ class SchemaTool
$this->_gatherRelationsSql($class, $table, $schema);
}
$pkColumns = array();
foreach ($class->identifier AS $identifierField) {
if (isset($class->fieldMappings[$identifierField])) {
$pkColumns[] = $class->getQuotedColumnName($identifierField, $this->_platform);
} else if (isset($class->associationMappings[$identifierField])) {
/* @var $assoc \Doctrine\ORM\Mapping\OneToOne */
$assoc = $class->associationMappings[$identifierField];
foreach ($assoc['joinColumns'] AS $joinColumn) {
$pkColumns[] = $joinColumn['name'];
}
}
}
if (!$table->hasIndex('primary')) {
$table->setPrimaryKey($pkColumns);
}
if (isset($class->table['indexes'])) {
foreach ($class->table['indexes'] AS $indexName => $indexData) {
$table->addIndex($indexData['columns'], $indexName);
@@ -301,11 +285,10 @@ class SchemaTool
$pkColumns[] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform);
}
}
// For now, this is a hack required for single table inheritence, since this method is called
// twice by single table inheritence relations
if(!$table->hasIndex('primary')) {
//$table->setPrimaryKey($pkColumns);
$table->setPrimaryKey($pkColumns);
}
return $columns;
@@ -426,47 +409,13 @@ class SchemaTool
}
}
/**
* Get the class metadata that is responsible for the definition of the referenced column name.
*
* Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its
* not a simple field, go through all identifier field names that are associations recursivly and
* find that referenced column name.
*
* TODO: Is there any way to make this code more pleasing?
*
* @param ClassMetadata $class
* @param string $referencedColumnName
* @return array(ClassMetadata, referencedFieldName)
*/
private function getDefiningClass($class, $referencedColumnName)
{
$referencedFieldName = $class->getFieldName($referencedColumnName);
if ($class->hasField($referencedFieldName)) {
return array($class, $referencedFieldName);
} else if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) {
// it seems to be an entity as foreign key
foreach ($class->getIdentifierFieldNames() AS $fieldName) {
if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) {
return $this->getDefiningClass(
$this->_em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']),
$class->getSingleAssociationReferencedJoinColumnName($fieldName)
);
}
}
}
return null;
}
/**
* Gather columns and fk constraints that are required for one part of relationship.
*
* @param array $joinColumns
* @param \Doctrine\DBAL\Schema\Table $theJoinTable
* @param ClassMetadata $class
* @param array $mapping
* @param \Doctrine\ORM\Mapping\AssociationMapping $mapping
* @param array $primaryKeyColumns
* @param array $uniqueConstraints
*/
@@ -479,9 +428,9 @@ class SchemaTool
foreach ($joinColumns as $joinColumn) {
$columnName = $joinColumn['name'];
list($definingClass, $referencedFieldName) = $this->getDefiningClass($class, $joinColumn['referencedColumnName']);
$referencedFieldName = $class->getFieldName($joinColumn['referencedColumnName']);
if (!$definingClass) {
if ( ! $class->hasField($referencedFieldName)) {
throw new \Doctrine\ORM\ORMException(
"Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ".
$mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist."
@@ -497,7 +446,7 @@ class SchemaTool
// It might exist already if the foreign key is mapped into a regular
// property as well.
$fieldMapping = $definingClass->getFieldMapping($referencedFieldName);
$fieldMapping = $class->getFieldMapping($referencedFieldName);
$columnDef = null;
if (isset($joinColumn['columnDefinition'])) {
@@ -516,7 +465,9 @@ class SchemaTool
$columnOptions['precision'] = $fieldMapping['precision'];
}
$theJoinTable->addColumn($columnName, $fieldMapping['type'], $columnOptions);
$theJoinTable->addColumn(
$columnName, $class->getTypeOfColumn($joinColumn['referencedColumnName']), $columnOptions
);
}
if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) {
@@ -626,12 +577,14 @@ class SchemaTool
}
foreach ($schema->getTables() AS $table) {
/* @var $sequence Table */
if ($table->hasPrimaryKey()) {
$columns = $table->getPrimaryKey()->getColumns();
if (count($columns) == 1) {
$checkSequence = $table->getName() . "_" . $columns[0] . "_seq";
if ($fullSchema->hasSequence($checkSequence)) {
$visitor->acceptSequence($fullSchema->getSequence($checkSequence));
foreach ($table->getIndexes() AS $index) {
if ($index->isPrimary()) {
$columns = $index->getColumns();
if (count($columns) == 1) {
$checkSequence = $table->getName() . "_" . $columns[0] . "_seq";
if ($fullSchema->hasSequence($checkSequence)) {
$visitor->acceptSequence($fullSchema->getSequence($checkSequence));
}
}
}
}
@@ -643,11 +596,9 @@ class SchemaTool
/**
* Updates the database schema of the given classes by comparing the ClassMetadata
* instances to the current database schema that is inspected. If $saveMode is set
* to true the command is executed in the Database, else SQL is returned.
* ins$tableNametances to the current database schema that is inspected.
*
* @param array $classes
* @param boolean $saveMode
* @return void
*/
public function updateSchema(array $classes, $saveMode=false)
@@ -663,11 +614,8 @@ class SchemaTool
/**
* Gets the sequence of SQL statements that need to be performed in order
* to bring the given class mappings in-synch with the relational schema.
* If $saveMode is set to true the command is executed in the Database,
* else SQL is returned.
*
* @param array $classes The classes to consider.
* @param boolean $saveMode True for writing to DB, false for SQL string
* @return array The sequence of SQL statements.
*/
public function getUpdateSchemaSql(array $classes, $saveMode=false)

View File

@@ -1,194 +0,0 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\Common\ClassLoader;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Mapping\Driver\XmlDriver;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
/**
* Convenience class for setting up Doctrine from different installations and configurations.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class Setup
{
/**
* Use this method to register all autoloaders for a setup where Doctrine is checked out from
* its github repository at {@link http://github.com/doctrine/doctrine2}
*
* @param string $gitCheckoutRootPath
* @return void
*/
static public function registerAutoloadGit($gitCheckoutRootPath)
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine\Common", $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib");
$loader->register();
$loader = new ClassLoader("Doctrine\DBAL", $gitCheckoutRootPath . "/lib/vendor/doctrine-dbal/lib");
$loader->register();
$loader = new ClassLoader("Doctrine\ORM", $gitCheckoutRootPath . "/lib");
$loader->register();
$loader = new ClassLoader("Symfony\Component", $gitCheckoutRootPath . "/lib/vendor");
$loader->register();
}
/**
* Use this method to register all autoloaders for a setup where Doctrine is installed
* though {@link http://pear.doctrine-project.org}.
*
* @return void
*/
static public function registerAutoloadPEAR()
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once "Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine");
$loader->register();
$parts = explode(PATH_SEPARATOR, get_include_path());
foreach ($parts AS $includePath) {
if ($includePath != "." && file_exists($includePath . "/Doctrine")) {
$loader = new ClassLoader("Symfony\Component", $includePath . "/Doctrine");
$loader->register();
return;
}
}
}
/**
* Use this method to register all autoloads for a downloaded Doctrine library.
* Pick the directory the library was uncompressed into.
*
* @param string $directory
*/
static public function registerAutoloadDirectory($directory)
{
if (!class_exists('Doctrine\Common\ClassLoader', false)) {
require_once $directory . "/Doctrine/Common/ClassLoader.php";
}
$loader = new ClassLoader("Doctrine", $directory);
$loader->register();
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");
$loader->register();
}
/**
* Create a configuration with an annotation metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
return $config;
}
/**
* Create a configuration with an annotation metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new XmlDriver($paths));
return $config;
}
/**
* Create a configuration with an annotation metadata driver.
*
* @param array $paths
* @param boolean $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
{
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
$config->setMetadataDriverImpl(new YamlDriver($paths));
return $config;
}
/**
* Create a configuration without a metadata driver.
*
* @param bool $isDevMode
* @param string $proxyDir
* @param Cache $cache
* @return Configuration
*/
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;
} else if (extension_loaded('xcache')) {
$cache = new \Doctrine\Common\Cache\XcacheCache;
} else if (extension_loaded('memcache')) {
$memcache = new \Memcache();
$memcache->connect('127.0.0.1');
$cache = new \Doctrine\Common\Cache\MemcacheCache();
$cache->setMemcache($memcache);
} else {
$cache = new ArrayCache;
}
} 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 );
$config->setProxyNamespace('DoctrineProxies');
$config->setAutoGenerateProxyClasses($isDevMode);
return $config;
}
}

View File

@@ -218,13 +218,6 @@ class UnitOfWork implements PropertyChangedListener
//private $_readOnlyObjects = array();
/**
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
*
* @var array
*/
private $eagerLoadingEntities = array();
/**
* Initializes a new UnitOfWork instance, bound to the given EntityManager.
*
@@ -455,7 +448,7 @@ class UnitOfWork implements PropertyChangedListener
// and we have a copy of the original data
$originalData = $this->originalEntityData[$oid];
$isChangeTrackingNotify = $class->isChangeTrackingNotify();
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
$changeSet = $isChangeTrackingNotify ? $this->entityChangeSets[$oid] : array();
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
@@ -477,7 +470,9 @@ class UnitOfWork implements PropertyChangedListener
}
} else if ($isChangeTrackingNotify) {
continue;
} else if ($orgValue !== $actualValue) {
} else if (is_object($orgValue) && $orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
} else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
}
@@ -515,9 +510,9 @@ class UnitOfWork implements PropertyChangedListener
$class = $this->em->getClassMetadata($className);
// Skip class if instances are read-only
if ($class->isReadOnly) {
continue;
}
//if ($class->isReadOnly) {
// continue;
//}
// If change tracking is explicit or happens through notification, then only compute
// changes on entities of that type that are explicitly marked for synchronization.
@@ -573,12 +568,10 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entry);
if ($state == self::STATE_NEW) {
if ( ! $assoc['isCascadePersist']) {
throw new InvalidArgumentException("A new entity was found through the relationship '"
. $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not"
. " configured to cascade persist operations for entity: " . self::objToStr($entry) . "."
throw new InvalidArgumentException("A new entity was found through a relationship that was not"
. " configured to cascade persist operations: " . self::objToStr($entry) . "."
. " Explicitly persist the new entity or configure cascading persist operations"
. " on the relationship. If you cannot find out which entity causes the problem"
. " implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.");
. " on the relationship.");
}
$this->persistNew($targetClass, $entry);
$this->computeChangeSet($targetClass, $entry);
@@ -761,9 +754,7 @@ class UnitOfWork implements PropertyChangedListener
);
}
if ($this->entityChangeSets[$oid]) {
$persister->update($entity);
}
$persister->update($entity);
unset($this->entityUpdates[$oid]);
if ($hasPostUpdateLifecycleCallbacks) {
@@ -834,8 +825,6 @@ class UnitOfWork implements PropertyChangedListener
// 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);
@@ -847,7 +836,7 @@ class UnitOfWork implements PropertyChangedListener
}
// Calculate dependencies for new nodes
while ($class = array_pop($newNodes)) {
foreach ($newNodes as $class) {
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
@@ -862,7 +851,6 @@ class UnitOfWork implements PropertyChangedListener
$targetSubClass = $this->em->getClassMetadata($subClassName);
if ( ! $calc->hasClass($subClassName)) {
$calc->addClass($targetSubClass);
$newNodes[] = $targetSubClass;
}
$calc->addDependency($targetSubClass, $class);
}
@@ -1105,22 +1093,6 @@ 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;
}
@@ -1387,10 +1359,6 @@ 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);
@@ -1429,7 +1397,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);
}
}
@@ -1765,7 +1733,7 @@ class UnitOfWork implements PropertyChangedListener
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
if ($this->getEntityState($entity) != self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
@@ -1832,10 +1800,6 @@ class UnitOfWork implements PropertyChangedListener
if ($this->commitOrderCalculator !== null) {
$this->commitOrderCalculator->clear();
}
if ($this->evm->hasListeners(Events::onClear)) {
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em));
}
}
/**
@@ -1891,19 +1855,11 @@ class UnitOfWork implements PropertyChangedListener
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];
}
$id[$fieldName] = $data[$fieldName];
}
$idHash = implode(' ', $id);
} else {
if (isset($class->associationMappings[$class->identifier[0]])) {
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
} else {
$idHash = $data[$class->identifier[0]];
}
$idHash = $data[$class->identifier[0]];
$id = array($class->identifier[0] => $idHash);
}
@@ -1913,16 +1869,13 @@ class UnitOfWork implements PropertyChangedListener
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__isInitialized__ = true;
$overrideLocalValues = true;
$this->originalEntityData[$oid] = $data;
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
} else {
$overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
}
if ($overrideLocalValues) {
$this->originalEntityData[$oid] = $data;
}
} else {
$entity = $class->newInstance();
$oid = spl_object_hash($entity);
@@ -1942,9 +1895,6 @@ class UnitOfWork implements PropertyChangedListener
$class->reflFields[$field]->setValue($entity, $value);
}
}
// 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])) {
@@ -1959,15 +1909,10 @@ class UnitOfWork implements PropertyChangedListener
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($assoc['isOwningSide']) {
$associatedId = array();
// TODO: Is this even computed right in all cases of composite keys?
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
if ($joinColumnValue !== null) {
if ($targetClass->containsForeignIdentifier) {
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
} else {
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
}
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
}
}
if ( ! $associatedId) {
@@ -1975,10 +1920,6 @@ class UnitOfWork implements PropertyChangedListener
$class->reflFields[$field]->setValue($entity, null);
$this->originalEntityData[$oid][$field] = null;
} else {
if (!isset($hints['fetchMode'][$class->name][$field])) {
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
}
// Foreign key is set
// Check identity map first
// FIXME: Can break easily with composite keys if join column values are in
@@ -1986,38 +1927,16 @@ 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!
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
isset($hints['deferEagerLoad']) &&
!$targetClass->isIdentifierComposite &&
$newValue instanceof Proxy &&
$newValue->__isInitialized__ === false) {
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
}
} else {
if ($targetClass->subClasses) {
// If it might be a subtype, it can not be lazy. There isn't even
// 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)
// If it might be a subtype, it can not be lazy
$newValue = $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, $associatedId);
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
} else {
// Deferred eager load only works for single identifier classes
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
// TODO: Is there a faster approach?
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
} else {
// TODO: This is very imperformant, ignore it?
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
}
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
// TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside
// the persister instead of this rather unperformant approach.
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
} else {
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
}
@@ -2031,30 +1950,25 @@ 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);
}
}
} else {
// Inverse side of x-to-one can never be lazy
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity));
->loadOneToOneEntity($assoc, $entity, null));
}
} else {
// Inject collection
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
$pColl->setOwner($entity, $assoc);
$reflField = $class->reflFields[$field];
$reflField->setValue($entity, $pColl);
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) {
$pColl->setInitialized(false);
} else {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
} else {
$pColl->setInitialized(false);
}
$this->originalEntityData[$oid][$field] = $pColl;
}
@@ -2073,25 +1987,6 @@ class UnitOfWork implements PropertyChangedListener
return $entity;
}
/**
* @return void
*/
public function triggerEagerLoads()
{
if (!$this->eagerLoadingEntities) {
return;
}
// avoid infinite recursion
$eagerLoadingEntities = $this->eagerLoadingEntities;
$this->eagerLoadingEntities = array();
foreach ($eagerLoadingEntities AS $entityName => $ids) {
$class = $this->em->getClassMetadata($entityName);
$this->getEntityPersister($entityName)->loadAll(array_combine($class->identifier, array(array_values($ids))));
}
}
/**
* Initializes (loads) an uninitialized persistent collection of an entity.
*
@@ -2171,7 +2066,7 @@ class UnitOfWork implements PropertyChangedListener
* @return array The identifier values.
*/
public function getEntityIdentifier($entity)
{
{
return $this->entityIdentifiers[spl_object_hash($entity)];
}
@@ -2234,7 +2129,7 @@ class UnitOfWork implements PropertyChangedListener
* Gets the EntityPersister for an Entity.
*
* @param string $entityName The name of the Entity.
* @return Doctrine\ORM\Persisters\AbstractEntityPersister
* @return Doctrine\ORM\Persister\AbstractEntityPersister
*/
public function getEntityPersister($entityName)
{
@@ -2299,7 +2194,7 @@ class UnitOfWork implements PropertyChangedListener
*/
public function clearEntityChangeSet($oid)
{
$this->entityChangeSets[$oid] = array();
unset($this->entityChangeSets[$oid]);
}
/* PropertyChangedListener implementation */

View File

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

View File

@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Use this configuration file as a template to run the tests against any dbms.
Procedure:
1) Save a copy of this file with a name of your chosing. It doesn't matter
where you place it as long as you know where it is.
i.e. "mysqlconf.xml" (It needs the ending .xml).
2) Edit the file and fill in your settings (database name, type, username, etc.)
Just change the "value"s, not the names of the var elements.
3) To run the tests against the database type the following from within the
tests/ folder: phpunit -c <filename> ...
Example: phpunit -c mysqlconf.xml AllTests
-->
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/Doctrine/Tests/TestInit.php"
>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./tests/Doctrine/Tests/ORM</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
<php>
<!-- "Real" test database -->
<!-- uncomment, otherwise sqlite memory runs
<var name="db_type" value="pdo_mysql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>-->
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
<!-- Database for temporary connections (i.e. to drop/create the main database) -->
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_name" value="doctrine_tests_tmp" />
<var name="tmpdb_port" value="3306"/>
</php>
</phpunit>

View File

@@ -1,21 +0,0 @@
#!/bin/bash
# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases.
# Just create the phpunit.xmls as described in the array below and configure the specific files <php /> section
# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped.
configs[1]="mysql.phpunit.xml"
configs[2]='postgres.phpunit.xml'
configs[3]='sqlite.phpunit.xml'
configs[4]='oracle.phpunit.xml'
configs[5]='db2.phpunit.xml'
configs[6]='pdo-ibm.phpunit.xml'
configs[7]='sqlsrv.phpunit.xml'
for i in "${configs[@]}"; do
if [ -f "$i" ];
then
echo "RUNNING TESTS WITH CONFIG $i"
phpunit -c "$i" "$@"
fi;
done

View File

@@ -0,0 +1,33 @@
<?php
namespace Doctrine\Tests;
use Doctrine\Tests\Common;
use Doctrine\Tests\ORM;
use Doctrine\Tests\DBAL;
if (!defined('PHPUnit_MAIN_METHOD')) {
define('PHPUnit_MAIN_METHOD', 'AllTests::main');
}
require_once __DIR__ . '/TestInit.php';
class AllTests
{
public static function main()
{
\PHPUnit_TextUI_TestRunner::run(self::suite());
}
public static function suite()
{
$suite = new DoctrineTestSuite('Doctrine Tests');
$suite->addTest(ORM\AllTests::suite());
return $suite;
}
}
if (PHPUnit_MAIN_METHOD == 'AllTests::main') {
AllTests::main();
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Doctrine\Tests;
class DbalFunctionalTestSuite extends DbalTestSuite
{
protected function setUp()
{
if ( ! isset($this->sharedFixture['conn'])) {
$this->sharedFixture['conn'] = TestUtil::getConnection();
}
}
protected function tearDown()
{
$this->sharedFixture['conn']->close();
$this->sharedFixture = null;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests;
/**
* The outermost test suite for all dbal related testcases & suites.
*/
class DbalTestSuite extends DoctrineTestSuite
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Doctrine\Tests;
/**
* Doctrine's basic test suite implementation. Provides functionality needed by all
* test suites.
*/
class DoctrineTestSuite extends \PHPUnit_Framework_TestSuite
{
}

View File

@@ -78,7 +78,7 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
$config = new \Doctrine\ORM\Configuration();
$config->setProxyDir(__DIR__ . '/../Proxies');
$config->setProxyNamespace('Doctrine\Tests\Proxies');
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver());
$config->setMetadataDriverImpl(\Doctrine\ORM\Mapping\Driver\AnnotationDriver::create());
}
if (is_null($eventManager)) {
$eventManager = new \Doctrine\Common\EventManager();

View File

@@ -59,7 +59,7 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister
$this->_updates[] = $entity;
}
public function exists($entity, array $extraConditions = array())
public function exists($entity)
{
$this->existsCalled = true;
}

View File

@@ -7,9 +7,6 @@ use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="cms_users")
* @NamedQueries({
* @NamedQuery(name="all", query="SELECT u FROM __CLASS__ u")
* })
*/
class CmsUser
{
@@ -31,7 +28,7 @@ class CmsUser
*/
public $name;
/**
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true)
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
*/
public $phonenumbers;
/**

View File

@@ -17,11 +17,6 @@ class CompanyEmployee extends CompanyPerson
* @Column(type="string", length=255)
*/
private $department;
/**
* @Column(type="datetime", nullable=true)
*/
private $startDate;
public function getSalary() {
return $this->salary;
@@ -38,12 +33,4 @@ 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

@@ -1,65 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117ApproveChanges
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
private $id;
/**
* @ManyToOne(targetEntity="DDC117ArticleDetails")
* @JoinColumn(name="details_id", referencedColumnName="article_id")
*/
private $articleDetails;
/**
* @ManyToOne(targetEntity="DDC117Reference")
* @JoinColumns({
* @JoinColumn(name="source_id", referencedColumnName="source_id"),
* @JoinColumn(name="target_id", referencedColumnName="target_id")
* })
*/
private $reference;
/**
* @ManyToOne(targetEntity="DDC117Translation")
* @JoinColumns({
* @JoinColumn(name="trans_article_id", referencedColumnName="article_id"),
* @JoinColumn(name="trans_language", referencedColumnName="language")
* })
*/
private $translation;
public function __construct($details, $reference, $translation)
{
$this->articleDetails = $details;
$this->reference = $reference;
$this->translation = $translation;
}
public function getId()
{
return $this->id;
}
public function getArticleDetails()
{
return $this->articleDetails;
}
public function getReference()
{
return $this->reference;
}
public function getTranslation()
{
return $this->translation;
}
}

View File

@@ -1,86 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117Article
{
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
private $id;
/** @Column */
private $title;
/**
* @OneToMany(targetEntity="DDC117Reference", mappedBy="source", cascade={"remove"})
*/
private $references;
/**
* @OneToOne(targetEntity="DDC117ArticleDetails", mappedBy="article", cascade={"persist", "remove"})
*/
private $details;
/**
* @OneToMany(targetEntity="DDC117Translation", mappedBy="article", cascade={"persist", "remove"})
*/
private $translations;
/**
* @OneToMany(targetEntity="DDC117Link", mappedBy="source")
*/
private $links;
public function __construct($title)
{
$this->title = $title;
$this->references = new \Doctrine\Common\Collections\ArrayCollection();
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
public function setDetails($details)
{
$this->details = $details;
}
public function id()
{
return $this->id;
}
public function addReference($reference)
{
$this->references[] = $reference;
}
public function references()
{
return $this->references;
}
public function addTranslation($language, $title)
{
$this->translations[] = new DDC117Translation($this, $language, $title);
}
public function getText()
{
return $this->details->getText();
}
public function getDetails()
{
return $this->details;
}
public function resetText()
{
$this->details = null;
}
public function getTranslations()
{
return $this->translations;
}
}

View File

@@ -1,39 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117ArticleDetails
{
/**
* @Id
* @OneToOne(targetEntity="DDC117Article", inversedBy="details")
* @JoinColumn(name="article_id", referencedColumnName="article_id")
*/
private $article;
/**
* @Column(type="text")
*/
private $text;
public function __construct($article, $text)
{
$this->article = $article;
$article->setDetails($this);
$this->update($text);
}
public function update($text)
{
$this->text = $text;
}
public function getText()
{
return $this->text;
}
}

View File

@@ -1,54 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117Editor
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
/**
* @Column(type="string")
*/
public $name;
/**
* @ManyToMany(targetEntity="DDC117Translation", inversedBy="reviewedByEditors")
* @JoinTable(
* inverseJoinColumns={
* @JoinColumn(name="article_id", referencedColumnName="article_id"),
* @JoinColumn(name="language", referencedColumnName="language")
* },
* joinColumns={
* @JoinColumn(name="editor_id", referencedColumnName="id")
* }
* )
*/
public $reviewingTranslations;
/**
* @ManyToOne(targetEntity="DDC117Translation", inversedBy="lastTranslatedBy")
* @JoinColumns({
* @JoinColumn(name="lt_article_id", referencedColumnName="article_id"),
* @JoinColumn(name="lt_language", referencedColumnName="language")
* })
*/
public $lastTranslation;
public function __construct($name = "")
{
$this->name = $name;
$this->reviewingTranslations = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addLastTranslation(DDC117Translation $t)
{
$this->lastTranslation = $t;
$t->lastTranslatedBy[] = $this;
}
}

View File

@@ -1,31 +0,0 @@
<?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

@@ -1,64 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117Reference
{
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article", inversedBy="references")
* @JoinColumn(name="source_id", referencedColumnName="article_id")
*/
private $source;
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article", inversedBy="references")
* @JoinColumn(name="target_id", referencedColumnName="article_id")
*/
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

@@ -1,65 +0,0 @@
<?php
namespace Doctrine\Tests\Models\DDC117;
/**
* @Entity
*/
class DDC117Translation
{
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article")
* @JoinColumn(name="article_id", referencedColumnName="article_id")
*/
private $article;
/**
* @Id @column(type="string")
*/
private $language;
/**
* @column(type="string")
*/
private $title;
/**
* @ManyToMany(targetEntity="DDC117Editor", mappedBy="reviewingTranslations")
*/
public $reviewedByEditors;
/**
* @OneToMany(targetEntity="DDC117Editor", mappedBy="lastTranslation")
*/
public $lastTranslatedBy;
public function __construct($article, $language, $title)
{
$this->article = $article;
$this->language = $language;
$this->title = $title;
$this->reviewedByEditors = new \Doctrine\Common\Collections\ArrayCollection();
$this->lastTranslatedBy = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getArticleId()
{
return $this->article->id();
}
public function getLanguage()
{
return $this->language;
}
public function getLastTranslatedBy()
{
return $this->lastTranslatedBy;
}
public function getReviewedByEditors()
{
return $this->reviewedByEditors;
}
}

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"}, fetch="EAGER")
* @OneToOne(targetEntity="ECommerceCustomer", cascade={"persist"})
* @JoinColumn(name="mentor_id", referencedColumnName="id")
*/
private $mentor;

View File

@@ -1,33 +0,0 @@
<?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

@@ -1,41 +0,0 @@
<?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

@@ -1,80 +0,0 @@
<?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

@@ -1,65 +0,0 @@
<?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

@@ -1,48 +0,0 @@
<?php
namespace Doctrine\Tests\Models\StockExchange;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Bonds have many stocks. This uses a many to many assocation and fails to model how many of a
* particular stock a bond has. But i Need a many-to-many assocation, so please bear with my modelling skills ;)
*
* @Entity
* @Table(name="exchange_bonds")
*/
class Bond
{
/**
* @Id @GeneratedValue @column(type="integer")
* @var int
*/
private $id;
/**
* @column(type="string")
* @var string
*/
private $name;
/**
* @ManyToMany(targetEntity="Stock", indexBy="symbol")
* @JoinTable(name="exchange_bonds_stocks")
* @var Stock[]
*/
public $stocks;
public function __construct($name)
{
$this->name = $name;
}
public function getId()
{
return $this->id;
}
public function addStock(Stock $stock)
{
$this->stocks[$stock->getSymbol()] = $stock;
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace Doctrine\Tests\Models\StockExchange;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="exchange_markets")
*/
class Market
{
/**
* @Id @Column(type="integer") @GeneratedValue
* @var int
*/
private $id;
/**
* @Column(type="string")
* @var string
*/
private $name;
/**
* @OneToMany(targetEntity="Stock", mappedBy="market", indexBy="symbol")
* @var Stock[]
*/
public $stocks;
public function __construct($name)
{
$this->name = $name;
$this->stocks = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function addStock(Stock $stock)
{
$this->stocks[$stock->getSymbol()] = $stock;
}
public function getStock($symbol)
{
return $this->stocks[$symbol];
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace Doctrine\Tests\Models\StockExchange;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="exchange_stocks")
*/
class Stock
{
/**
* @Id @GeneratedValue @Column(type="integer")
* @var int
*/
private $id;
/**
* For real this column would have to be unique=true. But I want to test behavior of non-unique overrides.
*
* @Column(type="string")
*/
private $symbol;
/**
* @Column(type="decimal")
*/
private $price;
/**
* @ManyToOne(targetEntity="Market", inversedBy="stocks")
* @var Market
*/
private $market;
public function __construct($symbol, $initialOfferingPrice, Market $market)
{
$this->symbol = $symbol;
$this->price = $initialOfferingPrice;
$this->market = $market;
$market->addStock($this);
}
public function getSymbol()
{
return $this->symbol;
}
}

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