Compare commits

...

626 Commits

Author SHA1 Message Date
Benjamin Eberlei 054ac220ac Release 2.1.0RC1 2011-06-18 22:08:37 +00:00
Benjamin Eberlei c6746d4a4a Merge 2.0.x build.xml changes into master build.xml 2011-06-18 22:07:14 +00:00
Benjamin Eberlei f0bc3d925d Bump dependencies of Common and DBAL to 2.1.0RC1 2011-06-18 22:02:40 +00:00
Benjamin Eberlei 7810f5fe69 Add convenience helper for running tests against multiple different databases 2011-06-18 23:23:43 +02:00
Benjamin Eberlei decd1482de Fix bug in ChangeTrackingNotify code 2011-06-18 23:06:07 +02:00
Benjamin Eberlei fa7574b2ba Removed AllTests files and Suites 2011-06-18 22:49:25 +02:00
Benjamin Eberlei 32b146ea8a Switch testsuite to run with phpunit.ini.dist from main folder, not using AllTests approach. Fixed global state problem in tests that was caused by EventManager being reused. Significantly enhanced error message about cascade persist 2011-06-18 22:47:21 +02:00
Benjamin Eberlei 1aa90dc872 Add UPGRADE note about annotations parser changes 2011-06-18 21:25:29 +02:00
Benjamin Eberlei 989d375be5 Some more tests for the Setup helper 2011-06-18 08:47:10 +02:00
Benjamin Eberlei ede68ec87b Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-18 01:05:48 +02:00
Benjamin Eberlei 02f06b6d52 Add convenience Setup Tool to register autoloaders and create configuration objects 2011-06-18 01:05:30 +02:00
Guilherme Blanco 699ccfddb6 Implemented COALESCE and NULLIF support in DQL. 2011-06-17 16:16:22 -03:00
Guilherme Blanco 626e467a17 Implemented COALESCE and NULLIF support in DQL. 2011-06-17 16:15:19 -03:00
Guilherme Blanco 1fed340793 Optimized AnnotationDriver to filter found files during getAllClassnames(). 2011-06-16 19:54:50 -03:00
Benjamin Eberlei 0cd0ae49a1 Fix regression introduced with DDC-1203,DDC-1204 patch 2011-06-16 23:00:59 +02:00
Benjamin Eberlei 713f4654fd Merge remote-tracking branch 'origin/master' 2011-06-16 22:49:29 +02:00
Benjamin Eberlei 22bdb1520a Merge branch 'DDC-1172' 2011-06-16 22:34:16 +02:00
Benjamin Eberlei 42c5382a03 DDC-1172 - Handle sequence dropping in SchemaTool. 2011-06-16 22:34:04 +02:00
Guilherme Blanco 98bc3c4e40 Merge pull request #74 from mridgway/DDC-1209
[DDC-1209] Fixed custom object types as @Id
2011-06-16 07:15:40 -07:00
Michael Ridgway d1106a730b Made DDC-1209 test pass 2011-06-16 08:55:09 -04:00
Michael Ridgway da2d83fc7d DDC-1209 tests 2011-06-15 17:15:46 -04:00
Benjamin Eberlei ec748b2a16 Merge branch 'DDC-1203' 2011-06-15 22:27:32 +02:00
Benjamin Eberlei 5ff44b5ec7 DDC-1203, DDC-1204 - Fix problems with mapped superclasses in midth of inheritance hierachy and entities not mapped in discriminator map. 2011-06-15 22:27:24 +02:00
Benjamin Eberlei dd329c903d Merge branch 'DDC-1208' 2011-06-15 18:32:06 +02:00
Benjamin Eberlei 3be6218341 DDC-1208 - Allow namespace separator in <discriminator-mapping /> 2011-06-15 18:31:59 +02:00
Benjamin Eberlei 71082bdba8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-15 18:29:01 +02:00
Benjamin Eberlei 866534334f Merge pull request #71 from fabpot/master
Updated Symfony2 vendors
2011-06-14 08:08:16 -07:00
Fabien Potencier b37c8f6a23 Update Symfony2 vendors 2011-06-14 17:01:33 +02:00
Benjamin Eberlei df500033bf Merge pull request #70 from pkruithof/patch-1
Change attribute 'columns' of type 'index' to 'xs:string'
2011-06-14 06:27:07 -07:00
Peter Kruithof 05bf8477a3 The columns attribute of the index type was xs:NMTOKENS, therefore not allowing a comma, which is needed when multiple columns need to be specified. (I've checked the XmlDriver code for this)
It should be the same as the `columns` attribute of the `unique-constraints` type, namely `xs:string`.
2011-06-14 02:36:49 -07:00
Benjamin Eberlei 2b52eff4eb Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-13 08:54:39 +02:00
Benjamin Eberlei d5daf161c6 Merge pull request #64 from mvrhov/schema_extension
Add extension points into the xml schema
2011-06-12 13:14:19 -07:00
Benjamin Eberlei 12dfb5ee38 Merge pull request #67 from Nico-B/master
joinTable error with DB to YML generator
2011-06-12 13:09:32 -07:00
Benjamin Eberlei 39ee9e5e5b Merge pull request #69 from Garfield-fr/master
Renamed function getFullName with getName
2011-06-12 13:08:43 -07:00
Bertrand Zuchuat c7eaf77d15 Renamed function getFullName with getName to match with last change on Symfony Console 2011-06-12 14:46:02 +02:00
NicoB 524c799e37 Merge remote-tracking branch 'remotes/upstream/master' 2011-06-11 17:56:50 +07:00
Benjamin Eberlei fe527fbf1a Merge pull request #59 from asm89/databasedriver-patch
Implemented tableName -> className and columnName -> fieldName mapping in DatabaseDriver.
2011-06-11 03:15:16 -07:00
Benjamin Eberlei 037daff891 Merge pull request #65 from weaverryan/update_command
[Tools][Console] Refactoring the UpdateCommand
2011-06-11 01:16:25 -07:00
Benjamin Eberlei 9945296472 Merge pull request #61 from stof/EntityGenerator_3
Updated the EntityGenerator to be compatible with Common 3.0.x
2011-06-11 01:13:05 -07:00
Guilherme Blanco fdbc909bde Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-09 15:43:37 -03:00
Guilherme Blanco 1f6b49d236 Added getRootEntities to QueryBuilder. 2011-06-09 15:42:40 -03:00
NicoB 3cdb4e007d joinTable can be undefined because ManyToMAny generation is bidirectional with inverse sides 2011-06-07 18:55:52 +07:00
Benjamin Eberlei a4cbb23fc8 Slight adjustment to build.xml 2011-06-05 17:21:37 +02:00
Benjamin Eberlei 97573425f9 Merge branch 'DDC-1163' 2011-06-05 16:21:35 +02:00
Benjamin Eberlei 4371e8fab0 DDC-1163 - Fix nasty bug with inheritance in UnitOfWork::executeUpdates() and executeRemovals() 2011-06-05 16:21:23 +02:00
Benjamin Eberlei 22826ac10d DDC-1156 - Do not throw exception for mapped superclass in middle of inheritance hierachy anymore. 2011-06-05 15:00:49 +02:00
Benjamin Eberlei 543432bf53 Merge branch 'DDC-1192' 2011-06-05 14:50:05 +02:00
Benjamin Eberlei d17d0f5452 DDC-1192 - Fix notice in XmlDriver, removed unnecessary code. 2011-06-05 14:49:54 +02:00
Benjamin Eberlei d3ab9b51fa DDC-1181 - Add test that verifies cascade remove works for entities with foreign identifiers 2011-06-05 13:57:44 +02:00
Benjamin Eberlei ddb647f39f DDC-1173 - Fix bug when calling UnitOfWork::clearEntityChangeSet() in listener 2011-06-05 13:34:07 +02:00
Benjamin Eberlei 70d756d59c DDC-1184 - Improve error handling in AssignedIdGenerator 2011-06-05 12:54:29 +02:00
Benjamin Eberlei f3677554e8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-05 12:52:05 +02:00
Benjamin Eberlei 3cdff65761 Merge pull request #63 from chesteroni/master
Added missing checks for associatation indexes
2011-06-05 03:28:52 -07:00
Benjamin Eberlei ff30f86082 Merge pull request #62 from mvrhov/yml_export_notice_fix
Fixing Notice: Undefined index in yaml export driver
2011-06-05 03:28:31 -07:00
Benjamin Eberlei 1038a866a4 DDC-1194 - Improve error handling for DQL INSTANCE OF 2011-06-05 10:48:21 +02:00
Benjamin Eberlei 2c9a12771b Merge branch 'DDC-1193' 2011-06-05 10:03:04 +02:00
Benjamin Eberlei bda4165bf8 DDC-1193 - Fix previous commit. 2011-06-05 10:02:57 +02:00
Benjamin Eberlei 84aefd6340 Merge branch 'DDC-1193' 2011-06-05 09:59:57 +02:00
Benjamin Eberlei acaf08d4b7 DDC-1193 - Fix bug with cascade remove and proxy classes. 2011-06-05 09:59:16 +02:00
Benjamin Eberlei 875912bffd DDC-733 - Add UnitOfWork::initializeObject() method. 2011-06-05 08:44:38 +02:00
Fabien Potencier 86c3744b8c Made orm:convert-mapping command more configurable (allow to change the extension of the generated files for instance) 2011-06-05 08:23:08 +02:00
Benjamin Eberlei 875f5c1fa8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-06-05 08:19:25 +02:00
Ryan Weaver 6468740915 [Tools][Console] Reworking changes to be more backwards compatible
This keeps the --dump-sql and --force options, but adds an exception if you try to use them both (which previously, only dumped the SQL but didn't tell you that it was *not* in fact also executing the queries).

One additional change is the introduction of a `$name` property, which was the only way that a parent task could allow a child task to override the task's name early enough that the task's overridden name is taken to account when the parent class references it for its help message.
2011-06-03 15:09:18 -05:00
Ryan Weaver 79643e32ed [Tools][Console] Refactoring the UpdateCommand
There are two basic changes:

  1) Changed --force and --dump-sql from options to a single argument. Prior, you couldn't pass both options simultaneously anyways, so making them an argument is more accurate.

  2) Changed the language and formatting of the task to be more user-friendly.
2011-06-03 08:02:51 -05:00
Miha Vrhovnik bf0775fbb6 Add extension points into the xml schema 2011-06-03 09:12:32 +02:00
Alexander 7ee8dc4e44 DDC-1179 - Make it possible to specify a namespace when mapping with --from-database 2011-06-02 21:45:03 +02:00
chesteroni 23540c17f1 Added checking for existing indexes in associatation mapping array. 2011-05-28 20:57:19 -07:00
Miha Vrhovnik bb873826ca Fixing Notice: Undefined index: orderBy in ...Doctrine/ORM/Tools/Export/Driver/YamlExporter 2011-05-27 08:43:29 +02:00
Guilherme Blanco 93521217a6 Moved getQuoted* from ClassMetadata to ClassMetadataInfo, since SchemaTool relies on them, making impossible to work with DisconnectedClassMetadataFactory. 2011-05-26 02:18:29 -03:00
Christophe Coevoet 693fc090b5 Updated the EntityGenerator to be compatible with Common 3.0.x 2011-05-25 12:35:54 +02:00
Benjamin Eberlei ca6ea65b1f Merge branch 'NewAnnotations' 2011-05-25 00:36:10 +02:00
Benjamin Eberlei 3adbf0de39 Add forward compatibility with Doctrine Common 3.0 2011-05-25 00:35:57 +02:00
Johannes Schmitt a0d79b03e7 [AnnotationDriver] compatibility with Doctrine Common 3.x 2011-05-25 00:32:55 +02:00
Benjamin Eberlei 2b3d0a209c Merge branch 'NewAnnotations' 2011-05-25 00:26:29 +02:00
Benjamin Eberlei 6d724ad9ff Make ORM forward compatible with new Doctrine Annotations library version 2.1 2011-05-25 00:26:20 +02:00
Benjamin Eberlei 0bb0937372 Started UPGRADE_TO_2_1 document 2011-05-20 21:13:25 +02:00
Alexander cec62db2d8 Removed _ prefix from private functions. 2011-05-20 16:53:35 +02:00
Alexander 262ae7c942 Implemented tableName -> className and columnName -> fieldName mapping in
DatabaseDriver.
2011-05-20 16:36:43 +02:00
Benjamin Eberlei 85fb1a3ebb Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-18 19:37:32 +02:00
Benjamin Eberlei a979852ee6 Merge branch 'DDC-1080' 2011-05-17 23:42:39 +02:00
Benjamin Eberlei 9ea03de84f DDC-1080 - Fix bug with hydration of derived entities/foreign key as primary key. 2011-05-17 23:42:24 +02:00
Benjamin Eberlei 9f01e9563f Bump Dev Version to 2.1.0BETA2-DEV 2011-05-16 19:35:00 +00:00
Benjamin Eberlei f4021e7469 Update Doctrine Common to latest version and add new methods to ClassMetadataInfo 2011-05-16 20:42:38 +02:00
Benjamin Eberlei 5d81e867be Add some logic to keep backwards compatibility in QueryBuilder 2011-05-15 23:16:44 +02:00
Benjamin Eberlei 85d40847ac Reintroduce QueryBuilder::getRootAlias() for backwards compatibility reasons, mark as @deprecated 2011-05-15 22:11:10 +02:00
Benjamin Eberlei 5e938b3147 Revert "Implemented auto-inference of isCascadeRemove when orphanRemoval is defined (marked as todo)."
This reverts commit 551247d11a.
2011-05-15 20:39:55 +02:00
Benjamin Eberlei 2cfdf2b05d Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-15 20:18:47 +02:00
Benjamin Eberlei d45f7c1302 DDC-694 - Add info command 2011-05-15 20:18:31 +02:00
Benjamin Eberlei 34ad2ccbf1 Merge pull request #23 from kertz/master
LifecycleCallbackMethods Beautification
2011-05-15 10:40:23 -07:00
Benjamin Eberlei 83372bd144 Merge pull request #23 from kertz/master
LifecycleCallbackMethods Beautification
2011-05-15 10:39:44 -07:00
Benjamin Eberlei 1b4f0a5e1f DDC-1080 - Add failing testcase 2011-05-14 13:32:12 +02:00
Benjamin Eberlei 14d630ae1c Merge branch 'DDC-1151' 2011-05-14 08:44:49 +02:00
Benjamin Eberlei cb3615ab47 DDC-1151 - Fix missing table quotes when adding foreign keys in SchemaTool 2011-05-14 08:44:19 +02:00
Benjamin Eberlei e66970b78b Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-14 08:16:21 +02:00
Guilherme Blanco 2a7364bb18 We now support @Id on @ManyToOne fields. Removed TODO and associated code. 2011-05-14 00:53:22 -03:00
Guilherme Blanco f4d62b317e Fixed endless recursion of DDC-719 test. 2011-05-14 00:49:46 -03:00
Guilherme Blanco 551247d11a Implemented auto-inference of isCascadeRemove when orphanRemoval is defined (marked as todo). 2011-05-14 00:43:33 -03:00
Guilherme Blanco 08f2af489e Changed QueryBuilder to allow retrieval of all defined root alises. 2011-05-14 00:40:23 -03:00
Guilherme Blanco e538128645 [DDC-1029] renaming "load()" in proxy to "__load()" 2011-05-13 00:23:27 -03:00
Guilherme Blanco 1e9e2de737 [DDC-1122] Added coverage for bug report. 2011-05-13 00:12:05 -03:00
Guilherme Blanco 8e3fdc5adc [DDC-1148] Implement auto-inference of types in setParameter. 2011-05-12 23:05:45 -03:00
Benjamin Eberlei 814473c27d Merge pull request #35 from brikou/master
[DDC-1136] prevent backup file to be recognized as valid Entity file
2011-05-12 05:41:31 -07:00
Guilherme Blanco a45a02fd64 Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-11 21:40:43 -03:00
Guilherme Blanco 905e05cd36 [DDC-1067][DDC-1145] Fixed bug with multiple froms and inclusion of joins. Added support for index by in QueryBuilder. This break BC only if users are using base support (->add). 2011-05-11 21:40:27 -03:00
Benjamin Eberlei 75b69f946d Merge pull request #57 from stof/DDC-1146
[DDC-1146] Updated the Symfony2 vendors
2011-05-11 16:14:25 -07:00
Christophe Coevoet a112be79ef [DDC-1146] Updated the Symfony2 vendors 2011-05-12 01:10:34 +02:00
Guilherme Blanco 54a0109d5d [DDC-1147] Allowed usage of 0-based input parameters in DQL. 2011-05-11 17:30:42 -03:00
Guilherme Blanco 0d0d61935f Merge pull request #53 from Garfield-fr/master
Fixed typo
2011-05-08 05:18:46 -07:00
Bertrand Zuchuat b959ab37bf Typo 2011-05-08 13:57:08 +02:00
Guilherme Blanco b025b2b343 Fixed where componentes (ie. MEMBER OF) that that are sensitive to parenthesis presence. Made OR and AND expressions smarter. Fixed related unit tests. 2011-05-07 20:14:04 -03:00
Guilherme Blanco bffca232e2 Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-06 01:42:01 -03:00
Guilherme Blanco 19dfe7b891 Added method to allow retrieve all registered entity namespace aliases. 2011-05-06 01:41:34 -03:00
Benjamin Eberlei ce1e446227 Merge branch 'master' of github.com:doctrine/doctrine2 2011-05-03 17:06:42 +02:00
Benjamin Eberlei 5d1905de13 DDC-1120 - Fix comment 2011-05-01 12:17:09 +02:00
Benjamin Eberlei 23131d6860 Merge branch 'DDC-1129' 2011-05-01 11:45:39 +02:00
Benjamin Eberlei d4569baa11 [DDC-1129] Fix bug in version changeset computation aswell as inline ClassMetadata::isCollectionValuedAssociation to increase performance by 2-5% 2011-05-01 11:44:31 +02:00
Benjamin Eberlei b5520aa304 Merge branch 'DDC-1091' 2011-05-01 11:01:39 +02:00
Benjamin Eberlei c53baa9935 [DDC-1091] Fix bug with custom string functions in StringPrimary 2011-05-01 11:01:30 +02:00
Benjamin Eberlei d0b95bb31c Merge branch 'DDC-1043' 2011-05-01 10:21:59 +02:00
Benjamin Eberlei 7a068c206e DDC-1043 - Make computeChangeSet() algorithm more strict, possible leading to more updates to to values that are not exactly the same. However this is necessary to avoid bugs with certain PHP casting rules, i.e. +44 = 44 2011-05-01 10:21:47 +02:00
Benjamin Eberlei 5a6ac2fb56 Merge branch 'DDC-1102' 2011-05-01 10:01:49 +02:00
Benjamin Eberlei 6b3dfaccfc DDC-1102 - Typo in EntityGenerator 2011-05-01 10:01:38 +02:00
Benjamin Eberlei e3d2a0e293 Merge branch 'PR39' 2011-05-01 00:18:22 +02:00
Francis Besset a141aaf663 [PR-39] Throw exception when hydrating joined entity without existing parent alias (NativeQuery problem only) 2011-05-01 00:17:40 +02:00
Benjamin Eberlei 85d6b9fd39 Merge pull request #42 from mridgway/DDC-1059.
DDC-1059: RSM Helper
2011-04-30 15:01:25 -07:00
Benjamin Eberlei e9067eb4a3 Merge branch 'DDC-1149' 2011-04-30 23:52:10 +02:00
Fabien Potencier 0c955fe54f Fix namespace/class parsing in the entity generator 2011-04-30 23:51:22 +02:00
Benjamin Eberlei ea5a623c88 Merge branch 'DDC-1094' 2011-04-30 23:18:48 +02:00
Benjamin Eberlei 73c7605a5c [DDC-1094] Add support for limit, offset and orderby in EntityRepository::findBy(). 2011-04-30 23:18:24 +02:00
Benjamin Eberlei 0b1077a3b3 Merge branch 'DBAL-115' 2011-04-30 17:16:47 +02:00
Benjamin Eberlei 5179ff921b [DBAL-115] REALLY fix issues with SchemaTool::getDropSchemaSQL(). 2011-04-30 17:16:34 +02:00
Benjamin Eberlei abbb3e8656 Merge branch 'DBAL-115' 2011-04-30 16:20:32 +02:00
Benjamin Eberlei 1f665e6ba8 [DBAL-115] Bugfix in SchemaTool not quoting table names when dropping schema. 2011-04-30 16:20:14 +02:00
Benjamin Eberlei d72217a00a Merge branch 'DDC-1133' 2011-04-30 14:23:59 +02:00
Benjamin Eberlei 261d3c892e DDC-1133 - Ducktype AnnotationReader in AnnotationDriver 2011-04-30 14:23:46 +02:00
Benjamin Eberlei 2c50faf5b2 Merge branch 'DDC-1109' 2011-04-30 12:49:52 +02:00
Benjamin Eberlei 7dd0dd273e [DDC-1109] ltrim discriminator map for convenience. 2011-04-30 12:49:37 +02:00
Benjamin Eberlei b462cfbe2b Merge branch 'DDC-1108' 2011-04-30 12:27:35 +02:00
Benjamin Eberlei 67b89eaa4f [DDC-1108] Fix bug with single char named input parameters in DQL lexer. 2011-04-30 12:27:16 +02:00
Benjamin Eberlei ed355d2eb6 Merge branch 'master' of github.com:doctrine/doctrine2 2011-04-30 11:19:58 +02:00
Benjamin Eberlei c6d725d6e8 Merge branch 'DDC-1132' 2011-04-30 11:16:41 +02:00
Benjamin Eberlei 42230a4c51 [DDC-1132] Fix many to many table detection. 2011-04-30 11:16:30 +02:00
Benjamin Eberlei 7929aea45a Merge branch 'DDC-1132' 2011-04-30 11:15:56 +02:00
Benjamin Eberlei f09d299660 [DDC-1132] Fix many to many table detection. 2011-04-30 11:15:45 +02:00
Benjamin Eberlei 41b3a372d3 Add new performance test checking compute changeset performance. 2011-04-30 10:47:56 +02:00
Guilherme Blanco 7aaecacc5b Merged pull request #36 from ajessu/fixTypo.
Fix typos on the help text of the schema commands
2011-04-26 18:12:32 -07:00
Guilherme Blanco 237a05b302 Merged pull request #44 from Chekote/parser_match_phpdoc_fix.
Parser::match phpdoc fix
2011-04-26 18:09:27 -07:00
Guilherme Blanco f73c7f3be0 Merged pull request #48 from rdohms/master.
Fixing docblocks in SchemaTool
2011-04-26 18:05:59 -07:00
Guilherme Blanco fe66d8bc04 Fixed SchemaTool which was failing to dropSchema due to foreignKeyContraint checks. Fixes DDC-1126 2011-04-26 12:32:04 -03:00
Guilherme Blanco 26bd3e3811 Implemented support for closure return on EntityManager::transactional. Fixes DDC-1125 2011-04-25 18:32:43 -03:00
Rafael Dohms 0b7feb359d Fixing outdated docblocks for SchemaTool 2011-04-17 23:39:59 -03:00
Michael Ridgway af4cf0d0ba Replaced prefix parameter with renamedColumns; Added exception when duplicate columns found 2011-04-14 20:55:03 -04:00
Guilherme Blanco 4d561651a1 Merge branch 'master' of github.com:doctrine/doctrine2 2011-04-13 00:11:08 -03:00
Benjamin Eberlei 7e3265e7c7 Merge branch 'DDC-1040' 2011-04-03 23:06:15 +02:00
Benjamin Eberlei 822481d360 [DDC-1040] Add regression tests for entity as multiple named/positional parameters. 2011-04-03 23:06:03 +02:00
Benjamin Eberlei 7905f2a972 [DDC-1040] Bugfix with named parameters and multiple entities passed as parameter. 2011-04-03 23:03:39 +02:00
Benjamin Eberlei 88dc18f88a Merge branch 'DDC-1093' 2011-04-03 20:29:15 +02:00
Benjamin Eberlei e685d59604 [DDC-1093] Fix docblock type hint 2011-04-03 20:29:07 +02:00
Benjamin Eberlei 300df03743 Merge branch 'DDC-1087' 2011-04-03 09:05:20 +02:00
Benjamin Eberlei a329007526 [DDC-1087] Add missing resolution to IS NULL in EntityRepository when passing a null value as a criteria. 2011-04-03 09:03:43 +02:00
Chekote 5784c7bacd Fixed phpdoc on Parser::match incorrectly stating that the token parameter can be a string value 2011-04-01 12:54:12 -05:00
Benjamin Eberlei db82ef3e61 Merge branch 'DDC-991' 2011-03-31 23:35:12 +02:00
Benjamin Eberlei ea52b3cc8f [DDC-991] Rename method to AbstractQuery::getOneOrNullResult(). 2011-03-31 23:35:01 +02:00
Benjamin Eberlei 24a7a72f59 [DDC-991] add AbstractQuery::getOneResult() method that returns null instead of throwing an exception as getSingleResult() does. 2011-03-31 23:32:49 +02:00
Michael Ridgway b1b17376ff Removing left over class import 2011-03-31 17:22:13 -04:00
Michael Ridgway c46d835146 Moved new functions to ResultSetMappingBuilder class 2011-03-30 10:27:31 -04:00
Michael Ridgway 20dc72ef9a First pass on RSM helper functions for adding entities 2011-03-29 20:35:01 -04:00
Benjamin Eberlei e9c4f612cf Merge branch 'DDC-692' 2011-03-29 20:17:56 +02:00
Benjamin Eberlei 34ad308599 [DDC-692] Add respective metadata mapping possiblities for read-only entities and a test. 2011-03-29 20:17:44 +02:00
Benjamin Eberlei 9a75277dd4 [DDC-692] Add ClassMetadataInfo::isReadOnly flag and ignore these entities in Change Tracking. 2011-03-29 20:04:14 +02:00
Benjamin Eberlei 003ab06465 Merge branch 'DDC-696' 2011-03-29 19:43:41 +02:00
Benjamin Eberlei 36985ee704 Merge remote branch 'mridgway/DDC-696' into DDC-696 2011-03-29 19:42:38 +02:00
Benjamin Eberlei 21acb67b01 Merge branch 'DDC-1077' 2011-03-27 21:11:30 +02:00
Benjamin Eberlei bda15231da [DDC-1077] Bugfix in not handling literals in Select Expressions. 2011-03-27 21:10:50 +02:00
Benjamin Eberlei 5022b04ff9 Merge branch 'DDC-1079' 2011-03-27 14:05:08 +02:00
Benjamin Eberlei 6ed0ff0a12 [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:04:53 +02:00
Benjamin Eberlei 789739f3e4 Merge branch 'DDC-1014' 2011-03-27 12:19:31 +02:00
Benjamin Eberlei 4f1af0114f [DDC-1014] Add DATE_ADD(), DATE_SUB(), DATE_DIFF() functions for DQL. 2011-03-27 12:18:47 +02:00
Benjamin Eberlei 5d1b4f98de [DDC-1014] Update DBAL remote to include date arithmetics related functionality. 2011-03-27 11:34:14 +02:00
Michael Ridgway 706cc838e5 Removed svn variable 2011-03-22 08:54:33 -04:00
Michael Ridgway 17cbb34952 Clean up of test case 2011-03-21 23:30:10 -04:00
Michael Ridgway 1f50dee8a8 DDC-696: Added onClear event 2011-03-21 23:17:08 -04:00
Benjamin Eberlei e126315c1b Bump DBAL dependency to include Security Fix for AbstractPlatform::modifyLimitQuery() 2011-03-21 23:54:40 +01:00
Benjamin Eberlei ccb5c57784 Merge branch 'DDC-992' 2011-03-20 17:07:31 +01:00
Benjamin Eberlei 7a41a205ee [DDC-992] Fix criteria usage of column names clashing with field or associations by prefixing with table names or alias. 2011-03-20 17:07:19 +01:00
Benjamin Eberlei 8430aefe21 Merge branch 'DDC-1053' 2011-03-20 14:07:44 +01:00
Benjamin Eberlei edfdbe10a0 [DDC-1053] Fix bug with usage of identification variables in GroupByItem. 2011-03-20 14:07:33 +01:00
Benjamin Eberlei 108ceae313 Merge branch 'DDC-1052' 2011-03-20 13:07:55 +01:00
Benjamin Eberlei e42a227a7c [DDC-1052] Fix bug with versioning and inheritance 2011-03-20 13:07:47 +01:00
Benjamin Eberlei 7e262dd42c Merge branch 'DDC-1068' 2011-03-20 12:36:03 +01:00
Benjamin Eberlei ac175d2c40 [DDC-1068] Fix case-sensitivity problems of first loading of Metadata. 2011-03-20 12:35:52 +01:00
Benjamin Eberlei c77dbd859b [DDC-1070] Fix global test state problem introduced with test. 2011-03-20 12:25:27 +01:00
Benjamin Eberlei efe26d00a1 Merge branch 'DDC-1070' 2011-03-20 12:19:12 +01:00
Benjamin Eberlei 62755cc647 [DDC-1070] Fix in AbstractQuery::iterate() method not respecting hydrator and parameters. 2011-03-20 12:19:01 +01:00
Guilherme Blanco 8c8a658dfd Merge branch 'master' of github.com:doctrine/doctrine2 2011-03-19 14:36:29 -03:00
Benjamin Eberlei 53ca54ced6 Merge branch 'master' of github.com:doctrine/doctrine2 2011-03-16 23:01:17 +01:00
Benjamin Eberlei e757e3beaf Merge branch 'DDC-952' 2011-03-16 23:00:57 +01:00
Benjamin Eberlei 5192306d39 [DDC-952] One last commit with some refactorings, additional comments and two new tests. Also added convenience method Query::setFetchMode($className, $assocName) 2011-03-16 22:51:32 +01:00
Benjamin Eberlei 4b98e3ea8e DDC-952 - Remove unnecessary instance variable and comment on one feature. 2011-03-16 00:03:43 +01:00
Benjamin Eberlei b7e522d7a7 DDC-952 - This nasty inheritance hydration bug slipped in again, fixed again now. 2011-03-15 23:39:19 +01:00
Benjamin Eberlei 6d27b4760f [DDC-952] Add Persister hydration performance tests. 2011-03-15 23:22:37 +01:00
Benjamin Eberlei 1b46208aa5 [DDC-952] More fixes 2011-03-15 21:34:47 +01:00
Benjamin Eberlei b3c01903b4 DDC-952 - Optimization 2011-03-15 20:03:05 +01:00
Benjamin Eberlei a04ba44874 [DDC-952] Introduced SimpleObjectHydrator again for performance reasons. 2011-03-15 19:48:04 +01:00
brikou 9a8e8ce35d Edited lib/Doctrine/ORM/Tools/EntityGenerator.php via GitHub 2011-03-15 05:39:38 -07:00
Albert Jessurum 234d2e5f0f Fix typo on schema help messages 2011-03-15 12:22:53 +01:00
Guilherme Blanco 180078d0f6 Added namedQueries as optional during serialization of ClassMetadata. 2011-03-14 01:04:50 -03:00
Benjamin Eberlei 38ad25ad4c [DDC-952] Remove all the unnecessary hydration code from all Persisters. 2011-03-13 00:23:46 +01:00
Benjamin Eberlei 1bc4b62805 [DDC-952] Make collection loading work with hydrators also. 2011-03-13 00:15:50 +01:00
Benjamin Eberlei 7c7106b1c1 DDC-952 - Fix bug in inverse one-to-one eager loading sql code. 2011-03-12 19:11:37 +01:00
Benjamin Eberlei 4677883acd [DDC-952] Added modelset and tests for Eager Loading, detected a bug with inverse one-to-one eager fetching that needs to be addressed. 2011-03-12 14:01:51 +01:00
Benjamin Eberlei 8794d35867 DDC-952 - Woah this still needs tons of tests. 2011-03-09 23:30:35 +01:00
Benjamin Eberlei 595c19207c [DDC-914] Always fetch joining inverse side one-to-one associations breaks a ton of DDC-117 tests, investigate why to make this working also. 2011-03-09 23:21:33 +01:00
Benjamin Eberlei 077ae9cee9 [DDC-914] Fetch join many-to-one/one-to-one associations configured as FETCH_EAGER inside the persisters. 2011-03-09 23:14:54 +01:00
Guilherme Blanco 925f1c281c Merge branch 'NamedQueries' 2011-03-09 14:44:56 -03:00
Guilherme Blanco 7d1fca1ca2 Added support to NamedQueries through ClassMetadata. 2011-03-09 14:43:42 -03:00
Benjamin Eberlei fd502631c7 DDC-734 - REname query hint to fetchEager. 2011-03-08 22:28:55 +01:00
Benjamin Eberlei 60eb755fe9 DDC-952, DDC-734 Add DQL query hint to switch associations from lazy to eager for deferred initialization optimizations. 2011-03-08 22:22:54 +01:00
Guilherme Blanco a31289b9d7 Added support to NamedQueries through ClassMetadata. 2011-03-06 18:45:09 -03:00
Benjamin Eberlei 112f9d1480 [DDC-1050] Change refresh of collection back 2011-03-06 21:49:02 +01:00
Benjamin Eberlei 851f44a066 [DDC-952] [DDC-1050] Use ObjectHydrator inside Persisters, removing a bunch of duplicate code (step1, more necessary) 2011-03-06 21:26:54 +01:00
Benjamin Eberlei d9c8a9eecb [DDC-952] Fix merge/rebase mistake. 2011-03-06 15:28:26 +01:00
Benjamin Eberlei 03630df20d Add support for IN(?) queries in repositories using the DBAL support for parameter lists. 2011-03-06 11:15:56 +01:00
Benjamin Eberlei 3d37e436dd DDC-952 - Refactor eager loading entities, it is only allowed for non composite primary key entities. 2011-03-05 11:09:38 +01:00
Benjamin Eberlei 32df9451fd DDC-952 - Implemented first approach for batching eager loads of ToOne associations. 2011-03-05 11:08:41 +01:00
Benjamin Eberlei e0b835178b Bump dependency of ORM master to DBAL 2.0.2 2011-03-05 10:09:16 +01:00
Benjamin Eberlei 78aa893efd Adjust tests to changes in DBAL dependency with regard to automatic foreign key and index naming. 2011-03-05 10:08:30 +01:00
Benjamin Eberlei 8c7261e7c3 Optimize build process even more, now generating PEAR Packages for Symfony YAML and Console. 2011-03-05 00:18:10 +01:00
Benjamin Eberlei ae61272c13 Merge branch 'DDC-1034' 2011-03-04 23:01:02 +01:00
Benjamin Eberlei 67ae22b911 DDC-1034 - Fix bug where callbacks where registered multiple times in inheritance hierachies. 2011-03-04 23:00:54 +01:00
Benjamin Eberlei c0d26f2308 Merge branch 'DDC-1056' 2011-03-04 22:22:19 +01:00
Benjamin Eberlei d58ae2ecda DDC-1056 - Fixed notice in StaticPHPDriver. 2011-03-04 22:22:07 +01:00
Benjamin Eberlei 1d5fef4144 Merge branch 'DDC-1041' 2011-03-03 23:11:24 +01:00
Benjamin Eberlei 49195ebe17 [DDC-1041] You could retrieve instances of the wrong type in inheritance hierachies because the identity map aggregates them by rootEntityName. 2011-03-03 23:11:09 +01:00
Benjamin Eberlei 9a33bd083d Merge branch 'DDC-1050' 2011-03-03 22:52:13 +01:00
Benjamin Eberlei b2c7a9c7fc [DDC-1050] Throw exception when trying to define inheritance information on a mapped superclass. It is not a valid use-case. 2011-03-03 22:51:53 +01:00
Benjamin Eberlei 8b9f12d924 Remove copied code of Symfony Console/Yaml and added submodules. 2011-02-28 21:51:56 +01:00
Benjamin Eberlei 521705a0f2 Merge branch 'DDC-1033' 2011-02-26 12:48:10 +01:00
Benjamin Eberlei c144df9be3 DDC-1033 - Fix cloning of not initialized proxies. 2011-02-26 12:47:59 +01:00
Benjamin Eberlei 1eb7f92956 DDC-1026 - Fix Result Cache Seperate chaining implementation that was wrong since DDC-892 was applied. 2011-02-26 00:39:54 +01:00
Romain Pouclet 9125413786 Fixed typo in AbstractQuery::execute() doc 2011-02-25 10:22:50 -06:00
Benjamin Eberlei afc9495b3f Revert "Merge branch 'DDC-884'"
This reverts commit 3eea19dcfa, reversing
changes made to b13c29944b.
2011-02-21 18:52:49 +01:00
Guilherme Blanco 3eea19dcfa Merge branch 'DDC-884' 2011-02-21 00:51:00 -03:00
Guilherme Blanco 04ab0cd8fc [DDC-884] Allow subclassing EntityManager. 2011-02-21 00:50:51 -03:00
Guilherme Blanco b13c29944b Merge branch 'DDC-1035' 2011-02-21 00:38:03 -03:00
Guilherme Blanco 68bb0c1ae1 [DDC-1035] Fixed orphanRemoval on YAML mapping driver. 2011-02-21 00:37:32 -03:00
Guilherme Blanco a18a7bb678 Merge branch 'DDC-1012' 2011-02-20 01:54:05 -03:00
Guilherme Blanco 834203d868 [DDC-1012] Implemented Expr isNull and isNotNull. 2011-02-20 01:53:55 -03:00
Guilherme Blanco 99c7924292 Merge branch 'DDC-1036' 2011-02-20 01:44:27 -03:00
Guilherme Blanco 505d9e2154 [DDC-1036] Modified the AggregateExpressions to support SimpleArithmeticExpression instead of StateFieldPathExpression. 2011-02-20 01:44:05 -03:00
Guilherme Blanco bca927f861 Merge branch 'DDC-982' 2011-02-20 01:31:12 -03:00
Guilherme Blanco 70d2cbe857 [DDC-982] Implemented more unique sql table alias. 2011-02-20 01:30:58 -03:00
Guilherme Blanco dcf358f154 Fixed some warnings from phpunit. 2011-02-19 19:50:58 -02:00
Guilherme Blanco 2b2d9e7a1d Fixed wrong test. It was failing if you have memcache extension loaded. 2011-02-19 17:20:37 -02:00
Jonathan H. Wage 328a5fe49a Updating to reverted version of common. 2011-02-16 10:25:29 -06:00
Jonathan H. Wage 839b6dd5e4 Revert "Removing old Driver interface in favor of the new one in Common\Persistence. Also changed to use fully qualified class name for interfaces in common to avoid weird aliases."
This reverts commit c988a99d55.
2011-02-16 10:24:42 -06:00
Jonathan H. Wage c988a99d55 Removing old Driver interface in favor of the new one in Common\Persistence. Also changed to use fully qualified class name for interfaces in common to avoid weird aliases. 2011-02-16 10:06:39 -06:00
Jonathan H. Wage a4a184b27c Implementing ClassMetadataFactory interface. 2011-02-15 21:00:48 -06:00
Jonathan H. Wage 68a4099684 Implementing initial Doctrine\Common\Persistence interfaces. 2011-02-15 20:02:45 -06:00
Benjamin Eberlei 64088fce5d Merge branch 'DDC-1030' 2011-02-13 10:02:34 +01:00
Benjamin Eberlei 35a152318e DDC-1030 - Fix Static Reflection with namespace levels deeper than one. 2011-02-13 10:02:18 +01:00
Benjamin Eberlei 3d0d31ddf8 Merge branch 'DDC-1024' 2011-02-12 17:40:16 +01:00
Benjamin Eberlei c456f27f60 DDC-1024 - Do not generate setter/getter for inherited fields. 2011-02-12 17:40:07 +01:00
Benjamin Eberlei 4ecb582c76 Update dependencies to Common 2.0.1 and DBAL 2.0.1 2011-02-05 11:46:35 +01:00
Benjamin Eberlei 7390030854 Merge branch 'DDC-250' 2011-02-05 11:44:00 +01:00
Benjamin Eberlei da2dee03e2 [DDC-250] Small typo fix in xsd 2011-02-05 11:43:50 +01:00
Benjamin Eberlei 9768d08458 [DDC-250] Add tests and fix some glitches and finalized index-by patch. 2011-02-05 11:42:10 +01:00
Benjamin Eberlei a9fe9f43e9 Merge branch 'DDC-1018' into DDC-250 2011-02-05 10:04:36 +01:00
Benjamin Eberlei 61e2cdc6b0 [DDC-1018] Bugfix: INDEX BY was not working in JOIN Declarations, only in FROM. 2011-02-05 10:04:18 +01:00
Benjamin Eberlei 60203af9b2 Merge branch 'DDC-1018' 2011-02-05 10:02:48 +01:00
Benjamin Eberlei 4532c2255a [DDC-1018] Bugfix: INDEX BY was not working in JOIN Declarations, only in FROM. 2011-02-05 10:02:37 +01:00
Benjamin Eberlei 17c1ed948e [DDC-250] Initial untested support for @ManyToMany(indexBy) and @OneToMany(indexBy) option. 2011-02-05 09:31:40 +01:00
Benjamin Eberlei 266d85e917 Merge branch 'DDC-1006' 2011-02-02 23:30:39 +01:00
Benjamin Eberlei 4122abf558 DDC-1008, DDC-1002 - Create constructor and id setter if necessary. 2011-02-02 23:30:16 +01:00
Benjamin Eberlei f9c1464879 DDC-1006, DDC-953 - Fix EntityGenerator creating empty classes 2011-02-02 23:21:42 +01:00
Benjamin Eberlei 277e0aee8c Merge remote branch 'origin/master' 2011-02-02 19:49:23 +01:00
Benjamin Eberlei 957aefe69e Merge branch 'DDC-892' 2011-01-23 20:54:36 +01:00
Benjamin Eberlei 3515df913f DDC-892 - Implement separate chaining approach for result caches to prevent hash colissions. 2011-01-23 20:54:29 +01:00
Benjamin Eberlei 8869678c0f Merge branch 'GenerationTools' 2011-01-23 20:26:37 +01:00
Benjamin Eberlei 05f41278a6 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:25:59 +01:00
Benjamin Eberlei 961cb6e9a3 Merge branch 'DDC-958' 2011-01-23 17:26:59 +01:00
Benjamin Eberlei ed53f8aa74 DDC-958 - Fire postLoad event when calling refresh(). 2011-01-23 17:26:11 +01:00
Benjamin Eberlei be2e00c991 Merge branch 'DDC-968' 2011-01-23 16:47:20 +01:00
Benjamin Eberlei f1809ce180 DDC-968 - Add AbstractQuery::getHints() method 2011-01-23 16:47:07 +01:00
Benjamin Eberlei 549965b4b2 Merge branch 'DDC-997' 2011-01-23 16:42:06 +01:00
Benjamin Eberlei f70ee3a038 DDC-997 - Fix bug in hydration that came up with DDC-117 2011-01-23 16:41:40 +01:00
Benjamin Eberlei 975d6ada66 Merge branch 'DDC-969' 2011-01-23 16:12:36 +01:00
Benjamin Eberlei a6e63d2676 DDC-969 - Use of field instead of column when accessing a table leads to error when both differ. 2011-01-23 16:12:26 +01:00
Benjamin Eberlei c5593be7c9 Merge branch 'DDC-978' 2011-01-23 15:40:34 +01:00
Benjamin Eberlei 65bbdc30de 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:16 +01:00
Benjamin Eberlei 482ff2d009 Merge branch 'DDC-996' 2011-01-23 14:21:23 +01:00
Benjamin Eberlei fd44894e9a DDC-996 - Throw more useful exception if fieldName is empty in a mapped field or association. 2011-01-23 14:20:15 +01:00
Benjamin Eberlei 92ed05fc84 Merge branch 'DDC-960' 2011-01-23 12:49:20 +01:00
Benjamin Eberlei 5d333045b9 DDC-960 - Bugfix in how Persisters generate Fetch last version of Entity SQL. 2011-01-23 12:48:28 +01:00
Benjamin Eberlei bcafa19386 Merge branch 'DDC-975' 2011-01-13 21:43:47 +01:00
Benjamin Eberlei 03698e4068 DDC-975 - Fix notice in SchemaTool in combination with XML mapping driver. 2011-01-13 21:43:33 +01:00
Benjamin Eberlei fdee7a9ae0 Merge branch 'DDC-980' 2011-01-13 21:16:24 +01:00
Benjamin Eberlei 078e19d1c7 DDC-980 - Fix Update and Delete statements reference of the root table when doing subselects. 2011-01-13 21:16:08 +01:00
Benjamin Eberlei 7a2c99353a Merge branch 'DDC-546' 2011-01-02 15:14:40 +01:00
Benjamin Eberlei 3539b32629 DDC-546 - Found some more code that needs DDC-117 compliance. 2011-01-02 15:14:12 +01:00
Benjamin Eberlei 247fc43cef DDC-546 - Rename ClassMetadataInfo::FETCH_EXTRALAZY to ClassMetadataInfo::FETCH_EXTRA_LAZY 2011-01-02 15:10:47 +01:00
Benjamin Eberlei a3cab174ca DDC-546 - Updated with support for DDC-117. 2011-01-02 14:04:52 +01:00
Benjamin Eberlei 89e7e8623c DDC-546 - Remove dynamic public property approach in PersistentCollection::count() EXTRA_LAZY. 2011-01-02 13:43:49 +01:00
Benjamin Eberlei cbfdf61976 DDC-546 - Bugfix for PersistentCollection::count() in EXTRA LAZY special case. 2011-01-02 13:41:18 +01:00
Benjamin Eberlei 3acc05d953 DDC-546 - Fix bug in inverse many-to-many contains. 2011-01-02 13:37:29 +01:00
Benjamin Eberlei 685e327b43 DDC-546 - Fix some rebasing issues. 2011-01-02 12:54:55 +01:00
Benjamin Eberlei f572be92e2 DDC-546 - Add EXTRALAZY to doctrine-mapping.xsd enumeration of fetch-types. 2011-01-02 12:46:08 +01:00
Benjamin Eberlei 75d59d8695 DDC-546 - Added functionality for extra-lazy PersistentCollection::contains(). 2011-01-02 12:46:08 +01:00
Benjamin Eberlei 7c567b305a Refactor DDC-546 persister approach. 2011-01-02 12:46:08 +01:00
Benjamin Eberlei d3d9957fd4 DDC-546 - Fix some minor glitches in patch. 2011-01-02 12:44:16 +01:00
Benjamin Eberlei c998797c55 DDC-546 - Add Extra Lazy Collection prototype. 2011-01-02 12:44:16 +01:00
Benjamin Eberlei 78d4277e4b Merge branch DDC-117 into master 2011-01-02 12:01:05 +01:00
Benjamin Eberlei f3abf9a30a Merge branch 'DDC-965' 2011-01-02 10:24:38 +01:00
Benjamin Eberlei 9177dc3d52 DDC-965 - Defer ID check after loadMetata event is fired. 2011-01-02 10:24:23 +01:00
Benjamin Eberlei 34b303845f Merge branch 'DDC-966' 2011-01-02 10:18:23 +01:00
Benjamin Eberlei c1edd5848f DDC-966 - Fix NOT NULL constraint SingleTableInheritance Generation using SchemaTool. 2011-01-02 10:18:02 +01:00
Benjamin Eberlei ed7ec261d0 Merge branch 'DDC-949' 2011-01-02 09:42:15 +01:00
Benjamin Eberlei a2cc9f0f6d 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:38:32 +01:00
Benjamin Eberlei 2a005019bf DDC-117 - Add XML and YML Driver support for associated identifier. 2011-01-01 21:47:04 +01:00
Benjamin Eberlei c2bbaa9ead DDC-117 - Slight changes in the patch and fixing inline comments. 2011-01-01 18:53:22 +01:00
Benjamin Eberlei 194a90923d DDC-117 - Finalize patch, fix all the problems of different use-cases by hugely expanding the test-model. 2011-01-01 18:17:19 +01:00
Benjamin Eberlei e1ed0bf52a Merge branch 'DDC-945-2' 2010-12-31 14:39:13 +01:00
Benjamin Eberlei 7112b551e2 DDC-945 - Fix regression, ManyToMany unidirectional owning side assocations should be allowed. 2010-12-31 14:39:01 +01:00
Benjamin Eberlei 3498f4d6ee Merge branch 'DDC-929' 2010-12-30 23:18:14 +01:00
Benjamin Eberlei 8658376713 DDC-929 - Fix bug with DatabaseDriver not detecting indexes that are not called primary. 2010-12-30 23:18:00 +01:00
Benjamin Eberlei 7a40e3f6f2 Merge branch 'DDC-961' 2010-12-30 22:30:59 +01:00
Benjamin Eberlei 58019fbac0 DDC-961 - Bugfix with missing first letter in automatic join table names in global namespace entities. 2010-12-30 22:30:51 +01:00
Benjamin Eberlei 2d27a99a0b DDC-117 - Began to fix some issues surrounding the DDC-881 report and references to composite fk entities. 2010-12-29 01:02:21 +01:00
Benjamin Eberlei 337e2fa043 Fix DDC-795 (subtask of DDC-117) and integrated a test for cascade (that only works with sequence id generators). 2010-12-28 19:05:46 +01:00
Benjamin Eberlei e7b4dca611 Merge master into DDC-117 2010-12-28 17:27:47 +01:00
Benjamin Eberlei 1d5b24ecc5 Merge branch 'DDC-837' 2010-12-28 14:56:24 +01:00
Benjamin Eberlei 2d89ddfb1f DDC-837 - Fix bug with associations of the same name not being possible in inheritance hierachies. 2010-12-28 14:56:13 +01:00
Benjamin Eberlei eeca184836 Merge branch 'DDC-928' 2010-12-28 12:19:16 +01:00
Benjamin Eberlei 1d2b2b2c8b DDC-928 - Fix undefined variable notice. 2010-12-28 12:18:42 +01:00
Benjamin Eberlei 26bb7978b5 Merge branch 'DDC-945' 2010-12-28 12:00:07 +01:00
Benjamin Eberlei aa6ac3d6b0 DDC-945 - Throw exception in ClassMetadataFactory when mapped superclass has to many associations. 2010-12-28 11:59:51 +01:00
Benjamin Eberlei 1720527f81 Merge branch 'DDC-617' 2010-12-28 10:17:53 +01:00
Benjamin Eberlei fe672d2f61 DDC-617 - Throw error if selecting identification variables without picking at least one root entity alias. 2010-12-28 10:17:33 +01:00
Benjamin Eberlei d488e84d8d Merge branch 'DDC-931' 2010-12-22 22:06:02 +01:00
Benjamin Eberlei a4f88407c2 DDC-931 - SchemaTool#dropSchema() should not stop on failure of a single query (as stated in docblocks). 2010-12-22 22:04:11 +01:00
Benjamin Eberlei e46c65db09 Fix for DDC-944 2010-12-22 00:23:22 +01:00
Benjamin Eberlei 6988b55f50 Bump Dev Version to 2.1.0-DEV 2010-12-21 16:45:50 -05:00
Benjamin Eberlei 7def30f283 Update dependencies to Common 2.0 stable and DBAL 2.0 stable. 2010-12-21 22:39:26 +01:00
Benjamin Eberlei 22ffbe7488 Fix tests so that PostgreSQL does not fail anymore on certain test. 2010-12-21 22:33:23 +01:00
Benjamin Eberlei 988d229c07 Fix XSD Schema 2010-12-21 22:04:13 +01:00
Benjamin Eberlei 43c63765db Extend phing build.xml for upcoming release. 2010-12-20 23:34:28 +01:00
Amal Raghav b736c4216c _generateEntityLifecycleCallbackMethods Beautification 2010-12-17 16:51:51 +05:30
Benjamin Eberlei c03fc00f45 DDC-936 - Fix target-entity and repository-class to be string rather than xs:NMTOKEN because of the class backslash. 2010-12-15 22:01:16 +01:00
Benjamin Eberlei 6ddfd06efe Merge branch 'DDC-933' 2010-12-14 23:26:50 +01:00
Benjamin Eberlei d87391e40c DDC-933 - Fix bug in lock sql generation of CTI classes. 2010-12-14 23:26:40 +01:00
Benjamin Eberlei 2648c1a6ca Fix build.xml to generate proper package.xml for Windows PEAR Package. 2010-12-14 23:10:45 +01:00
Benjamin Eberlei 6c9eeb6127 Update Dependency to DBAL RC5 2010-12-12 16:02:46 +01:00
Benjamin Eberlei 6c26af069c DDC-920 - Fix bug in DetachedEntityTest that occours with pre-persist generators (Postgresql, Oracle). Didnt came up when testing against Sqlite. 2010-12-12 15:43:12 +01:00
Benjamin Eberlei 3c0f92f4c7 Remove call to EntityManager#flush() if the unitofwork contains pending insertions. Flush should always be triggered explicitly. 2010-12-11 00:54:54 +01:00
Benjamin Eberlei 4f154b6aa1 DDC-920 - Fix bug when detaching a managed entity that is not yet in the identity map (no id). 2010-12-10 21:55:48 +01:00
Benjamin Eberlei 06326918a5 DDC-915, DDC-925 - Fix Identification Ordering in combination with Tree Walkers. 2010-12-10 21:22:48 +01:00
Benjamin Eberlei 5e788a0b84 DDC-915 - Bugfix in Identification Variable reordering in combination with SQL Walkers. 2010-12-08 23:42:02 +01:00
Benjamin Eberlei 1daf658ec6 DDC-917 - Skip Mapped Superclasses in the Drop Sequence Loop in SchemaTool. 2010-12-08 23:36:15 +01:00
Benjamin Eberlei aa2501eb96 DDC-917 - Bugfix with DriverChain::getAllClassNames() - It was not semantically correct and returning too many metadata. 2010-12-08 23:29:21 +01:00
Benjamin Eberlei ef50d940de CleanUp in SchemaTool. 2010-12-08 21:21:00 +01:00
Benjamin Eberlei ad50327744 Fixed some build.xml problems. 2010-12-04 11:32:12 +01:00
Benjamin Eberlei 9a68015ccf Bump Dev Version to 2.0.0-DEV 2010-12-04 05:29:18 -05:00
Benjamin Eberlei 72ba369dbb Revert Version to 2.0.0RC1-DEV 2010-12-04 05:28:26 -05:00
Benjamin Eberlei 5b20838aec Merge branch 'master' of git@github.com:doctrine/doctrine2 2010-12-04 05:24:41 -05:00
Benjamin Eberlei 8654d060c6 Bump Dev Version to 2.0.0-DEV 2010-12-04 05:24:00 -05:00
Benjamin Eberlei 2ba9d5a597 Update Dependency of DBAL from RC3 to RC4 2010-12-04 11:04:20 +01:00
Benjamin Eberlei ca682b3840 Update build.xml with deployment tasks. 2010-12-04 10:59:14 +01:00
Jonathan H. Wage 687548cde2 Merge branch 'master' of github.com:doctrine/doctrine2 2010-12-03 11:44:53 -06:00
Jonathan H. Wage 054f26c0a7 Fixing issue with change to ClassMetadataFactory constructor and ConvertMappingCommand. 2010-12-03 11:44:22 -06:00
Benjamin Eberlei c6a6aaf493 DDC-899 - Add method to check if EntityManager is still open. 2010-12-03 17:44:24 +01:00
Benjamin Eberlei 8e4197adc5 DDC-909 - Fix Result Cache with entities as parameters. 2010-12-03 17:34:56 +01:00
Pascal Borreli 0ba9321f06 [ORM] Fixed typo 2010-11-30 02:50:34 +08:00
Fabien Potencier 892eec2f26 Update Symfony\Component\Console to latest version 2010-11-30 02:45:04 +08:00
Benjamin Eberlei 796b62cd2c DDC-897 - Fix DisconnecetdClassMetadataFactory with regards to namespace setting, now inferred from the FQCN. 2010-11-27 22:17:55 +01:00
Benjamin Eberlei dff5dae416 DDC-897 - Make ClassMetadataFactory configurable. 2010-11-27 20:53:26 +01:00
Benjamin Eberlei 7196999b69 DDC-895 - Fix Generated XML from XMLExporter and remove silly formatting xml code used on SimpleXML by just stuffing it into DOMDocument with formatOutput=true. 2010-11-27 20:32:14 +01:00
Benjamin Eberlei 386b7e26d6 DDC-888 - Fix Warning in AnnotationsDriver when using @JoinTable without explicitly defining join- and inverse join-columns. 2010-11-27 20:05:36 +01:00
Benjamin Eberlei 51922a1ff0 DDC-855 - Fix EntityGenerator annoyance when run successive times. 2010-11-27 19:57:37 +01:00
Benjamin Eberlei e4f74d8290 DDC-886 - Bugfix for composite identifier flag not being mapped to child classes in inheritence/mapped superclass scenarios. 2010-11-27 17:38:12 +01:00
Benjamin Eberlei 6cd0fefef2 Merge branch 'sqlsrv' 2010-11-18 23:14:20 +01:00
Benjamin Eberlei e142bd1f30 Merge and modify juokazs Sqlsrv changes 2010-11-18 23:14:07 +01:00
Benjamin Eberlei 97b80d69f1 Fix several test-issues after upgrading dependencies and a Bug in a SchemaTool and DatabaseDriver 2010-11-18 23:07:32 +01:00
Benjamin Eberlei 154176516e Upgrade Dependencies to Common RC2 and DBAL RC3 2010-11-18 23:05:34 +01:00
Benjamin Eberlei a2cbb8f72f DDC-882 - Bugfix with typehint 2010-11-18 20:45:44 +01:00
Juozas Kaziukenas ec50125568 Fix for foreign keys and autoincrement 2010-11-17 14:07:05 -08:00
Juozas Kaziukenas 831b40e093 Fixes required for Microsoft SQL tests 2010-11-17 13:18:18 -08:00
Juozas Kaziukenas b6da2e0e42 Proper FORM clause generation to support locking 2010-11-16 16:12:08 -08:00
Juozas Kaziukenas 494bfc8966 Fixed length, lower, upper and mod AST functions to use platform for generating SQL 2010-11-16 15:41:41 -08:00
Benjamin Eberlei c1661dd53e DDC-511 - MappedSuperclasses specifications of inheritance mapping details make no sense and are ignored 2010-11-16 21:53:46 +01:00
Benjamin Eberlei ae76b2ab8d DDC-853, DDC-629 - Fix drop schema always dropping everything at the cost of potential failures when dropping due to foreign keys. Added a full-database drop mode that resembles the old behavior. 2010-11-16 21:31:54 +01:00
Benjamin Eberlei 85a579febc DDC-867 - Deep clone of the QueryBuilder nested expression objects 2010-11-15 21:32:38 +01:00
Benjamin Eberlei e62fb0b48e DDC-849 - Test verifies all different behaviors, clear contains, remove contains and clear count to be correct. 2010-11-15 19:03:09 +01:00
Benjamin Eberlei ece27e39c4 Merge branch 'DDC-736' 2010-11-13 09:53:00 +01:00
Benjamin Eberlei d3d3032759 DDC-736 - Simplified patch and extended test to verify scalar results are still in order. 2010-11-13 09:52:35 +01:00
Benjamin Eberlei ae9080aa98 DDC-873 - throw exception if trying to add @version to @id 2010-11-11 21:21:17 +01:00
Benjamin Eberlei 4ea3277c28 DDC-856 - Add default "string" type to discriminator column, throw exception on specification of a bunch of invalid types 2010-11-11 21:13:03 +01:00
Benjamin Eberlei e4280cf82e DDC-736 - Fix ordering of identification variables in DQL parser to be by specification. 2010-11-11 21:12:09 +01:00
Benjamin Eberlei ac85584e9b DDC-870 - Fix several bugs with optimistic locking, conversion of types, multiple updating of values and inheritance related stuff. 2010-11-09 23:15:14 +01:00
Benjamin Eberlei 53e8b8f32d DDC-868 - Fix bug where a ClassMetadata instance from a STI child level is processed before the parent. 2010-11-09 22:13:35 +01:00
Benjamin Eberlei a966cb6cc7 Merge branch 'DDC-861' 2010-11-05 22:54:05 +01:00
Benjamin Eberlei 31efc9a149 DDC-861 - Rework ProxyFactory to not use autoloader anymore 2010-11-05 22:17:05 +01:00
Benjamin Eberlei 6a904a2d67 DDC-860 - Allow access to EntityManager in loadClassMetadata event 2010-11-05 22:13:19 +01:00
Benjamin Eberlei 634aa0b291 DDC-832 - Small adjustments in the patch 2010-10-31 11:11:23 +01:00
Roman S. Borschel 4f71c3e6a3 [DDC-812] Fix uninitialized collections of managed entities not being initialized on subsequent fetch-join. 2010-10-31 11:07:26 +01:00
Benjamin Eberlei 9211bc2f4e DDC-832 - Fix regression in testsuite due to SchemaTool not being able to handle reserved word table names. 2010-10-31 09:20:33 +01:00
Benjamin Eberlei 692c35e7e2 DDC-826, DDC-841, DDC-671 - Added another testcase to verify mapped superclass + association dql alias generation 2010-10-31 08:13:59 +01:00
Benjamin Eberlei 4804f623c1 Merge branch 'DDC-758' 2010-10-31 07:35:09 +01:00
Benjamin Eberlei 008601f2ea DDC-758 - Respect notify change tracking. 2010-10-31 07:23:58 +01:00
Benjamin Eberlei 23795605fc DDC-758 - Fix bugs with adding and removing elements from a cascade merge Collection. This fix leads to a significant hit in merge performance of collections since they have to be initialized to the current database state, leading to an additional sql query being executed + hydration. 2010-10-31 07:06:53 +01:00
Benjamin Eberlei 97eeb437b2 DDC-832 - Fix regression introduced with last commit. 2010-10-30 19:54:36 +02:00
Benjamin Eberlei 3936f179e9 Merge branch 'DDC-832' 2010-10-30 19:34:00 +02:00
Benjamin Eberlei 515ef33665 Fix quoting in BasicEntityPersister::_updateTable and BasicEntityPersister::delete. Added 6 tests for quoting of table names in different update, delete and inheritance scenario combinations 2010-10-30 19:33:20 +02:00
Benjamin Eberlei b5c5ec3c69 DBAL-16 - Update Symfony\Component\Console to latest version to fix upstream bug 2010-10-30 13:24:50 +02:00
Benjamin Eberlei aa2a80f3ff DDC-787 - Fix table name casing in DatabaseDriver 2010-10-30 12:35:22 +02:00
Benjamin Eberlei 338476805d DDC-830 - Fix extended class in EntityGenerator 2010-10-30 09:16:55 +02:00
Benjamin Eberlei bf79168952 Added Testcase to verify failure 2010-10-30 08:43:15 +02:00
Benjamin Eberlei 0a8ff7a030 Fix Testsuite to run with PHPUnit 3.5 2010-10-29 16:46:21 +02:00
Benjamin Eberlei 6006979fc6 Merge branch 'DDC-839' 2010-10-29 13:14:52 +02:00
Benjamin Eberlei 35860d9a94 Fix for DDC-839: Fetch joined collections are not initialized correctly. 2010-10-29 13:14:35 +02:00
Benjamin Eberlei 89d0a52c4f DDC-833 - Fix some nasty bug occouring when re-creating an entity that was a proxy before. Also found another nasty issue with refreshing entity that had an already loaded many-to-many or one-to-many association. 2010-10-11 22:15:18 +02:00
Benjamin Eberlei 07016f6da5 DDC-834 - Commit fix for requesting references of classes that have subclasses. This is not possible, so we do an eager find instead. Yes this means there is yet another negative performance impact when using Inheritance STI and CTI. 2010-10-11 20:11:23 +02:00
Benjamin Eberlei b4aabf0ba6 DDC-831 - Fix docblock in ClassMetadataInfo 2010-10-10 17:13:23 +02:00
Benjamin Eberlei 7551bb3762 Fix notice due to wrong variable reference 2010-10-06 23:09:49 +02:00
Benjamin Eberlei 394c67d482 Fix DDC-672 2010-10-06 22:18:48 +02:00
Benjamin Eberlei b05e1ad7ad Fix typo in last patch 2010-10-01 21:05:54 +02:00
Benjamin Eberlei 638c3df3a6 DDC-822 - Fix making queries with detached entities 2010-09-30 21:59:01 +02:00
Benjamin Eberlei de236e0456 DDC-784 - Implement doctrine CLI for Windows and refactor CLI stuff in general 2010-09-30 20:57:14 +02:00
Benjamin Eberlei 3ad429a5aa Fix bug in OrmFunctionalTestCase in combination with vendors that dont deferr foreign key checks (like MySQL) 2010-09-28 22:36:26 +02:00
Benjamin Eberlei 394469d4b7 DDC-525 - Single Table Inheritance fields of child entities ALWAYS have to be nullable, Schema-Tool now enforces this. 2010-09-27 23:22:52 +02:00
Benjamin Eberlei 84bd843eed Merge branch 'DDC-817' 2010-09-27 22:38:07 +02:00
Benjamin Eberlei 140ddf5098 DDC-817 - Add possibility to query by owning side association join column ids and tests for plain entities, single- and joined table inheritance 2010-09-27 22:31:18 +02:00
Benjamin Eberlei d2630ff54e DDC-819 - Fix bug with invalid parameter exception because of using isset instead of array_key_exists() 2010-09-27 21:03:12 +02:00
beberlei 039293c27a DDC-816 - Fix output of warning message in update and drop schema-tool commands 2010-09-25 12:12:19 +02:00
Benjamin Eberlei 8f80c94923 DDC-514 - Implemented default for discriminator column 2010-09-23 23:10:31 +02:00
Benjamin Eberlei 8b766b6c92 Merge branch 'DDC-573' 2010-09-23 22:32:37 +02:00
Benjamin Eberlei 01ffa2dc9e DDC-573 - Implement resetDQLPart() and resetDQLParts() methods on QueryBuilder. 2010-09-23 22:32:23 +02:00
Benjamin Eberlei 75e5c40a50 DDC-742 - More tests on the issue about possible caching problem, could not verify however 2010-09-22 23:01:08 +02:00
Benjamin Eberlei 6390653df7 Merge branch 'DDC-671' 2010-09-22 00:19:37 +02:00
Benjamin Eberlei 13047aa12e Fixed Mappedsuperclass Functional Test to work with new modelset and verify that relevant features work 2010-09-22 00:15:45 +02:00
Benjamin Eberlei 39f732ab91 Refactored DDC-671 model to become a first-class modelset, we dont have one with mapped superclass yet. 2010-09-21 23:53:26 +02:00
Benjamin Eberlei 7dc8ef1db9 Fix DDC-671 - The sourceEntity field has to be corrected to the subclass name when copied from a mapped superclass. Otherwise DQL queries will be wrong, generating wrong table aliases. 2010-09-21 23:14:45 +02:00
Benjamin Eberlei 62a8e2aad5 Enhance Schema-Tool commands by being more aggresive on warning the user that these commands do stuff that can be potentially dangerous to the database (and its contents). 2010-09-21 22:08:29 +02:00
Benjamin Eberlei 6d20d7d5ed Merge branch 'DDC-809' 2010-09-21 00:32:57 +02:00
Benjamin Eberlei c70f32f4c9 DDC-809 - Fix nasty issue in ObjectHydrator yielding Many-To-Many hydration problems with multi-valued collections that are join-fetched. 2010-09-21 00:32:07 +02:00
Benjamin Eberlei 72f65c3665 Some changes to the TestCase 2010-09-20 19:23:41 +02:00
steffkes 25f5ab6557 add inverseBy-Attribute to User-Entity to get valide schema 2010-09-17 11:55:00 -05:00
steffkes 9fa8ff86f8 show given path for MappingException::fileMappingDriversRequireConfiguredDirectoryPath 2010-09-17 11:54:31 -05:00
Benjamin Eberlei 8a92bf075b DDC-671 - Fetch YamlMappingDriverTest from Shurakais remote branch 2010-09-16 22:27:04 +02:00
Benjamin Eberlei d3419780f9 DDC-727 - Test shows expected behavior, no failure 2010-09-15 22:24:17 +02:00
Benjamin Eberlei 810a129a32 DDC-767 - Add testcase that shows described behavior works and not produces notices. 2010-09-15 22:11:09 +02:00
Benjamin Eberlei 97e572e2d8 DDC-806 - Fix xsd schema not allowing entities without id (in inheritance hierachies) 2010-09-15 21:51:44 +02:00
Guilherme Blanco 2e3c1506fb [DDC-792] Fixed issue with run-dql when using max result was triggering undefined method error. 2010-09-15 14:29:55 -03:00
Guilherme Blanco 4845745337 [DDC-802] Fixed wrong variable reference in XML exporter. 2010-09-15 14:16:53 -03:00
Benjamin Eberlei da63bad9c8 DDC-762 - Fixed notice when mapping foreign keys to field having null values 2010-09-13 21:48:25 +02:00
Benjamin Eberlei ee9158ffb4 DDC-749 - Refactor Query tests, add a test for setParameters() 2010-09-12 22:44:02 +02:00
Benjamin Eberlei 4727489134 DDC-761 - Fix join columns not using the same lengh, precision and scale for string and decimal types. 2010-09-12 22:34:32 +02:00
Benjamin Eberlei 13da816f4e DDC-748 - Fix bug in EntityManager::refresh() when entity has an owning side many-to-one bi-directional association 2010-09-12 21:41:22 +02:00
Benjamin Eberlei 24c6bb3f46 Fix errors in EntityRepository docblocks 2010-09-04 12:18:02 +02:00
Roman S. Borschel f415fa7174 Bumping dev. version. 2010-09-01 20:57:12 +02:00
Roman S. Borschel 20af9d6d9f Bumping DBAL dependency to BETA4. 2010-09-01 20:47:01 +02:00
Roman S. Borschel 207d624f5f Bumped Common dependency to RC1, fixing related issues in the test suite. 2010-09-01 20:43:23 +02:00
Benjamin Eberlei 7ff9976b3c Made using schema-tool:drop and schema-tool:update more secure by requiring the user to confirm the operation with another flag --force. 2010-08-31 23:42:27 +02:00
Benjamin Eberlei 803e338365 DDC-684 - Fix flaw in build process. ORM not overwriting DBAL and Common dirs now, instead PEAR packages depend on it now, full package is generated for downloading. 2010-08-30 23:35:54 +02:00
Benjamin Eberlei 0b5c694a7e DDC-778 - Fix AbstractQuery::__clone implementation that was wrongly implemented in DDC-770. Added more tests. 2010-08-30 20:30:11 +02:00
beberlei 0904bc5cc5 DDC-762 - Added test for NULL association finding in OneToOne relations 2010-08-29 11:19:23 +02:00
beberlei 33d0bb454b DDC-752 - Moved verify inheritance block behind the loadMetadata event 2010-08-28 16:41:18 +02:00
beberlei c77a12ac83 DDC-770 - Refactored EntityGenerator Bugfix NOT to generate a use statement. Simplifies code and circumvents further problems (like importing a class from the namespace we are in) 2010-08-28 16:29:08 +02:00
beberlei 2a2936fde5 DDC-771 - Bugfix in EntityGenerator generated use statement 2010-08-28 14:54:31 +02:00
Benjamin Eberlei 5bd8ffa53c Merge master into DDC-117 2010-08-27 22:27:00 +02:00
Benjamin Eberlei 43f8398fbb DDC-770 - Commit missing changes to XmlDriver and YamlDriver 2010-08-27 22:21:18 +02:00
Benjamin Eberlei 187d9289ad Merge branch 'DDC-752' 2010-08-27 22:15:19 +02:00
Benjamin Eberlei d115f7af4f DDC-752 - Postpone Inheritance Related Metadata Validation into CMF 2010-08-27 22:14:48 +02:00
Benjamin Eberlei 797d9f1be5 Merge branch 'master' of github.com:doctrine/doctrine2 2010-08-27 21:28:46 +02:00
Benjamin Eberlei 8a21ab4755 DDC-770 - Cleanup Query instance when its cloned 2010-08-27 21:28:26 +02:00
Roman S. Borschel 506973a92e [DDC-757] Fixed. Also fixed some failing postgres tests due to changes to the default allocation size for sequences. 2010-08-26 13:47:37 +02:00
Benjamin Eberlei 241e4d2f53 DDC-737 - Implemented slice() on PersistentCollection for fowards compatibility reasons. The method will be required on Collection interface with the next Doctrine\Common release 2010-08-24 21:56:29 +02:00
Roman S. Borschel d0717ee458 Fixed typo and simplified method as mentioned in an earlier comment. 2010-08-24 17:17:58 +02:00
Jonathan H. Wage aa5826b69a Symfony/Components renamed into Symfony/Component 2010-08-23 12:44:15 -05:00
Sébastien HOUZE 4a9f36800e Symfony/Components renamed into Symfony/Component 2010-08-23 12:39:30 -05:00
Benjamin Eberlei 1496250833 Merge branch 'master' into DDC-117 2010-08-15 20:17:56 +02:00
Benjamin Eberlei 2f00db08e1 Merge branch 'master' of github.com:doctrine/doctrine2 2010-08-15 20:16:28 +02:00
Benjamin Eberlei dd5574162a Merge branch 'DDC-742' 2010-08-15 19:51:26 +02:00
Benjamin Eberlei 6f4f8f8cb1 DDC-742 - Fix issue with collections that are set to fetch=EAGER not being initialized with takeSnapshot() correctly 2010-08-15 19:51:06 +02:00
Benjamin Eberlei 772e592489 Try Assoc-Id Mapping with Id that has its column renamed. 2010-08-15 19:15:34 +02:00
Benjamin Eberlei fb44fa6b5a Fix hydration of Assoc-Id Entities, duplicate the hydration of the foreign key once for for use with the assoc-entity as a meta-column. Added isIdentifier capabilities to meta columns. 2010-08-15 18:58:25 +02:00
Benjamin Eberlei 5799e391c6 Fix bug with updating assoc-id entities 2010-08-15 14:40:06 +02:00
Roman S. Borschel d56d118458 Fixed field access. 2010-08-14 19:10:28 +02:00
Benjamin Eberlei e45c52b024 Merge Removal of association classes into DDC-117 branch, quite some merge efforts necessary to get it working again 2010-08-13 23:23:11 +02:00
Guilherme Blanco 5719f8523b [DDC-577] Updated allocationSize to 1 based on discussion on DDC-569. 2010-08-12 01:07:48 -03:00
Guilherme Blanco 496a34a4d2 [DDC-581] Implemented support to SingleValuedPathExpression to InExpression. 2010-08-12 00:16:07 -03:00
Guilherme Blanco c3064336ab Merge branch 'master' of github.com:doctrine/doctrine2 2010-08-11 23:13:03 -03:00
Guilherme Blanco 84b9eda17c [DDC-680] Implemented support to complex mathematical expression in subselect part aswell as improved support of comparison expressions. 2010-08-11 23:12:44 -03:00
Benjamin Eberlei 98785122d3 DDC-562 - Finally able to generate Unique Constraint on @OneToOne foreign keys 2010-08-10 22:07:43 +02:00
Roman S. Borschel ca1931de81 More association mapping doc-block updates. 2010-08-09 23:23:30 +02:00
Roman S. Borschel 575858774d Updated API doc for association mappings. 2010-08-09 23:13:34 +02:00
Benjamin Eberlei a812dab4d4 DDC-729 Add more tests for merging with many to many relations 2010-08-09 23:02:44 +02:00
Roman S. Borschel 139f8b52ab Merge branch 'DDC-626' 2010-08-09 22:50:21 +02:00
Roman S. Borschel dd7be5b13a Improving API and docblocks. Removing superfluous tests. 2010-08-09 22:48:07 +02:00
Roman S. Borschel 4826739824 Simplified ClassMetadata lookup in UnitOfWork and added docblock. 2010-08-09 22:48:07 +02:00
Roman S. Borschel 5178f4b7d6 Corrected TO_ONE check just to be sure. 2010-08-09 22:48:07 +02:00
Roman S. Borschel 8d3e0e61ea Moved association mappings to plain arrays, just like field mappings. 2010-08-09 22:48:07 +02:00
Benjamin Eberlei da809fdeda DDC-735 - Forward compatible fix for ArrayCollection::removeElement() returning the element instead of true 2010-08-09 21:26:10 +02:00
Benjamin Eberlei db936035e0 Added more tests for DQL joining the primary key entity and querying other fields 2010-08-08 19:46:45 +02:00
Benjamin Eberlei 8ea1d3825f DDC-735 - Fix PersistentCollection::remove() and PersistentCollection::removeElement() behaving differently with regards to orphan removal 2010-08-08 17:13:03 +02:00
Benjamin Eberlei 21753c71c9 DDC-651 - Fix short name being used instead of long-name in extends section of EntityGenerator 2010-08-08 16:43:52 +02:00
Benjamin Eberlei ea954e8123 DDC-703 - Fixed change tracking not passed to child classes in inheritance hierachy 2010-08-08 16:15:35 +02:00
Benjamin Eberlei 37a1a35b75 Merge branch 'DDC-729' 2010-08-08 16:08:43 +02:00
Benjamin Eberlei a705b81d9b DDC-729 - Fix issue with merging of collections leading to dereference behaviour after next flush(). Added a test for dereferencing of collections as there was none 2010-08-08 16:07:40 +02:00
Roman S. Borschel 0424d87099 [DDC-386][DDC-675] Fixed. 2010-08-08 15:03:40 +02:00
Benjamin Eberlei dcebc241b4 DDC-728 - Verified with test that checking for an inherited single valued association works 2010-08-08 14:23:57 +02:00
Benjamin Eberlei 7b07a17886 Merge branch 'master' into DDC-117 2010-08-08 14:07:24 +02:00
Roman S. Borschel a1bf4dc4f6 API doc adjustments. 2010-08-08 13:10:53 +02:00
Roman S. Borschel 34262aeae1 Removed support for experimental C extension. Will be re-examined for 3.0. 2010-08-08 12:52:46 +02:00
Roman S. Borschel 3630e06b84 [DDC-522][DDC-419][DDC-383] Fixed. 2010-08-08 12:40:32 +02:00
Benjamin Eberlei 69e9fd3145 DDC-704 - Added better validation of inheritence type constructs in Xml, Annotation and Yaml Drivers 2010-08-08 12:29:14 +02:00
Benjamin Eberlei eaa78b981b DDC-700 - Skip generation of proxy classes for Mapped superclasses 2010-08-08 12:05:21 +02:00
Benjamin Eberlei f2715c9af4 DDC-654 - Removed unused lines of code in AST/Functions namespace 2010-08-08 11:49:39 +02:00
Benjamin Eberlei 9347263a43 DDC-709 Add further validation for @OrderBy field names on target entities 2010-08-08 11:39:44 +02:00
Benjamin Eberlei bd28cb1b12 DDC-645 - Demoted <change-tracking-policy /> to an attribute on <entity /> to allow better validation, fixed XSD schema 2010-08-08 11:32:12 +02:00
Benjamin Eberlei 251247c16f DDC-633 - Fix Eager ManyToOne or OneToOne relations being replaced by a proxy instead. 2010-08-08 11:05:30 +02:00
Benjamin Eberlei e3a4c8ddeb Refactored TestCase and added several more use-cases 2010-08-08 10:46:01 +02:00
Benjamin Eberlei 013262a9b7 Add support for EntityManager::remove() of full or partial association primary keys 2010-08-07 21:09:19 +02:00
Benjamin Eberlei 10f47389ae Made single identifier One-To-One + Id work also and added a test-case 2010-08-07 20:07:10 +02:00
Benjamin Eberlei c697a2d47f Prototype hack of @ManyToOne + @Id support with two test-scenarios, composite association key only composite key, and a mixed key scenario. I think single foreign association would work also 2010-08-07 19:33:54 +02:00
Benjamin Eberlei 11b25422d6 Merge branch 'master' of github.com:doctrine/doctrine2 2010-08-07 12:42:23 +02:00
Benjamin Eberlei 0bc22a240b Updated UPDATE_TO_2_0 document with BC changes and stuff related to the Beta2 to Beta3 Migration 2010-08-07 12:31:24 +02:00
Roman S. Borschel 92a79df156 Bumping dev. version. 2010-08-07 12:15:01 +02:00
Roman S. Borschel 29bf4adac7 Fixed merging issue with sequence identifiers. 2010-08-07 11:33:33 +02:00
Guilherme Blanco 35af98260a [DDC-719] Fixed issue with Empty and also Size function that were generating an incorrect SQL for associations counting. 2010-08-06 13:01:06 -03:00
Jonathan H. Wage 623c02c7dc Updating common to 2.0.0BETA4 2010-08-02 16:40:52 -05:00
Roman S. Borschel 0c07b31136 Tweaked INSTANCE OF implementation. 2010-07-30 17:56:11 +02:00
Roman S. Borschel a25101add1 Fixed merging with NOTIFY policy. 2010-07-30 17:19:51 +02:00
Roman S. Borschel 69073c4b37 Fixes for merging bidirectional associations where both sides define cascade=merge as well as fixes for handling null values and proxies properly in single-valued associations. 2010-07-30 17:19:50 +02:00
Roman S. Borschel 954a8c3935 Updated tests. 2010-07-30 17:19:50 +02:00
Guilherme Blanco e69c7c7c60 Merge branch 'DDC-722' 2010-07-30 01:30:40 -03:00
Guilherme Blanco c1fec32f58 [2.0][DDC-482] Added support to INSTANCE OF in DQL. 2010-07-30 01:30:02 -03:00
Benjamin Eberlei af59a581f0 Fix DDC440Test to comply with Oracle Identifier-Name Restrictions 2010-07-29 00:10:23 +02:00
Benjamin Eberlei 5bebf13d58 Bugfix in Test Models that made Oracle tests fail for illegal identifier name 2010-07-29 00:02:42 +02:00
Benjamin Eberlei 104dd7aa77 Fix bug in testsuite that makes oci8 testsuite fail 2010-07-28 23:36:52 +02:00
Benjamin Eberlei d6a1075b53 DDC-644 - Added more tests for Hydrators and unknown to be skipped columns 2010-07-28 22:09:43 +02:00
Benjamin Eberlei 56b3c0e8b0 DDC-644 - Added missing code for the _getScalarRowData() 2010-07-28 21:57:05 +02:00
Benjamin Eberlei 553e93ae27 DDC-644 - Fixed issue with undefined columns in ResultSetMapping by skipping them, added a functional test for modified limit query functionality 2010-07-28 21:50:25 +02:00
Benjamin Eberlei 86e24d373b DDC-723 - Update ORM to depend on latest DBAL Beta3 release, fixed Schema-Tool errors - Use git submodule update to get the latest code 2010-07-28 20:20:47 +02:00
Guilherme Blanco d2740f0e77 Reverted extensibility of Lexer. This is not ideal. 2010-07-27 01:20:31 -03:00
Guilherme Blanco 2a7a72b3f9 Reverted extensibility of Lexer. This is not ideal. 2010-07-27 01:19:51 -03:00
Guilherme Blanco b477211b4b [DDC-490] Added extensibility support to Query Lexer. 2010-07-26 01:24:46 -03:00
Guilherme Blanco 17528224b9 Merge branch 'DDC-698' 2010-07-26 01:13:32 -03:00
Guilherme Blanco ad115f0ac8 [DDC-698] Fixed wrong SQL generation of m2m with starting path on inverse side. 2010-07-26 01:13:03 -03:00
Benjamin Eberlei 75fa640bfe Merge branch 'DDC-714' 2010-07-25 16:32:13 +02:00
Benjamin Eberlei c217b33c45 DDC-714 - Bug in refactored persistNew() functionality leads to failure with pre-persist id generators 2010-07-25 16:32:04 +02:00
Guilherme Blanco 5f109c5d6c Merge branch 'DDC-619' 2010-07-23 01:56:38 -03:00
Guilherme Blanco d50b3c42e2 [DDC-619] Removed support to complex PathExpressions. 2010-07-23 01:55:33 -03:00
Benjamin Eberlei 7a5ab94780 DDC-716 - Added LOCK_EX constant to file_put_contents to avoid file corruption on concurrent access when auto-generating proxy classes is set to TRUE 2010-07-22 23:11:23 +02:00
Benjamin Eberlei 0be6b3fd85 Clarified Exception Message 2010-07-22 22:33:37 +02:00
Benjamin Eberlei c2ee1d2439 DDC-697 - QueryBuilder did not support passing parameter types to the setParameter() or setParameters() methods. 2010-07-21 23:16:45 +02:00
Benjamin Eberlei 6007084324 DDC-706 - Fix DriverChain::isTransient() to comply with interface of Driver 2010-07-21 21:20:55 +02:00
Roman S. Borschel 913e58e385 [DDC-167] Implemented. 2010-07-20 14:27:14 +02:00
Guilherme Blanco 2c28872af8 [2.0][DDC-614] Added support to multiple FROM identification variables. Also, fixed bug with missing lock on subselect. 2010-07-20 00:51:01 -03:00
Roman S. Borschel a05003016b Stricter detection of the NOTIFY policy. 2010-07-16 11:16:06 +02:00
Roman S. Borschel d86c2f6709 Prepared some more DQL tests. 2010-07-15 15:55:45 +02:00
Roman S. Borschel e7ac35ed95 [DDC-119] Fixed. 2010-07-15 15:55:45 +02:00
Benjamin Eberlei d288e99a34 DDC-681 - Fix call to wrong function, need field not column names in lock() 2010-07-10 19:23:38 +02:00
Benjamin Eberlei 722d4a38f4 DDC-130 - fixed a bug in TestCases 2010-07-10 14:04:32 +02:00
Benjamin Eberlei bbaec5bf36 Merge branch 'DDC-130' 2010-07-10 13:45:10 +02:00
Benjamin Eberlei ede6205204 DDC-130 - Make self-referential relationsships work by deleting both the owning and the inverse pair of keys 2010-07-10 13:35:58 +02:00
Benjamin Eberlei b0e4d06c40 DDC-130 - Refactored deleteJoinTableRecords() even more, simplified approach disfavouring evil legacy database schemas 2010-07-10 13:12:33 +02:00
Benjamin Eberlei 1794127d51 DDC-130 - Refactored deleteJoinTableRecords() support 2010-07-10 12:04:02 +02:00
Benjamin Eberlei c0d26b97dd DDC-596 - Added validation of inheritance hierachies 2010-07-10 09:13:02 +02:00
beberlei 233b3cd0b9 DDC-130 - Add initial version of deleteJoinTableRecords code on the persisters, flanked by 4 tests. 2010-07-09 22:55:30 +02:00
Roman S. Borschel aa6edb7903 Added another little contains() after find() test. 2010-07-09 13:18:53 +02:00
Roman S. Borschel a1c13b58d3 Fixed behavior of PersistentCollection#clear(). Fixed single-valued association path expression support in DQL UpdateItems. 2010-07-08 17:30:39 +02:00
Roman S. Borschel 4212b88edc Fixed DDC-455, DDC-600. Some behavior and API polish in the UoW as well as continued _ prefix removal in some core classes. Cleanups and fixes for one-to-one orphan removal including tests. 2010-07-08 00:20:54 +02:00
Christian Heinrich 9b03a1ecdd Added testcase for #DDC-671 2010-07-06 20:19:43 +02:00
Christian Heinrich 54476882c7 CLI configured for development purposes 2010-07-06 17:06:31 +02:00
Roman S. Borschel 88b0813536 Improved entity state detection. 2010-07-06 11:43:14 +02:00
Benjamin Eberlei 26d8b4dafd Added test to verify DDC-635 is not a problem 2010-07-04 16:19:28 +02:00
Benjamin Eberlei c45bd0edec Merge branch 'DDC-178' 2010-07-04 14:38:04 +02:00
Benjamin Eberlei d4de420349 DDC-178 - Finish missing support for lock() on Class Table Inheritance Entities 2010-07-04 14:37:17 +02:00
Benjamin Eberlei aa5a2a049d Merge branch 'DDC-649' 2010-07-04 13:35:34 +02:00
Benjamin Eberlei 552865a867 DDC-649 - Remove IN queries for empty discriminator values if abstract base class is not part of the discriminator map 2010-07-04 13:34:41 +02:00
Benjamin Eberlei fdf1f1b29e Fix testsuite by re-ordering how tests are executed, added debug capabilities for SQL Global State problems using SQL Comments when DOCTRINE_MARK_SQL_LOGS phpunit configuration variable isset 2010-07-04 13:34:40 +02:00
Benjamin Eberlei ae1c40636c Fix testsuite by re-ordering how tests are executed, added debug capabilities for SQL Global State problems using SQL Comments when DOCTRINE_MARK_SQL_LOGS phpunit configuration variable isset 2010-07-04 13:17:26 +02:00
Benjamin Eberlei d922631efe DDC-649 - Refactoring Single Table Inheritance Tests 2010-07-04 12:17:15 +02:00
beberlei b56e1e31af DDC656 - Fix bug with order of fields when recomputeSingleEntityChangeSet() is called after lifecycle events are invoked 2010-07-04 08:54:57 +02:00
Benjamin Eberlei fbaa4e3215 DDC-555 - Fixed toggling of collections when using Collection::clear() in a PersistentCollection context 2010-07-03 16:55:56 +02:00
Benjamin Eberlei c5656eb039 DDC-662 - Fix warning in ensureProductionSettings() 2010-07-02 00:36:31 +02:00
Benjamin Eberlei 6479fcec68 DDC-501 - Cleanup Ticket Test file a little bit 2010-07-01 23:58:46 +02:00
beberlei 92daf7f43e Merge branch 'DDC-501' 2010-07-01 23:49:35 +02:00
beberlei f7e8109d07 DDC-501 - Cascade Merging unitialized Persistent Collections leads to weird behaviour 2010-07-01 23:48:44 +02:00
beberlei 13affb2eb2 Merge branch 'DDC-660' 2010-07-01 21:42:48 +02:00
beberlei 6ba4fa002b DDC-660 - Fix notIn() not quoting passed literals correctly. 2010-07-01 21:42:38 +02:00
beberlei 178f35aaa1 Merge remote branch 'origin/DDC-597' 2010-07-01 19:43:15 +02:00
Benjamin Eberlei 1f059b7f99 Merge branch 'DDC-618' 2010-06-29 00:06:34 +02:00
Benjamin Eberlei c1091485b0 DDC-618 - Bugfix INDEX BY was not yet implemented in SqlWalker 2010-06-29 00:06:19 +02:00
Benjamin Eberlei afd4121116 DDC-611 - Throw an exception if trying to clear an APC Cache from the CLI since there is no access to the webserver shared memory (thanks Romain Dalmaso for reporting) 2010-06-28 23:36:09 +02:00
Christian Heinrich 321fc2506d Fixed #DDC-647
Added missing length element

DBAL-647 - Added test to check for length of string type in all mapping drivers
2010-06-28 23:05:49 +02:00
Christian Heinrich fc7224f73e Merge branch 'master' of http://github.com/doctrine/doctrine2 2010-06-22 13:53:14 +02:00
Benjamin Eberlei 1d5d092453 DDC-616 - Fixed issue appearing when running the whole test-suite against the changes in the DatabasaeDriver 2010-06-20 23:39:21 +02:00
Benjamin Eberlei 206710f91a Merge branch 'DDC-616' 2010-06-20 19:34:20 +02:00
Benjamin Eberlei cd978fb8c9 DDC-616 Made Database Reverse Engineering a Two-Step Approach, first collect details on all tables once and try to detect which tables are many-to-many tables. Then build metadata from this information. This allows to support even many-to-many tables in reverse engineering correctly 2010-06-20 19:34:09 +02:00
Benjamin Eberlei 51e6681934 DDC-646 - Bugfix with missing inclusion of Namespace, added test for ConvertDoctrine1SchemaCommand 2010-06-20 00:28:20 +02:00
Jonathan H. Wage 440a255e5a Removing double slashes when not needed. 2010-06-17 18:23:17 -04:00
Jonathan H. Wage bead9b8b4a Fixing sandbox for source control code and removing it from the build.xml 2010-06-17 14:32:26 -04:00
Jonathan H. Wage d27733f690 [DDC-613] Fixing issue with using function expressions in select not being allowed 2010-06-16 11:55:22 -04:00
Jonathan H. Wage 20c1ff3146 [DDC-642] Fixes issue with exporters not including inversedBy functionality 2010-06-16 11:47:22 -04:00
Jonathan H. Wage e6f465ec80 [DDC-641] Fixing issue with XmlExporter cascade options 2010-06-16 11:19:26 -04:00
Roman S. Borschel 438de30aa3 Merge branch 'master' of github.com:doctrine/doctrine2 2010-06-16 16:32:39 +02:00
Roman S. Borschel 4d27b40423 Bumped Common dependency to BETA3. 2010-06-16 16:32:15 +02:00
Benjamin Eberlei 8639735e91 Adding more Tests for DateTime, Date and Time type handling, related to DBAL-22. Failures in Oracle Time Type handling have to fixed in DBAL package. 2010-06-14 23:46:04 +02:00
Benjamin Eberlei 4705c1cb03 Merge branch 'DDC-631' 2010-06-13 23:17:09 +02:00
Benjamin Eberlei 190d115dce DDC-616 - Fix a regression with many-to-many tables introduced with the DDC-627 commit 2010-06-13 23:02:18 +02:00
Benjamin Eberlei 7018509126 Fix a bug with Table Indexes on XML Driver. Added tests for setting indexes on tables for all the mapping drivers 2010-06-13 22:59:56 +02:00
Benjamin Eberlei 7c2fac64fb Merge branch 'master' of github.com:doctrine/doctrine2 2010-06-13 20:14:03 +02:00
Benjamin Eberlei c69b2d2b54 Merge branch 'DDC-627' 2010-06-13 20:10:10 +02:00
Benjamin Eberlei 434325ed4f Fixed DDC-557 - Default allocation size for sequences changed from 10 to 1. Documented in UPGRADE file 2010-06-13 20:09:59 +02:00
Roman S. Borschel c8e20aa217 Bumping dev version. 2010-06-13 19:56:43 +02:00
Benjamin Eberlei b7db8df7ef Fixed DDC-627 and DDC-616 2010-06-13 19:36:49 +02:00
Christian Heinrich 45ec58fc7e Updated message and added behaviour for public static vars
Signed-off-by: David Abdemoulaie <dave@hobodave.com>
2010-05-23 09:59:12 -05:00
Christian Heinrich 773f56bef2 Fixed #DDC-597
Added a test on public properties in entities. I did not check whether these properties are actually mapped to a column because personally, I believe that within an entity, there should only be protected / private members.

Signed-off-by: David Abdemoulaie <dave@hobodave.com>
2010-05-23 09:59:12 -05:00
Christian Heinrich 64309398e2 Merge commit 'upstream/master' 2010-05-20 14:41:56 +02:00
Christian Heinrich 3fc9971e98 Merge commit 'upstream/master'
Conflicts:
	lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
2010-05-20 14:17:39 +02:00
Christian Heinrich b63afb6ce4 Fixed #DDC-580 2010-05-10 16:41:42 +02:00
Christian Heinrich c2d2e45859 Fixed #DDC-578
Also added a new testcase
2010-05-10 16:17:17 +02:00
Christian Heinrich 4bf3058ab5 Fixed #DDC-571 2010-05-08 00:59:21 +02:00
Christian Heinrich 02e582e00e Merge branch 'master' of git://github.com/doctrine/doctrine2 2010-05-06 20:44:29 +02:00
Christian Heinrich e50f77a780 Corrected method names; the interface already used SQL, the files still used Sql in method names 2010-05-05 13:12:38 +02:00
341 changed files with 19433 additions and 11318 deletions
+6
View File
@@ -4,3 +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
+70
View File
@@ -1,3 +1,73 @@
# Update from 2.0-BETA3 to 2.0-BETA4
## XML Driver <change-tracking-policy /> element demoted to attribute
We changed how the XML Driver allows to define the change-tracking-policy. The working case is now:
<entity change-tracking-policy="DEFERRED_IMPLICT" />
# Update from 2.0-BETA2 to 2.0-BETA3
## Serialization of Uninitialized Proxies
As of Beta3 you can now serialize uninitialized proxies, an exception will only be thrown when
trying to access methods on the unserialized proxy as long as it has not been re-attached to the
EntityManager using `EntityManager#merge()`. See this example:
$proxy = $em->getReference('User', 1);
$serializedProxy = serialize($proxy);
$detachedProxy = unserialized($serializedProxy);
echo $em->contains($detachedProxy); // FALSE
try {
$detachedProxy->getId(); // uninitialized detached proxy
} catch(Exception $e) {
}
$attachedProxy = $em->merge($detachedProxy);
echo $attackedProxy->getId(); // works!
## Changed SQL implementation of Postgres and Oracle DateTime types
The DBAL Type "datetime" included the Timezone Offset in both Postgres and Oracle. As of this version they are now
generated without Timezone (TIMESTAMP WITHOUT TIME ZONE instead of TIMESTAMP WITH TIME ZONE).
See [this comment to Ticket DBAL-22](http://www.doctrine-project.org/jira/browse/DBAL-22?focusedCommentId=13396&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_13396)
for more details as well as migration issues for PostgreSQL and Oracle.
Both Postgres and Oracle will throw Exceptions during hydration of Objects with "DateTime" fields unless migration steps are taken!
## Removed multi-dot/deep-path expressions in DQL
The support for implicit joins in DQL through the multi-dot/Deep Path Expressions
was dropped. For example:
SELECT u FROM User u WHERE u.group.name = ?1
See the "u.group.id" here is using multi dots (deep expression) to walk
through the graph of objects and properties. Internally the DQL parser
would rewrite these queries to:
SELECT u FROM User u JOIN u.group g WHERE g.name = ?1
This explicit notation will be the only supported notation as of now. The internal
handling of multi-dots in the DQL Parser was very complex, error prone in edge cases
and required special treatment for several features we added. Additionally
it had edge cases that could not be solved without making the DQL Parser
even much more complex. For this reason we will drop the support for the
deep path expressions to increase maintainability and overall performance
of the DQL parsing process. This will benefit any DQL query being parsed,
even those not using deep path expressions.
Note that the generated SQL of both notations is exactly the same! You
don't loose anything through this.
## Default Allocation Size for Sequences
The default allocation size for sequences has been changed from 10 to 1. This step was made
to not cause confusion with users and also because it is partly some kind of premature optimization.
# Update from 2.0-BETA1 to 2.0-BETA2
There are no backwards incompatible changes in this release.
+22
View File
@@ -0,0 +1,22 @@
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:
$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.
+9
View File
@@ -0,0 +1,9 @@
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*
+21 -29
View File
@@ -1,4 +1,21 @@
<?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>.
*/
require_once 'Doctrine/Common/ClassLoader.php';
@@ -19,40 +36,15 @@ if (file_exists($configFile)) {
}
require $configFile;
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof \Symfony\Components\Console\Helper\HelperSet) {
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
$helperSet = $helperSetCandidate;
break;
}
}
}
$helperSet = ($helperSet) ?: new \Symfony\Components\Console\Helper\HelperSet();
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
$cli = new \Symfony\Components\Console\Application('Doctrine Command Line Interface', Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
// ORM Commands
new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
));
$cli->run();
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);
+4 -1
View File
@@ -1,10 +1,13 @@
version=2.0.0BETA2
dependencies.common=2.0.0BETA4
dependencies.dbal=2.0.0BETA4
stability=beta
build.dir=build
dist.dir=dist
report.dir=reports
log.archive.dir=logs
svn.path=/usr/bin/svn
project.pirum_dir=
project.download_dir=
test.phpunit_configuration_file=
test.phpunit_generate_coverage=0
test.pmd_reports=0
+122 -33
View File
@@ -5,8 +5,6 @@
-->
<project name="Doctrine2" default="build" basedir=".">
<taskdef classname="NativePhpunitTask" classpath="./tests/" name="nativephpunit" />
<taskdef classname="phing.tasks.ext.d51PearPkg2Task" name="d51pearpkg2" />
<property file="build.properties" />
@@ -24,6 +22,7 @@
<fileset id="bin-scripts" dir="./bin">
<include name="doctrine"/>
<include name="doctrine.php"/>
<include name="doctrine.bat"/>
</fileset>
<!--
@@ -51,21 +50,7 @@
Fileset for source of the Symfony YAML and Console components.
-->
<fileset id="symfony-sources" dir="./lib/vendor">
<include name="Symfony/Components/**"/>
</fileset>
<!--
Fileset for the Doctrine ORM tools + sandbox.
-->
<fileset id="orm-tools" dir=".">
<include name="tools/sandbox/Entities"/>
<include name="tools/sandbox/xml"/>
<include name="tools/sandbox/yaml"/>
<include name="tools/sandbox/cli-config.php"/>
<include name="tools/sandbox/config.php"/>
<include name="tools/sandbox/doctrine"/>
<include name="tools/sandbox/doctrine.php"/>
<include name="tools/sandbox/index.php"/>
<include name="Symfony/Component/**"/>
</fileset>
<!--
@@ -97,24 +82,27 @@
<!--
Builds ORM package, preparing it for distribution.
-->
<target name="build-orm" depends="test">
<copy todir="${build.dir}/orm">
<target name="build-orm" depends="prepare">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<copy todir="${build.dir}/doctrine-orm">
<fileset refid="shared-artifacts"/>
<fileset refid="orm-tools"/>
</copy>
<copy todir="${build.dir}/orm">
<copy todir="${build.dir}/doctrine-orm">
<fileset refid="common-sources"/>
<fileset refid="dbal-sources"/>
<fileset refid="orm-sources"/>
</copy>
<copy todir="${build.dir}/orm/Doctrine">
<copy todir="${build.dir}/doctrine-orm/Doctrine">
<fileset refid="symfony-sources"/>
</copy>
<copy todir="${build.dir}/orm/bin">
<copy todir="${build.dir}/doctrine-orm/bin">
<fileset refid="bin-scripts"/>
</copy>
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/orm/Doctrine/ORM/Version.php > ${build.dir}/orm/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${build.dir}/orm/Doctrine/ORM/Version2.php ${build.dir}/orm/Doctrine/ORM/Version.php" passthru="true" />
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php > ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php" passthru="true" />
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Yaml/.git" includeemptydirs="true"/>
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Console/.git" includeemptydirs="true"/>
</target>
<target name="build" depends="test, build-orm"/>
@@ -156,8 +144,8 @@
<!--
Builds distributable PEAR packages.
-->
<target name="build-packages" depends="build">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/orm">
<target name="build-packages" depends="build-orm">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineORM</name>
<summary>Doctrine Object Relational Mapper</summary>
<channel>pear.doctrine-project.org</channel>
@@ -165,6 +153,7 @@
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
<lead user="romanb" name="Roman Borschel" email="roman@code-factory.org" />
<lead user="beberlei" name="Benjamin Eberlei" email="kontakt@beberlei.de" />
<license>LGPL</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
@@ -172,17 +161,117 @@
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
<package name="DoctrineSymfonyConsole" channel="pear.doctrine-project.org" minimum_version="2.0.0" />
<package name="DoctrineSymfonyYaml" channel="pear.doctrine-project.org" minimum_version="2.0.0" />
</dependencies>
<dirroles key="bin">script</dirroles>
<replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.php" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.php" type="pear-config" from="@bin_dir@" to="bin_dir" />
<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" />
<install as="doctrine.bat" name="bin/doctrine.bat" />
</release>
<replacement path="bin/doctrine.bat" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.bat" type="pear-config" from="@bin_dir@" to="bin_dir" />
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/orm" passthru="true" />
<exec command="mv DoctrineORM-${version}.tgz ../../dist" dir="${build.dir}/orm" passthru="true" />
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineORM-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<tar destfile="dist/DoctrineORM-${version}-full.tar.gz" compression="gzip" basedir="${build.dir}">
<fileset dir="${build.dir}">
<include name="**/**" />
<exclude name="logs/" />
<exclude name="doctrine-orm/package.xml" />
</fileset>
</tar>
</target>
</project>
<target name="git-tag">
<exec command="grep '${version}-DEV' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<exec command="sed 's/${version}-DEV/${version}/' ${project.basedir}/lib/Doctrine/ORM/Version.php > ${project.basedir}/lib/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${project.basedir}/lib/Doctrine/ORM/Version2.php ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git add ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git commit -m 'Release ${version}'" />
<exec command="git tag -m 'Tag ${version}' -a ${version}" passthru="true" />
</target>
<target name="pirum-release">
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineORM-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
<target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
</target>
<target name="update-dev-version">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<propertyprompt propertyName="next_version" defaultValue="${version}" promptText="Enter next version string (without -DEV)" />
<exec command="sed 's/${version}/${next_version}-DEV/' ${project.basedir}/lib/Doctrine/ORM/Version.php > ${project.basedir}/lib/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${project.basedir}/lib/Doctrine/ORM/Version2.php ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git add ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
<!--
Builds distributable PEAR packages for the Symfony Dependencies
-->
<target name="release-symfony-dependencies" depends="build-orm">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineSymfonyConsole</name>
<summary>Symfony Console Component</summary>
<channel>pear.doctrine-project.org</channel>
<description>A command line interface tool from the Symfony project. Packaged for shipping with Doctrine projects using ORM version numbers.</description>
<lead user="fabpot" name="Fabien Potencier" email="fabien.potencier@symfony-project.com" />
<license>NewBSD License</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
<notes>-</notes>
<dependencies>
<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>
<ignore>Symfony/Component/Yaml/</ignore>
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineSymfonyConsole-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineSymfonyYaml</name>
<summary>Symfony Yaml Component</summary>
<channel>pear.doctrine-project.org</channel>
<description>A YAML Parser from the Symfony project. Packaged for shipping with Doctrine projects using ORM version numbers.</description>
<lead user="fabpot" name="Fabien Potencier" email="fabien.potencier@symfony-project.com" />
<license>NewBSD License</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
<notes>-</notes>
<dependencies>
<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>
<ignore>Symfony/Component/Console/</ignore>
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineSymfonyYaml-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineSymfonyConsole-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineSymfonyYaml-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
</project>
+126 -14
View File
@@ -17,11 +17,18 @@
<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:complexType name="emptyType">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
<xs:complexType name="cascade-type">
<xs:sequence>
@@ -30,7 +37,9 @@
<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">
@@ -46,13 +55,32 @@
</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>
@@ -63,25 +91,43 @@
<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="id" type="orm:id" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<xs:element name="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" />
<xs:attribute name="schema" type="xs:NMTOKEN" />
<xs:attribute name="repository-class" type="xs:NMTOKEN"/>
<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:extension base="orm:entity">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:anyAttribute namespace="##other"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="change-tracking-policy">
<xs:restriction base="xs:token">
<xs:enumeration value="DEFERRED_IMPLICIT"/>
<xs:enumeration value="DEFERRED_EXPLICIT"/>
<xs:enumeration value="NOTIFY"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="inheritance-type">
<xs:restriction base="xs:token">
@@ -112,10 +158,14 @@
<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" />
@@ -126,75 +176,114 @@
<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:NMTOKENS" use="required"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:anyAttribute namespace="##other"/>
</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: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" use="required" />
<xs:attribute name="type" type="xs:NMTOKEN" />
<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" />
@@ -202,32 +291,43 @@
<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:attribute name="name" type="xs:NMTOKEN" use="required" />
<xs:attribute name="direction" type="orm:order-by-direction" default="ASC" />
<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:complexType>
<xs:simpleType name="order-by-direction">
@@ -242,24 +342,30 @@
<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:NMTOKEN" use="required" />
<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:NMTOKEN" use="required" />
<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">
@@ -268,13 +374,16 @@
<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:NMTOKEN" use="required" />
<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">
@@ -283,14 +392,17 @@
<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:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<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>
</xs:schema>
+135 -43
View File
@@ -1,7 +1,5 @@
<?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
@@ -57,6 +55,11 @@ 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.
*/
@@ -138,10 +141,16 @@ abstract class AbstractQuery
/**
* Frees the resources used by the query object.
*
* Resets Parameters, Parameter Types and Query Hints.
*
* @return void
*/
public function free()
{
$this->_params = array();
$this->_paramTypes = array();
$this->_hints = array();
}
/**
@@ -186,10 +195,13 @@ abstract class AbstractQuery
*/
public function setParameter($key, $value, $type = null)
{
if ($type !== null) {
$this->_paramTypes[$key] = $type;
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
return $this;
}
@@ -263,7 +275,7 @@ abstract class AbstractQuery
* @param boolean $bool
* @param integer $timeToLive
* @param string $resultCacheId
* @return This query instance.
* @return Doctrine\ORM\AbstractQuery This query instance.
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
{
@@ -325,6 +337,26 @@ 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.
*
@@ -384,6 +416,31 @@ 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.
*
@@ -452,6 +509,16 @@ abstract class AbstractQuery
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
}
/**
* Return the key value map of query hints that are currently set.
*
* @return array
*/
public function getHints()
{
return $this->_hints;
}
/**
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
@@ -460,31 +527,8 @@ abstract class AbstractQuery
* @param integer $hydrationMode The hydration mode to use.
* @return IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
public function iterate(array $params = array(), $hydrationMode = null)
{
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
$this->_doExecute($params, $hydrationMode), $this->_resultSetMapping, $this->_hints
);
}
/**
* Executes the query.
*
* @param string $params Any additional query parameters.
* @param integer $hydrationMode Processing mode to be used during the hydration process.
* @return mixed
*/
public function execute($params = array(), $hydrationMode = null)
{
// If there are still pending insertions in the UnitOfWork we need to flush
// in order to guarantee a correct result.
//TODO: Think this over. Its tricky. Not doing this can lead to strange results
// potentially, but doing it could result in endless loops when querying during
// a flush, i.e. inside an event listener.
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
$this->_em->flush();
}
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
@@ -493,29 +537,49 @@ abstract class AbstractQuery
$this->setParameters($params);
}
if (isset($this->_params[0])) {
throw QueryException::invalidParameterPosition(0);
$stmt = $this->_doExecute();
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
$stmt, $this->_resultSetMapping, $this->_hints
);
}
/**
* Executes the query.
*
* @param array $params Any additional query parameters.
* @param integer $hydrationMode Processing mode to be used during the hydration process.
* @return mixed
*/
public function execute($params = array(), $hydrationMode = null)
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if ($params) {
$this->setParameters($params);
}
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
$id = $this->_getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($id);
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
if ($cached === false) {
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($id, $result, $this->_resultCacheTTL);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached;
return $cached[$key];
}
}
@@ -549,17 +613,33 @@ abstract class AbstractQuery
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
*
* @return string $id
* @return array ($key, $hash)
*/
protected function _getResultCacheId()
protected function getResultCacheId()
{
if ($this->_resultCacheId) {
return $this->_resultCacheId;
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
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);
}
$params[$key] = $idValues;
} else {
$params[$key] = $value;
}
}
$sql = $this->getSql();
ksort($this->_hints);
return md5(implode(";", (array)$sql) . var_export($this->_params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
}
}
@@ -569,4 +649,16 @@ abstract class AbstractQuery
* @return Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
*/
abstract protected function _doExecute();
/**
* Cleanup Query resource when clone is called.
*
* @return void
*/
public function __clone()
{
$this->_params = array();
$this->_paramTypes = array();
$this->_hints = array();
}
}
+51 -30
View File
@@ -20,7 +20,8 @@
namespace Doctrine\ORM;
use Doctrine\Common\Cache\Cache,
Doctrine\ORM\Mapping\Driver\Driver;
Doctrine\ORM\Mapping\Driver\Driver,
Doctrine\Common\Cache\ArrayCache;
/**
* Configuration container for all configuration options of Doctrine.
@@ -120,9 +121,21 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function newDefaultAnnotationDriver($paths = array())
{
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
} else if (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-BETA3-DEV', '>=')) {
$reader = new \Doctrine\Common\Annotations\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 \Doctrine\Common\Annotations\AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
}
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
}
@@ -163,6 +176,16 @@ 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.
@@ -239,29 +262,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataCacheImpl'] = $cacheImpl;
}
/**
* Gets a boolean flag that indicates whether Doctrine should make use of the
* C extension.
*
* @return boolean TRUE if Doctrine is configured to use the C extension, FALSE otherwise.
*/
public function getUseCExtension()
{
return isset($this->_attributes['useCExtension']) ?
$this->_attributes['useCExtension'] : false;
}
/**
* Sets a boolean flag that indicates whether Doctrine should make use of the
* C extension.
*
* @param boolean $boolean Whether to make use of the C extension or not.
*/
public function setUseCExtension($boolean)
{
$this->_attributes['useCExtension'] = $boolean;
}
/**
* Adds a named DQL query to the configuration.
*
@@ -323,13 +323,13 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function ensureProductionSettings()
{
if ( ! $this->_attributes['queryCacheImpl']) {
if ( !$this->getQueryCacheImpl()) {
throw ORMException::queryCacheNotConfigured();
}
if ( ! $this->_attributes['metadataCacheImpl']) {
if ( !$this->getMetadataCacheImpl()) {
throw ORMException::metadataCacheNotConfigured();
}
if ($this->_attributes['autoGenerateProxyClasses']) {
if ($this->getAutoGenerateProxyClasses()) {
throw ORMException::proxyClassesAlwaysRegenerating();
}
}
@@ -485,4 +485,25 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
$this->_attributes['customHydrationModes'][$modeName] = $hydrator;
}
/**
* Set a class metadata factory.
*
* @param string $cmf
*/
public function setClassMetadataFactoryName($cmfName)
{
$this->_attributes['classMetadataFactoryName'] = $cmfName;
}
/**
* @return string
*/
public function getClassMetadataFactoryName()
{
if (!isset($this->_attributes['classMetadataFactoryName'])) {
$this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory';
}
return $this->_attributes['classMetadataFactoryName'];
}
}
+155 -79
View File
@@ -21,10 +21,12 @@ namespace Doctrine\ORM;
use Closure, Exception,
Doctrine\Common\EventManager,
Doctrine\Common\Persistence\ObjectManager,
Doctrine\DBAL\Connection,
Doctrine\DBAL\LockMode,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataFactory,
Doctrine\ORM\Query\ResultSetMapping,
Doctrine\ORM\Proxy\ProxyFactory;
/**
@@ -36,73 +38,77 @@ use Closure, Exception,
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityManager
class EntityManager implements ObjectManager
{
/**
* The used Configuration.
*
* @var Doctrine\ORM\Configuration
*/
private $_config;
private $config;
/**
* The database connection used by the EntityManager.
*
* @var Doctrine\DBAL\Connection
*/
private $_conn;
private $conn;
/**
* The metadata factory, used to retrieve the ORM metadata of entity classes.
*
* @var Doctrine\ORM\Mapping\ClassMetadataFactory
*/
private $_metadataFactory;
private $metadataFactory;
/**
* The EntityRepository instances.
*
* @var array
*/
private $_repositories = array();
private $repositories = array();
/**
* The UnitOfWork used to coordinate object-level transactions.
*
* @var Doctrine\ORM\UnitOfWork
*/
private $_unitOfWork;
private $unitOfWork;
/**
* The event manager that is the central point of the event system.
*
* @var Doctrine\Common\EventManager
*/
private $_eventManager;
private $eventManager;
/**
* The maintained (cached) hydrators. One instance per type.
*
* @var array
*/
private $_hydrators = array();
private $hydrators = array();
/**
* The proxy factory used to create dynamic proxies.
*
* @var Doctrine\ORM\Proxy\ProxyFactory
*/
private $_proxyFactory;
private $proxyFactory;
/**
* @var ExpressionBuilder The expression builder instance used to generate query expressions.
* The expression builder instance used to generate query expressions.
*
* @var Doctrine\ORM\Query\Expr
*/
private $_expressionBuilder;
private $expressionBuilder;
/**
* Whether the EntityManager is closed or not.
*
* @var bool
*/
private $_closed = false;
private $closed = false;
/**
* Creates a new EntityManager that operates on the given database connection
@@ -114,13 +120,17 @@ class EntityManager
*/
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
{
$this->_conn = $conn;
$this->_config = $config;
$this->_eventManager = $eventManager;
$this->_metadataFactory = new ClassMetadataFactory($this);
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
$this->_unitOfWork = new UnitOfWork($this);
$this->_proxyFactory = new ProxyFactory($this,
$this->conn = $conn;
$this->config = $config;
$this->eventManager = $eventManager;
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
$this->metadataFactory = new $metadataFactoryClassName;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses());
@@ -133,7 +143,7 @@ class EntityManager
*/
public function getConnection()
{
return $this->_conn;
return $this->conn;
}
/**
@@ -143,7 +153,7 @@ class EntityManager
*/
public function getMetadataFactory()
{
return $this->_metadataFactory;
return $this->metadataFactory;
}
/**
@@ -158,14 +168,14 @@ class EntityManager
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
* </code>
*
* @return ExpressionBuilder
* @return Doctrine\ORM\Query\Expr
*/
public function getExpressionBuilder()
{
if ($this->_expressionBuilder === null) {
$this->_expressionBuilder = new Query\Expr;
if ($this->expressionBuilder === null) {
$this->expressionBuilder = new Query\Expr;
}
return $this->_expressionBuilder;
return $this->expressionBuilder;
}
/**
@@ -175,7 +185,7 @@ class EntityManager
*/
public function beginTransaction()
{
$this->_conn->beginTransaction();
$this->conn->beginTransaction();
}
/**
@@ -192,14 +202,19 @@ class EntityManager
*/
public function transactional(Closure $func)
{
$this->_conn->beginTransaction();
$this->conn->beginTransaction();
try {
$func($this);
$return = $func($this);
$this->flush();
$this->_conn->commit();
$this->conn->commit();
return $return ?: true;
} catch (Exception $e) {
$this->close();
$this->_conn->rollback();
$this->conn->rollback();
throw $e;
}
}
@@ -211,7 +226,7 @@ class EntityManager
*/
public function commit()
{
$this->_conn->commit();
$this->conn->commit();
}
/**
@@ -221,18 +236,25 @@ class EntityManager
*/
public function rollback()
{
$this->_conn->rollback();
$this->conn->rollback();
}
/**
* Returns the metadata for a class.
* Returns the ORM metadata descriptor for a class.
*
* The class name must be the fully-qualified class name without a leading backslash
* (as it is returned by get_class($obj)) or an aliased class name.
*
* Examples:
* MyProject\Domain\User
* sales:PriceRequest
*
* @return Doctrine\ORM\Mapping\ClassMetadata
* @internal Performance-sensitive method.
*/
public function getClassMetadata($className)
{
return $this->_metadataFactory->getMetadataFor($className);
return $this->metadataFactory->getMetadataFor($className);
}
/**
@@ -258,7 +280,7 @@ class EntityManager
*/
public function createNamedQuery($name)
{
return $this->createQuery($this->_config->getNamedQuery($name));
return $this->createQuery($this->config->getNamedQuery($name));
}
/**
@@ -268,7 +290,7 @@ class EntityManager
* @param ResultSetMapping $rsm The ResultSetMapping to use.
* @return NativeQuery
*/
public function createNativeQuery($sql, \Doctrine\ORM\Query\ResultSetMapping $rsm)
public function createNativeQuery($sql, ResultSetMapping $rsm)
{
$query = new NativeQuery($this);
$query->setSql($sql);
@@ -284,7 +306,7 @@ class EntityManager
*/
public function createNamedNativeQuery($name)
{
list($sql, $rsm) = $this->_config->getNamedNativeQuery($name);
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
return $this->createNativeQuery($sql, $rsm);
}
@@ -308,8 +330,8 @@ class EntityManager
*/
public function flush()
{
$this->_errorIfClosed();
$this->_unitOfWork->commit();
$this->errorIfClosed();
$this->unitOfWork->commit();
}
/**
@@ -330,27 +352,67 @@ class EntityManager
/**
* Gets a reference to the entity identified by the given type and identifier
* without actually loading it.
*
* If partial objects are allowed, this method will return a partial object that only
* has its identifier populated. Otherwise a proxy is returned that automatically
* loads itself on first access.
* without actually loading it, if the entity is not yet loaded.
*
* @param string $entityName The name of the entity type.
* @param mixed $identifier The entity identifier.
* @return object The entity reference.
*/
public function getReference($entityName, $identifier)
{
$class = $this->_metadataFactory->getMetadataFor($entityName);
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
// Check identity map first, if its already in there just return it.
if ($entity = $this->_unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return $entity;
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ($class->subClasses) {
$entity = $this->find($entityName, $identifier);
} else {
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
}
return $entity;
}
/**
* Gets a partial reference to the entity identified by the given type and identifier
* without actually loading it, if the entity is not yet loaded.
*
* The returned reference may be a partial object if the entity is not yet loaded/managed.
* If it is a partial object it will not initialize the rest of the entity state on access.
* Thus you can only ever safely access the identifier of an entity obtained through
* this method.
*
* The use-cases for partial references involve maintaining bidirectional associations
* without loading one side of the association or to update an entity without loading it.
* Note, however, that in the latter case the original (persistent) entity data will
* never be visible to the application (especially not event listeners) as it will
* never be loaded in the first place.
*
* @param string $entityName The name of the entity type.
* @param mixed $identifier The entity identifier.
* @return object The (partial) entity reference.
*/
public function getPartialReference($entityName, $identifier)
{
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
// Check identity map first, if its already in there just return it.
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->_proxyFactory->getProxy($class->name, $identifier);
$this->_unitOfWork->registerManaged($entity, $identifier, array());
$entity = $class->newInstance();
$class->setIdentifierValues($entity, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
return $entity;
}
@@ -364,7 +426,7 @@ class EntityManager
public function clear($entityName = null)
{
if ($entityName === null) {
$this->_unitOfWork->clear();
$this->unitOfWork->clear();
} else {
//TODO
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
@@ -379,7 +441,7 @@ class EntityManager
public function close()
{
$this->clear();
$this->_closed = true;
$this->closed = true;
}
/**
@@ -398,8 +460,8 @@ class EntityManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->_errorIfClosed();
$this->_unitOfWork->persist($entity);
$this->errorIfClosed();
$this->unitOfWork->persist($entity);
}
/**
@@ -415,8 +477,8 @@ class EntityManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->_errorIfClosed();
$this->_unitOfWork->remove($entity);
$this->errorIfClosed();
$this->unitOfWork->remove($entity);
}
/**
@@ -430,8 +492,8 @@ class EntityManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->_errorIfClosed();
$this->_unitOfWork->refresh($entity);
$this->errorIfClosed();
$this->unitOfWork->refresh($entity);
}
/**
@@ -448,7 +510,7 @@ class EntityManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->_unitOfWork->detach($entity);
$this->unitOfWork->detach($entity);
}
/**
@@ -464,8 +526,8 @@ class EntityManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->_errorIfClosed();
return $this->_unitOfWork->merge($entity);
$this->errorIfClosed();
return $this->unitOfWork->merge($entity);
}
/**
@@ -492,19 +554,20 @@ class EntityManager
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
$this->_unitOfWork->lock($entity, $lockMode, $lockVersion);
$this->unitOfWork->lock($entity, $lockMode, $lockVersion);
}
/**
* Gets the repository for an entity class.
*
* @param string $entityName The name of the Entity.
* @return EntityRepository The repository.
* @param string $entityName The name of the entity.
* @return EntityRepository The repository class.
*/
public function getRepository($entityName)
{
if (isset($this->_repositories[$entityName])) {
return $this->_repositories[$entityName];
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
@@ -516,7 +579,7 @@ class EntityManager
$repository = new EntityRepository($this, $metadata);
}
$this->_repositories[$entityName] = $repository;
$this->repositories[$entityName] = $repository;
return $repository;
}
@@ -529,9 +592,9 @@ class EntityManager
*/
public function contains($entity)
{
return $this->_unitOfWork->isScheduledForInsert($entity) ||
$this->_unitOfWork->isInIdentityMap($entity) &&
! $this->_unitOfWork->isScheduledForDelete($entity);
return $this->unitOfWork->isScheduledForInsert($entity) ||
$this->unitOfWork->isInIdentityMap($entity) &&
! $this->unitOfWork->isScheduledForDelete($entity);
}
/**
@@ -541,7 +604,7 @@ class EntityManager
*/
public function getEventManager()
{
return $this->_eventManager;
return $this->eventManager;
}
/**
@@ -551,7 +614,7 @@ class EntityManager
*/
public function getConfiguration()
{
return $this->_config;
return $this->config;
}
/**
@@ -559,13 +622,23 @@ class EntityManager
*
* @throws ORMException If the EntityManager is closed.
*/
private function _errorIfClosed()
private function errorIfClosed()
{
if ($this->_closed) {
if ($this->closed) {
throw ORMException::entityManagerClosed();
}
}
/**
* Check if the Entity manager is open or closed.
*
* @return bool
*/
public function isOpen()
{
return (!$this->closed);
}
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*
@@ -573,7 +646,7 @@ class EntityManager
*/
public function getUnitOfWork()
{
return $this->_unitOfWork;
return $this->unitOfWork;
}
/**
@@ -587,11 +660,11 @@ class EntityManager
*/
public function getHydrator($hydrationMode)
{
if ( ! isset($this->_hydrators[$hydrationMode])) {
$this->_hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
if ( ! isset($this->hydrators[$hydrationMode])) {
$this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode);
}
return $this->_hydrators[$hydrationMode];
return $this->hydrators[$hydrationMode];
}
/**
@@ -615,8 +688,11 @@ class EntityManager
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)) {
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
$hydrator = new $class($this);
break;
}
@@ -633,7 +709,7 @@ class EntityManager
*/
public function getProxyFactory()
{
return $this->_proxyFactory;
return $this->proxyFactory;
}
/**
+28 -11
View File
@@ -20,6 +20,7 @@
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
@@ -34,7 +35,7 @@ use Doctrine\DBAL\LockMode;
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class EntityRepository
class EntityRepository implements ObjectRepository
{
/**
* @var string
@@ -77,6 +78,17 @@ class EntityRepository
->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.
*/
@@ -97,6 +109,10 @@ class EntityRepository
{
// Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
if (!($entity instanceof $this->_class->name)) {
return null;
}
if ($lockMode != LockMode::NONE) {
$this->_em->lock($entity, $lockMode, $lockVersion);
}
@@ -133,7 +149,6 @@ class EntityRepository
/**
* Finds all entities in the repository.
*
* @param int $hydrationMode
* @return array The entities.
*/
public function findAll()
@@ -144,20 +159,21 @@ class EntityRepository
/**
* Finds entities by a set of criteria.
*
* @param string $column
* @param string $value
* @return array
* @param array $criteria
* @param array|null $orderBy
* @param int|null $limit
* @param int|null $offset
* @return array The objects.
*/
public function findBy(array $criteria)
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset);
}
/**
* Finds a single entity by a set of criteria.
*
* @param string $column
* @param string $value
* @param array $criteria
* @return object
*/
public function findOneBy(array $criteria)
@@ -188,13 +204,14 @@ class EntityRepository
);
}
if ( ! isset($arguments[0])) {
if ( !isset($arguments[0])) {
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
throw ORMException::findByRequiresParameter($method.$by);
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName)) {
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
@@ -4,6 +4,9 @@ namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\EntityManager;
/**
* Class that holds event arguments for a loadMetadata event.
*
@@ -12,16 +15,40 @@ use Doctrine\Common\EventArgs;
*/
class LoadClassMetadataEventArgs extends EventArgs
{
private $_classMetadata;
/**
* @var ClassMetadata
*/
private $classMetadata;
public function __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata)
/**
* @var EntityManager
*/
private $em;
/**
* @param ClassMetadataInfo $classMetadata
* @param EntityManager $em
*/
public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
{
$this->_classMetadata = $classMetadata;
$this->classMetadata = $classMetadata;
$this->em = $em;
}
/**
* @return ClassMetadataInfo
*/
public function getClassMetadata()
{
return $this->_classMetadata;
return $this->classMetadata;
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
}
@@ -0,0 +1,54 @@
<?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;
}
}
+8
View File
@@ -119,4 +119,12 @@ 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';
}
+21 -3
View File
@@ -47,9 +47,18 @@ class AssignedGenerator extends AbstractIdGenerator
if ($class->isIdentifierComposite) {
$idFields = $class->getIdentifierFieldNames();
foreach ($idFields as $idField) {
$value = $class->getReflectionProperty($idField)->getValue($entity);
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
$identifier[$idField] = $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;
}
} else {
throw ORMException::entityMissingAssignedId($entity);
}
@@ -58,7 +67,16 @@ class AssignedGenerator extends AbstractIdGenerator
$idField = $class->identifier[0];
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
$identifier[$idField] = $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;
}
} else {
throw ORMException::entityMissingAssignedId($entity);
}
@@ -184,11 +184,18 @@ abstract class AbstractHydrator
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
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'] = $this->_rsm->metaMappings[$key];
$cache[$key]['fieldName'] = $fieldName;
$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]);
}
}
@@ -199,15 +206,24 @@ abstract class AbstractHydrator
$dqlAlias = $cache[$key]['dqlAlias'];
if (isset($cache[$key]['isMetaColumn'])) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
continue;
}
if ($cache[$key]['isIdentifier']) {
$id[$dqlAlias] .= '|' . $value;
}
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;
}
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
@@ -245,6 +261,10 @@ abstract class AbstractHydrator
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue;
} else {
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$cache[$key]['isMetaColumn'] = true;
@@ -19,7 +19,7 @@
namespace Doctrine\ORM\Internal\Hydration;
use PDO, Doctrine\DBAL\Connection;
use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
/**
* The ArrayHydrator produces a nested array "graph" that is often (not always)
@@ -109,7 +109,7 @@ class ArrayHydrator extends AbstractHydrator
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
// Check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) {
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($baseElement[$relationAlias])) {
@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Internal\Hydration;
use PDO,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Query,
Doctrine\Common\Collections\ArrayCollection,
@@ -38,9 +39,9 @@ class ObjectHydrator extends AbstractHydrator
* This local cache is maintained between hydration runs and not cleared.
*/
private $_ce = array();
/* The following parts are reinitialized on every hydration run. */
private $_identifierMap;
private $_resultPointers;
private $_idTemplate;
@@ -49,7 +50,7 @@ class ObjectHydrator extends AbstractHydrator
private $_initializedCollections = array();
private $_existingCollections = array();
//private $_createdEntities;
/** @override */
protected function _prepare()
@@ -58,6 +59,9 @@ 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();
@@ -67,31 +71,35 @@ class ObjectHydrator extends AbstractHydrator
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $class;
}
// Remember which associations are "fetch joined", so that we know where to inject
// collection stubs or proxies and where not.
if (isset($this->_rsm->relationMap[$dqlAlias])) {
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
}
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$sourceClassName][$assoc->sourceFieldName] = true;
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
if ($sourceClass->subClasses) {
foreach ($sourceClass->subClasses as $sourceSubclassName) {
$this->_hints['fetched'][$sourceSubclassName][$assoc->sourceFieldName] = true;
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
}
}
if ( ! $assoc->isManyToMany()) {
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
// Mark any non-collection opposite sides as fetched, too.
if ($assoc->mappedBy) {
$this->_hints['fetched'][$className][$assoc->mappedBy] = true;
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
} else {
if ($assoc->inversedBy) {
$inverseAssoc = $class->associationMappings[$assoc->inversedBy];
if ($inverseAssoc->isOneToOne()) {
$this->_hints['fetched'][$className][$inverseAssoc->sourceFieldName] = true;
if ($assoc['inversedBy']) {
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
if ($class->subClasses) {
foreach ($class->subClasses as $targetSubclassName) {
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc->sourceFieldName] = true;
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
}
}
}
@@ -107,11 +115,17 @@ 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();
}
}
/**
@@ -144,36 +158,38 @@ class ObjectHydrator extends AbstractHydrator
{
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
$value = $class->reflFields[$fieldName]->getValue($entity);
if ($value === null) {
$value = new ArrayCollection;
}
if ( ! $value instanceof PersistentCollection) {
$value = new PersistentCollection(
$this->_em,
$this->_ce[$relation->targetEntityName],
$this->_ce[$relation['targetEntity']],
$value
);
$value->setOwner($entity, $relation);
$class->reflFields[$fieldName]->setValue($entity, $value);
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH])) {
// Is already PersistentCollection, but REFRESH
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
! $value->isInitialized()) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
$value->setInitialized(true);
$value->unwrap()->clear();
$this->_initializedCollections[$oid . $fieldName] = $value;
} else {
// Is already PersistentCollection, and DONT REFRESH
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
$this->_existingCollections[$oid . $fieldName] = $value;
}
return $value;
}
/**
* Gets an entity instance.
*
@@ -183,7 +199,7 @@ class ObjectHydrator extends AbstractHydrator
*/
private function _getEntity(array $data, $dqlAlias)
{
$className = $this->_rsm->aliasMap[$dqlAlias];
$className = $this->_rsm->aliasMap[$dqlAlias];
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
$discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
@@ -191,7 +207,7 @@ class ObjectHydrator extends AbstractHydrator
}
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
{
$class = $this->_ce[$className];
@@ -205,7 +221,7 @@ class ObjectHydrator extends AbstractHydrator
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
}
}
/**
* Gets a ClassMetadata instance from the local cache.
* If the instance is not yet in the local cache, it is loaded into the
@@ -263,15 +279,18 @@ class ObjectHydrator extends AbstractHydrator
// Hydrate the data chunks
foreach ($rowData as $dqlAlias => $data) {
$entityName = $this->_rsm->aliasMap[$dqlAlias];
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
// It's a joined result
$parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
// we need the $path to save into the identifier map which entities were already
// seen for this parent-child relationship
$path = $parentAlias . '.' . $dqlAlias;
// Get a reference to the parent object to which the joined element belongs.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
$first = reset($this->_resultPointers);
$first = reset($this->_resultPointers);
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
} else if (isset($this->_resultPointers[$parentAlias])) {
$parentObject = $this->_resultPointers[$parentAlias];
@@ -285,9 +304,9 @@ class ObjectHydrator extends AbstractHydrator
$relationField = $this->_rsm->relationMap[$dqlAlias];
$relation = $parentClass->associationMappings[$relationField];
$reflField = $parentClass->reflFields[$relationField];
// Check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) {
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
// PATH A: Collection-valued association
if (isset($nonemptyComponents[$dqlAlias])) {
$collKey = $oid . $relationField;
@@ -296,11 +315,11 @@ class ObjectHydrator extends AbstractHydrator
} else if ( ! isset($this->_existingCollections[$collKey])) {
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
}
$indexExists = isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] : false;
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
if ( ! $indexExists || ! $indexIsValid) {
if (isset($this->_existingCollections[$collKey])) {
// Collection exists, only look for the element in the identity map.
@@ -316,11 +335,11 @@ class ObjectHydrator extends AbstractHydrator
$field = $this->_rsm->indexByMap[$dqlAlias];
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
$reflFieldValue->hydrateSet($indexValue, $element);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $indexValue;
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
} else {
$reflFieldValue->hydrateAdd($element);
$reflFieldValue->last();
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $reflFieldValue->key();
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
}
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
@@ -331,6 +350,7 @@ class ObjectHydrator extends AbstractHydrator
}
} else if ( ! $reflField->getValue($parentObject)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$coll->setOwner($parentObject, $relation);
$reflField->setValue($parentObject, $coll);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
}
@@ -342,24 +362,24 @@ class ObjectHydrator extends AbstractHydrator
$element = $this->_getEntity($data, $dqlAlias);
$reflField->setValue($parentObject, $element);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation->targetEntityName];
if ($relation->isOwningSide) {
$targetClass = $this->_ce[$relation['targetEntity']];
if ($relation['isOwningSide']) {
//TODO: Just check hints['fetched'] here?
// If there is an inverse mapping on the target class its bidirectional
if ($relation->inversedBy) {
$inverseAssoc = $targetClass->associationMappings[$relation->inversedBy];
if ($inverseAssoc->isOneToOne()) {
$targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($element, $parentObject);
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc->sourceFieldName, $parentObject);
if ($relation['inversedBy']) {
$inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
}
} else if ($parentClass === $targetClass && $relation->mappedBy) {
} else if ($parentClass === $targetClass && $relation['mappedBy']) {
// Special case: bi-directional self-referencing one-one on the same class
$targetClass->reflFields[$relationField]->setValue($element, $parentObject);
}
} else {
// For sure bidirectional, as there is no inverse side in unidirectional mappings
$targetClass->reflFields[$relation->mappedBy]->setValue($element, $parentObject);
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation->mappedBy, $parentObject);
$targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
}
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
@@ -388,6 +408,10 @@ 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);
@@ -395,6 +419,10 @@ 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
@@ -0,0 +1,141 @@
<?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;
class SimpleObjectHydrator extends AbstractHydrator
{
const REFRESH_ENTITY = 'doctrine_refresh_entity';
/**
* @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[self::REFRESH_ENTITY])) {
$this->_hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[$fieldName] = $data[$fieldName];
}
} else {
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
}
$this->_em->getUnitOfWork()->registerManaged($this->_hints[self::REFRESH_ENTITY], $id, $data);
}
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
}
}
@@ -1,399 +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\Mapping;
/**
* Base class for association mappings.
*
* <b>IMPORTANT NOTE:</b>
*
* The fields of this class are only public for 2 reasons:
* 1) To allow fast, internal READ access.
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
abstract class AssociationMapping
{
/**
* Specifies that an association is to be fetched when it is first accessed.
*
* @var integer
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*
* @var integer
*/
const FETCH_EAGER = 3;
/**
* READ-ONLY: Whether the association cascades delete() operations from the source entity
* to the target entity/entities.
*
* @var boolean
*/
public $isCascadeRemove;
/**
* READ-ONLY: Whether the association cascades persist() operations from the source entity
* to the target entity/entities.
*
* @var boolean
*/
public $isCascadePersist;
/**
* READ-ONLY: Whether the association cascades refresh() operations from the source entity
* to the target entity/entities.
*
* @var boolean
*/
public $isCascadeRefresh;
/**
* READ-ONLY: Whether the association cascades merge() operations from the source entity
* to the target entity/entities.
*
* @var boolean
*/
public $isCascadeMerge;
/**
* READ-ONLY: Whether the association cascades detach() operations from the source entity
* to the target entity/entities.
*
* @var boolean
*/
public $isCascadeDetach;
/**
* READ-ONLY: The fetch mode used for the association.
*
* @var integer
*/
public $fetchMode;
/**
* READ-ONLY: Flag that indicates whether the class that defines this mapping is
* the owning side of the association.
*
* @var boolean
*/
public $isOwningSide = true;
/**
* READ-ONLY: The name of the source Entity (the Entity that defines this mapping).
*
* @var string
*/
public $sourceEntityName;
/**
* READ-ONLY: The name of the target Entity (the Enitity that is the target of the
* association).
*
* @var string
*/
public $targetEntityName;
/**
* READ-ONLY: Identifies the field on the source class (the class this AssociationMapping
* belongs to) that represents the association and stores the reference to the
* other entity/entities.
*
* @var string
*/
public $sourceFieldName;
/**
* READ-ONLY: Identifies the field on the owning side of a bidirectional association that
* controls the mapping for the association. This is only set on the inverse side
* of an association.
*
* @var string
*/
public $mappedBy;
/**
* READ-ONLY: Identifies the field on the inverse side of a bidirectional association.
* This is only set on the owning side of an association.
*
* @var string
*/
public $inversedBy;
/**
* READ-ONLY: The join table definition, if any.
*
* @var array
*/
public $joinTable;
/**
* READ-ONLY: The name of the entity class from which the association was
* inherited in an inheritance hierarchy.
*
* @var string
*/
public $inherited;
/**
* READ-ONLY: The name of the entity or mapped superclass that declares
* the association field in an inheritance hierarchy.
*
* @var string
*/
public $declared;
/**
* Initializes a new instance of a class derived from AssociationMapping.
*
* @param array $mapping The mapping definition.
*/
public function __construct(array $mapping)
{
$this->_validateAndCompleteMapping($mapping);
}
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
* @param array $mapping
* @throws MappingException If something is wrong with the mapping.
*/
protected function _validateAndCompleteMapping(array $mapping)
{
// Mandatory attributes for both sides
if ( ! isset($mapping['fieldName'])) {
throw MappingException::missingFieldName();
}
$this->sourceFieldName = $mapping['fieldName'];
if ( ! isset($mapping['sourceEntity'])) {
throw MappingException::missingSourceEntity($mapping['fieldName']);
}
$this->sourceEntityName = $mapping['sourceEntity'];
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
$this->targetEntityName = $mapping['targetEntity'];
// Mandatory and optional attributes for either side
if ( ! isset($mapping['mappedBy'])) {
// Optional
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
if ($mapping['joinTable']['name'][0] == '`') {
$mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
$mapping['joinTable']['quoted'] = true;
}
$this->joinTable = $mapping['joinTable'];
}
if (isset($mapping['inversedBy'])) {
$this->inversedBy = $mapping['inversedBy'];
}
} else {
$this->isOwningSide = false;
$this->mappedBy = $mapping['mappedBy'];
}
// Optional attributes for both sides
$this->fetchMode = isset($mapping['fetch']) ? $mapping['fetch'] : self::FETCH_LAZY;
$cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
if (in_array('all', $cascades)) {
$cascades = array(
'remove',
'persist',
'refresh',
'merge',
'detach'
);
}
$this->isCascadeRemove = in_array('remove', $cascades);
$this->isCascadePersist = in_array('persist', $cascades);
$this->isCascadeRefresh = in_array('refresh', $cascades);
$this->isCascadeMerge = in_array('merge', $cascades);
$this->isCascadeDetach = in_array('detach', $cascades);
}
/**
* Whether the target entity/entities of the association are eagerly fetched.
*
* @return boolean
*/
public function isEagerlyFetched()
{
return $this->fetchMode == self::FETCH_EAGER;
}
/**
* Whether the target entity/entities of the association are lazily fetched.
*
* @return boolean
*/
public function isLazilyFetched()
{
return $this->fetchMode == self::FETCH_LAZY;
}
/**
* Whether the association is a one-to-one association.
*
* @return boolean
*/
public function isOneToOne()
{
return false;
}
/**
* Whether the association is a one-to-many association.
*
* @return boolean
*/
public function isOneToMany()
{
return false;
}
/**
* Whether the association is a many-to-many association.
*
* @return boolean
*/
public function isManyToMany()
{
return false;
}
/**
* Whether the association uses a join table for the mapping.
*
* @return boolean
*/
public function usesJoinTable()
{
return (bool) $this->joinTable;
}
/**
* Checks whether the association has any cascades configured.
*
* @return boolean
*/
public function hasCascades()
{
return $this->isCascadePersist ||
$this->isCascadeRemove ||
$this->isCascadeRefresh ||
$this->isCascadeMerge ||
$this->isCascadeDetach;
}
/**
* Loads data in $target domain object using this association.
* The data comes from the association navigated from $sourceEntity
* using $em.
*
* @param object $sourceEntity
* @param object $target an entity or a collection
* @param EntityManager $em
* @param array $joinColumnValues foreign keys (significative for this
* association) of $sourceEntity, if needed
*/
abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array());
/**
* Gets the (possibly quoted) name of the join table.
*
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedJoinTableName($platform)
{
return isset($this->joinTable['quoted'])
? $platform->quoteIdentifier($this->joinTable['name'])
: $this->joinTable['name'];
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = array(
'sourceEntityName',
'targetEntityName',
'sourceFieldName',
'fetchMode'
);
if ($this->isCascadeDetach) {
$serialized[] = 'isCascadeDetach';
}
if ($this->isCascadeMerge) {
$serialized[] = 'isCascadeMerge';
}
if ($this->isCascadePersist) {
$serialized[] = 'isCascadePersist';
}
if ($this->isCascadeRefresh) {
$serialized[] = 'isCascadeRefresh';
}
if ($this->isCascadeRemove) {
$serialized[] = 'isCascadeRemove';
}
if ( ! $this->isOwningSide) {
$serialized[] = 'isOwningSide';
}
if ($this->mappedBy) {
$serialized[] = 'mappedBy';
}
if ($this->inversedBy) {
$serialized[] = 'inversedBy';
}
if ($this->joinTable) {
$serialized[] = 'joinTable';
}
if ($this->inherited) {
$serialized[] = 'inherited';
}
if ($this->declared) {
$serialized[] = 'declared';
}
return $serialized;
}
}
+25 -42
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
}
/**
@@ -143,7 +143,11 @@ class ClassMetadata extends ClassMetadataInfo
}
return $id;
} else {
return array($this->identifier[0] => $this->reflFields[$this->identifier[0]]->getValue($entity));
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
}
@@ -154,14 +158,10 @@ class ClassMetadata extends ClassMetadataInfo
* @param mixed $id
* @todo Rename to assignIdentifier()
*/
public function setIdentifierValues($entity, $id)
public function setIdentifierValues($entity, array $id)
{
if ($this->isIdentifierComposite) {
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
} else {
$this->reflFields[$this->identifier[0]]->setValue($entity, $id);
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
}
@@ -193,47 +193,18 @@ class ClassMetadata extends ClassMetadataInfo
*
* @param AssociationMapping $assocMapping
*/
protected function _storeAssociationMapping(AssociationMapping $assocMapping)
protected function _storeAssociationMapping(array $assocMapping)
{
parent::_storeAssociationMapping($assocMapping);
// Store ReflectionProperty of mapped field
$sourceFieldName = $assocMapping->sourceFieldName;
$sourceFieldName = $assocMapping['fieldName'];
$refProp = $this->reflClass->getProperty($sourceFieldName);
$refProp->setAccessible(true);
$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'];
}
/**
* Creates a string representation of this instance.
*
@@ -304,6 +275,10 @@ class ClassMetadata extends ClassMetadataInfo
$serialized[] = 'isMappedSuperclass';
}
if ($this->containsForeignIdentifier) {
$serialized[] = 'containsForeignIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
@@ -313,6 +288,14 @@ class ClassMetadata extends ClassMetadataInfo
$serialized[] = 'lifecycleCallbacks';
}
if ($this->namedQueries) {
$serialized[] = 'namedQueries';
}
if ($this->isReadOnly) {
$serialized[] = 'isReadOnly';
}
return $serialized;
}
@@ -337,8 +320,8 @@ class ClassMetadata extends ClassMetadataInfo
}
foreach ($this->associationMappings as $field => $mapping) {
if ($mapping->declared) {
$reflField = new ReflectionProperty($mapping->declared, $field);
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
+132 -83
View File
@@ -23,7 +23,8 @@ use ReflectionException,
Doctrine\ORM\ORMException,
Doctrine\ORM\EntityManager,
Doctrine\DBAL\Platforms,
Doctrine\ORM\Events;
Doctrine\ORM\Events,
Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface;
/**
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
@@ -36,28 +37,49 @@ use ReflectionException,
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class ClassMetadataFactory
class ClassMetadataFactory implements ClassMetadataFactoryInterface
{
private $_em;
/** The targeted database platform. */
private $_targetPlatform;
/** The used metadata driver. */
private $_driver;
/** The event manager instance */
private $_evm;
/** The used cache driver. */
private $_cacheDriver;
private $_loadedMetadata = array();
private $_initialized = false;
/**
* @var EntityManager
*/
private $em;
/**
* Creates a new factory instance that uses the given metadata driver implementation.
*
* @param $driver The metadata driver to use.
* @var AbstractPlatform
*/
public function __construct(EntityManager $em)
private $targetPlatform;
/**
* @var Driver\Driver
*/
private $driver;
/**
* @var \Doctrine\Common\EventManager
*/
private $evm;
/**
* @var \Doctrine\Common\Cache\Cache
*/
private $cacheDriver;
/**
* @var array
*/
private $loadedMetadata = array();
/**
* @var bool
*/
private $initialized = false;
/**
* @param EntityManager $$em
*/
public function setEntityManager(EntityManager $em)
{
$this->_em = $em;
$this->em = $em;
}
/**
@@ -67,7 +89,7 @@ class ClassMetadataFactory
*/
public function setCacheDriver($cacheDriver)
{
$this->_cacheDriver = $cacheDriver;
$this->cacheDriver = $cacheDriver;
}
/**
@@ -77,12 +99,12 @@ class ClassMetadataFactory
*/
public function getCacheDriver()
{
return $this->_cacheDriver;
return $this->cacheDriver;
}
public function getLoadedMetadata()
{
return $this->_loadedMetadata;
return $this->loadedMetadata;
}
/**
@@ -93,12 +115,12 @@ class ClassMetadataFactory
*/
public function getAllMetadata()
{
if ( ! $this->_initialized) {
$this->_initialize();
if ( ! $this->initialized) {
$this->initialize();
}
$metadata = array();
foreach ($this->_driver->getAllClassNames() as $className) {
foreach ($this->driver->getAllClassNames() as $className) {
$metadata[] = $this->getMetadataFor($className);
}
@@ -109,12 +131,12 @@ class ClassMetadataFactory
* Lazy initialization of this stuff, especially the metadata driver,
* since these are not needed at all when a metadata cache is active.
*/
private function _initialize()
private function initialize()
{
$this->_driver = $this->_em->getConfiguration()->getMetadataDriverImpl();
$this->_targetPlatform = $this->_em->getConnection()->getDatabasePlatform();
$this->_evm = $this->_em->getEventManager();
$this->_initialized = true;
$this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
$this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
$this->evm = $this->em->getEventManager();
$this->initialized = true;
}
/**
@@ -125,43 +147,43 @@ class ClassMetadataFactory
*/
public function getMetadataFor($className)
{
if ( ! isset($this->_loadedMetadata[$className])) {
if ( ! isset($this->loadedMetadata[$className])) {
$realClassName = $className;
// Check for namespace alias
if (strpos($className, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $className);
$realClassName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
$realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
if (isset($this->_loadedMetadata[$realClassName])) {
if (isset($this->loadedMetadata[$realClassName])) {
// We do not have the alias name in the map, include it
$this->_loadedMetadata[$className] = $this->_loadedMetadata[$realClassName];
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
return $this->_loadedMetadata[$realClassName];
return $this->loadedMetadata[$realClassName];
}
}
if ($this->_cacheDriver) {
if (($cached = $this->_cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
$this->_loadedMetadata[$realClassName] = $cached;
if ($this->cacheDriver) {
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
$this->loadedMetadata[$realClassName] = $cached;
} else {
foreach ($this->_loadMetadata($realClassName) as $loadedClassName) {
$this->_cacheDriver->save(
"$loadedClassName\$CLASSMETADATA", $this->_loadedMetadata[$loadedClassName], null
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
$this->cacheDriver->save(
"$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
);
}
}
} else {
$this->_loadMetadata($realClassName);
$this->loadMetadata($realClassName);
}
if ($className != $realClassName) {
// We do not have the alias name in the map, include it
$this->_loadedMetadata[$className] = $this->_loadedMetadata[$realClassName];
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
}
}
return $this->_loadedMetadata[$className];
return $this->loadedMetadata[$className];
}
/**
@@ -172,7 +194,7 @@ class ClassMetadataFactory
*/
public function hasMetadataFor($className)
{
return isset($this->_loadedMetadata[$className]);
return isset($this->loadedMetadata[$className]);
}
/**
@@ -185,7 +207,7 @@ class ClassMetadataFactory
*/
public function setMetadataFor($className, $class)
{
$this->_loadedMetadata[$className] = $class;
$this->loadedMetadata[$className] = $class;
}
/**
@@ -194,12 +216,12 @@ class ClassMetadataFactory
* @param string $name
* @return array $parentClasses
*/
protected function _getParentClasses($name)
protected function getParentClasses($name)
{
// Collect parent classes, ignoring transient (not-mapped) classes.
$parentClasses = array();
foreach (array_reverse(class_parents($name)) as $parentClass) {
if ( ! $this->_driver->isTransient($parentClass)) {
if ( ! $this->driver->isTransient($parentClass)) {
$parentClasses[] = $parentClass;
}
}
@@ -213,55 +235,52 @@ class ClassMetadataFactory
* @param string $name The name of the class for which the metadata should get loaded.
* @param array $tables The metadata collection to which the loaded metadata is added.
*/
protected function _loadMetadata($name)
protected function loadMetadata($name)
{
if ( ! $this->_initialized) {
$this->_initialize();
if ( ! $this->initialized) {
$this->initialize();
}
$loaded = array();
$parentClasses = $this->_getParentClasses($name);
$parentClasses = $this->getParentClasses($name);
$parentClasses[] = $name;
// Move down the hierarchy of parent classes, starting from the topmost class
$parent = null;
$visited = array();
foreach ($parentClasses as $className) {
if (isset($this->_loadedMetadata[$className])) {
$parent = $this->_loadedMetadata[$className];
if (isset($this->loadedMetadata[$className])) {
$parent = $this->loadedMetadata[$className];
if ( ! $parent->isMappedSuperclass) {
array_unshift($visited, $className);
}
continue;
}
$class = $this->_newClassMetadataInstance($className);
$class = $this->newClassMetadataInstance($className);
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
$class->setIdGeneratorType($parent->generatorType);
$this->_addInheritedFields($class, $parent);
$this->_addInheritedRelations($class, $parent);
$this->addInheritedFields($class, $parent);
$this->addInheritedRelations($class, $parent);
$class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField);
$class->setDiscriminatorMap($parent->discriminatorMap);
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
}
// Invoke driver
try {
$this->_driver->loadMetadataForClass($className, $class);
} catch(ReflectionException $e) {
$this->driver->loadMetadataForClass($className, $class);
} catch (ReflectionException $e) {
throw MappingException::reflectionFailure($className, $e);
}
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
if ($parent && ! $parent->isMappedSuperclass) {
if ($parent->isIdGeneratorSequence()) {
$class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
@@ -275,7 +294,7 @@ class ClassMetadataFactory
$class->setIdGenerator($parent->idGenerator);
}
} else {
$this->_completeIdGeneratorMapping($class);
$this->completeIdGeneratorMapping($class);
}
if ($parent && $parent->isInheritanceTypeSingleTable()) {
@@ -284,12 +303,35 @@ class ClassMetadataFactory
$class->setParentClasses($visited);
if ($this->_evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
$this->_evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->_loadedMetadata[$className] = $class;
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
// 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 && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
$this->loadedMetadata[$className] = $class;
$parent = $class;
@@ -309,18 +351,18 @@ class ClassMetadataFactory
* @param string $className
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
protected function _newClassMetadataInstance($className)
protected function newClassMetadataInstance($className)
{
return new ClassMetadata($className);
}
/**
* Adds inherited fields to the subclass mapping.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function _addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
@@ -342,17 +384,24 @@ class ClassMetadataFactory
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->associationMappings as $field => $mapping) {
$subclassMapping = clone $mapping;
if ( ! isset($mapping->inherited) && ! $parentClass->isMappedSuperclass) {
$subclassMapping->inherited = $parentClass->name;
if ($parentClass->isMappedSuperclass) {
if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field);
}
$mapping['sourceEntity'] = $subClass->name;
}
if ( ! isset($mapping->declared)) {
$subclassMapping->declared = $parentClass->name;
//$subclassMapping = $mapping;
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
$mapping['inherited'] = $parentClass->name;
}
$subClass->addInheritedAssociationMapping($subclassMapping);
if ( ! isset($mapping['declared'])) {
$mapping['declared'] = $parentClass->name;
}
$subClass->addInheritedAssociationMapping($mapping);
}
}
@@ -362,13 +411,13 @@ class ClassMetadataFactory
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function _completeIdGeneratorMapping(ClassMetadataInfo $class)
private function completeIdGeneratorMapping(ClassMetadataInfo $class)
{
$idGenType = $class->generatorType;
if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
if ($this->_targetPlatform->prefersSequences()) {
if ($this->targetPlatform->prefersSequences()) {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
} else if ($this->_targetPlatform->prefersIdentityColumns()) {
} else if ($this->targetPlatform->prefersIdentityColumns()) {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
} else {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
@@ -381,7 +430,7 @@ class ClassMetadataFactory
// For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
// Not pretty but necessary and the simplest solution that currently works.
$seqName = $this->_targetPlatform instanceof Platforms\PostgreSQLPlatform ?
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
null;
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
@@ -391,8 +440,8 @@ class ClassMetadataFactory
$definition = $class->sequenceGeneratorDefinition;
if ( ! $definition) {
$sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq';
$definition['sequenceName'] = $this->_targetPlatform->fixSchemaElementName($sequenceName);
$definition['allocationSize'] = 10;
$definition['sequenceName'] = $this->targetPlatform->fixSchemaElementName($sequenceName);
$definition['allocationSize'] = 1;
$definition['initialValue'] = 1;
$class->setSequenceGeneratorDefinition($definition);
}
+690 -55
View File
@@ -19,6 +19,7 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use ReflectionClass;
/**
@@ -39,7 +40,7 @@ use ReflectionClass;
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
*/
class ClassMetadataInfo
class ClassMetadataInfo implements ClassMetadata
{
/* The inheritance mapping types */
/**
@@ -112,6 +113,45 @@ class ClassMetadataInfo
* the <tt>NotifyPropertyChanged</tt> interface.
*/
const CHANGETRACKING_NOTIFY = 3;
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* 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.
*/
const ONE_TO_ONE = 1;
/**
* Identifies a many-to-one association.
*/
const MANY_TO_ONE = 2;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Identifies a one-to-many association.
*/
const ONE_TO_MANY = 4;
/**
* Identifies a many-to-many association.
*/
const MANY_TO_MANY = 8;
/**
* Combined bitmask for to-many (collection-valued) associations.
*/
const TO_MANY = 12;
/**
* READ-ONLY: The name of the entity class.
@@ -164,6 +204,13 @@ class ClassMetadataInfo
*/
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.
@@ -222,7 +269,7 @@ class ClassMetadataInfo
* - <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
@@ -288,7 +335,6 @@ class ClassMetadataInfo
* uniqueConstraints => array
*
* @var array
* @todo Rename to just $table
*/
public $table;
@@ -302,6 +348,57 @@ class ClassMetadataInfo
/**
* READ-ONLY: The association mappings of this class.
*
* The mapping definition array supports the following keys:
*
* - <b>fieldName</b> (string)
* The name of the field in the entity the association is mapped to.
*
* - <b>targetEntity</b> (string)
* The class name of the target entity. If it is fully-qualified it is used as is.
* If it is a simple, unqualified class name the namespace is assumed to be the same
* as the namespace of the source entity.
*
* - <b>mappedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the owning side.
* This key must be specified on the inverse side of a bidirectional association.
*
* - <b>inversedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the inverse side.
* This key must be specified on the owning side of a bidirectional association.
*
* - <b>cascade</b> (array, optional)
* The names of persistence operations to cascade on the association. The set of possible
* values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
*
* - <b>orderBy</b> (array, one-to-many/many-to-many only)
* A map of field names (of the target entity) to sorting directions (ASC/DESC).
* Example: array('priority' => 'desc')
*
* - <b>fetch</b> (integer, optional)
* The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
* Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
*
* - <b>joinTable</b> (array, optional, many-to-many only)
* Specification of the join table and its join columns (foreign keys).
* 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>
* array(
* 'name' => <join table name>,
* 'joinColumns' => array(<join column mapping from join table to source table>),
* 'inverseJoinColumns' => array(<join column mapping from join table to target table>)
* )
* </pre>
*
*
* @var array
*/
public $associationMappings = array();
@@ -313,6 +410,15 @@ class ClassMetadataInfo
*/
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.
*
@@ -377,6 +483,17 @@ class ClassMetadataInfo
*/
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.
@@ -519,9 +636,10 @@ class ClassMetadataInfo
/**
* Gets the mapping of an association.
*
* @see ClassMetadataInfo::$associationMappings
* @param string $fieldName The field name that represents the association in
* the object model.
* @return Doctrine\ORM\Mapping\AssociationMapping The mapping.
* @return array The mapping.
*/
public function getAssociationMapping($fieldName)
{
@@ -554,6 +672,32 @@ class ClassMetadataInfo
$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.
*
@@ -563,8 +707,8 @@ class ClassMetadataInfo
protected function _validateAndCompleteFieldMapping(array &$mapping)
{
// Check mandatory fields
if ( ! isset($mapping['fieldName'])) {
throw MappingException::missingFieldName($this->name, $mapping);
if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
throw MappingException::missingFieldName($this->name);
}
if ( ! isset($mapping['type'])) {
// Default to string
@@ -590,6 +734,10 @@ class ClassMetadataInfo
// Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) {
if ($this->versionField == $mapping['fieldName']) {
throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
}
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
$this->identifier[] = $mapping['fieldName'];
}
@@ -600,6 +748,276 @@ class ClassMetadataInfo
}
}
/**
* Validates & completes the basic mapping information that is common to all
* association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
*
* @param array $mapping The mapping.
* @return array The updated mapping.
* @throws MappingException If something is wrong with the mapping.
*/
protected function _validateAndCompleteAssociationMapping(array $mapping)
{
if ( ! isset($mapping['mappedBy'])) {
$mapping['mappedBy'] = null;
}
if ( ! isset($mapping['inversedBy'])) {
$mapping['inversedBy'] = null;
}
$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']) && 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);
}
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
// Mandatory and optional attributes for either side
if ( ! $mapping['mappedBy']) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') {
$mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
$mapping['joinTable']['quoted'] = true;
}
}
} 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'])) {
$mapping['fetch'] = self::FETCH_LAZY;
}
// Cascades
$cascades = isset($mapping['cascade']) ? $mapping['cascade'] : array();
if (in_array('all', $cascades)) {
$cascades = array(
'remove',
'persist',
'refresh',
'merge',
'detach'
);
}
$mapping['cascade'] = $cascades;
$mapping['isCascadeRemove'] = in_array('remove', $cascades);
$mapping['isCascadePersist'] = in_array('persist', $cascades);
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
return $mapping;
}
/**
* Validates & completes a one-to-one association mapping.
*
* @param array $mapping The mapping to validate & complete.
* @return array The validated & completed mapping.
* @override
*/
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$mapping['isOwningSide'] = true;
}
if ($mapping['isOwningSide']) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
$mapping['joinColumns'] = array(array(
'name' => $mapping['fieldName'] . '_id',
'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'];
}
}
if (empty($joinColumn['name'])) {
$joinColumn['name'] = $mapping['fieldName'] . '_id';
}
if (empty($joinColumn['referencedColumnName'])) {
$joinColumn['referencedColumnName'] = 'id';
}
$mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
$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']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
}
return $mapping;
}
/**
* Validates and completes the mapping.
*
* @param array $mapping The mapping to validate and complete.
* @return array The validated and completed mapping.
* @override
*/
protected function _validateAndCompleteOneToManyMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
// OneToMany-side MUST be inverse (must have mappedBy)
if ( ! isset($mapping['mappedBy'])) {
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
protected function _validateAndCompleteManyToManyMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if ($mapping['isOwningSide']) {
if (strpos($mapping['sourceEntity'], '\\') !== false) {
$sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1));
} else {
$sourceShortName = strtolower($mapping['sourceEntity']);
}
if (strpos($mapping['targetEntity'], '\\') !== false) {
$targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1));
} else {
$targetShortName = strtolower($mapping['targetEntity']);
}
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']['name'])) {
$mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
}
if ( ! isset($mapping['joinTable']['joinColumns'])) {
$mapping['joinTable']['joinColumns'] = array(array(
'name' => $sourceShortName . '_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'));
}
if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
$mapping['joinTable']['inverseJoinColumns'] = array(array(
'name' => $targetShortName . '_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'));
}
foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
if (empty($joinColumn['name'])) {
$joinColumn['name'] = $sourceShortName . '_id';
}
if (empty($joinColumn['referencedColumnName'])) {
$joinColumn['referencedColumnName'] = 'id';
}
if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
$mapping['isOnDeleteCascade'] = true;
}
$mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
$mapping['joinTableColumns'][] = $joinColumn['name'];
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
if (empty($inverseJoinColumn['name'])) {
$inverseJoinColumn['name'] = $targetShortName . '_id';
}
if (empty($inverseJoinColumn['referencedColumnName'])) {
$inverseJoinColumn['referencedColumnName'] = 'id';
}
if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
$mapping['isOnDeleteCascade'] = true;
}
$mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
$mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
}
}
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
/**
* Gets the identifier (primary key) field names of the class.
*
@@ -647,6 +1065,17 @@ class ClassMetadataInfo
public function setIdentifier(array $identifier)
{
$this->identifier = $identifier;
$this->isIdentifierComposite = (count($this->identifier) > 1);
}
/**
* Gets the mapped identifier field of this class.
*
* @return string $identifier
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
@@ -687,11 +1116,19 @@ class ClassMetadataInfo
if ($this->isIdentifierComposite) {
$columnNames = array();
foreach ($this->identifier as $idField) {
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
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'];
}
}
return $columnNames;
} else {
} 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']);
}
}
@@ -897,7 +1334,7 @@ class ClassMetadataInfo
*/
public function isInheritedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]->inherited);
return isset($this->associationMappings[$fieldName]['inherited']);
}
/**
@@ -919,22 +1356,33 @@ class ClassMetadataInfo
* indexes => array of indexes (optional)
* uniqueConstraints => array of constraints (optional)
*
* @param array $table
* If a key is omitted, the current value is kept.
*
* @param array $table The table description.
*/
public function setPrimaryTable(array $table)
{
if (isset($table['name']) && $table['name'][0] == '`') {
$table['name'] = trim($table['name'], '`');
$table['quoted'] = true;
if (isset($table['name'])) {
if ($table['name'][0] == '`') {
$this->table['name'] = trim($table['name'], '`');
$this->table['quoted'] = true;
} else {
$this->table['name'] = $table['name'];
}
}
if (isset($table['indexes'])) {
$this->table['indexes'] = $table['indexes'];
}
if (isset($table['uniqueConstraints'])) {
$this->table['uniqueConstraints'] = $table['uniqueConstraints'];
}
$this->table = $table;
}
/**
* Checks whether the given type identifies an inheritance type.
*
* @param string $type
* @return boolean
* @param integer $type
* @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
*/
private function _isInheritanceType($type)
{
@@ -944,22 +1392,6 @@ class ClassMetadataInfo
$type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
}
/**
* Makes some automatic additions to the association mapping to make the life
* easier for the user, and store join columns in the metadata.
*
* @param array $mapping
* @todo Pass param by ref?
*/
private function _completeAssociationMapping(array $mapping)
{
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false && strlen($this->namespace) > 0) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
return $mapping;
}
/**
* Adds a mapped field to the class.
*
@@ -979,15 +1411,14 @@ class ClassMetadataInfo
* Adds an association mapping without completing/validating it.
* This is mainly used to add inherited association mappings to derived classes.
*
* @param AssociationMapping $mapping
* @param string $owningClassName The name of the class that defined this mapping.
* @param array $mapping
*/
public function addInheritedAssociationMapping(AssociationMapping $mapping/*, $owningClassName = null*/)
public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
{
if (isset($this->associationMappings[$mapping->sourceFieldName])) {
throw MappingException::duplicateAssociationMapping($this->name, $mapping->sourceFieldName);
if (isset($this->associationMappings[$mapping['fieldName']])) {
throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
}
$this->associationMappings[$mapping->sourceFieldName] = $mapping;
$this->associationMappings[$mapping['fieldName']] = $mapping;
}
/**
@@ -996,7 +1427,6 @@ class ClassMetadataInfo
* This is mainly used to add inherited field mappings to derived classes.
*
* @param array $mapping
* @todo Rename: addInheritedFieldMapping
*/
public function addInheritedFieldMapping(array $fieldMapping)
{
@@ -1005,6 +1435,22 @@ class ClassMetadataInfo
$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.
*
@@ -1012,9 +1458,9 @@ class ClassMetadataInfo
*/
public function mapOneToOne(array $mapping)
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToOneMapping = new OneToOneMapping($mapping);
$this->_storeAssociationMapping($oneToOneMapping);
$mapping['type'] = self::ONE_TO_ONE;
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
@@ -1024,9 +1470,9 @@ class ClassMetadataInfo
*/
public function mapOneToMany(array $mapping)
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToManyMapping = new OneToManyMapping($mapping);
$this->_storeAssociationMapping($oneToManyMapping);
$mapping['type'] = self::ONE_TO_MANY;
$mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
@@ -1036,8 +1482,10 @@ class ClassMetadataInfo
*/
public function mapManyToOne(array $mapping)
{
// A many-to-one mapping is simply a one-one backreference
$this->mapOneToOne($mapping);
$mapping['type'] = self::MANY_TO_ONE;
// A many-to-one mapping is essentially a one-one backreference
$mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
@@ -1047,9 +1495,9 @@ class ClassMetadataInfo
*/
public function mapManyToMany(array $mapping)
{
$mapping = $this->_completeAssociationMapping($mapping);
$manyToManyMapping = new ManyToManyMapping($mapping);
$this->_storeAssociationMapping($manyToManyMapping);
$mapping['type'] = self::MANY_TO_MANY;
$mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
$this->_storeAssociationMapping($mapping);
}
/**
@@ -1057,9 +1505,9 @@ class ClassMetadataInfo
*
* @param AssociationMapping $assocMapping
*/
protected function _storeAssociationMapping(AssociationMapping $assocMapping)
protected function _storeAssociationMapping(array $assocMapping)
{
$sourceFieldName = $assocMapping->sourceFieldName;
$sourceFieldName = $assocMapping['fieldName'];
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
}
@@ -1156,6 +1604,13 @@ class ClassMetadataInfo
if ( ! isset($columnDef['fieldName'])) {
$columnDef['fieldName'] = $columnDef['name'];
}
if ( ! isset($columnDef['type'])) {
$columnDef['type'] = "string";
}
if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
}
$this->discriminatorColumn = $columnDef;
}
}
@@ -1172,6 +1627,7 @@ class ClassMetadataInfo
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;
@@ -1186,6 +1642,17 @@ class ClassMetadataInfo
}
}
/**
* 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.
*
@@ -1207,7 +1674,7 @@ class ClassMetadataInfo
public function isSingleValuedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]) &&
$this->associationMappings[$fieldName]->isOneToOne();
($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
}
/**
@@ -1220,7 +1687,75 @@ class ClassMetadataInfo
public function isCollectionValuedAssociation($fieldName)
{
return isset($this->associationMappings[$fieldName]) &&
! $this->associationMappings[$fieldName]->isOneToOne();
! ($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);
}
}
/**
@@ -1294,4 +1829,104 @@ class ClassMetadataInfo
{
$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'];
}
}
@@ -155,7 +155,7 @@ abstract class AbstractFileDriver implements Driver
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
@@ -62,22 +62,22 @@ class AnnotationDriver implements Driver
* @param array
*/
protected $_classNames;
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
* @param $reader The AnnotationReader to use.
* @param string|array $paths One or multiple paths where mapping classes can be found.
*
* @param AnnotationReader $reader The AnnotationReader to use, duck-typed.
* @param string|array $paths One or multiple paths where mapping classes can be found.
*/
public function __construct(AnnotationReader $reader, $paths = null)
public function __construct($reader, $paths = null)
{
$this->_reader = $reader;
if ($paths) {
$this->addPaths((array) $paths);
}
}
/**
* Append lookup paths to metadata driver.
*
@@ -128,10 +128,21 @@ 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 {
@@ -165,27 +176,44 @@ 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'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value));
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) {
$discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => $discrColumnAnnot->name,
'type' => $discrColumnAnnot->type,
'length' => $discrColumnAnnot->length
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate DiscriminatorMap annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
$discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
$metadata->setDiscriminatorMap($discrMapAnnot->value);
}
}
}
// Evaluate DiscriminatorColumn annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) {
$discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => $discrColumnAnnot->name,
'type' => $discrColumnAnnot->type,
'length' => $discrColumnAnnot->length
));
}
// Evaluate DiscriminatorMap annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
$discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
$metadata->setDiscriminatorMap($discrMapAnnot->value);
}
// Evaluate DoctrineChangeTrackingPolicy annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) {
@@ -283,20 +311,25 @@ 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;
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
$mapping['cascade'] = $oneToOneAnnot->cascade;
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToOneAnnot->fetch);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch);
$metadata->mapOneToOne($mapping);
} else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
$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\AssociationMapping::FETCH_' . $oneToManyAnnot->fetch);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@@ -304,11 +337,15 @@ 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;
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToOneAnnot->fetch);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch);
$metadata->mapManyToOne($mapping);
} else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
$joinTable = array();
@@ -349,7 +386,8 @@ class AnnotationDriver implements Driver
$mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyAnnot->fetch);
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@@ -362,9 +400,17 @@ class AnnotationDriver implements Driver
// Evaluate @HasLifecycleCallbacks annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
foreach ($class->getMethods() as $method) {
if ($method->isPublic()) {
// filter for the declaring class only, callbacks from parents will already be registered.
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);
}
@@ -410,6 +456,20 @@ 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']);
}
@@ -432,21 +492,23 @@ class AnnotationDriver implements Driver
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
$iterator = new \RegexIterator(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
),
'/^.+\\' . $this->_fileExtension . '$/i',
\RecursiveRegexIterator::GET_MATCH
);
foreach ($iterator as $file) {
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
continue;
}
$sourceFile = realpath($file->getPathName());
$sourceFile = realpath($file[0]);
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
}
@@ -468,7 +530,7 @@ class AnnotationDriver implements Driver
/**
* Factory method for the Annotation Driver
*
*
* @param array|string $paths
* @param AnnotationReader $reader
* @return AnnotationDriver
@@ -1,7 +1,5 @@
<?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
@@ -24,6 +22,7 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Cache\ArrayCache,
Doctrine\Common\Annotations\AnnotationReader,
Doctrine\DBAL\Schema\AbstractSchemaManager,
Doctrine\DBAL\Schema\SchemaException,
Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\MappingException,
Doctrine\Common\Util\Inflector;
@@ -34,15 +33,46 @@ use Doctrine\Common\Cache\ArrayCache,
* @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 Benjamin Eberlei <kontakt@beberlei.de>
*/
class DatabaseDriver implements Driver
{
/** The SchemaManager. */
/**
* @var AbstractSchemaManager
*/
private $_sm;
/**
* @var array
*/
private $tables = null;
private $classToTableNames = array();
/**
* @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.
@@ -53,37 +83,113 @@ class DatabaseDriver implements Driver
{
$this->_sm = $schemaManager;
}
/**
* Set tables manually instead of relying on the reverse engeneering capabilities of SchemaManager.
*
* @param array $entityTables
* @param array $manyToManyTables
* @return void
*/
public function setTables($entityTables, $manyToManyTables)
{
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($entityTables AS $table) {
$className = $this->getClassNameForTable($table->getName());
$this->classToTableNames[$className] = $table->getName();
$this->tables[$table->getName()] = $table;
}
foreach ($manyToManyTables AS $table) {
$this->manyToManyTables[$table->getName()] = $table;
}
}
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables AS $tableName => $table) {
/* @var $table Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $table->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys AS $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
/**
* {@inheritdoc}
*/
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
{
$tableName = $className;
$className = Inflector::classify(strtolower($tableName));
$this->reverseEngineerMappingFromDatabase();
if (!isset($this->classToTableNames[$className])) {
throw new \InvalidArgumentException("Unknown class " . $className);
}
$tableName = $this->classToTableNames[$className];
$metadata->name = $className;
$metadata->table['name'] = $tableName;
$columns = $this->_sm->listTableColumns($tableName);
$columns = $this->tables[$tableName]->getColumns();
$indexes = $this->tables[$tableName]->getIndexes();
try {
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $this->_sm->listTableForeignKeys($tableName);
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
} else {
$foreignKeys = array();
}
$indexes = $this->_sm->listTableIndexes($tableName);
$allForeignKeyColumns = array();
foreach ($foreignKeys AS $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
$ids = array();
$fieldMappings = array();
foreach ($columns as $column) {
$fieldMapping = array();
if (isset($indexes['primary']) && in_array($column->getName(), $indexes['primary']->getColumns())) {
if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
} else if (in_array($column->getName(), $allForeignKeyColumns)) {
continue;
}
$fieldMapping['fieldName'] = Inflector::camelize(strtolower($column->getName()));
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
@@ -116,15 +222,72 @@ class DatabaseDriver implements Driver
$metadata->mapField($fieldMapping);
}
foreach ($foreignKeys as $foreignKey) {
$cols = $foreignKey->getColumns();
$localColumn = current($cols);
foreach ($this->manyToManyTables AS $manyTable) {
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
// foreign key maps to the table of the current entity, many to many association probably exists
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
$myFk = $foreignKey;
$otherFk = null;
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
if ($foreignKey != $myFk) {
$otherFk = $foreignKey;
break;
}
}
if (!$otherFk) {
// the definition of this many to many table does not contain
// enough foreign key information to continue reverse engeneering.
continue;
}
$localColumn = current($myFk->getColumns());
$associationMapping = array();
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
if (current($manyTable->getColumns())->getName() == $localColumn) {
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
$associationMapping['joinTable'] = array(
'name' => strtolower($manyTable->getName()),
'joinColumns' => array(),
'inverseJoinColumns' => array(),
);
$fkCols = $myFk->getForeignColumns();
$cols = $myFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$fkCols = $otherFk->getForeignColumns();
$cols = $otherFk->getColumns();
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
} else {
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
}
$metadata->mapManyToMany($associationMapping);
break;
}
}
}
foreach ($foreignKeys as $foreignKey) {
$foreignTable = $foreignKey->getForeignTableName();
$cols = $foreignKey->getColumns();
$fkCols = $foreignKey->getForeignColumns();
$localColumn = current($cols);
$associationMapping = array();
$associationMapping['fieldName'] = Inflector::camelize(str_ireplace('_id', '', $localColumn));
$associationMapping['targetEntity'] = Inflector::classify($foreignKey->getForeignTableName());
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
@@ -132,7 +295,6 @@ class DatabaseDriver implements Driver
'referencedColumnName' => $fkCols[$i],
);
}
$metadata->mapManyToOne($associationMapping);
}
}
@@ -146,18 +308,90 @@ class DatabaseDriver implements Driver
}
/**
* {@inheritDoc}
* Return all the class names supported by this driver.
*
* IMPORTANT: This method must return an array of class not tables names.
*
* @return array
*/
public function getAllClassNames()
{
$classes = array();
foreach ($this->_sm->listTables() as $table) {
// This method must return an array of table names because we need
// to know the table name after we inflect it to create the entity class name.
$classes[] = $table->getName();
$this->reverseEngineerMappingFromDatabase();
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 $classes;
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;
}
}
@@ -1,7 +1,5 @@
<?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
@@ -27,6 +25,7 @@ use Doctrine\Common\Annotations\Annotation;
final class Entity extends Annotation {
public $repositoryClass;
public $readOnly = false;
}
final class MappedSuperclass extends Annotation {}
final class InheritanceType extends Annotation {}
@@ -80,6 +79,7 @@ final class OneToMany extends Annotation {
public $cascade;
public $fetch = 'LAZY';
public $orphanRemoval = false;
public $indexBy;
}
final class ManyToOne extends Annotation {
public $targetEntity;
@@ -93,6 +93,7 @@ final class ManyToMany extends Annotation {
public $inversedBy;
public $cascade;
public $fetch = 'LAZY';
public $indexBy;
}
final class ElementCollection extends Annotation {
public $tableName;
@@ -114,17 +115,23 @@ final class Index extends Annotation {
final class JoinTable extends Annotation {
public $name;
public $schema;
public $joinColumns;
public $inverseJoinColumns;
public $joinColumns = array();
public $inverseJoinColumns = array();
}
final class SequenceGenerator extends Annotation {
public $sequenceName;
public $allocationSize = 10;
public $allocationSize = 1;
public $initialValue = 1;
}
final class ChangeTrackingPolicy extends Annotation {}
final class OrderBy extends Annotation {}
final class NamedQueries extends Annotation {}
final class NamedQuery extends Annotation {
public $name;
public $query;
}
/* Annotations for lifecycle callbacks */
final class HasLifecycleCallbacks extends Annotation {}
final class PrePersist extends Annotation {}
@@ -88,16 +88,21 @@ class DriverChain implements Driver
public function getAllClassNames()
{
$classNames = array();
foreach ($this->_drivers AS $driver) {
$classNames = array_merge($classNames, $driver->getAllClassNames());
foreach ($this->_drivers AS $namespace => $driver) {
$driverClasses = $driver->getAllClassNames();
foreach ($driverClasses AS $className) {
if (strpos($className, $namespace) === 0) {
$classNames[] = $className;
}
}
}
return $classNames;
return array_unique($classNames);
}
/**
* Whether the class with the specified name should have its metadata loaded.
* This is only the case if it is either mapped as an Entity or a
* MappedSuperclass.
*
* This is only the case for non-transient classes either mapped as an Entity or MappedSuperclass.
*
* @param string $className
* @return boolean
@@ -110,6 +115,7 @@ class DriverChain implements Driver
}
}
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
// class isTransient, i.e. not an entity or mapped superclass
return true;
}
}
@@ -1,7 +1,5 @@
<?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
@@ -22,7 +20,7 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo,
Doctrine\ORM\Mapping\MappingException;
Doctrine\ORM\Mapping\MappingException;
/**
* The StaticPHPDriver calls a static loadMetadata() method on your entity
@@ -31,15 +29,33 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class StaticPHPDriver implements Driver
{
/**
* Paths of entity directories.
*
* @var array
*/
private $_paths = array();
/**
* Map of all class names.
*
* @var array
*/
private $_classNames;
/**
* The file extension of mapping documents.
*
* @var string
*/
private $_fileExtension = '.php';
public function __construct($paths)
{
@@ -58,7 +74,7 @@ class StaticPHPDriver implements Driver
{
call_user_func_array(array($className, 'loadMetadata'), array($metadata));
}
/**
* {@inheritDoc}
* @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it?
@@ -77,13 +93,13 @@ class StaticPHPDriver implements Driver
$includedFiles = array();
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
@@ -102,7 +118,7 @@ class StaticPHPDriver implements Driver
foreach ($declared as $className) {
$rc = new \ReflectionClass($className);
$sourceFile = $rc->getFileName();
if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) {
$classes[] = $className;
}
}
@@ -119,4 +135,4 @@ class StaticPHPDriver implements Driver
{
return method_exists($className, 'loadMetadata') ? false : true;
}
}
}
+75 -34
View File
@@ -55,6 +55,9 @@ 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 {
@@ -69,6 +72,16 @@ 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'];
@@ -77,50 +90,59 @@ class XmlDriver extends AbstractFileDriver
if (isset($xmlRoot['inheritance-type'])) {
$inheritanceType = (string)$xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
}
// Evaluate <discriminator-column...>
if (isset($xmlRoot->{'discriminator-column'})) {
$discrColumn = $xmlRoot->{'discriminator-column'};
$metadata->setDiscriminatorColumn(array(
'name' => (string)$discrColumn['name'],
'type' => (string)$discrColumn['type'],
'length' => (string)$discrColumn['length']
));
}
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate <discriminator-column...>
if (isset($xmlRoot->{'discriminator-column'})) {
$discrColumn = $xmlRoot->{'discriminator-column'};
$metadata->setDiscriminatorColumn(array(
'name' => (string)$discrColumn['name'],
'type' => (string)$discrColumn['type'],
'length' => (string)$discrColumn['length']
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate <discriminator-map...>
if (isset($xmlRoot->{'discriminator-map'})) {
$map = array();
foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} AS $discrMapElement) {
$map[(string)$discrMapElement['value']] = (string)$discrMapElement['class'];
// Evaluate <discriminator-map...>
if (isset($xmlRoot->{'discriminator-map'})) {
$map = array();
foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} AS $discrMapElement) {
$map[(string)$discrMapElement['value']] = (string)$discrMapElement['class'];
}
$metadata->setDiscriminatorMap($map);
}
}
$metadata->setDiscriminatorMap($map);
}
// Evaluate <change-tracking-policy...>
if (isset($xmlRoot->{'change-tracking-policy'})) {
if (isset($xmlRoot['change-tracking-policy'])) {
$metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
. strtoupper((string)$xmlRoot->{'change-tracking-policy'})));
. strtoupper((string)$xmlRoot['change-tracking-policy'])));
}
// Evaluate <indexes...>
if (isset($xmlRoot->indexes)) {
$metadata->table['indexes'] = array();
foreach ($xmlRoot->indexes->index as $index) {
if (is_string($index['columns'])) {
$columns = explode(',', $index['columns']);
} else {
$columns = $index['columns'];
}
$columns = explode(',', (string)$index['columns']);
$metadata->table['indexes'][$index['name']] = array(
'columns' => $columns
);
if (isset($index['name'])) {
$metadata->table['indexes'][(string)$index['name']] = array(
'columns' => $columns
);
} else {
$metadata->table['indexes'][] = array(
'columns' => $columns
);
}
}
}
// Evaluate <unique-constraints..>
if (isset($xmlRoot->{'unique-constraints'})) {
$metadata->table['uniqueConstraints'] = array();
foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) {
$columns = explode(',', (string)$unique['columns']);
@@ -185,7 +207,13 @@ 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'],
@@ -226,8 +254,12 @@ 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\AssociationMapping::FETCH_' . (string)$oneToOneElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']);
}
if (isset($oneToOneElement['mapped-by'])) {
@@ -271,7 +303,7 @@ class XmlDriver extends AbstractFileDriver
);
if (isset($oneToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$oneToManyElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']);
}
if (isset($oneToManyElement->cascade)) {
@@ -290,6 +322,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($oneToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$oneToManyElement->{'index-by'};
}
$metadata->mapOneToMany($mapping);
}
}
@@ -302,8 +338,12 @@ 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\AssociationMapping::FETCH_' . (string)$manyToOneElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversed-by'])) {
@@ -316,9 +356,6 @@ class XmlDriver extends AbstractFileDriver
$joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement->{'join-column'});
} else if (isset($manyToOneElement->{'join-columns'})) {
foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
if (!isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $name;
}
$joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement);
}
}
@@ -346,7 +383,7 @@ class XmlDriver extends AbstractFileDriver
);
if (isset($manyToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . (string)$manyToManyElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']);
}
if (isset($manyToManyElement['mapped-by'])) {
@@ -392,6 +429,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
}
$metadata->mapManyToMany($mapping);
}
}
@@ -483,4 +524,4 @@ class XmlDriver extends AbstractFileDriver
return $result;
}
}
}
+85 -20
View File
@@ -49,6 +49,9 @@ 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 {
@@ -62,6 +65,21 @@ 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'];
@@ -69,22 +87,27 @@ class YamlDriver extends AbstractFileDriver
if (isset($element['inheritanceType'])) {
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate discriminatorColumn
if (isset($element['discriminatorColumn'])) {
$discrColumn = $element['discriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => $discrColumn['name'],
'type' => $discrColumn['type'],
'length' => $discrColumn['length']
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate discriminatorMap
if (isset($element['discriminatorMap'])) {
$metadata->setDiscriminatorMap($element['discriminatorMap']);
}
}
}
// Evaluate discriminatorColumn
if (isset($element['discriminatorColumn'])) {
$discrColumn = $element['discriminatorColumn'];
$metadata->setDiscriminatorColumn(array(
'name' => $discrColumn['name'],
'type' => $discrColumn['type'],
'length' => $discrColumn['length']
));
}
// Evaluate discriminatorMap
if (isset($element['discriminatorMap'])) {
$metadata->setDiscriminatorMap($element['discriminatorMap']);
}
// Evaluate changeTrackingPolicy
if (isset($element['changeTrackingPolicy'])) {
@@ -130,9 +153,15 @@ 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);
}
@@ -147,6 +176,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['columnName'] = $idElement['column'];
}
if (isset($idElement['length'])) {
$mapping['length'] = $idElement['length'];
}
$metadata->mapField($mapping);
if (isset($idElement['generator'])) {
@@ -225,8 +258,12 @@ 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\AssociationMapping::FETCH_' . $oneToOneElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']);
}
if (isset($oneToOneElement['mappedBy'])) {
@@ -257,6 +294,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $oneToOneElement['cascade'];
}
if (isset($oneToOneElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval'];
}
$metadata->mapOneToOne($mapping);
}
}
@@ -271,17 +312,25 @@ class YamlDriver extends AbstractFileDriver
);
if (isset($oneToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $oneToManyElement['fetch']);
$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['indexBy'])) {
$mapping['indexBy'] = $oneToManyElement['indexBy'];
}
$metadata->mapOneToMany($mapping);
}
}
@@ -294,8 +343,12 @@ 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\AssociationMapping::FETCH_' . $manyToOneElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']);
}
if (isset($manyToOneElement['inversedBy'])) {
@@ -322,6 +375,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToOneElement['cascade'];
}
if (isset($manyToOneElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToOneElement['orphanRemoval'];
}
$metadata->mapManyToOne($mapping);
}
}
@@ -335,7 +392,7 @@ class YamlDriver extends AbstractFileDriver
);
if (isset($manyToManyElement['fetch'])) {
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\AssociationMapping::FETCH_' . $manyToManyElement['fetch']);
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']);
}
if (isset($manyToManyElement['mappedBy'])) {
@@ -377,10 +434,18 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToManyElement['cascade'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphan-removal'];
}
if (isset($manyToManyElement['orderBy'])) {
$mapping['orderBy'] = $manyToManyElement['orderBy'];
}
if (isset($manyToManyElement['indexBy'])) {
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
$metadata->mapManyToMany($mapping);
}
}
@@ -441,6 +506,6 @@ class YamlDriver extends AbstractFileDriver
*/
protected function _loadMappingFile($file)
{
return \Symfony\Components\Yaml\Yaml::load($file);
return \Symfony\Component\Yaml\Yaml::parse($file);
}
}
}
@@ -1,177 +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\Mapping;
/**
* A many-to-many mapping describes the mapping between two collections of
* entities.
*
* <b>IMPORTANT NOTE:</b>
*
* The fields of this class are only public for 2 reasons:
* 1) To allow fast READ access.
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class ManyToManyMapping extends AssociationMapping
{
/**
* READ-ONLY: Maps the columns in the relational table to the columns in the source table.
*/
public $relationToSourceKeyColumns = array();
/**
* READ-ONLY: Maps the columns in the relation table to the columns in the target table.
*/
public $relationToTargetKeyColumns = array();
/**
* READ-ONLY: List of aggregated column names on the join table.
*/
public $joinTableColumns = array();
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
//public $keyColumn;
/**
* READ-ONLY: Order this collection by the given DQL snippet.
*
* Only simple unqualified field names and ASC|DESC are allowed
*
* @var array
*/
public $orderBy;
/**
* {@inheritdoc}
*/
protected function _validateAndCompleteMapping(array $mapping)
{
parent::_validateAndCompleteMapping($mapping);
if ($this->isOwningSide) {
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']) || ! $mapping['joinTable']) {
// Apply default join table
$sourceShortName = substr($this->sourceEntityName, strrpos($this->sourceEntityName, '\\') + 1);
$targetShortName = substr($this->targetEntityName, strrpos($this->targetEntityName, '\\') + 1);
$mapping['joinTable'] = array(
'name' => $sourceShortName .'_' . $targetShortName,
'joinColumns' => array(
array(
'name' => $sourceShortName . '_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'
)
),
'inverseJoinColumns' => array(
array(
'name' => $targetShortName . '_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'
)
)
);
$this->joinTable = $mapping['joinTable'];
}
// owning side MUST specify joinColumns
else if ( ! isset($mapping['joinTable']['joinColumns'])) {
throw MappingException::missingRequiredOption(
$this->sourceFieldName, 'joinColumns',
'Did you think of case sensitivity / plural s?'
);
}
// owning side MUST specify inverseJoinColumns
else if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
throw MappingException::missingRequiredOption(
$this->sourceFieldName, 'inverseJoinColumns',
'Did you think of case sensitivity / plural s?'
);
}
foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) {
$this->relationToSourceKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
$this->joinTableColumns[] = $joinColumn['name'];
}
foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
$this->relationToTargetKeyColumns[$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
$this->joinTableColumns[] = $inverseJoinColumn['name'];
}
}
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
$this->orderBy = $mapping['orderBy'];
}
}
/**
* Loads entities in $targetCollection using $em.
* The data of $sourceEntity are used to restrict the collection
* via the join table.
*
* @param object The owner of the collection.
* @param object The collection to populate.
* @param array
* @todo Remove
*/
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
{
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadManyToManyCollection($this, $sourceEntity, $targetCollection);
}
/** {@inheritdoc} */
public function isManyToMany()
{
return true;
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
$serialized[] = 'joinTableColumns';
$serialized[] = 'relationToSourceKeyColumns';
$serialized[] = 'relationToTargetKeyColumns';
if ($this->orderBy) {
$serialized[] = 'orderBy';
}
return $serialized;
}
}
+99 -6
View File
@@ -48,9 +48,9 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("Id generators can't be used with a composite id.");
}
public static function missingFieldName()
public static function missingFieldName($entity)
{
return new self("The association mapping misses the 'fieldName' attribute.");
return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'.");
}
public static function missingTargetEntity($fieldName)
@@ -68,9 +68,14 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("No mapping file found named '$fileName' for class '$entityName'.");
}
public static function mappingNotFound($fieldName)
public static function mappingNotFound($className, $fieldName)
{
return new self("No mapping found for field '$fieldName'.");
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)
@@ -160,6 +165,10 @@ 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);
}
@@ -170,9 +179,16 @@ class MappingException extends \Doctrine\ORM\ORMException
);
}
public static function fileMappingDriversRequireConfiguredDirectoryPath()
public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null)
{
return new self('File mapping drivers must have a directory path');
if ( ! empty($path)) {
$path = '[' . $path . ']';
}
return new self(
'File mapping drivers must have a valid directory path, ' .
'however the given path ' . $path . ' seems to be incorrect!'
);
}
/**
@@ -190,6 +206,26 @@ class MappingException extends \Doctrine\ORM\ORMException
);
}
public static function missingDiscriminatorMap($className)
{
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
}
public static function missingDiscriminatorColumn($className)
{
return new self("Entity class '$className' is using inheritance but no discriminator column was defined.");
}
public static function invalidDiscriminatorColumnType($className, $type)
{
return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!");
}
public static function cannotVersionIdField($className, $fieldName)
{
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
}
/**
* @param string $className
* @param string $columnName
@@ -199,4 +235,61 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping.");
}
public static function illegalToManyAssocationOnMappedSuperclass($className, $field)
{
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. If you want to avoid instantiation of this type mark it abstract."
);
}
}
@@ -1,144 +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\Mapping;
/**
* Represents a one-to-many mapping.
*
* NOTE: One-to-many mappings can currently not be uni-directional (one -> many).
* They must either be bidirectional (one <-> many) or unidirectional (many -> one).
* In other words, the many-side MUST be the owning side and the one-side MUST be
* the inverse side.
*
* <b>IMPORTANT NOTE:</b>
*
* The fields of this class are only public for 2 reasons:
* 1) To allow fast READ access.
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @since 2.0
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class OneToManyMapping extends AssociationMapping
{
/**
* READ-ONLY: Whether to delete orphaned elements (removed from the collection)
*
* @var boolean
*/
public $orphanRemoval = false;
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
//public $keyColumn;
/**
* READ-ONLY: Order this collection by the given SQL snippet.
*/
public $orderBy;
/**
* Validates and completes the mapping.
*
* @param array $mapping The mapping to validate and complete.
* @return array The validated and completed mapping.
* @override
*/
protected function _validateAndCompleteMapping(array $mapping)
{
parent::_validateAndCompleteMapping($mapping);
// OneToMany-side MUST be inverse (must have mappedBy)
if ( ! isset($mapping['mappedBy'])) {
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$this->orphanRemoval = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) {
if (!is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
$this->orderBy = $mapping['orderBy'];
}
}
/**
* Whether orphaned elements (removed from the collection) should be deleted.
*
* @return boolean TRUE if orphaned elements should be deleted, FALSE otherwise.
*/
public function shouldDeleteOrphans()
{
return $this->deleteOrphans;
}
/**
* {@inheritdoc}
*/
public function isOneToMany()
{
return true;
}
/**
* Loads a one-to-many collection.
*
* @param $sourceEntity The entity that owns the collection.
* @param $targetCollection The collection to load/fill.
* @param $em The EntityManager to use.
* @param $joinColumnValues
* @return void
* @todo Remove
*/
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
{
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToManyCollection($this, $sourceEntity, $targetCollection);
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
if ($this->orderBy) {
$serialized[] = 'orderBy';
}
if ($this->orphanRemoval) {
$serialized[] = 'orphanRemoval';
}
return $serialized;
}
}
@@ -1,165 +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\Mapping;
/**
* A one-to-one mapping describes a uni-directional mapping from one entity
* to another entity.
*
* <b>IMPORTANT NOTE:</b>
*
* The fields of this class are only public for 2 reasons:
* 1) To allow fast READ access.
* 2) To drastically reduce the size of a serialized instance (private/protected members
* get the whole class name, namespace inclusive, prepended to every property in
* the serialized representation).
*
* Instances of this class are stored serialized in the metadata cache together with the
* owning <tt>ClassMetadata</tt> instance.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @todo Potentially remove if assoc mapping objects get replaced by simple arrays.
*/
class OneToOneMapping extends AssociationMapping
{
/**
* READ-ONLY: Maps the source foreign/primary key columns to the target primary/foreign key columns.
* i.e. source.id (pk) => target.user_id (fk).
* Reverse mapping of _targetToSourceKeyColumns.
*/
public $sourceToTargetKeyColumns = array();
/**
* READ-ONLY: Maps the target primary/foreign key columns to the source foreign/primary key columns.
* i.e. target.user_id (fk) => source.id (pk).
* Reverse mapping of _sourceToTargetKeyColumns.
*/
public $targetToSourceKeyColumns = array();
/**
* READ-ONLY: Whether to delete orphaned elements (when nulled out, i.e. $foo->other = null)
*
* @var boolean
*/
public $orphanRemoval = false;
/**
* READ-ONLY: The join column definitions. Only present on the owning side.
*
* @var array
*/
public $joinColumns = array();
/**
* READ-ONLY: A map of join column names to field names that are used in cases
* when the join columns are fetched as part of the query result.
*
* @var array
*/
public $joinColumnFieldNames = array();
/**
* {@inheritdoc}
*
* @param array $mapping The mapping to validate & complete.
* @return array The validated & completed mapping.
* @override
*/
protected function _validateAndCompleteMapping(array $mapping)
{
parent::_validateAndCompleteMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$this->isOwningSide = true;
}
if ($this->isOwningSide) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
$mapping['joinColumns'] = array(array(
'name' => $this->sourceFieldName . '_id',
'referencedColumnName' => 'id'
));
}
foreach ($mapping['joinColumns'] as $joinColumn) {
$this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName'];
$this->joinColumnFieldNames[$joinColumn['name']] = isset($joinColumn['fieldName'])
? $joinColumn['fieldName'] : $joinColumn['name'];
}
$this->joinColumns = $mapping['joinColumns'];
$this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$this->orphanRemoval = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
return $mapping;
}
/**
* {@inheritdoc}
*
* @return boolean
* @override
*/
public function isOneToOne()
{
return true;
}
/**
* {@inheritdoc}
*
* @param object $sourceEntity the entity source of this association
* @param object $targetEntity the entity to load data in
* @param EntityManager $em
* @param array $joinColumnValues Values of the join columns of $sourceEntity.
* @todo Remove
*/
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues = array())
{
return $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->loadOneToOneEntity($this, $sourceEntity, $targetEntity, $joinColumnValues);
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
$serialized = parent::__sleep();
$serialized[] = 'joinColumns';
$serialized[] = 'joinColumnFieldNames';
$serialized[] = 'sourceToTargetKeyColumns';
$serialized[] = 'targetToSourceKeyColumns';
if ($this->orphanRemoval) {
$serialized[] = 'orphanRemoval';
}
return $serialized;
}
}
+24 -18
View File
@@ -34,10 +34,25 @@ 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.");
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."
);
}
public static function unrecognizedField($field)
@@ -45,23 +60,6 @@ class ORMException extends Exception
return new self("Unrecognized field: $field");
}
public static function removedEntityInCollectionDetected($entity, $assoc)
{
return new self("Removed entity of type " . get_class($entity)
. " detected in collection '" . $assoc->sourceFieldName . "' during flush."
. " Remove deleted entities from collections.");
}
public static function invalidEntityState($state)
{
return new self("Invalid entity state: $state.");
}
public static function detachedEntityCannotBeRemoved()
{
return new self("A detached entity can not be removed.");
}
public static function invalidFlushMode($mode)
{
return new self("'$mode' is an invalid flush mode.");
@@ -95,6 +93,14 @@ class ORMException extends Exception
);
}
public static function invalidFindByInverseAssociation($entityName, $associationFieldName)
{
return new self(
"You cannot search for the association field '".$entityName."#".$associationFieldName."', ".
"because it is the inverse side of an association. Find methods only work on owning side associations."
);
}
public static function invalidResultCacheDriver() {
return new self("Invalid result cache driver; it must implement \Doctrine\Common\Cache\Cache.");
}
+169 -138
View File
@@ -19,7 +19,7 @@
namespace Doctrine\ORM;
use Doctrine\ORM\Mapping\AssociationMapping,
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\Common\Collections\Collection,
Closure;
@@ -32,12 +32,11 @@ use Doctrine\ORM\Mapping\AssociationMapping,
* Similarly, if you remove entities from a collection that is part of a one-many
* mapping this will only result in the nulling out of the foreign keys on flush.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @since 2.0
* @version $Revision: 4930 $
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @todo Design for inheritance to allow custom implementations?
*/
final class PersistentCollection implements Collection
{
@@ -47,29 +46,29 @@ final class PersistentCollection implements Collection
*
* @var array
*/
private $_snapshot = array();
private $snapshot = array();
/**
* The entity that owns this collection.
*
* @var object
*/
private $_owner;
private $owner;
/**
* The association mapping the collection belongs to.
* This is currently either a OneToManyMapping or a ManyToManyMapping.
*
* @var Doctrine\ORM\Mapping\AssociationMapping
* @var array
*/
private $_association;
private $association;
/**
* The EntityManager that manages the persistence of the collection.
*
* @var Doctrine\ORM\EntityManager
*/
private $_em;
private $em;
/**
* The name of the field on the target entities that points to the owner
@@ -77,12 +76,12 @@ final class PersistentCollection implements Collection
*
* @var string
*/
private $_backRefFieldName;
private $backRefFieldName;
/**
* The class descriptor of the collection's entity type.
*/
private $_typeClass;
private $typeClass;
/**
* Whether the collection is dirty and needs to be synchronized with the database
@@ -90,21 +89,21 @@ final class PersistentCollection implements Collection
*
* @var boolean
*/
private $_isDirty = false;
private $isDirty = false;
/**
* Whether the collection has already been initialized.
*
* @var boolean
*/
private $_initialized = true;
private $initialized = true;
/**
* The wrapped Collection instance.
*
* @var Collection
*/
private $_coll;
private $coll;
/**
* Creates a new persistent collection.
@@ -115,9 +114,9 @@ final class PersistentCollection implements Collection
*/
public function __construct(EntityManager $em, $class, $coll)
{
$this->_coll = $coll;
$this->_em = $em;
$this->_typeClass = $class;
$this->coll = $coll;
$this->em = $em;
$this->typeClass = $class;
}
/**
@@ -128,11 +127,11 @@ final class PersistentCollection implements Collection
* @param object $entity
* @param AssociationMapping $assoc
*/
public function setOwner($entity, AssociationMapping $assoc)
public function setOwner($entity, array $assoc)
{
$this->_owner = $entity;
$this->_association = $assoc;
$this->_backRefFieldName = $assoc->inversedBy ?: $assoc->mappedBy;
$this->owner = $entity;
$this->association = $assoc;
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
}
/**
@@ -143,12 +142,12 @@ final class PersistentCollection implements Collection
*/
public function getOwner()
{
return $this->_owner;
return $this->owner;
}
public function getTypeClass()
{
return $this->_typeClass;
return $this->typeClass;
}
/**
@@ -160,17 +159,17 @@ final class PersistentCollection implements Collection
*/
public function hydrateAdd($element)
{
$this->_coll->add($element);
$this->coll->add($element);
// If _backRefFieldName is set and its a one-to-many association,
// we need to set the back reference.
if ($this->_backRefFieldName && $this->_association->isOneToMany()) {
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
// Set back reference to owner
$this->_typeClass->reflFields[$this->_backRefFieldName]
->setValue($element, $this->_owner);
$this->_em->getUnitOfWork()->setOriginalEntityProperty(
$this->typeClass->reflFields[$this->backRefFieldName]
->setValue($element, $this->owner);
$this->em->getUnitOfWork()->setOriginalEntityProperty(
spl_object_hash($element),
$this->_backRefFieldName,
$this->_owner);
$this->backRefFieldName,
$this->owner);
}
}
@@ -183,13 +182,13 @@ final class PersistentCollection implements Collection
*/
public function hydrateSet($key, $element)
{
$this->_coll->set($key, $element);
$this->coll->set($key, $element);
// If _backRefFieldName is set, then the association is bidirectional
// and we need to set the back reference.
if ($this->_backRefFieldName && $this->_association->isOneToMany()) {
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
// Set back reference to owner
$this->_typeClass->reflFields[$this->_backRefFieldName]
->setValue($element, $this->_owner);
$this->typeClass->reflFields[$this->backRefFieldName]
->setValue($element, $this->owner);
}
}
@@ -197,24 +196,24 @@ final class PersistentCollection implements Collection
* Initializes the collection by loading its contents from the database
* if the collection is not yet initialized.
*/
private function _initialize()
public function initialize()
{
if ( ! $this->_initialized) {
if ($this->_isDirty) {
if ( ! $this->initialized && $this->association) {
if ($this->isDirty) {
// Has NEW objects added through add(). Remember them.
$newObjects = $this->_coll->toArray();
$newObjects = $this->coll->toArray();
}
$this->_coll->clear();
$this->_association->load($this->_owner, $this, $this->_em);
$this->coll->clear();
$this->em->getUnitOfWork()->loadCollection($this);
$this->takeSnapshot();
// Reattach NEW objects added through add(), if any.
if (isset($newObjects)) {
foreach ($newObjects as $obj) {
$this->_coll->add($obj);
$this->coll->add($obj);
}
$this->_isDirty = true;
$this->isDirty = true;
}
$this->_initialized = true;
$this->initialized = true;
}
}
@@ -224,8 +223,8 @@ final class PersistentCollection implements Collection
*/
public function takeSnapshot()
{
$this->_snapshot = $this->_coll->toArray();
$this->_isDirty = false;
$this->snapshot = $this->coll->toArray();
$this->isDirty = false;
}
/**
@@ -236,7 +235,7 @@ final class PersistentCollection implements Collection
*/
public function getSnapshot()
{
return $this->_snapshot;
return $this->snapshot;
}
/**
@@ -247,7 +246,7 @@ final class PersistentCollection implements Collection
*/
public function getDeleteDiff()
{
return array_udiff_assoc($this->_snapshot, $this->_coll->toArray(),
return array_udiff_assoc($this->snapshot, $this->coll->toArray(),
function($a, $b) {return $a === $b ? 0 : 1;});
}
@@ -259,7 +258,7 @@ final class PersistentCollection implements Collection
*/
public function getInsertDiff()
{
return array_udiff_assoc($this->_coll->toArray(), $this->_snapshot,
return array_udiff_assoc($this->coll->toArray(), $this->snapshot,
function($a, $b) {return $a === $b ? 0 : 1;});
}
@@ -270,31 +269,32 @@ final class PersistentCollection implements Collection
*/
public function getMapping()
{
return $this->_association;
return $this->association;
}
/**
* Marks this collection as changed/dirty.
*/
private function _changed()
private function changed()
{
if ( ! $this->_isDirty) {
$this->_isDirty = true;
//if ($this->_isNotifyRequired) {
//$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this);
//}
if ( ! $this->isDirty) {
$this->isDirty = true;
if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY &&
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
}
}
/**
* Gets a boolean flag indicating whether this colleciton is dirty which means
* Gets a boolean flag indicating whether this collection is dirty which means
* its state needs to be synchronized with the database.
*
* @return boolean TRUE if the collection is dirty, FALSE otherwise.
*/
public function isDirty()
{
return $this->_isDirty;
return $this->isDirty;
}
/**
@@ -304,7 +304,7 @@ final class PersistentCollection implements Collection
*/
public function setDirty($dirty)
{
$this->_isDirty = $dirty;
$this->isDirty = $dirty;
}
/**
@@ -314,7 +314,7 @@ final class PersistentCollection implements Collection
*/
public function setInitialized($bool)
{
$this->_initialized = $bool;
$this->initialized = $bool;
}
/**
@@ -324,21 +324,21 @@ final class PersistentCollection implements Collection
*/
public function isInitialized()
{
return $this->_initialized;
return $this->initialized;
}
/** {@inheritdoc} */
public function first()
{
$this->_initialize();
return $this->_coll->first();
$this->initialize();
return $this->coll->first();
}
/** {@inheritdoc} */
public function last()
{
$this->_initialize();
return $this->_coll->last();
$this->initialize();
return $this->coll->last();
}
/**
@@ -350,13 +350,13 @@ final class PersistentCollection implements Collection
// and the collection is not initialized and orphanRemoval is
// not used we can issue a straight SQL delete/update on the
// association (table). Without initializing the collection.
$this->_initialize();
$removed = $this->_coll->remove($key);
$this->initialize();
$removed = $this->coll->remove($key);
if ($removed) {
$this->_changed();
if ($this->_association !== null && $this->_association->isOneToMany() &&
$this->_association->orphanRemoval) {
$this->_em->getUnitOfWork()->scheduleOrphanRemoval($removed);
$this->changed();
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
}
@@ -373,15 +373,21 @@ final class PersistentCollection implements Collection
// if the collection is not initialized, we could issue a straight
// SQL DELETE/UPDATE on the association (table) without initializing
// the collection.
/*if ( ! $this->_initialized) {
$this->_em->getUnitOfWork()->getCollectionPersister($this->_association)
/*if ( ! $this->initialized) {
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
->deleteRows($this, $element);
}*/
$this->_initialize();
$result = $this->_coll->removeElement($element);
$this->_changed();
return $result;
$this->initialize();
$removed = $this->coll->removeElement($element);
if ($removed) {
$this->changed();
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
}
return $removed;
}
/**
@@ -389,8 +395,8 @@ final class PersistentCollection implements Collection
*/
public function containsKey($key)
{
$this->_initialize();
return $this->_coll->containsKey($key);
$this->initialize();
return $this->coll->containsKey($key);
}
/**
@@ -398,25 +404,15 @@ final class PersistentCollection implements Collection
*/
public function contains($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;
}*/
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);
}
$this->_initialize();
return $this->_coll->contains($element);
$this->initialize();
return $this->coll->contains($element);
}
/**
@@ -424,8 +420,8 @@ final class PersistentCollection implements Collection
*/
public function exists(Closure $p)
{
$this->_initialize();
return $this->_coll->exists($p);
$this->initialize();
return $this->coll->exists($p);
}
/**
@@ -433,8 +429,8 @@ final class PersistentCollection implements Collection
*/
public function indexOf($element)
{
$this->_initialize();
return $this->_coll->indexOf($element);
$this->initialize();
return $this->coll->indexOf($element);
}
/**
@@ -442,8 +438,8 @@ final class PersistentCollection implements Collection
*/
public function get($key)
{
$this->_initialize();
return $this->_coll->get($key);
$this->initialize();
return $this->coll->get($key);
}
/**
@@ -451,8 +447,8 @@ final class PersistentCollection implements Collection
*/
public function getKeys()
{
$this->_initialize();
return $this->_coll->getKeys();
$this->initialize();
return $this->coll->getKeys();
}
/**
@@ -460,8 +456,8 @@ final class PersistentCollection implements Collection
*/
public function getValues()
{
$this->_initialize();
return $this->_coll->getValues();
$this->initialize();
return $this->coll->getValues();
}
/**
@@ -469,8 +465,14 @@ final class PersistentCollection implements Collection
*/
public function count()
{
$this->_initialize();
return $this->_coll->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();
}
/**
@@ -478,9 +480,9 @@ final class PersistentCollection implements Collection
*/
public function set($key, $value)
{
$this->_initialize();
$this->_coll->set($key, $value);
$this->_changed();
$this->initialize();
$this->coll->set($key, $value);
$this->changed();
}
/**
@@ -488,8 +490,8 @@ final class PersistentCollection implements Collection
*/
public function add($value)
{
$this->_coll->add($value);
$this->_changed();
$this->coll->add($value);
$this->changed();
return true;
}
@@ -498,8 +500,8 @@ final class PersistentCollection implements Collection
*/
public function isEmpty()
{
$this->_initialize();
return $this->_coll->isEmpty();
$this->initialize();
return $this->coll->isEmpty();
}
/**
@@ -507,8 +509,8 @@ final class PersistentCollection implements Collection
*/
public function getIterator()
{
$this->_initialize();
return $this->_coll->getIterator();
$this->initialize();
return $this->coll->getIterator();
}
/**
@@ -516,8 +518,8 @@ final class PersistentCollection implements Collection
*/
public function map(Closure $func)
{
$this->_initialize();
return $this->_coll->map($func);
$this->initialize();
return $this->coll->map($func);
}
/**
@@ -525,8 +527,8 @@ final class PersistentCollection implements Collection
*/
public function filter(Closure $p)
{
$this->_initialize();
return $this->_coll->filter($p);
$this->initialize();
return $this->coll->filter($p);
}
/**
@@ -534,8 +536,8 @@ final class PersistentCollection implements Collection
*/
public function forAll(Closure $p)
{
$this->_initialize();
return $this->_coll->forAll($p);
$this->initialize();
return $this->coll->forAll($p);
}
/**
@@ -543,8 +545,8 @@ final class PersistentCollection implements Collection
*/
public function partition(Closure $p)
{
$this->_initialize();
return $this->_coll->partition($p);
$this->initialize();
return $this->coll->partition($p);
}
/**
@@ -552,8 +554,8 @@ final class PersistentCollection implements Collection
*/
public function toArray()
{
$this->_initialize();
return $this->_coll->toArray();
$this->initialize();
return $this->coll->toArray();
}
/**
@@ -561,14 +563,20 @@ final class PersistentCollection implements Collection
*/
public function clear()
{
$this->_initialize();
$result = $this->_coll->clear();
if ($this->_association->isOwningSide) {
$this->_changed();
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
if ($this->initialized && $this->isEmpty()) {
return;
}
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
foreach ($this->coll as $element) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
}
$this->coll->clear();
if ($this->association['isOwningSide']) {
$this->changed();
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
$this->takeSnapshot();
}
return $result;
}
/**
@@ -580,7 +588,7 @@ final class PersistentCollection implements Collection
*/
public function __sleep()
{
return array('_coll');
return array('coll', 'initialized');
}
/* ArrayAccess implementation */
@@ -623,7 +631,7 @@ final class PersistentCollection implements Collection
public function key()
{
return $this->_coll->key();
return $this->coll->key();
}
/**
@@ -631,7 +639,7 @@ final class PersistentCollection implements Collection
*/
public function current()
{
return $this->_coll->current();
return $this->coll->current();
}
/**
@@ -639,7 +647,7 @@ final class PersistentCollection implements Collection
*/
public function next()
{
return $this->_coll->next();
return $this->coll->next();
}
/**
@@ -647,6 +655,29 @@ final class PersistentCollection implements Collection
*/
public function unwrap()
{
return $this->_coll;
return $this->coll;
}
/**
* Extract a slice of $length elements starting at position $offset from the Collection.
*
* If $length is null it returns all elements from $offset to the end of the Collection.
* Keys have to be preserved by this method. Calling this method will only return the
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @param int $offset
* @param int $length
* @return array
*/
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);
}
}
@@ -64,7 +64,8 @@ abstract class AbstractCollectionPersister
*/
public function delete(PersistentCollection $coll)
{
if ( ! $coll->getMapping()->isOwningSide) {
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$sql = $this->_getDeleteSQL($coll);
@@ -94,7 +95,8 @@ abstract class AbstractCollectionPersister
*/
public function update(PersistentCollection $coll)
{
if ( ! $coll->getMapping()->isOwningSide) {
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$this->deleteRows($coll);
@@ -123,6 +125,31 @@ 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.
*
@@ -28,17 +28,11 @@ 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 names that declare the field the column is mapped to.
*
* @var array
*/
private $_declaringClassMap = array();
/**
* {@inheritdoc}
*/
@@ -62,42 +56,22 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
/**
* {@inheritdoc}
*/
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];
$data[$field] = Type::getType($class->fieldMappings[$field]['type'])
->convertToPHPValue($value, $this->_platform);
}
} else {
$data[$realColumnName] = $value;
}
}
return array($entityName, $data);
}
/**
* {@inheritdoc}
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class)
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
$this->_declaringClassMap[$columnAlias] = $class;
}
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
return "$sql AS $columnAlias";
}
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
{
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
return $tableAlias . ".$joinColumnName AS $columnAlias";
}
}
File diff suppressed because it is too large Load Diff
@@ -20,13 +20,16 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ManyToManyMapping;
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\LockMode,
Doctrine\ORM\Query\ResultSetMapping;
/**
* 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
*/
@@ -35,9 +38,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* Map that maps column names to the table names that own them.
* This is mainly a temporary cache, used during a single request.
*
* @var array
*/
private $_owningTableMap = array();
/**
* Map of table to quoted table names.
*
* @var array
*/
private $_quotedTableMap = array();
/**
* {@inheritdoc}
*/
@@ -74,18 +86,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/
public function getOwningTable($fieldName)
{
if ( ! isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName]->inherited)) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->associationMappings[$fieldName]->inherited
)->table['name'];
if (!isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->fieldMappings[$fieldName]['inherited']
)->table['name'];
$cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']);
} else {
$this->_owningTableMap[$fieldName] = $this->_class->table['name'];
$cm = $this->_class;
}
$this->_owningTableMap[$fieldName] = $cm->table['name'];
$this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform);
}
return $this->_owningTableMap[$fieldName];
@@ -100,10 +110,6 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
return;
}
if ($this->_class->isVersioned) {
$versionedClass = $this->_getVersionedClassMetadata();
}
$postInsertIds = array();
$idGen = $this->_class->idGenerator;
$isPostInsertId = $idGen->isPostInsertGenerator();
@@ -169,8 +175,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$stmt->closeCursor();
}
if (isset($versionedClass)) {
$this->_assignDefaultVersionValue($versionedClass, $entity, $id);
if ($this->_class->isVersioned) {
$this->assignDefaultVersionValue($entity, $id);
}
$this->_queuedInserts = array();
@@ -186,17 +192,21 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$updateData = $this->_prepareUpdateData($entity);
if ($isVersioned = $this->_class->isVersioned) {
$versionedTable = $this->_getVersionedClassMetadata()->table['name'];
$versionedClass = $this->_getVersionedClassMetadata();
$versionedTable = $versionedClass->table['name'];
}
if ($updateData) {
foreach ($updateData as $tableName => $data) {
$this->_updateTable($entity, $tableName, $data, $isVersioned && $versionedTable == $tableName);
$this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName);
}
// Make sure the table with the version column is updated even if no columns on that
// table were affected.
if ($isVersioned && ! isset($updateData[$versionedTable])) {
$this->_updateTable($entity, $versionedTable, array(), true);
$this->_updateTable($entity, $versionedClass->getQuotedTableName($this->_platform), array(), true);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->assignDefaultVersionValue($entity, $id);
}
}
}
@@ -206,10 +216,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/
public function delete($entity)
{
$id = array_combine(
$this->_class->getIdentifierColumnNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
);
$identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->deleteJoinTableRecords($identifier);
$id = array_combine($this->_class->getIdentifierColumnNames(), $identifier);
// If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest.
@@ -228,13 +238,17 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* {@inheritdoc}
*/
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
{
$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) {
@@ -247,17 +261,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add foreign key columns
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
$tableAlias = $assoc2->inherited ?
$this->_getSQLTableAlias($assoc2->inherited)
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) {
$tableAlias = isset($assoc2['inherited']) ?
$this->_getSQLTableAlias($assoc2['inherited'])
: $baseTableAlias;
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ", $tableAlias.$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name
);
}
}
}
@@ -272,7 +284,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
$this->_rsm->setDiscriminatorColumn('r', $discrColumn);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
}
// INNER JOIN parent tables
@@ -304,14 +317,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add join columns (foreign keys)
foreach ($subClass->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne() && ! $assoc2->inherited) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE
&& ! isset($assoc2['inherited'])) {
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
);
}
}
}
@@ -326,36 +338,56 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
}
}
$joinSql .= $assoc != null && $assoc->isManyToMany() ?
$joinSql .= $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBySql = '';
if ($assoc != null && isset($assoc->orderBy)) {
$orderBySql = $this->_getCollectionOrderBySQL($assoc->orderBy, $baseTableAlias);
}
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : '';
if ($this->_selectColumnListSql === null) {
$this->_selectColumnListSql = $columnList;
}
return 'SELECT ' . $this->_selectColumnListSql
$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
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias
. $joinSql
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset)
. $lockSql;
}
/**
* Lock all rows of this entity matching the given criteria with the specified pessimistic lock mode
* Get the FROM and optionally JOIN conditions to lock the entity managed by this persister.
*
* @param array $criteria
* @param int $lockMode
* @return void
* @return string
*/
public function lock(array $criteria, $lockMode)
public function getLockTablesSql()
{
throw new \BadMethodCallException("lock() is not yet supported for JoinedSubclassPersister");
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// INNER JOIN parent tables
$joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
}
}
return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias . $joinSql;
}
/* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */
@@ -372,15 +404,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($this->_class->reflFields as $name => $field) {
if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id'])
|| isset($this->_class->associationMappings[$name]->inherited)
|| isset($this->_class->associationMappings[$name]['inherited'])
|| ($this->_class->isVersioned && $this->_class->versionField == $name)) {
continue;
}
if (isset($this->_class->associationMappings[$name])) {
$assoc = $this->_class->associationMappings[$name];
if ($assoc->isOneToOne() && $assoc->isOwningSide) {
foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {
if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) {
foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) {
$columns[] = $sourceCol;
}
}
@@ -397,4 +429,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
return $columns;
}
/**
* {@inheritdoc}
*/
protected function assignDefaultVersionValue($entity, $id)
{
$value = $this->fetchVersionValue($this->_getVersionedClassMetadata(), $id);
$this->_class->setFieldValue($entity, $this->_class->versionField, $value);
}
}
@@ -21,7 +21,8 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
/**
* Persister for many-to-many collections.
@@ -39,8 +40,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
protected function _getDeleteRowSQL(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->joinTable;
$columns = $mapping->joinTableColumns;
$joinTable = $mapping['joinTable'];
$columns = $mapping['joinTableColumns'];
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
}
@@ -74,8 +75,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
protected function _getInsertRowSQL(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->joinTable;
$columns = $mapping->joinTableColumns;
$joinTable = $mapping['joinTable'];
$columns = $mapping['joinTableColumns'];
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')'
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
}
@@ -104,7 +105,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
{
$params = array();
$mapping = $coll->getMapping();
$isComposite = count($mapping->joinTableColumns) > 2;
$isComposite = count($mapping['joinTableColumns']) > 2;
$identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner());
$identifier2 = $this->_uow->getEntityIdentifier($element);
@@ -114,16 +115,24 @@ class ManyToManyPersister extends AbstractCollectionPersister
$class2 = $coll->getTypeClass();
}
foreach ($mapping->joinTableColumns as $joinTableColumn) {
if (isset($mapping->relationToSourceKeyColumns[$joinTableColumn])) {
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
if ($isComposite) {
$params[] = $identifier1[$class1->fieldNames[$mapping->relationToSourceKeyColumns[$joinTableColumn]]];
if ($class1->containsForeignIdentifier) {
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
} else {
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
}
} else {
$params[] = array_pop($identifier1);
}
} else {
if ($isComposite) {
$params[] = $identifier2[$class2->fieldNames[$mapping->relationToTargetKeyColumns[$joinTableColumn]]];
if ($class2->containsForeignIdentifier) {
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
} else {
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
}
} else {
$params[] = array_pop($identifier2);
}
@@ -141,9 +150,9 @@ class ManyToManyPersister extends AbstractCollectionPersister
protected function _getDeleteSQL(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$joinTable = $mapping->joinTable;
$joinTable = $mapping['joinTable'];
$whereClause = '';
foreach ($mapping->relationToSourceKeyColumns as $relationColumn => $srcColumn) {
foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) {
if ($whereClause !== '') $whereClause .= ' AND ';
$whereClause .= "$relationColumn = ?";
}
@@ -162,9 +171,9 @@ class ManyToManyPersister extends AbstractCollectionPersister
$params = array();
$mapping = $coll->getMapping();
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
if (count($mapping->relationToSourceKeyColumns) > 1) {
if (count($mapping['relationToSourceKeyColumns']) > 1) {
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
foreach ($mapping->relationToSourceKeyColumns as $relColumn => $srcColumn) {
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
}
} else {
@@ -173,4 +182,119 @@ 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);
}
}
@@ -21,7 +21,8 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\PersistentCollection,
Doctrine\ORM\UnitOfWork;
/**
* Persister for one-to-many collections.
@@ -50,7 +51,7 @@ class OneToManyPersister extends AbstractCollectionPersister
$targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName());
$table = $targetClass->getTableName();
$ownerMapping = $targetClass->getAssociationMapping($mapping->mappedBy);
$ownerMapping = $targetClass->getAssociationMapping($mapping['mappedBy']);
$setClause = '';
foreach ($ownerMapping->sourceToTargetKeyColumns as $sourceCol => $targetCol) {
@@ -116,4 +117,67 @@ class OneToManyPersister extends AbstractCollectionPersister
*/
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
{}
/**
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
$params = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
$where = '';
foreach ($class->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
if ($where != '') {
$where .= ' AND ';
}
$where .= $joinColumn['name'] . " = ?";
if ($class->containsForeignIdentifier) {
$params[] = $id[$class->getFieldForColumn($joinColumn['referencedColumnName'])];
} else {
$params[] = $id[$class->fieldNames[$joinColumn['referencedColumnName']]];
}
}
$sql = "SELECT count(*) FROM " . $class->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));
}
}
@@ -26,6 +26,7 @@ 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
*/
@@ -48,7 +49,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
$this->_rsm->setDiscriminatorColumn('r', $discrColumn);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Append subclass columns
foreach ($this->_class->subClasses as $subClassName) {
@@ -61,14 +63,12 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
}
// Foreign key columns
foreach ($subClass->associationMappings as $assoc) {
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! $assoc->inherited) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name
);
}
}
}
@@ -88,9 +88,9 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
}
/** {@inheritdoc} */
protected function _getSQLTableAlias($className)
protected function _getSQLTableAlias($className, $assocName = '')
{
return parent::_getSQLTableAlias($this->_class->rootEntityName);
return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName);
}
/** {@inheritdoc} */
@@ -100,7 +100,11 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
// Append discriminator condition
if ($conditionSql) $conditionSql .= ' AND ';
$values = array($this->_conn->quote($this->_class->discriminatorValue));
$values = array();
if ($this->_class->discriminatorValue !== null) { // discriminators can be 0
$values[] = $this->_conn->quote($this->_class->discriminatorValue);
}
$discrValues = array_flip($this->_class->discriminatorMap);
foreach ($this->_class->subClasses as $subclassName) {
$values[] = $this->_conn->quote($discrValues[$subclassName]);
+38 -16
View File
@@ -77,9 +77,11 @@ class ProxyFactory
$proxyClassName = str_replace('\\', '', $className) . 'Proxy';
$fqn = $this->_proxyNamespace . '\\' . $proxyClassName;
if ($this->_autoGenerate && ! class_exists($fqn, false)) {
if (! class_exists($fqn, false)) {
$fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php';
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
if ($this->_autoGenerate) {
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
}
require $fileName;
}
@@ -105,6 +107,11 @@ class ProxyFactory
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass) {
continue;
}
$proxyClassName = str_replace('\\', '', $class->name) . 'Proxy';
$proxyFileName = $proxyDir . $proxyClassName . '.php';
$this->_generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate);
@@ -123,11 +130,12 @@ class ProxyFactory
{
$methods = $this->_generateMethods($class);
$sleepImpl = $this->_generateSleep($class);
$cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive
$placeholders = array(
'<namespace>',
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
'<methods>', '<sleepImpl>', '<cloneImpl>'
);
if(substr($class->name, 0, 1) == "\\") {
@@ -139,12 +147,12 @@ class ProxyFactory
$replacements = array(
$this->_proxyNamespace,
$proxyClassName, $className,
$methods, $sleepImpl
$methods, $sleepImpl, $cloneImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
file_put_contents($fileName, $file, LOCK_EX);
}
/**
@@ -159,7 +167,7 @@ class ProxyFactory
foreach ($class->reflClass->getMethods() as $method) {
/* @var $method ReflectionMethod */
if ($method->isConstructor() || strtolower($method->getName()) == "__sleep") {
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone"))) {
continue;
}
@@ -201,7 +209,7 @@ class ProxyFactory
$methods .= $parameterString . ')';
$methods .= PHP_EOL . ' {' . PHP_EOL;
$methods .= ' $this->_load();' . PHP_EOL;
$methods .= ' $this->__load();' . PHP_EOL;
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= PHP_EOL . ' }' . PHP_EOL;
}
@@ -221,9 +229,9 @@ class ProxyFactory
$sleepImpl = '';
if ($class->reflClass->hasMethod('__sleep')) {
$sleepImpl .= 'return parent::__sleep();';
$sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());";
} else {
$sleepImpl .= 'return array(';
$sleepImpl .= "return array('__isInitialized__', ";
$first = true;
foreach ($class->getReflectionProperties() as $name => $prop) {
@@ -261,26 +269,40 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
$this->_entityPersister = $entityPersister;
$this->_identifier = $identifier;
}
private function _load()
/** @private */
public function __load()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}
unset($this->_entityPersister);
unset($this->_identifier);
unset($this->_entityPersister, $this->_identifier);
}
}
<methods>
public function __sleep()
{
if (!$this->__isInitialized__) {
throw new \RuntimeException("Not fully loaded proxy can not be serialized.");
}
<sleepImpl>
}
public function __clone()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
$class = $this->_entityPersister->getClassMetadata();
$original = $this->_entityPersister->load($this->_identifier);
if ($original === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
}
foreach ($class->reflFields AS $field => $reflProperty) {
$reflProperty->setValue($this, $reflProperty->getValue($original));
}
unset($this->_entityPersister, $this->_identifier);
}
<cloneImpl>
}
}';
}
+23 -2
View File
@@ -242,9 +242,19 @@ final class Query extends AbstractQuery
}
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
$values = $this->_em->getUnitOfWork()->getEntityIdentifier($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];
$sqlParams += array_combine((array)$sqlPositions, $values);
$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;
@@ -543,4 +553,15 @@ final class Query extends AbstractQuery
'&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT'
);
}
/**
* Cleanup Query resource when clone is called.
*
* @return void
*/
public function __clone()
{
parent::__clone();
$this->_state = self::STATE_DIRTY;
}
}
@@ -0,0 +1,47 @@
<?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);
}
}
@@ -1,7 +1,5 @@
<?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
@@ -27,7 +25,6 @@ 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>
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class AbsFunction extends FunctionNode
{
@@ -53,8 +51,6 @@ class AbsFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ConcatFunction extends FunctionNode
{
@@ -56,8 +54,6 @@ class ConcatFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class CurrentDateFunction extends FunctionNode
{
@@ -49,8 +47,6 @@ class CurrentDateFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class CurrentTimeFunction extends FunctionNode
{
@@ -49,8 +47,6 @@ class CurrentTimeFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
@@ -1,7 +1,20 @@
<?php
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
/*
* 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;
@@ -11,7 +24,13 @@ use Doctrine\ORM\Query\Lexer;
/**
* "CURRENT_TIMESTAMP"
*
* @author robo
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class CurrentTimestampFunction extends FunctionNode
{
@@ -28,8 +47,6 @@ class CurrentTimestampFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
@@ -0,0 +1,71 @@
<?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);
}
}
@@ -0,0 +1,58 @@
<?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);
}
}
@@ -0,0 +1,58 @@
<?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.');
}
}
}
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\AST\Node;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
abstract class FunctionNode extends Node
{
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LengthFunction extends FunctionNode
{
@@ -43,8 +41,9 @@ class LengthFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'LENGTH(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**
@@ -52,8 +51,6 @@ class LengthFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LocateFunction extends FunctionNode
{
@@ -61,8 +59,6 @@ class LocateFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -72,6 +68,7 @@ class LocateFunction extends FunctionNode
$this->secondStringPrimary = $parser->StringPrimary();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LowerFunction extends FunctionNode
{
@@ -43,8 +41,9 @@ class LowerFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'LOWER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**
@@ -52,8 +51,6 @@ class LowerFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ModFunction extends FunctionNode
{
@@ -44,12 +42,10 @@ class ModFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'MOD('
. $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression)
. ', '
. $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression)
. ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),
$sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression)
);
}
/**
@@ -57,8 +53,6 @@ class ModFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_MOD);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,7 +27,6 @@ use Doctrine\ORM\Query\Lexer;
* @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>
@@ -45,46 +42,66 @@ class SizeFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
$dqlAlias = $this->collectionPathExpression->identificationVariable;
$parts = $this->collectionPathExpression->parts;
$assocField = array_pop($parts);
$qComp = $sqlWalker->getQueryComponent(implode('.', array_merge((array) $dqlAlias, $parts)));
$assoc = $qComp['metadata']->associationMappings[$assocField];
$sql = '';
$assocField = $this->collectionPathExpression->field;
if ($assoc->isOneToMany()) {
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName);
$targetAssoc = $targetClass->associationMappings[$assoc->mappedBy];
$targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->table['name']);
$sourceTableAlias = $sqlWalker->getSqlTableAlias($qComp['metadata']->table['name'], $dqlAlias);
$whereSql = '';
$qComp = $sqlWalker->getQueryComponent($dqlAlias);
$class = $qComp['metadata'];
$assoc = $class->associationMappings[$assocField];
$sql = 'SELECT COUNT(*) FROM ';
foreach ($targetAssoc->targetToSourceKeyColumns as $targetKeyColumn => $sourceKeyColumn) {
$whereSql .= (($whereSql == '') ? ' WHERE ' : ' AND ')
. $targetTableAlias . '.' . $sourceKeyColumn . ' = '
. $sourceTableAlias . '.' . $targetKeyColumn;
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);
$sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
$first = true;
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $targetTableAlias . '.' . $sourceColumn
. ' = '
. $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $platform);
}
} else { // many-to-many
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$tableName = $targetClass->table['name'];
} else if ($assoc->isManyToMany()) {
$targetTableAlias = $sqlWalker->getSqlTableAlias($assoc->joinTable['name']);
$sourceTableAlias = $sqlWalker->getSqlTableAlias($qComp['metadata']->table['name'], $dqlAlias);
$whereSql = '';
$owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
$joinTable = $owningAssoc['joinTable'];
foreach ($assoc->relationToSourceKeyColumns as $targetKeyColumn => $sourceKeyColumn) {
$whereSql .= (($whereSql == '') ? ' WHERE ' : ' AND ')
. $targetTableAlias . '.' . $targetKeyColumn . ' = '
. $sourceTableAlias . '.' . $sourceKeyColumn;
// SQL table aliases
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
// join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';
$joinColumns = $assoc['isOwningSide']
? $joinTable['joinColumns']
: $joinTable['inverseJoinColumns'];
$first = true;
foreach ($joinColumns as $joinColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sourceColumnName = $class->getQuotedColumnName(
$class->fieldNames[$joinColumn['referencedColumnName']], $platform
);
$sql .= $joinTableAlias . '.' . $joinColumn['name']
. ' = '
. $sourceTableAlias . '.' . $sourceColumnName;
}
$tableName = $assoc->joinTable['name'];
}
return '(SELECT COUNT(*) FROM ' . $tableName . ' ' . $targetTableAlias . $whereSql . ')';
return '(' . $sql . ')';
}
/**
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class SqrtFunction extends FunctionNode
{
@@ -52,8 +50,6 @@ class SqrtFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class SubstringFunction extends FunctionNode
{
@@ -62,8 +60,6 @@ class SubstringFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -73,6 +69,7 @@ class SubstringFunction extends FunctionNode
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
@@ -1,7 +1,5 @@
<?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
@@ -30,10 +28,10 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class TrimFunction extends FunctionNode
{
@@ -1,7 +1,5 @@
<?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
@@ -29,10 +27,10 @@ use Doctrine\ORM\Query\Lexer;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class UpperFunction extends FunctionNode
{
@@ -43,8 +41,9 @@ class UpperFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'UPPER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**
@@ -52,8 +51,6 @@ class UpperFunction extends FunctionNode
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
@@ -0,0 +1,49 @@
<?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;
/**
* InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (AbstractSchemaName | InputParameter)
*
* @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>
*/
class InstanceOfExpression extends Node
{
public $not;
public $identificationVariable;
public $value;
public function __construct($identVariable)
{
$this->identificationVariable = $identVariable;
}
public function dispatch($sqlWalker)
{
return $sqlWalker->walkInstanceOfExpression($this);
}
}
@@ -0,0 +1,49 @@
<?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);
}
}
@@ -23,11 +23,10 @@ namespace Doctrine\ORM\Query\AST;
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
* StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
* StateField ::= {EmbeddedClassStateField "."}* SimpleStateField
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
* SimpleStateFieldAssociationPathExpression ::= SingleValuedAssociationPathExpression "." StateField
*
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
@@ -43,13 +42,13 @@ class PathExpression extends Node
public $type;
public $expectedType;
public $identificationVariable;
public $parts;
public $field;
public function __construct($expectedType, $identificationVariable, array $parts)
public function __construct($expectedType, $identificationVariable, $field = null)
{
$this->expectedType = $expectedType;
$this->identificationVariable = $identificationVariable;
$this->parts = $parts;
$this->field = $field;
}
public function dispatch($walker)
+3 -4
View File
@@ -36,13 +36,12 @@ namespace Doctrine\ORM\Query\AST;
*/
class UpdateItem extends Node
{
public $identificationVariable;
public $field;
public $pathExpression;
public $newValue;
public function __construct($field, $newValue)
public function __construct($pathExpression, $newValue)
{
$this->field = $field;
$this->pathExpression = $pathExpression;
$this->newValue = $newValue;
}
@@ -63,9 +63,11 @@ 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);
@@ -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,11 +64,14 @@ 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)
@@ -84,8 +87,10 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$updateSql = 'UPDATE ' . $class->getQuotedTableName($platform) . ' SET ';
foreach ($updateItems as $updateItem) {
$field = $updateItem->field;
if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited'])) {
$field = $updateItem->pathExpression->field;
if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) ||
isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) {
$newValue = $updateItem->newValue;
if ( ! $affected) {
@@ -102,6 +107,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
if ($newValue instanceof AST\InputParameter) {
$paramKey = $newValue->name;
$this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey);
++$this->_numParametersInUpdateClause;
}
}
@@ -119,14 +125,17 @@ 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 = 'DROP TABLE ' . $tempTable;
}
+30 -3
View File
@@ -1,7 +1,5 @@
<?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
@@ -27,10 +25,10 @@ namespace Doctrine\ORM\Query;
* @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>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @todo Rename: ExpressionBuilder
*/
class Expr
@@ -435,9 +433,38 @@ class Expr
*/
public function notIn($x, $y)
{
if (is_array($y)) {
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
}
}
}
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.
*
+2 -2
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 Base
class Andx extends Composite
{
protected $_separator = ') AND (';
protected $_separator = ' AND ';
protected $_allowedClasses = array(
'Doctrine\ORM\Query\Expr\Comparison',
'Doctrine\ORM\Query\Expr\Func',
+1 -1
View File
@@ -39,7 +39,7 @@ abstract class Base
protected $_postSeparator = ')';
protected $_allowedClasses = array();
private $_parts = array();
protected $_parts = array();
public function __construct($args = array())
{
+53
View File
@@ -0,0 +1,53 @@
<?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[] = (is_object($part) && $part instanceof self && $part->count() > 1)
? $this->_preSeparator . ((string) $part) . $this->_postSeparator
: ((string) $part);
}
return implode($this->_separator, $components);
}
}
+9 -6
View File
@@ -45,20 +45,23 @@ class Join
private $_alias;
private $_conditionType;
private $_condition;
private $_indexBy;
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null)
public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = 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->_condition = $condition;
$this->_indexBy = $indexBy;
}
public function __toString()
{
return strtoupper($this->_joinType) . ' JOIN ' . $this->_join
. ($this->_alias ? ' ' . $this->_alias : '')
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '');
. ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '')
. ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
}
}
+2 -2
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 Base
class Orx extends Composite
{
protected $_separator = ') OR (';
protected $_separator = ' OR ';
protected $_allowedClasses = array(
'Doctrine\ORM\Query\Expr\Andx',
'Doctrine\ORM\Query\Expr\Comparison',
+3
View File
@@ -36,4 +36,7 @@ class Select extends Base
{
protected $_preSeparator = '';
protected $_postSeparator = '';
protected $_allowedClasses = array(
'Doctrine\ORM\Query\Expr\Func'
);
}
+52 -44
View File
@@ -61,47 +61,52 @@ class Lexer extends \Doctrine\Common\Lexer
const T_BETWEEN = 107;
const T_BOTH = 108;
const T_BY = 109;
const T_COUNT = 110;
const T_DELETE = 111;
const T_DESC = 112;
const T_DISTINCT = 113;
const T_EMPTY = 114;
const T_ESCAPE = 115;
const T_EXISTS = 116;
const T_FALSE = 117;
const T_FROM = 118;
const T_GROUP = 119;
const T_HAVING = 120;
const T_IN = 121;
const T_INDEX = 122;
const T_INNER = 123;
const T_IS = 124;
const T_JOIN = 125;
const T_LEADING = 126;
const T_LEFT = 127;
const T_LIKE = 128;
const T_MAX = 129;
const T_MEMBER = 130;
const T_MIN = 131;
const T_NOT = 132;
const T_NULL = 133;
const T_OF = 134;
const T_OR = 135;
const T_ORDER = 136;
const T_OUTER = 137;
const T_SELECT = 138;
const T_SET = 139;
const T_SIZE = 140;
const T_SOME = 141;
const T_SUM = 142;
const T_TRAILING = 143;
const T_TRUE = 144;
const T_UPDATE = 145;
const T_WHERE = 146;
const T_WITH = 147;
const T_PARTIAL = 148;
const T_MOD = 149;
const T_CASE = 110;
const T_COALESCE = 111;
const T_COUNT = 112;
const T_DELETE = 113;
const T_DESC = 114;
const T_DISTINCT = 115;
const T_EMPTY = 116;
const T_ESCAPE = 117;
const T_EXISTS = 118;
const T_FALSE = 119;
const T_FROM = 120;
const T_GROUP = 121;
const T_HAVING = 122;
const T_IN = 123;
const T_INDEX = 124;
const T_INNER = 125;
const T_INSTANCE = 126;
const T_IS = 127;
const T_JOIN = 128;
const T_LEADING = 129;
const T_LEFT = 130;
const T_LIKE = 131;
const T_MAX = 132;
const T_MEMBER = 133;
const T_MIN = 134;
const T_NOT = 135;
const T_NULL = 136;
const T_NULLIF = 137;
const T_OF = 138;
const T_OR = 139;
const T_ORDER = 140;
const T_OUTER = 141;
const T_SELECT = 142;
const T_SET = 143;
const T_SIZE = 144;
const T_SOME = 145;
const T_SUM = 146;
const T_TRAILING = 147;
const T_TRUE = 148;
const T_UPDATE = 149;
const T_WHEN = 150;
const T_WHERE = 151;
const T_WITH = 153;
const T_PARTIAL = 154;
const T_MOD = 155;
/**
* Creates a new query scanner object.
*
@@ -118,10 +123,10 @@ class Lexer extends \Doctrine\Common\Lexer
protected function getCatchablePatterns()
{
return array(
'[a-z_][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
"'(?:[^']|'')*'",
'\?[1-9][0-9]*|:[a-z][a-z0-9_]+'
'\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
);
}
@@ -136,7 +141,7 @@ class Lexer extends \Doctrine\Common\Lexer
/**
* @inheritdoc
*/
protected function _getType(&$value)
protected function getType(&$value)
{
$type = self::T_NONE;
@@ -152,12 +157,15 @@ class Lexer extends \Doctrine\Common\Lexer
return self::T_STRING;
} else if (ctype_alpha($value[0]) || $value[0] === '_') {
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
if (defined($name)) {
$type = constant($name);
if ($type > 100) {
return $type;
}
}
return self::T_IDENTIFIER;
} else if ($value[0] === '?' || $value[0] === ':') {
return self::T_INPUT_PARAMETER;
@@ -0,0 +1,72 @@
<?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;
}
}
+331 -200
View File
@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Query;
use Doctrine\ORM\Query;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
@@ -44,19 +45,22 @@ 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'
'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',
);
/** 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'
'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
);
/**
@@ -124,6 +128,11 @@ class Parser
*/
private $_customOutputWalker;
/**
* @var array
*/
private $_identVariableExpressions = array();
/**
* Creates a new query parser object.
*
@@ -225,7 +234,7 @@ class Parser
* If they match, updates the lookahead token; otherwise raises a syntax
* error.
*
* @param int|string token type or value
* @param int token type
* @return void
* @throws QueryException If the tokens dont match.
*/
@@ -271,6 +280,9 @@ class Parser
{
$AST = $this->getAST();
$this->fixIdentificationVariableOrder($AST);
$this->assertSelectEntityRootAliasRequirement();
if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
$this->_customTreeWalkers = $customWalkers;
}
@@ -311,6 +323,46 @@ class Parser
return $this->_parserResult;
}
private function assertSelectEntityRootAliasRequirement()
{
if ( count($this->_identVariableExpressions) > 0) {
$foundRootEntity = false;
foreach ($this->_identVariableExpressions AS $dqlAlias => $expr) {
if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) {
$foundRootEntity = true;
}
}
if (!$foundRootEntity) {
$this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
}
}
}
/**
* Fix order of identification variables.
*
* They have to appear in the select clause in the same order as the
* declarations (from ... x join ... y join ... z ...) appear in the query
* as the hydration process relies on that order for proper operation.
*
* @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
* @return void
*/
private function fixIdentificationVariableOrder($AST)
{
if ( count($this->_identVariableExpressions) > 1) {
foreach ($this->_queryComponents as $dqlAlias => $qComp) {
if (isset($this->_identVariableExpressions[$dqlAlias])) {
$expr = $this->_identVariableExpressions[$dqlAlias];
$key = array_search($expr, $AST->selectClause->selectExpressions);
unset($AST->selectClause->selectExpressions[$key]);
$AST->selectClause->selectExpressions[] = $expr;
}
}
}
}
/**
* Generates a new syntax error.
@@ -396,6 +448,41 @@ class Parser
return $peek;
}
/**
* Peek beyond the matched closing parenthesis and return the first token after that one.
*
* @return array
*/
private function _peekBeyondClosingParenthesis()
{
$token = $this->_lexer->peek();
$numUnmatched = 1;
while ($numUnmatched > 0 && $token !== null) {
if ($token['value'] == ')') {
--$numUnmatched;
} else if ($token['value'] == '(') {
++$numUnmatched;
}
$token = $this->_lexer->peek();
}
$this->_lexer->resetPeek();
return $token;
}
/**
* Checks if the given token indicates a mathematical operator.
*
* @return boolean TRUE if the token is a mathematical operator, FALSE otherwise.
*/
private function _isMathOperator($token)
{
return in_array($token['value'], array("+", "-", "/", "*"));
}
/**
* Checks if the next-next (after lookahead) token starts a function.
*
@@ -450,7 +537,7 @@ class Parser
}
/**
* Validates that the given <tt>IdentificationVariable</tt> is a semantically correct.
* Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
@@ -485,11 +572,18 @@ class Parser
}
}
/**
* Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
*/
private function _processDeferredPartialObjectExpressions()
{
foreach ($this->_deferredPartialObjectExpressions as $deferredItem) {
$expr = $deferredItem['expression'];
$class = $this->_queryComponents[$expr->identificationVariable]['metadata'];
foreach ($expr->partialFieldSet as $field) {
if ( ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
@@ -498,6 +592,7 @@ class Parser
);
}
}
if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
$this->semanticalError(
"The partial field selection of class " . $class->name . " must contain the identifier.",
@@ -508,7 +603,7 @@ class Parser
}
/**
* Validates that the given <tt>ResultVariable</tt> is a semantically correct.
* Validates that the given <tt>ResultVariable</tt> is semantically correct.
* It must exist in query components list.
*
* @return void
@@ -544,13 +639,13 @@ class Parser
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct for grammar rules:
* Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:
*
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
* StateFieldPathExpression ::= IdentificationVariable "." StateField | SingleValuedAssociationPathExpression "." StateField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
* StateFieldPathExpression ::= IdentificationVariable "." StateField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
*
* @param array $deferredItem
* @param mixed $AST
@@ -561,93 +656,31 @@ class Parser
$pathExpression = $deferredItem['expression'];
$qComp = $this->_queryComponents[$pathExpression->identificationVariable];
$numParts = count($pathExpression->parts);
$class = $qComp['metadata'];
if ($numParts == 0) {
$pathExpression->parts = array($qComp['metadata']->identifier[0]);
$numParts++;
if (($field = $pathExpression->field) === null) {
$field = $pathExpression->field = $class->identifier[0];
}
$parts = $pathExpression->parts;
$aliasIdentificationVariable = $pathExpression->identificationVariable;
$parentField = $pathExpression->identificationVariable;
$class = $qComp['metadata'];
$fieldType = null;
$curIndex = 0;
// Check if field or association exists
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field or association named ' . $field,
$deferredItem['token']
);
}
foreach ($parts as $field) {
// Check if it is not in a state field
if ($fieldType & AST\PathExpression::TYPE_STATE_FIELD) {
$this->semanticalError(
'Cannot navigate through state field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
);
}
if (isset($class->fieldMappings[$field])) {
$fieldType = AST\PathExpression::TYPE_STATE_FIELD;
} else {
$assoc = $class->associationMappings[$field];
$class = $this->_em->getClassMetadata($assoc['targetEntity']);
// Check if it is not a collection field
if ($fieldType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
$this->semanticalError(
'Cannot navigate through collection field named ' . $field . ' on ' . $parentField,
$deferredItem['token']
);
}
// Check if field or association exists
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field or association named ' . $field,
$deferredItem['token']
);
}
$parentField = $field;
if (isset($class->fieldMappings[$field])) {
$fieldType = AST\PathExpression::TYPE_STATE_FIELD;
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$fieldType = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
} else {
$assoc = $class->associationMappings[$field];
$class = $this->_em->getClassMetadata($assoc->targetEntityName);
if (
($curIndex != $numParts - 1) &&
! isset($this->_queryComponents[$aliasIdentificationVariable . '.' . $field])
) {
// Building queryComponent
$joinQueryComponent = array(
'metadata' => $class,
'parent' => $aliasIdentificationVariable,
'relation' => $assoc,
'map' => null,
'nestingLevel' => $this->_nestingLevel,
'token' => $deferredItem['token'],
);
// Create AST node
$joinVariableDeclaration = new AST\JoinVariableDeclaration(
new AST\Join(
AST\Join::JOIN_TYPE_INNER,
new AST\JoinAssociationPathExpression($aliasIdentificationVariable, $field),
$aliasIdentificationVariable . '.' . $field,
false
),
null
);
$AST->fromClause->identificationVariableDeclarations[0]->joinVariableDeclarations[] = $joinVariableDeclaration;
$this->_queryComponents[$aliasIdentificationVariable . '.' . $field] = $joinQueryComponent;
}
$aliasIdentificationVariable .= '.' . $field;
if ($assoc->isOneToOne()) {
$fieldType = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
} else {
$fieldType = AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
}
$fieldType = AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
}
++$curIndex;
}
// Validate if PathExpression is one of the expected types
@@ -683,7 +716,7 @@ class Parser
$this->semanticalError($semanticalError, $deferredItem['token']);
}
// We need to force the type in PathExpression
$pathExpression->type = $fieldType;
}
@@ -825,7 +858,7 @@ class Parser
{
$this->match(Lexer::T_IDENTIFIER);
$schemaName = $this->_lexer->token['value'];
$schemaName = ltrim($this->_lexer->token['value'], '\\');
if (strrpos($schemaName, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $schemaName);
@@ -891,11 +924,15 @@ class Parser
public function JoinAssociationPathExpression()
{
$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);
//$this->match($this->_lexer->lookahead['type']);
$field = $this->_lexer->token['value'];
// Validate association field
@@ -913,7 +950,7 @@ class Parser
* Parses an arbitrary path expression and defers semantical validation
* based on expected types.
*
* PathExpression ::= IdentificationVariable {"." identifier}* "." identifier
* PathExpression ::= IdentificationVariable "." identifier
*
* @param integer $expectedTypes
* @return \Doctrine\ORM\Query\AST\PathExpression
@@ -922,17 +959,17 @@ class Parser
{
$token = $this->_lexer->lookahead;
$identVariable = $this->IdentificationVariable();
$parts = array();
$field = null;
while ($this->_lexer->isNextToken(Lexer::T_DOT)) {
if ($this->_lexer->isNextToken(Lexer::T_DOT)) {
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
$parts[] = $this->_lexer->token['value'];
$field = $this->_lexer->token['value'];
}
// Creating AST node
$pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $parts);
$pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
// Defer PathExpression validation if requested to be defered
$this->_deferredPathExpressions[] = array(
@@ -971,7 +1008,7 @@ class Parser
}
/**
* StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
* StateFieldPathExpression ::= IdentificationVariable "." StateField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
@@ -981,7 +1018,7 @@ class Parser
}
/**
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
@@ -991,7 +1028,7 @@ class Parser
}
/**
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
@@ -1000,26 +1037,6 @@ class Parser
return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
}
/**
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
*
* @return \Doctrine\ORM\Query\AST\PathExpression
*/
public function SimpleStateFieldPathExpression()
{
$pathExpression = $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
$parts = $pathExpression->parts;
if (count($parts) > 1) {
$this->semanticalError(
"Invalid SimpleStateFieldPathExpression. " .
"Expected state field, got association '{$parts[0]}'."
);
}
return $pathExpression;
}
/**
* SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
*
@@ -1285,34 +1302,17 @@ class Parser
}
/**
* UpdateItem ::= IdentificationVariable "." {StateField | SingleValuedAssociationField} "=" NewValue
* UpdateItem ::= SingleValuedPathExpression "=" NewValue
*
* @return \Doctrine\ORM\Query\AST\UpdateItem
*/
public function UpdateItem()
{
$token = $this->_lexer->lookahead;
$identVariable = $this->IdentificationVariable();
$this->match(Lexer::T_DOT);
$this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value'];
// Check if field exists
$class = $this->_queryComponents[$identVariable]['metadata'];
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field named ' . $field, $token
);
}
$pathExpr = $this->SingleValuedPathExpression();
$this->match(Lexer::T_EQUALS);
$newValue = $this->NewValue();
$updateItem = new AST\UpdateItem($field, $newValue);
$updateItem->identificationVariable = $identVariable;
$updateItem = new AST\UpdateItem($pathExpr, $this->NewValue());
return $updateItem;
}
@@ -1327,10 +1327,14 @@ class Parser
// We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
$glimpse = $this->_lexer->glimpse();
if ($glimpse['value'] != '.') {
if ($glimpse['type'] != Lexer::T_DOT) {
$token = $this->_lexer->lookahead;
$identVariable = $this->IdentificationVariable();
if (!isset($this->_queryComponents[$identVariable])) {
$this->semanticalError('Cannot group by undefined identification variable.');
}
return $identVariable;
}
@@ -1352,7 +1356,7 @@ class Parser
// We need to check if we are in a ResultVariable or StateFieldPathExpression
$glimpse = $this->_lexer->glimpse();
if ($glimpse['value'] != '.') {
if ($glimpse['type'] != Lexer::T_DOT) {
$token = $this->_lexer->lookahead;
$expr = $this->ResultVariable();
} else {
@@ -1428,9 +1432,11 @@ class Parser
*/
public function SubselectIdentificationVariableDeclaration()
{
$peek = $this->_lexer->glimpse();
$glimpse = $this->_lexer->glimpse();
if ($peek['value'] == '.') {
/* NOT YET IMPLEMENTED!
if ($glimpse['type'] == Lexer::T_DOT) {
$subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration();
$subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression();
$this->match(Lexer::T_AS);
@@ -1438,6 +1444,7 @@ class Parser
return $subselectIdVarDecl;
}
*/
return $this->IdentificationVariableDeclaration();
}
@@ -1505,11 +1512,13 @@ class Parser
$this->match(Lexer::T_OPEN_CURLY_BRACE);
$this->match(Lexer::T_IDENTIFIER);
$partialFieldSet[] = $this->_lexer->token['value'];
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$this->match(Lexer::T_IDENTIFIER);
$partialFieldSet[] = $this->_lexer->token['value'];
}
$this->match(Lexer::T_CLOSE_CURLY_BRACE);
$partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
@@ -1570,7 +1579,7 @@ class Parser
);
}
$targetClassName = $parentClass->getAssociationMapping($assocField)->targetEntityName;
$targetClassName = $parentClass->associationMappings[$assocField]['targetEntity'];
// Building queryComponent
$joinQueryComponent = array(
@@ -1596,7 +1605,7 @@ class Parser
}
/**
* IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression
* IndexBy ::= "INDEX" "BY" StateFieldPathExpression
*
* @return Doctrine\ORM\Query\AST\IndexBy
*/
@@ -1604,13 +1613,12 @@ class Parser
{
$this->match(Lexer::T_INDEX);
$this->match(Lexer::T_BY);
$pathExp = $this->SimpleStateFieldPathExpression();
$pathExpr = $this->StateFieldPathExpression();
// Add the INDEX BY info to the query component
$parts = $pathExp->parts;
$this->_queryComponents[$pathExp->identificationVariable]['map'] = $parts[0];
$this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
return $pathExp;
return new AST\IndexBy($pathExpr);
}
/**
@@ -1629,15 +1637,31 @@ class Parser
$peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
$this->_lexer->resetPeek();
if ($peek['value'] == '+' || $peek['value'] == '-' || $peek['value'] == '/' || $peek['value'] == '*') {
if ($this->_isMathOperator($peek)) {
return $this->SimpleArithmeticExpression();
}
return $this->StateFieldPathExpression();
} else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) {
return $this->SimpleArithmeticExpression();
} else if ($this->_isFunction()) {
return $this->FunctionDeclaration();
} 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(); // "("
$peek = $this->_peekBeyondClosingParenthesis();
if ($this->_isMathOperator($peek)) {
return $this->SimpleArithmeticExpression();
}
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
return $this->AggregateExpression();
} else {
return $this->FunctionDeclaration();
}
} else if ($lookahead == Lexer::T_STRING) {
return $this->StringPrimary();
} else if ($lookahead == Lexer::T_INPUT_PARAMETER) {
@@ -1645,8 +1669,6 @@ 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();
}
@@ -1654,11 +1676,66 @@ class Parser
public function CaseExpression()
{
$lookahead = $this->_lexer->lookahead['type'];
// if "CASE" "WHEN" => GeneralCaseExpression
// else if "CASE" => SimpleCaseExpression
// else if "COALESCE" => CoalesceExpression
// else if "NULLIF" => NullifExpression
$this->semanticalError('CaseExpression not yet supported.');
// [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);
}
/**
@@ -1671,17 +1748,19 @@ class Parser
public function SelectExpression()
{
$expression = null;
$identVariable = null;
$fieldAliasIdentificationVariable = null;
$peek = $this->_lexer->glimpse();
$supportsAlias = true;
if ($peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
if ($peek['value'] == '.') {
// ScalarExpression
$expression = $this->ScalarExpression();
} else {
$supportsAlias = false;
$expression = $this->IdentificationVariable();
$expression = $identVariable = $this->IdentificationVariable();
}
} else if ($this->_lexer->lookahead['value'] == '(') {
if ($peek['type'] == Lexer::T_SELECT) {
@@ -1694,8 +1773,17 @@ class Parser
$expression = $this->SimpleArithmeticExpression();
}
} else if ($this->_isFunction()) {
if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
$this->_lexer->peek(); // "("
$lookaheadType = $this->_lexer->lookahead['type'];
$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();
@@ -1703,8 +1791,10 @@ class Parser
} else if ($this->_lexer->lookahead['type'] == Lexer::T_PARTIAL) {
$supportsAlias = false;
$expression = $this->PartialObjectExpression();
$identVariable = $expression->identificationVariable;
} else if ($this->_lexer->lookahead['type'] == Lexer::T_INTEGER ||
$this->_lexer->lookahead['type'] == Lexer::T_FLOAT) {
$this->_lexer->lookahead['type'] == Lexer::T_FLOAT ||
$this->_lexer->lookahead['type'] == Lexer::T_STRING) {
// Shortcut: ScalarExpression => SimpleArithmeticExpression
$expression = $this->SimpleArithmeticExpression();
} else {
@@ -1731,30 +1821,52 @@ class Parser
}
}
return new AST\SelectExpression($expression, $fieldAliasIdentificationVariable);
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable);
if (!$supportsAlias) {
$this->_identVariableExpressions[$identVariable] = $expr;
}
return $expr;
}
/**
* SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable | (AggregateExpression [["AS"] AliasResultVariable])
* SimpleSelectExpression ::=
* StateFieldPathExpression | IdentificationVariable |
* ((AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable])
*
* @return \Doctrine\ORM\Query\AST\SimpleSelectExpression
*/
public function SimpleSelectExpression()
{
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
// SingleValuedPathExpression | IdentificationVariable
$peek = $this->_lexer->glimpse();
$peek = $this->_lexer->glimpse();
if ($peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
// SingleValuedPathExpression | IdentificationVariable
if ($peek['value'] == '.') {
return new AST\SimpleSelectExpression($this->StateFieldPathExpression());
$expression = $this->StateFieldPathExpression();
} else {
$expression = $this->IdentificationVariable();
}
$this->match(Lexer::T_IDENTIFIER);
return new AST\SimpleSelectExpression($expression);
} else if ($this->_lexer->lookahead['value'] == '(') {
if ($peek['type'] == Lexer::T_SELECT) {
// Subselect
$this->match(Lexer::T_OPEN_PARENTHESIS);
$expression = $this->Subselect();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
} else {
// Shortcut: ScalarExpression => SimpleArithmeticExpression
$expression = $this->SimpleArithmeticExpression();
}
return new AST\SimpleSelectExpression($this->_lexer->token['value']);
return new AST\SimpleSelectExpression($expression);
}
$expr = new AST\SimpleSelectExpression($this->AggregateExpression());
$this->_lexer->peek();
$expression = $this->ScalarExpression();
$expr = new AST\SimpleSelectExpression($expression);
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS);
@@ -1889,7 +2001,8 @@ class Parser
* SimpleConditionalExpression ::=
* ComparisonExpression | BetweenExpression | LikeExpression |
* InExpression | NullComparisonExpression | ExistsExpression |
* EmptyCollectionComparisonExpression | CollectionMemberExpression
* EmptyCollectionComparisonExpression | CollectionMemberExpression |
* InstanceOfExpression
*/
public function SimpleConditionalExpression()
{
@@ -1945,6 +2058,8 @@ class Parser
return $this->LikeExpression();
case Lexer::T_IN:
return $this->InExpression();
case Lexer::T_INSTANCE:
return $this->InstanceOfExpression();
case Lexer::T_IS:
if ($lookahead['type'] == Lexer::T_NULL) {
return $this->NullComparisonExpression();
@@ -1957,23 +2072,6 @@ class Parser
}
}
private function _peekBeyondClosingParenthesis()
{
$numUnmatched = 1;
$token = $this->_lexer->peek();
while ($numUnmatched > 0 && $token !== null) {
if ($token['value'] == ')') {
--$numUnmatched;
} else if ($token['value'] == '(') {
++$numUnmatched;
}
$token = $this->_lexer->peek();
}
$this->_lexer->resetPeek();
return $token;
}
/**
* EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
*
@@ -2217,7 +2315,7 @@ class Parser
return $this->SingleValuedPathExpression();
}
return $this->SimpleStateFieldPathExpression();
return $this->StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:
return $this->InputParameter();
@@ -2271,7 +2369,8 @@ class Parser
if ($peek['value'] == '.') {
return $this->StateFieldPathExpression();
} else if ($peek['value'] == '(') {
return $this->FunctionsReturningStrings();
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
return $this->FunctionDeclaration();
} else {
$this->syntaxError("'.' or '('");
}
@@ -2358,7 +2457,7 @@ class Parser
$functionName = $this->_lexer->token['value'];
$this->match(Lexer::T_OPEN_PARENTHESIS);
$pathExp = $this->StateFieldPathExpression();
$pathExp = $this->SimpleArithmeticExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
}
@@ -2443,13 +2542,13 @@ class Parser
}
/**
* InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
* InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
*
* @return \Doctrine\ORM\Query\AST\InExpression
*/
public function InExpression()
{
$inExpression = new AST\InExpression($this->StateFieldPathExpression());
$inExpression = new AST\InExpression($this->SingleValuedPathExpression());
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
@@ -2478,6 +2577,38 @@ class Parser
return $inExpression;
}
/**
* InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (AbstractSchemaName | InputParameter)
*
* @return \Doctrine\ORM\Query\AST\InstanceOfExpression
*/
public function InstanceOfExpression()
{
$instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
$this->match(Lexer::T_NOT);
$instanceOfExpression->not = true;
}
$this->match(Lexer::T_INSTANCE);
if ($this->_lexer->isNextToken(Lexer::T_OF)) {
$this->match(Lexer::T_OF);
}
if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
$this->match(Lexer::T_INPUT_PARAMETER);
$exprValue = new AST\InputParameter($this->_lexer->token['value']);
} else {
$exprValue = $this->AliasIdentificationVariable();
}
$instanceOfExpression->value = $exprValue;
return $instanceOfExpression;
}
/**
* LikeExpression ::= StringExpression ["NOT"] "LIKE" (string | input_parameter) ["ESCAPE" char]
*
+16 -6
View File
@@ -47,6 +47,11 @@ class QueryException extends \Doctrine\ORM\ORMException
return new self('[Semantical Error] ' . $message);
}
public static function invalidParameterType($expected, $received)
{
return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.');
}
public static function invalidParameterPosition($pos)
{
return new self('Invalid parameter position: ' . $pos);
@@ -70,8 +75,7 @@ class QueryException extends \Doctrine\ORM\ORMException
public static function invalidPathExpression($pathExpr)
{
return new self(
"Invalid PathExpression '" . $pathExpr->identificationVariable .
"." . implode('.', $pathExpr->parts) . "'."
"Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'."
);
}
@@ -86,7 +90,7 @@ class QueryException extends \Doctrine\ORM\ORMException
{
return new self(
"Invalid query operation: Not allowed to iterate over fetch join collections ".
"in class ".$assoc->sourceEntityName." assocation ".$assoc->sourceFieldName
"in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName']
);
}
@@ -103,7 +107,7 @@ class QueryException extends \Doctrine\ORM\ORMException
{
return new self(
"Unsupported query operation: It is not yet possible to overwrite the join ".
"conditions in class ".$assoc->sourceEntityName." assocation ".$assoc->sourceFieldName.". ".
"conditions in class ".$assoc['sourceEntityName']." assocation ".$assoc['fieldName'].". ".
"Use WITH to append additional join conditions to the association."
);
}
@@ -118,8 +122,8 @@ class QueryException extends \Doctrine\ORM\ORMException
public static function iterateWithFetchJoinNotAllowed($assoc) {
return new self(
"Iterate with fetch join in class " . $assoc->sourceEntityName .
" using association " . $assoc->sourceFieldName . " not allowed."
"Iterate with fetch join in class " . $assoc['sourceEntity'] .
" using association " . $assoc['fieldName'] . " not allowed."
);
}
@@ -131,4 +135,10 @@ 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.");
}
}
+16 -8
View File
@@ -1,7 +1,5 @@
<?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
@@ -114,6 +112,13 @@ 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.
@@ -383,14 +388,17 @@ class ResultSetMapping
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
* @param $alias
* @param $columnName
* @param $fieldName
* @param string $alias
* @param string $columnName
* @param string $fieldName
* @param bool
*/
public function addMetaResult($alias, $columnName, $fieldName)
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
if ($isIdentifierColumn) {
$this->isIdentifierColumn[$alias][$columnName] = true;
}
}
}
}
@@ -0,0 +1,100 @@
<?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;
/**
* 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);
$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);
}
}
/**
* 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);
$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);
}
}
}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -290,6 +290,14 @@ interface TreeWalker
*/
function walkInExpression($inExpr);
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr);
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
@@ -324,6 +324,14 @@ abstract class TreeWalkerAdapter implements TreeWalker
*/
public function walkInExpression($inExpr) {}
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr) {}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
@@ -472,6 +472,19 @@ class TreeWalkerChain implements TreeWalker
}
}
/**
* Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL.
*
* @param InstanceOfExpression
* @return string The SQL.
*/
function walkInstanceOfExpression($instanceOfExpr)
{
foreach ($this->_walkers as $walker) {
$walker->walkInstanceOfExpression($instanceOfExpr);
}
}
/**
* Walks down a literal that represents an AST node, thereby generating the appropriate SQL.
*
+218 -24
View File
@@ -79,6 +79,11 @@ class QueryBuilder
* @var array The query parameters.
*/
private $_params = array();
/**
* @var array The parameter type map of this query.
*/
private $_paramTypes = array();
/**
* @var integer The index of the first result to retrieve.
@@ -208,13 +213,34 @@ class QueryBuilder
public function getQuery()
{
return $this->_em->createQuery($this->getDQL())
->setParameters($this->_params)
->setParameters($this->_params, $this->_paramTypes)
->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults);
}
/**
* Gets the FIRST 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');
*
* echo $qb->getRootAlias(); // u
* </code>
*
* @deprecated Please use $qb->getRootAliases() instead.
* @return string $rootAlias
*/
public function getRootAlias()
{
$aliases = $this->getRootAliases();
return $aliases[0];
}
/**
* Gets the root alias of the query. This is the first entity alias involved
* Gets the root aliases of the query. This is the entity aliases involved
* in the construction of the query.
*
* <code>
@@ -222,15 +248,61 @@ class QueryBuilder
* ->select('u')
* ->from('User', 'u');
*
* echo $qb->getRootAlias(); // u
* $qb->getRootAliases(); // array('u')
* </code>
*
* @return string $rootAlias
* @todo Rename/Refactor: getRootAliases(), there can be multiple roots!
* @return array $rootAliases
*/
public function getRootAlias()
public function getRootAliases()
{
return $this->_dqlParts['from'][0]->getAlias();
$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;
}
/**
@@ -246,11 +318,18 @@ class QueryBuilder
*
* @param string|integer $key The parameter position or name.
* @param mixed $value The parameter value.
* @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
* @return QueryBuilder This QueryBuilder instance.
*/
public function setParameter($key, $value)
public function setParameter($key, $value, $type = null)
{
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
return $this;
}
@@ -271,9 +350,15 @@ class QueryBuilder
* @param array $params The query parameters to set.
* @return QueryBuilder This QueryBuilder instance.
*/
public function setParameters(array $params)
public function setParameters(array $params, array $types = array())
{
$this->_params = $params;
foreach ($params as $key => $value) {
if (isset($types[$key])) {
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
}
return $this;
}
@@ -358,9 +443,29 @@ 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) {
$this->_dqlParts[$dqlPartName][] = $dqlPart;
if (is_array($dqlPart)) {
$key = key($dqlPart);
$this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
} else {
$this->_dqlParts[$dqlPartName][] = $dqlPart;
}
} else {
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
}
@@ -513,11 +618,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 join($join, $alias, $conditionType = null, $condition = null)
public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
return $this->innerJoin($join, $alias, $conditionType, $condition);
return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
}
/**
@@ -537,12 +643,18 @@ 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)
public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
return $this->add('join', new Expr\Join(
Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition
$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)
), true);
}
@@ -564,12 +676,18 @@ 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)
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
return $this->add('join', new Expr\Join(
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition
$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)
), true);
}
@@ -619,7 +737,7 @@ class QueryBuilder
*/
public function where($predicates)
{
if ( ! (func_num_args() == 1 && ($predicates instanceof Expr\Andx || $predicates instanceof Expr\Orx))) {
if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) {
$predicates = new Expr\Andx(func_get_args());
}
@@ -855,14 +973,36 @@ class QueryBuilder
private function _getDQLForSelect()
{
return 'SELECT'
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('from', array('pre' => ' FROM ', 'separator' => ', '))
. $this->_getReducedDQLQueryPart('join', array('pre' => ' ', 'separator' => ' '))
$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 (isset($joinParts[$from->getAlias()])) {
foreach ($joinParts[$from->getAlias()] as $join) {
$fromClause .= ' ' . ((string) $join);
}
}
$fromClauses[] = $fromClause;
}
}
$dql .= implode(', ', $fromClauses)
. $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())
@@ -878,6 +1018,40 @@ class QueryBuilder
. (isset($options['post']) ? $options['post'] : '');
}
/**
* Reset DQL parts
*
* @param array $parts
* @return QueryBuilder
*/
public function resetDQLParts($parts = null)
{
if (is_null($parts)) {
$parts = array_keys($this->_dqlParts);
}
foreach ($parts as $part) {
$this->resetDQLPart($part);
}
return $this;
}
/**
* Reset single DQL part
*
* @param string $part
* @return QueryBuilder;
*/
public function resetDQLPart($part)
{
if (is_array($this->_dqlParts[$part])) {
$this->_dqlParts[$part] = array();
} else {
$this->_dqlParts[$part] = null;
}
$this->_state = self::STATE_DIRTY;
return $this;
}
/**
* Gets a string representation of this QueryBuilder which corresponds to
* the final DQL query being constructed.
@@ -888,4 +1062,24 @@ class QueryBuilder
{
return $this->getDQL();
}
/**
* Deep clone of all expression objects in the DQL parts.
*
* @return void
*/
public function __clone()
{
foreach ($this->_dqlParts AS $part => $elements) {
if (is_array($this->_dqlParts[$part])) {
foreach ($this->_dqlParts[$part] AS $idx => $element) {
if (is_object($element)) {
$this->_dqlParts[$part][$idx] = clone $element;
}
}
} else if (\is_object($elements)) {
$this->_dqlParts[$part] = clone $elements;
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to clear the metadata cache of the various cache drivers.
@@ -66,6 +66,10 @@ EOT
throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Metadata cache entries' . PHP_EOL);
$cacheIds = $cacheDriver->deleteAll();
@@ -78,4 +82,4 @@ EOT
$output->write('No entries to be deleted.' . PHP_EOL);
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to clear the query cache of the various cache drivers.
@@ -66,6 +66,10 @@ EOT
throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$output->write('Clearing ALL Query cache entries' . PHP_EOL);
$cacheIds = $cacheDriver->deleteAll();
@@ -78,4 +82,4 @@ EOT
$output->write('No entries to be deleted.' . PHP_EOL);
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to clear the result cache of the various cache drivers.
@@ -49,19 +49,19 @@ class ResultCommand extends Console\Command\Command
->setDescription('Clear result cache of the various cache drivers.')
->setDefinition(array(
new InputOption(
'id', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'id', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'ID(s) of the cache entry to delete (accepts * wildcards).', array()
),
new InputOption(
'regex', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'regex', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that match the given regular expression(s).', array()
),
new InputOption(
'prefix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'prefix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that have the given prefix(es).', array()
),
new InputOption(
'suffix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'suffix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that have the given suffix(es).', array()
),
))
@@ -84,6 +84,10 @@ EOT
throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.');
}
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
}
$outputed = false;
// Removing based on --id
@@ -161,4 +165,4 @@ EOT
$output->write('No entries to be deleted.' . PHP_EOL);
}
}
}
}
@@ -21,11 +21,12 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\ORM\Tools\ConvertDoctrine1Schema;
Doctrine\ORM\Tools\ConvertDoctrine1Schema,
Doctrine\ORM\Tools\EntityGenerator;
/**
* Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file.
@@ -41,6 +42,56 @@ use Symfony\Components\Console\Input\InputArgument,
*/
class ConvertDoctrine1SchemaCommand extends Console\Command\Command
{
/**
* @var EntityGenerator
*/
private $entityGenerator = null;
/**
* @var ClassMetadataExporter
*/
private $metadataExporter = null;
/**
* @return EntityGenerator
*/
public function getEntityGenerator()
{
if ($this->entityGenerator == null) {
$this->entityGenerator = new EntityGenerator();
}
return $this->entityGenerator;
}
/**
* @param EntityGenerator $entityGenerator
*/
public function setEntityGenerator(EntityGenerator $entityGenerator)
{
$this->entityGenerator = $entityGenerator;
}
/**
* @return ClassMetadataExporter
*/
public function getMetadataExporter()
{
if ($this->metadataExporter == null) {
$this->metadataExporter = new ClassMetadataExporter();
}
return $this->metadataExporter;
}
/**
* @param ClassMetadataExporter $metadataExporter
*/
public function setMetadataExporter(ClassMetadataExporter $metadataExporter)
{
$this->metadataExporter = $metadataExporter;
}
/**
* @see Console\Command\Command
*/
@@ -61,16 +112,16 @@ class ConvertDoctrine1SchemaCommand extends Console\Command\Command
'The path to generate your Doctrine 2.X mapping information.'
),
new InputOption(
'from', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Optional paths of Doctrine 1.X schema information.',
array()
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
@@ -90,6 +141,27 @@ EOT
// Process source directories
$fromPaths = array_merge(array($input->getArgument('from-path')), $input->getOption('from'));
// Process destination directory
$destPath = realpath($input->getArgument('dest-path'));
$toType = $input->getArgument('to-type');
$extend = $input->getOption('extend');
$numSpaces = $input->getOption('num-spaces');
$this->convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output);
}
/**
* @param \Doctrine\ORM\EntityManager $em
* @param array $fromPaths
* @param string $destPath
* @param string $toType
* @param int $numSpaces
* @param string|null $extend
* @param Console\Output\OutputInterface $output
*/
public function convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output)
{
foreach ($fromPaths as &$dirName) {
$dirName = realpath($dirName);
@@ -104,9 +176,6 @@ EOT
}
}
// Process destination directory
$destPath = realpath($input->getArgument('dest-path'));
if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException(
sprintf("Doctrine 2.X mapping destination directory '<info>%s</info>' does not exist.", $destPath)
@@ -117,18 +186,16 @@ EOT
);
}
$toType = $input->getArgument('to-type');
$cme = new ClassMetadataExporter();
$cme = $this->getMetadataExporter();
$exporter = $cme->getExporter($toType, $destPath);
if (strtolower($toType) === 'annotation') {
$entityGenerator = new EntityGenerator();
$entityGenerator = $this->getEntityGenerator();
$exporter->setEntityGenerator($entityGenerator);
$entityGenerator->setNumSpaces($input->getOption('num-spaces'));
$entityGenerator->setNumSpaces($numSpaces);
if (($extend = $input->getOption('extend')) !== null) {
if ($extend !== null) {
$entityGenerator->setClassToExtend($extend);
}
}
@@ -153,4 +220,4 @@ EOT
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\Export\ClassMetadataExporter,
Doctrine\ORM\Tools\EntityGenerator,
@@ -53,7 +53,7 @@ class ConvertMappingCommand extends Console\Command\Command
->setDescription('Convert mapping information between supported formats.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
@@ -63,20 +63,42 @@ class ConvertMappingCommand extends Console\Command\Command
'dest-path', InputArgument::REQUIRED,
'The path to generate your entities classes.'
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
'Force to overwrite existing mapping files.'
),
new InputOption(
'from-database', null, null, 'Whether or not to convert mapping information from existing database.'
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'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.
This is an execute <info>one-time</info> command. It should not be necessary for
you to call this method multiple times, escpecially when using the <comment>--from-database</comment>
flag.
Converting an existing databsae schema into mapping files only solves about 70-80%
of the necessary mapping information. Additionally the detection from an existing
database cannot detect inverse associations, inheritance types,
entities with foreign keys as primary keys and many of the
semantical operations on associations such as cascade.
<comment>Hint:</comment> There is no need to convert YAML or XML mapping files to annotations
every time you make changes. All mapping drivers are first class citizens
in Doctrine 2 and can be used as runtime mapping for the ORM.
EOT
);
}
@@ -89,14 +111,21 @@ EOT
$em = $this->getHelper('em')->getEntityManager();
if ($input->getOption('from-database') === true) {
$em->getConfiguration()->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
$databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
);
$em->getConfiguration()->setMetadataDriverImpl(
$databaseDriver
);
if (($namespace = $input->getOption('namespace')) !== null) {
$databaseDriver->setNamespace($namespace);
}
}
$cmf = new DisconnectedClassMetadataFactory($em);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));
@@ -118,8 +147,8 @@ EOT
$toType = strtolower($input->getArgument('to-type'));
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter($toType, $destPath);
$exporter = $this->getExporter($toType, $destPath);
$exporter->setOverwriteExistingFiles( ($input->getOption('force') !== false) );
if ($toType == 'annotation') {
$entityGenerator = new EntityGenerator();
@@ -147,4 +176,11 @@ EOT
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}
protected function getExporter($toType, $destPath)
{
$cme = new ClassMetadataExporter();
return $cme->getExporter($toType, $destPath);
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console;
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console;
/**
* Command to ensure that Doctrine is properly configured for a production environment.
@@ -49,7 +49,7 @@ class EnsureProductionSettingsCommand extends Console\Command\Command
->setDescription('Verify that Doctrine is properly configured for a production environment.')
->setDefinition(array(
new InputOption(
'complete', null, InputOption::PARAMETER_NONE,
'complete', null, InputOption::VALUE_NONE,
'Flag to also inspect database connection existance.'
)
))
@@ -82,4 +82,4 @@ EOT
$output->write('<info>Environment is correctly configured for production.</info>' . PHP_EOL);
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\EntityGenerator,
Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
@@ -52,39 +52,56 @@ class GenerateEntitiesCommand extends Console\Command\Command
->setDescription('Generate entity classes and method stubs from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.'
),
new InputOption(
'generate-annotations', null, InputOption::PARAMETER_OPTIONAL,
'generate-annotations', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate annotation metadata on entities.', false
),
new InputOption(
'generate-methods', null, InputOption::PARAMETER_OPTIONAL,
'generate-methods', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate stub methods on entities.', true
),
new InputOption(
'regenerate-entities', null, InputOption::PARAMETER_OPTIONAL,
'regenerate-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should regenerate entity if it exists.', false
),
new InputOption(
'update-entities', null, InputOption::PARAMETER_OPTIONAL,
'update-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should only update entity if it exists.', true
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
->setHelp(<<<EOT
Generate entity classes and method stubs from your mapping information.
If you use the <comment>--update-entities</comment> or <comment>--regenerate-entities</comment> flags your exisiting
code gets overwritten. The EntityGenerator will only append new code to your
file and will not delete the old code. However this approach may still be prone
to error and we suggest you use code repositories such as GIT or SVN to make
backups of your code.
It makes sense to generate the entity code if you are using entities as Data
Access Objects only and dont put much additional logic on them. If you are
however putting much more logic on the entities you should refrain from using
the entity-generator and code your entities manually.
<error>Important:</error> Even if you specified Inheritance options in your
XML or YAML Mapping files the generator cannot generate the base and
child classes for you correctly, because it doesn't know which
class is supposed to extend which. You have to adjust the entity
code manually for inheritance to work!
EOT
);
}
@@ -96,7 +113,8 @@ EOT
{
$em = $this->getHelper('em')->getEntityManager();
$cmf = new DisconnectedClassMetadataFactory($em);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadatas = $cmf->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));
@@ -142,4 +160,4 @@ EOT
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter;
/**
@@ -50,7 +50,7 @@ class GenerateProxiesCommand extends Console\Command\Command
->setDescription('Generates proxy classes for entity classes.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
@@ -112,4 +112,4 @@ EOT
}
}
}
}
@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Tools\Console\Command;
use Symfony\Components\Console\Input\InputArgument,
Symfony\Components\Console\Input\InputOption,
Symfony\Components\Console,
use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption,
Symfony\Component\Console,
Doctrine\ORM\Tools\Console\MetadataFilter,
Doctrine\ORM\Tools\EntityRepositoryGenerator;
@@ -51,7 +51,7 @@ class GenerateRepositoriesCommand extends Console\Command\Command
->setDescription('Generate repository classes from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
@@ -113,4 +113,4 @@ EOT
$output->write('No Metadata Classes to process.' . PHP_EOL);
}
}
}
}

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