Compare commits

...

156 Commits

Author SHA1 Message Date
Benjamin Eberlei 4e9438ba60 Release 2.2.2 2012-04-13 10:02:34 +02:00
Benjamin Eberlei 73483bf003 Bump Versions 2012-04-13 10:01:15 +02:00
Benjamin Eberlei 245c30a1a0 Merge branch 'DDC-1534' into 2.2 2012-04-07 10:43:49 +02:00
Benjamin Eberlei 15dc64f2f9 [DDC-1534] YamlDriver wrongly used "inversedBy" inside join table condition although its independent. 2012-04-07 10:43:21 +02:00
Benjamin Eberlei 66951f3c47 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-04-07 10:35:59 +02:00
Benjamin Eberlei 5f21f177e9 Merge branch 'DDC-1771' into 2.2 2012-04-07 10:31:02 +02:00
Benjamin Eberlei 7af877d17e [DDC-1771] Abstract classes cannot be proxies and should be skipped in complete generation. 2012-04-07 10:30:32 +02:00
Benjamin Eberlei f628a7f609 Merge pull request #327 from Netpositive/2.2
2.2 addDiscriminatorMapClass fix
2012-04-07 00:44:16 -07:00
Benjamin Eberlei 64c222e4d3 Merge branch 'DDC-1766' into 2.2 2012-04-07 09:12:17 +02:00
Benjamin Eberlei 13db27609c [DDC-1766] Rewrite getHydrationCacheId() to use existing processParameterValue() method. Other code style changes. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei f039902a44 [DDC-1766] More cleanups 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 59d0c02aa9 [DDC-1766] Cleaned up code. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 63d1d847ac [DDC-1766] Explain details of Hydration cache, introduce AbstractQuery#setResultCacheProfile method 2012-04-07 09:11:18 +02:00
Benjamin Eberlei ce4aacaaee [DDC-1766] Add usage of default result cache driver, add more docs. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei dadcc7e9fc [DDC-1766] Add test with explicit cache key. 2012-04-07 09:11:18 +02:00
Benjamin Eberlei cc9b96e259 [DDC-1766] Remove some testcode 2012-04-07 09:11:18 +02:00
Benjamin Eberlei 006412bad9 [DDC-1766] Rename closure 2012-04-07 09:11:18 +02:00
Benjamin Eberlei ffbaaece93 [DDC-1766] Initial implementation of hydration cache. 2012-04-07 09:11:18 +02:00
Somfai Mátyás c3fa29f298 Fixing a bug when calling setDiscriminatorMap from multiple sources (ie: from Events::loadClassMetadata and annotation). 2012-04-04 14:47:42 +02:00
Benjamin Eberlei aa82a75726 Merge branch 'DDC-1705' into 2.2 2012-04-01 12:51:53 +02:00
Benjamin Eberlei fba85d481f [DDC-1705] Fix notice 2012-04-01 12:51:35 +02:00
Benjamin Eberlei 5bdfad9e0a Merge branch 'DDC-1648' into 2.2 2012-03-14 21:40:19 +01:00
rivaros d69a79c723 Convention fix 2012-03-14 21:39:45 +01:00
Rivaros f51409b627 convention fixes #2 2012-03-14 21:39:45 +01:00
Rivaros 8c8deb9c9b Convention fixes 2012-03-14 21:39:45 +01:00
Rivaros 79c8f42483 Primary Keys as Foreign Keys - reverse engineering 2012-03-14 21:39:45 +01:00
Benjamin Eberlei 04ae8f2c64 Merge branch 'DDC-1692' into 2.2 2012-03-14 21:10:44 +01:00
Benjamin Eberlei ecb495d293 [DBAL-1692] Throw exception if table has no primary key instead of fatal error. 2012-03-14 21:10:09 +01:00
Benjamin Eberlei 1418173870 Merge branch 'DDC-1683' into 2.2 2012-03-14 20:50:27 +01:00
Benjamin Eberlei 26a6b69993 [DDC-1683] Fix bug with booleans not handled by Expr#literal() in query builder. 2012-03-14 20:49:51 +01:00
Benjamin Eberlei fa5ee57faf Merge branch 'DDC-1698' into 2.2 2012-03-14 20:05:55 +01:00
Benjamin Eberlei 2a2f010788 [DDC-1698] Add autoloader especially for the non PSR-0 Proxy class names. This is necessary when you want to deserialize your proxy classes from the session. 2012-03-14 20:03:34 +01:00
Benjamin Eberlei e958085559 Merge branch 'DDC-1695' into 2.2 2012-03-11 23:31:33 +01:00
Benjamin Eberlei 98ba0c1ab2 [DDC-1695] Fix bug in SQL Walker array hydration with escaped fields. 2012-03-11 23:31:01 +01:00
Benjamin Eberlei b574be5e3b Merge branch 'DDC-1693' into 2.2 2012-03-11 22:30:05 +01:00
Benjamin Eberlei 79ad9068e1 [DDC-1693] Fix fatal errors in DQL when using Optimistic or None lock modes. Added tests. 2012-03-11 22:29:38 +01:00
Benjamin Eberlei af403d52fb Bump dev version to 2.2.2 2012-03-03 22:26:27 +01:00
Benjamin Eberlei 78fa740b94 Release 2.2.1 2012-03-03 22:26:27 +01:00
Benjamin Eberlei 715fdd7559 Merge branch 'DDC-1668' into 2.2 2012-03-03 22:25:36 +01:00
Benjamin Eberlei 884ef42075 [DDC-1668] Fix problem with the is_int fowards compatibility check. Its not really necesssary anymore, we should remove this code in the future. 2012-03-03 22:25:11 +01:00
Benjamin Eberlei 5d3a626c2b Merge branch 'DDC-1678' into 2.2 2012-03-03 22:17:07 +01:00
Francisco Facioni 587dda90d3 UnitTest for ManyToMany update notification 2012-03-03 22:16:44 +01:00
Francisco Facioni 21ae0d2a45 When using a ManyToMany relationship no listener is notified about any change to the owning entity.
What I'm doing with this patch is marking the entity for update when there is a modification in the ManyToMany relationship so the listeners are notified about it.

The main reason for this is for hooking up services like Solr or other indexers to update the entities even for ManyToMany relationships.
2012-03-03 22:16:44 +01:00
Benjamin Eberlei f4dc533127 Bump Common to 2.2.1 2012-03-03 22:08:36 +01:00
Benjamin Eberlei c9d1b3a428 Merge branch 'DDC-1652' into 2.2 2012-03-03 21:11:15 +01:00
Benjamin Eberlei 9170e1b810 [DDC-1652] Fix SqlWalker to include foreign key identifiers in SQL SELECT statement no matter what the meta column setting is suggesting. 2012-03-03 21:10:33 +01:00
Benjamin Eberlei f1d2a79a87 Merge branch 'DDC-1673' into 2.2 2012-03-03 19:44:27 +01:00
Guilherme Blanco dd2f3967cd [DDC-1673] Fixed unused in ProxyFactory. 2012-03-03 19:26:27 +01:00
Benjamin Eberlei 4100a6a7a3 Merge branch 'DDC-1667' into 2.2 2012-03-03 19:11:05 +01:00
Guilherme Blanco bfb590459e [DDC-1667] Removed implicit obligation to define an Index and UniqueConstraint name. It is optional, but Annotations Driver was broken if not defined. 2012-03-03 19:10:06 +01:00
Sergio Moya 191520439b No unique join column fields for Single Table inheritance type. 2012-02-20 15:56:11 +01:00
Benjamin Eberlei 31709b4c1a Merge branch 'DDC-1649' into 2.2 2012-02-20 15:40:07 +01:00
Benjamin Eberlei af6de10c5b [DDC-1649] Fix notice by last commit. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei f4a78e13ee [DDC-1649] Add additional check for not allowed mapping of dependent association keys. 2012-02-20 15:39:39 +01:00
Benjamin Eberlei bd37b83f4f Merge branch 'DDC-1654' into 2.2 2012-02-20 10:34:27 +01:00
Benjamin Eberlei 19969e2d4b [DDC-1654] Add support for orphanRemoval on ManyToMany associations. This only makes sense when ManyToMany is used as uni-directional OneToMany association with join table. The join column has a unique constraint on it to enforce this on the DB level, but we dont validate that this actually happens. Foreign Key constraints help prevent issues and notify developers early if they use it wrong. 2012-02-20 10:33:43 +01:00
Benjamin Eberlei 573a7f81e1 Merge branch 'DDC-1659' into 2.2 2012-02-20 09:37:59 +01:00
Benjamin Eberlei 2460b3f115 [DDC-1659] Remove read only marker when clearing entities. 2012-02-20 09:37:11 +01:00
Benjamin Michotte 5f2fa7c08a Add fluent code for relations 2012-02-20 09:24:42 +01:00
Asmir Mustafic a04df0622d nullable assoc 2012-02-20 00:32:30 +01:00
Benjamin Eberlei b7e80decf3 Merge branch 'DDC-1651' into 2.2 2012-02-18 16:10:57 +01:00
Benjamin Eberlei 56d35f7932 [DDC-1651] Convert entities as parameters early in setParameter() to avoid them being part of result cache strings, which causes non-uniqueness. 2012-02-18 16:08:43 +01:00
Benjamin Eberlei 2116518096 Merge branch 'DDC-1643' into 2.2 2012-02-18 00:43:51 +01:00
Benjamin Eberlei 647bd2b2f2 [DDC-1643] Fix bugs when cloning PersistentCollection and re-using it. 2012-02-18 00:43:19 +01:00
Benjamin Eberlei dadc85a650 Merge branch 'DDC-1655' into 2.2 2012-02-17 23:28:23 +01:00
Benjamin Eberlei 835943bd8d [DDC-1655][DDC-1650][DDC-1556] Fix issues with @postLoad Callback being not fired, or fired multiple times. 2012-02-17 23:27:35 +01:00
Benjamin Eberlei 39ff164c7e DDC-1641 - Fix test producing failure when skipped. 2012-02-10 21:38:42 +01:00
Benjamin Eberlei 5cdbc8ea6d Merge branch 'DDC-1634' into 2.2 2012-02-05 11:15:29 +01:00
Miha Vrhovnik 34ccde978a Proxy not initialized when parent has get<IDENTIFIER> function. Fixes DDC-1625 2012-02-05 11:14:55 +01:00
Benjamin Eberlei 5675e8cc8c Bump dev version to 2.2.1 2012-01-29 17:04:24 +01:00
Benjamin Eberlei d679154fba Release 2.2.0 2012-01-29 17:04:23 +01:00
Benjamin Eberlei 7cf4337dc1 Bump and bind dependencies of ORM 2.2 2012-01-29 15:22:06 +01:00
Benjamin Eberlei 41cf2ce4b1 Fix test for non-mysql like datetimes. 2012-01-29 15:01:36 +01:00
Benjamin Eberlei 13b8d05def Bump dependencies to release versions. 2012-01-29 13:12:25 +01:00
Benjamin Eberlei 209d098672 Fix Oracle Tests 2012-01-29 13:09:51 +01:00
Benjamin Eberlei 53600484cd Merge branch 'DDC-1526' into 2.2 2012-01-28 12:26:54 +01:00
Benjamin Eberlei ac25d62a24 [DDC-1526] Collections are not marked as initialized when they are fetch joined but dont contain any results. This only occurs when using LEFT JOINs on the assocations and causes another query to be fired when the empty collection is accessed again. 2012-01-28 12:26:25 +01:00
Benjamin Eberlei f7936bda70 Merge branch 'DDC-1617' into 2.2 2012-01-28 11:17:08 +01:00
Benjamin Eberlei 78899cedac [DDC-1617] Implement support for Generating Unique Constraints/Indexes in @Table annotation of EntityGenerator. 2012-01-28 11:16:58 +01:00
Benjamin Eberlei 73fef2867f Merge remote-tracking branch 'origin/2.2' into 2.2 2012-01-25 23:29:44 +01:00
armetiz c883ff2644 Update lib/Doctrine/ORM/Tools/SchemaTool.php 2012-01-25 22:06:37 +01:00
Benjamin Eberlei 7595025f26 Merge branch 'DDC-1619' into 2.2 2012-01-25 10:26:42 +01:00
Benjamin Eberlei 8fb6f986dd [DDC-1619] Add QueryBuilder#distinct 2012-01-25 10:25:38 +01:00
Benjamin Eberlei 6dbb368995 Merge branch 'DDC-1618' into 2.2 2012-01-25 00:02:44 +01:00
Thomas Rabaix c39ad9250a Add SqlWalker::HINT_DISTINCT constant 2012-01-25 00:02:16 +01:00
Thomas Rabaix 8adc267d87 Fix DDC-1618 - add more check before throwing an iterateWithFetchJoinNotAllowed exception 2012-01-25 00:02:16 +01:00
Benjamin Eberlei 2a8dd72b33 Bump dev version to 2.2.0 2012-01-22 17:54:32 +01:00
Benjamin Eberlei e44289dbdb Release 2.2.0-RC1 2012-01-22 17:54:32 +01:00
Benjamin Eberlei dbf1c4cdb9 Bump DBAL to 2.2.0-RC3 2012-01-22 17:54:23 +01:00
Benjamin Eberlei aa0c89e45e Fix MySqlSchemaTest failing with current DBAL adjustments. 2012-01-22 17:30:28 +01:00
Benjamin Eberlei f77a1c2c8b Merge branch 'DDC-1613' into 2.2 2012-01-22 13:36:07 +01:00
Benjamin Eberlei ab1835a9db [DDC-1613] Merge KnpLabs/Pagerfanta Pagination into a Doctrine\ORM\Tools\Pagination namespace. Thanks to @hobodave, pablo and the knplabs team for developing and maintaining this code. 2012-01-22 13:35:45 +01:00
Benjamin Eberlei e5950d5fe9 Merge branch 'DDC-1603' into 2.2 2012-01-21 15:36:17 +01:00
armetiz 41f41d2bd8 Unique key name isn't correctly set - DDC-1603 2012-01-21 15:35:54 +01:00
Benjamin Eberlei da47aa3ea2 Merge branch 'DDC-1610' into 2.2 2012-01-21 13:58:52 +01:00
Benjamin Eberlei 2f976ea03a [DDC-1610] Add test and fix wakeup reflection in combination with event listener 2012-01-21 13:58:44 +01:00
Benjamin Eberlei e10d865b01 Merge branch 'DDC-1612' into 2.2 2012-01-21 13:07:00 +01:00
Benjamin Eberlei d91e633eff [DDC-1612] Fix bug with EntityManager#flush($entity) on new entities. 2012-01-21 13:06:53 +01:00
Benjamin Eberlei 845d152604 Merge branch 'DBAL-204' into 2.2 2012-01-21 11:30:10 +01:00
Benjamin Eberlei 01eb894c4e [DBAL-204] Filter namespaced assets if Schemas/Emulation is not supported. 2012-01-21 11:29:48 +01:00
Benjamin Eberlei d668d50ca4 DDC-742 - Flush Memcache, otherwise fail. 2012-01-18 21:31:49 +01:00
Benjamin Eberlei cecfac5222 Merge branch 'DDC-1608' into 2.2 2012-01-18 20:15:26 +01:00
Guilherme Blanco d391e28ffb Fixed DDC-1608. Non-initialized PersistentCollection methods removeElement and contains now deal correctly with managed entities. 2012-01-18 20:11:37 +01:00
Daniel Holmes f94b405f6e Updated some comparisons to strict equality 2012-01-18 20:11:37 +01:00
Daniel Holmes be6890e5aa Added fix for collection->contains when many-to-many extra lazy fetchMode 2012-01-18 20:11:37 +01:00
Guilherme Blanco 286eef1ca3 Fixes DDC-1596. Added table alias to discriminator column when using STI. 2012-01-16 14:09:29 +01:00
Guilherme Blanco 0aecff7f71 Added coverage to DDC-1595 and DDC-1596. 2012-01-16 14:01:57 +01:00
Guilherme Blanco c73dae141f Fixed DDC-657. Added type conversion to scalar result. 2012-01-16 14:01:48 +01:00
Benjamin Eberlei d535e1c2bc Merge branch 'DDC-1604' into 2.2 2012-01-16 12:52:22 +01:00
Benjamin Eberlei f6d41ad9ce [DDC-1604] Have ORM Proxy implement new \Doctrine\Common\Persistence\Proxy
* Adjust ProxyFactory to generate proxies according to new naming schema.
* Change proxy naming and file-name generation to be a bit more consistent than previous approach.

[DDC-1598] Additional regexp to check for simple ID methods to make it even more safe.
2012-01-16 12:51:55 +01:00
Benjamin Eberlei 60de86e678 Fix 2.2 composer.json to have common and dbal versions correctly 2012-01-15 21:59:52 +01:00
jsor a37aabb11f Pass options attribute in @Column annotation to Schema\Column's customSchemaOptions 2012-01-15 18:07:41 +01:00
Benjamin Eberlei 789ce1604f Merge branch 'DDC-1594' into 2.2 2012-01-15 17:44:53 +01:00
Benjamin Eberlei e058b47966 DDC-1594 - Fix problem with merge and an existing managed proxy instance. 2012-01-15 17:43:00 +01:00
Benjamin Eberlei 7d77373a71 Merge branch 'DDC-1585' into 2.2 2012-01-15 14:59:48 +01:00
Benjamin Eberlei b6896a04e5 DDC-1585 - Throw exception if setting target entity of the wrong type to an assocation. 2012-01-15 14:59:40 +01:00
Benjamin Eberlei 45fbc058a9 Merge remote-tracking branch 'origin/2.2' into 2.2 2012-01-15 12:15:17 +01:00
Benjamin Eberlei 1574d482fe Merge branch 'DDC-1601' into 2.2 2012-01-15 12:12:51 +01:00
Benjamin Eberlei fb85cdfce0 [DDC-1601] Fix failing test and remove unused code 2012-01-15 12:12:40 +01:00
Benjamin Eberlei 0b6b87912c [DDC-1601] Fix bugs in SchemaValidator, using all modelsets as testdata for a large test 2012-01-15 12:12:40 +01:00
Benjamin Eberlei 212e1d6df6 Fix notice when using regenerate if exists and file is not new. 2012-01-12 11:21:11 +01:00
Benjamin Eberlei 41ae873048 DDC-1588 - Improve ResultCache API. The default cache impl is passed to new query cache profiles automatically now. 2012-01-09 08:26:07 +01:00
Benjamin Eberlei 0014afe746 Fix Typo 2012-01-09 08:04:21 +01:00
Benjamin Eberlei e16803de61 [doctrine/common-GH-92] Fixing notice when annotation driver is used in combination with static reflection. 2012-01-09 08:02:53 +01:00
Benjamin Eberlei d34c39555d Bump dev version to 2.2.0 2012-01-03 22:27:03 +01:00
Benjamin Eberlei 8d3d604ed3 Release 2.2.0-BETA2 2012-01-03 22:27:03 +01:00
Benjamin Eberlei 4deeb23af0 Update dependencies 2012-01-03 21:56:04 +01:00
Benjamin Eberlei 133232eb6b Merge pull request #249 from doctrine/DCOM-93
[DCOM-93] Remove Reflection dependency from ClassMetadata
2012-01-03 10:12:03 -08:00
Benjamin Eberlei ef8703e3e9 DCOM-93 - Allow to check testsuite with any constructor-less cache implementation 2012-01-03 19:01:53 +01:00
Benjamin Eberlei a07fc515c7 DCOM-93 - Fix docblocks 2012-01-03 18:41:48 +01:00
Benjamin Eberlei 76e4f5a80b DCOM-93 - Removed reflection dependency from ClassMetadata completly, moving all the code into ClassMetadataInfo for BC reasons. 2012-01-02 21:32:18 +01:00
Benjamin Eberlei c7d8c9f34e DCOM-93 - Factor out ClassMetadata constructor into delegate method initializeReflection 2012-01-02 17:06:22 +01:00
Benjamin Eberlei 1cecc9c429 DCOM-93 - Factor out __wakeup into a delegate-method from ClassMetadataFactory#wakeupReflection to ClassMetadataInfo#wakeupReflection 2012-01-02 15:57:32 +01:00
Benjamin Eberlei ea2d4e4282 DCOM-93 - Add ClassMetadataFactory#wakeupReflection implementation 2012-01-02 15:46:20 +01:00
Benjamin Eberlei 80408ac34f DCOM-93 - Add empty initialize and wakeup methods. 2012-01-02 15:36:36 +01:00
Benjamin Eberlei 9bdf9a9904 DCOM-93 - Adjust ClassMetadataFactory#getClassParents() to use reflection service. 2012-01-02 15:30:25 +01:00
Benjamin Eberlei 239ffe468a Merge pull request #237 from asm89/ddc-551-collections-filters
[DDC-551] Support for inheritance with filters in lazy collections
2012-01-02 06:11:34 -08:00
Benjamin Eberlei 87e0c69381 Merge remote-tracking branch 'origin/2.2' into 2.2 2011-12-28 20:29:53 +01:00
Benjamin Eberlei a6deb51a05 DDC-1360 - Bugfix in quoting mechanism inside ClassMetadataInfo 2011-12-28 20:29:01 +01:00
holtkamp 21cfe4ba9f Allow ExporterDrivers that implement the exportClassMetadata() function to return FALSE when no content is available/needs to be written to a file by the AbstractExporter, preventing empty files to be generated foreach processed ClassMetadataInfo instance. 2011-12-28 09:03:00 +01:00
Adrien BRAULT bd49aa5d2c Fix some PHPDoc @return type. 2011-12-28 08:50:17 +01:00
Alexander 4cf5f70bea Update test 2011-12-22 23:21:56 +01:00
Alexander c3c174512a Added tests for OneToMany associations and lazy collection to CTI entity 2011-12-22 21:10:13 +01:00
Alexander f49a4e9c40 Added tests for OneToMany associations and lazy collection to STI entity 2011-12-22 20:50:57 +01:00
Alexander 223c47069e Added tests for ManyToMany associations and lazy collection to CTI entity 2011-12-22 17:49:57 +01:00
Alexander 62be27b295 Added tests for ManyToMany associations and lazy collection to STI entity 2011-12-22 00:25:21 +01:00
Benjamin Eberlei b91689fe2f Update common with fix on interface detection 2011-12-21 23:57:33 +01:00
Benjamin Eberlei f6f2acad4c Merge Improve Error Messages into 2.2 2011-12-21 23:56:25 +01:00
Benjamin Eberlei ca470d8ba7 Fix glitch in Version produced by build-script 2011-12-21 00:00:43 +01:00
Benjamin Eberlei 0551ccca92 Bump dev version to 2.2.01 2011-12-20 22:39:27 +01:00
Benjamin Eberlei 719e05e53e Extract more messages into ORMInvalidArgumentException 2011-10-22 12:57:55 +02:00
Benjamin Eberlei a8906ce572 Stringify entity in all UnitOfWork exceptions 2011-10-22 12:49:33 +02:00
Benjamin Eberlei 5392737de4 Improved and extracted UnitOfWork error messages 2011-10-22 12:40:12 +02:00
Benjamin Eberlei 3aea203b9c Throw exception if target entity is not found. 2011-10-22 11:28:07 +02:00
Benjamin Eberlei dba8360166 Merge master into ImproveErrorMessages 2011-10-22 11:06:51 +02:00
Benjamin Eberlei cb28bfd484 Improve Error Messages in ClassMetadata and UnitOfWork 2011-10-15 17:38:55 +02:00
114 changed files with 4640 additions and 731 deletions
+2 -2
View File
@@ -92,8 +92,8 @@
<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="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" maximum_version="2.2.99" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" maximum_version="2.2.99" />
<package name="Console" channel="pear.symfony.com" minimum_version="2.0.0" />
<package name="Yaml" channel="pear.symfony.com" minimum_version="2.0.0" />
</dependencies>
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "doctrine/orm",
"type": "library","version":"2.2.0-BETA1",
"type": "library","version":"2.2.2",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
@@ -14,8 +14,8 @@
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": "master-dev",
"doctrine/dbal": "master-dev"
"doctrine/common": ">=2.2.0,<2.2.99",
"doctrine/dbal": ">=2.2.1,<2.2.99"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }
+1
View File
@@ -350,6 +350,7 @@
<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:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
+173 -5
View File
@@ -20,8 +20,9 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Cache\QueryCacheProfile,
Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
Doctrine\ORM\Internal\Hydration\CacheHydrator;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -29,7 +30,6 @@ use Doctrine\DBAL\Types\Type,
* @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 Jonathan Wage <jonwage@gmail.com>
@@ -101,6 +101,11 @@ abstract class AbstractQuery
*/
protected $_expireResultCache = false;
/**
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
*/
protected $_hydrationCacheProfile;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
@@ -208,6 +213,7 @@ abstract class AbstractQuery
{
$key = trim($key, ':');
$value = $this->processParameterValue($value);
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
@@ -218,6 +224,53 @@ abstract class AbstractQuery
return $this;
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return $value;
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
return $this->convertObjectParameterToScalarValue($value);
default:
return $value;
}
}
protected function convertObjectParameterToScalarValue($value)
{
$class = $this->_em->getClassMetadata(get_class($value));
if ($class->isIdentifierComposite) {
throw new \InvalidArgumentException("Binding an entity with a composite primary key to a query is not supported. You should split the parameter into the explicit fields and bind them seperately.");
}
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
$values = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$values = $class->getIdentifierValues($value);
}
$value = $values[$class->getSingleIdentifierFieldName()];
if (!$value) {
throw new \InvalidArgumentException("Binding entities to query parameters only allowed for entities that have an identifier.");
}
return $value;
}
/**
* Sets a collection of query parameters.
*
@@ -247,6 +300,68 @@ abstract class AbstractQuery
return $this;
}
/**
* Set a cache profile for hydration caching.
*
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* Important: Hydration caching does NOT register entities in the
* UnitOfWork when retrieved from the cache. Never use result cached
* entities for requests that also flush the EntityManager. If you want
* some form of caching with UnitOfWork registration you should use
* {@see AbstractQuery::setResultCacheProfile()}.
*
* @example
* $lifetime = 100;
* $resultKey = "abc";
* $query->setHydrationCacheProfile(new QueryCacheProfile());
* $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
* @return \Doctrine\ORM\AbstractQuery
*/
public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
{
if ( ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_hydrationCacheProfile = $profile;
return $this;
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function getHydrationCacheProfile()
{
return $this->_hydrationCacheProfile;
}
/**
* Set a cache profile for the result cache.
*
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
* @return \Doctrine\ORM\AbstractQuery
*/
public function setResultCacheProfile(QueryCacheProfile $profile = null)
{
if ( ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_queryCacheProfile = $profile;
return $this;
}
/**
* Defines a cache driver to be used for caching result sets and implictly enables caching.
*
@@ -316,7 +431,7 @@ abstract class AbstractQuery
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setLifetime($lifetime)
: new QueryCacheProfile($lifetime);
: new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl());
return $this;
}
@@ -592,15 +707,68 @@ abstract class AbstractQuery
$this->setParameters($params);
}
$setCacheEntry = function() {};
if ($this->_hydrationCacheProfile !== null) {
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
$queryCacheProfile = $this->getHydrationCacheProfile();
$cache = $queryCacheProfile->getResultCacheDriver();
$result = $cache->fetch($cacheKey);
if (isset($result[$realCacheKey])) {
return $result[$realCacheKey];
}
if ( ! $result) {
$result = array();
}
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
$result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
};
}
$stmt = $this->_doExecute();
if (is_numeric($stmt)) {
$setCacheEntry($stmt);
return $stmt;
}
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$setCacheEntry($data);
return $data;
}
/**
* Get the result cache id to use to store the result set cache entry.
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
*
* @return array ($key, $hash)
*/
protected function getHydrationCacheId()
{
$params = $this->getParameters();
foreach ($params AS $key => $value) {
$params[$key] = $this->processParameterValue($value);
}
$sql = $this->getSQL();
$queryCacheProfile = $this->getHydrationCacheProfile();
$hints = $this->getHints();
$hints['hydrationMode'] = $this->getHydrationMode();
ksort($hints);
return $queryCacheProfile->generateCacheKeys($sql, $params, $hints);
}
/**
@@ -615,7 +783,7 @@ abstract class AbstractQuery
{
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setCacheKey($id)
: new QueryCacheProfile(0, $id);
: new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl());
return $this;
}
+22
View File
@@ -230,6 +230,28 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['queryCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for the hydration cache (SQL cache).
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getHydrationCacheImpl()
{
return isset($this->_attributes['hydrationCacheImpl'])
? $this->_attributes['hydrationCacheImpl']
: null;
}
/**
* Sets the cache driver implementation that is used for the hydration cache (SQL cache).
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setHydrationCacheImpl(Cache $cacheImpl)
{
$this->_attributes['hydrationCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for metadata caching.
*
@@ -209,6 +209,7 @@ abstract class AbstractHydrator
case (isset($this->_rsm->scalarMappings[$key])):
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]);
$cache[$key]['isScalar'] = true;
break;
@@ -231,6 +232,8 @@ abstract class AbstractHydrator
}
if (isset($cache[$key]['isScalar'])) {
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;
@@ -286,4 +286,4 @@ class ArrayHydrator extends AbstractHydrator
return $this->_ce[$className];
}
}
}
@@ -234,20 +234,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_hints['fetchAlias'] = $dqlAlias;
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->_ce[$className]->lifecycleCallbacks[Events::postLoad])) {
$this->_ce[$className]->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
return $entity;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
@@ -359,7 +346,6 @@ class ObjectHydrator extends AbstractHydrator
continue;
}
$parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
$oid = spl_object_hash($parentObject);
$relationField = $this->_rsm->relationMap[$dqlAlias];
@@ -368,6 +354,7 @@ class ObjectHydrator extends AbstractHydrator
// Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
$reflFieldValue = $reflField->getValue($parentObject);
// PATH A: Collection-valued association
if (isset($nonemptyComponents[$dqlAlias])) {
$collKey = $oid . $relationField;
@@ -408,9 +395,12 @@ class ObjectHydrator extends AbstractHydrator
// Update result pointer
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
}
} else if ( ! $reflField->getValue($parentObject)) {
} else if ( ! $reflFieldValue) {
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
} else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
$reflFieldValue->setInitialized(true);
}
} else {
// PATH B: Single-valued association
$reflFieldValue = $reflField->getValue($parentObject);
@@ -130,17 +130,6 @@ class SimpleObjectHydrator extends AbstractHydrator
$uow = $this->_em->getUnitOfWork();
$entity = $uow->createEntity($entityName, $data, $this->_hints);
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($this->class->lifecycleCallbacks[Events::postLoad])) {
$this->class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
$result[] = $entity;
}
@@ -191,4 +180,4 @@ class SimpleObjectHydrator extends AbstractHydrator
);
}
}
}
}
+1 -321
View File
@@ -20,7 +20,6 @@
namespace Doctrine\ORM\Mapping;
use ReflectionClass, ReflectionProperty;
use Doctrine\Common\Persistence\Mapping\ClassMetadata AS IClassMetadata;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
@@ -40,325 +39,6 @@ use Doctrine\Common\Persistence\Mapping\ClassMetadata AS IClassMetadata;
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
*/
class ClassMetadata extends ClassMetadataInfo implements IClassMetadata
class ClassMetadata extends ClassMetadataInfo
{
/**
* The ReflectionProperty instances of the mapped class.
*
* @var array
*/
public $reflFields = array();
/**
* The prototype from which new instances of the mapped class are created.
*
* @var object
*/
private $_prototype;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
*
* @param string $entityName The name of the entity class the new instance is used for.
*/
public function __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
}
/**
* Gets the ReflectionPropertys of the mapped class.
*
* @return array An array of ReflectionProperty instances.
*/
public function getReflectionProperties()
{
return $this->reflFields;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
* @param string $name
* @return ReflectionProperty
*/
public function getReflectionProperty($name)
{
return $this->reflFields[$name];
}
/**
* Gets the ReflectionProperty for the single identifier field.
*
* @return ReflectionProperty
* @throws BadMethodCallException If the class has a composite identifier.
*/
public function getSingleIdReflectionProperty()
{
if ($this->isIdentifierComposite) {
throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
}
return $this->reflFields[$this->identifier[0]];
}
/**
* Validates & completes the given field mapping.
*
* @param array $mapping The field mapping to validated & complete.
* @return array The validated and completed field mapping.
*
* @throws MappingException
*/
protected function _validateAndCompleteFieldMapping(array &$mapping)
{
parent::_validateAndCompleteFieldMapping($mapping);
// Store ReflectionProperty of mapped field
$refProp = $this->reflClass->getProperty($mapping['fieldName']);
$refProp->setAccessible(true);
$this->reflFields[$mapping['fieldName']] = $refProp;
}
/**
* Extracts the identifier values of an entity of this class.
*
* For composite identifiers, the identifier values are returned as an array
* with the same order as the field order in {@link identifier}.
*
* @param object $entity
* @return array
*/
public function getIdentifierValues($entity)
{
if ($this->isIdentifierComposite) {
$id = array();
foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) {
$id[$idField] = $value;
}
}
return $id;
}
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
/**
* Populates the entity identifier of an entity.
*
* @param object $entity
* @param mixed $id
* @todo Rename to assignIdentifier()
*/
public function setIdentifierValues($entity, array $id)
{
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
}
/**
* Sets the specified field to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setFieldValue($entity, $field, $value)
{
$this->reflFields[$field]->setValue($entity, $value);
}
/**
* Gets the specified field's value off the given entity.
*
* @param object $entity
* @param string $field
*/
public function getFieldValue($entity, $field)
{
return $this->reflFields[$field]->getValue($entity);
}
/**
* Stores the association mapping.
*
* @param AssociationMapping $assocMapping
*/
protected function _storeAssociationMapping(array $assocMapping)
{
parent::_storeAssociationMapping($assocMapping);
// Store ReflectionProperty of mapped field
$sourceFieldName = $assocMapping['fieldName'];
$refProp = $this->reflClass->getProperty($sourceFieldName);
$refProp->setAccessible(true);
$this->reflFields[$sourceFieldName] = $refProp;
}
/**
* Creates a string representation of this instance.
*
* @return string The string representation of this instance.
* @todo Construct meaningful string representation.
*/
public function __toString()
{
return __CLASS__ . '@' . spl_object_hash($this);
}
/**
* 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.
*
* Parts that are also NOT serialized because they can not be properly unserialized:
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
// This metadata is always serialized/cached.
$serialized = array(
'associationMappings',
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
'fieldMappings',
'fieldNames',
'identifier',
'isIdentifierComposite', // TODO: REMOVE
'name',
'namespace', // TODO: REMOVE
'table',
'rootEntityName',
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
);
// The rest of the metadata is only serialized if necessary.
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
$serialized[] = 'changeTrackingPolicy';
}
if ($this->customRepositoryClassName) {
$serialized[] = 'customRepositoryClassName';
}
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
$serialized[] = 'inheritanceType';
$serialized[] = 'discriminatorColumn';
$serialized[] = 'discriminatorValue';
$serialized[] = 'discriminatorMap';
$serialized[] = 'parentClasses';
$serialized[] = 'subClasses';
}
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
$serialized[] = 'generatorType';
if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
$serialized[] = 'sequenceGeneratorDefinition';
}
}
if ($this->isMappedSuperclass) {
$serialized[] = 'isMappedSuperclass';
}
if ($this->containsForeignIdentifier) {
$serialized[] = 'containsForeignIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
}
if ($this->lifecycleCallbacks) {
$serialized[] = 'lifecycleCallbacks';
}
if ($this->namedQueries) {
$serialized[] = 'namedQueries';
}
if ($this->isReadOnly) {
$serialized[] = 'isReadOnly';
}
return $serialized;
}
/**
* Restores some state that can not be serialized/unserialized.
*
* @return void
*/
public function __wakeup()
{
// Restore ReflectionClass and properties
$this->reflClass = new ReflectionClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) {
$reflField = isset($mapping['declared'])
? new ReflectionProperty($mapping['declared'], $field)
: $this->reflClass->getProperty($field);
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
foreach ($this->associationMappings as $field => $mapping) {
$reflField = isset($mapping['declared'])
? new ReflectionProperty($mapping['declared'], $field)
: $this->reflClass->getProperty($field);
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
}
/**
* Creates a new instance of the mapped class, without invoking the constructor.
*
* @return object
*/
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
return clone $this->_prototype;
}
/**
* @param string $callback
* @param string $event
*/
public function addLifecycleCallback($callback, $event)
{
if ( !$this->reflClass->hasMethod($callback) ||
($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) {
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback);
}
return parent::addLifecycleCallback($callback, $event);
}
}
@@ -24,6 +24,8 @@ use ReflectionException,
Doctrine\ORM\EntityManager,
Doctrine\DBAL\Platforms,
Doctrine\ORM\Events,
Doctrine\Common\Persistence\Mapping\RuntimeReflectionService,
Doctrine\Common\Persistence\Mapping\ReflectionService,
Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface;
/**
@@ -74,6 +76,11 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
*/
private $initialized = false;
/**
* @var ReflectionService
*/
private $reflectionService;
/**
* @param EntityManager $$em
*/
@@ -165,6 +172,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
if ($this->cacheDriver) {
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
$this->wakeupReflection($cached, $this->getReflectionService());
$this->loadedMetadata[$realClassName] = $cached;
} else {
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
@@ -220,7 +228,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
{
// Collect parent classes, ignoring transient (not-mapped) classes.
$parentClasses = array();
foreach (array_reverse(class_parents($name)) as $parentClass) {
foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) {
if ( ! $this->driver->isTransient($parentClass)) {
$parentClasses[] = $parentClass;
}
@@ -261,6 +269,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
}
$class = $this->newClassMetadataInstance($className);
$this->initializeReflection($class, $this->getReflectionService());
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
@@ -323,6 +332,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->wakeupReflection($class, $this->getReflectionService());
$this->validateRuntimeMetadata($class, $parent);
@@ -349,11 +359,15 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
*/
protected function validateRuntimeMetadata($class, $parent)
{
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($class->name);
if ( ! $class->reflClass ) {
// only validate if there is a reflection class instance
return;
}
$class->validateIdentifier();
$class->validateAssocations();
$class->validateLifecycleCallbacks($this->getReflectionService());
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
@@ -371,10 +385,6 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
if ($class->usesIdGenerator() && $class->isIdentifierComposite) {
throw MappingException::compositeKeyAssignedIdGeneratorRequired($class->name);
}
}
/**
@@ -533,4 +543,51 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
return $this->driver->isTransient($class);
}
/**
* Get reflectionService.
*
* @return \Doctrine\Common\Persistence\Mapping\ReflectionService
*/
public function getReflectionService()
{
if ($this->reflectionService === null) {
$this->reflectionService = new RuntimeReflectionService();
}
return $this->reflectionService;
}
/**
* Set reflectionService.
*
* @param reflectionService the value to set.
*/
public function setReflectionService(ReflectionService $reflectionService)
{
$this->reflectionService = $reflectionService;
}
/**
* Wakeup reflection after ClassMetadata gets unserialized from cache.
*
* @param ClassMetadataInfo $class
* @param ReflectionService $reflService
* @return void
*/
protected function wakeupReflection(ClassMetadataInfo $class, ReflectionService $reflService)
{
$class->wakeupReflection($reflService);
}
/**
* Initialize Reflection after ClassMetadata was constructed.
*
* @param ClassMetadataInfo $class
* @param ReflectionService $reflService
* @return void
*/
protected function initializeReflection(ClassMetadataInfo $class, ReflectionService $reflService)
{
$class->initializeReflection($reflService);
}
}
+327 -9
View File
@@ -21,6 +21,7 @@ namespace Doctrine\ORM\Mapping;
use Doctrine\DBAL\Types\Type;
use ReflectionClass;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
@@ -40,7 +41,7 @@ use ReflectionClass;
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
*/
class ClassMetadataInfo
class ClassMetadataInfo implements ClassMetadata
{
/* The inheritance mapping types */
/**
@@ -494,6 +495,20 @@ class ClassMetadataInfo
*/
public $isReadOnly = false;
/**
* The ReflectionProperty instances of the mapped class.
*
* @var array
*/
public $reflFields = array();
/**
* The prototype from which new instances of the mapped class are created.
*
* @var object
*/
private $_prototype;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
@@ -506,6 +521,308 @@ class ClassMetadataInfo
$this->rootEntityName = $entityName;
}
/**
* Gets the ReflectionPropertys of the mapped class.
*
* @return array An array of ReflectionProperty instances.
*/
public function getReflectionProperties()
{
return $this->reflFields;
}
/**
* Gets a ReflectionProperty for a specific field of the mapped class.
*
* @param string $name
* @return ReflectionProperty
*/
public function getReflectionProperty($name)
{
return $this->reflFields[$name];
}
/**
* Gets the ReflectionProperty for the single identifier field.
*
* @return ReflectionProperty
* @throws BadMethodCallException If the class has a composite identifier.
*/
public function getSingleIdReflectionProperty()
{
if ($this->isIdentifierComposite) {
throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
}
return $this->reflFields[$this->identifier[0]];
}
/**
* Extracts the identifier values of an entity of this class.
*
* For composite identifiers, the identifier values are returned as an array
* with the same order as the field order in {@link identifier}.
*
* @param object $entity
* @return array
*/
public function getIdentifierValues($entity)
{
if ($this->isIdentifierComposite) {
$id = array();
foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) {
$id[$idField] = $value;
}
}
return $id;
}
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
/**
* Populates the entity identifier of an entity.
*
* @param object $entity
* @param mixed $id
* @todo Rename to assignIdentifier()
*/
public function setIdentifierValues($entity, array $id)
{
foreach ($id as $idField => $idValue) {
$this->reflFields[$idField]->setValue($entity, $idValue);
}
}
/**
* Sets the specified field to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setFieldValue($entity, $field, $value)
{
$this->reflFields[$field]->setValue($entity, $value);
}
/**
* Gets the specified field's value off the given entity.
*
* @param object $entity
* @param string $field
*/
public function getFieldValue($entity, $field)
{
return $this->reflFields[$field]->getValue($entity);
}
/**
* Creates a string representation of this instance.
*
* @return string The string representation of this instance.
* @todo Construct meaningful string representation.
*/
public function __toString()
{
return __CLASS__ . '@' . spl_object_hash($this);
}
/**
* 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.
*
* Parts that are also NOT serialized because they can not be properly unserialized:
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
{
// This metadata is always serialized/cached.
$serialized = array(
'associationMappings',
'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
'fieldMappings',
'fieldNames',
'identifier',
'isIdentifierComposite', // TODO: REMOVE
'name',
'namespace', // TODO: REMOVE
'table',
'rootEntityName',
'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
);
// The rest of the metadata is only serialized if necessary.
if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
$serialized[] = 'changeTrackingPolicy';
}
if ($this->customRepositoryClassName) {
$serialized[] = 'customRepositoryClassName';
}
if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
$serialized[] = 'inheritanceType';
$serialized[] = 'discriminatorColumn';
$serialized[] = 'discriminatorValue';
$serialized[] = 'discriminatorMap';
$serialized[] = 'parentClasses';
$serialized[] = 'subClasses';
}
if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
$serialized[] = 'generatorType';
if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
$serialized[] = 'sequenceGeneratorDefinition';
}
}
if ($this->isMappedSuperclass) {
$serialized[] = 'isMappedSuperclass';
}
if ($this->containsForeignIdentifier) {
$serialized[] = 'containsForeignIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
}
if ($this->lifecycleCallbacks) {
$serialized[] = 'lifecycleCallbacks';
}
if ($this->namedQueries) {
$serialized[] = 'namedQueries';
}
if ($this->isReadOnly) {
$serialized[] = 'isReadOnly';
}
return $serialized;
}
/**
* Creates a new instance of the mapped class, without invoking the constructor.
*
* @return object
*/
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
return clone $this->_prototype;
}
/**
* Restores some state that can not be serialized/unserialized.
*
* @param ReflectionService $reflService
* @return void
*/
public function wakeupReflection($reflService)
{
// Restore ReflectionClass and properties
$this->reflClass = $reflService->getClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) {
$this->reflFields[$field] = isset($mapping['declared'])
? $reflService->getAccessibleProperty($mapping['declared'], $field)
: $reflService->getAccessibleProperty($this->name, $field);
}
foreach ($this->associationMappings as $field => $mapping) {
$this->reflFields[$field] = isset($mapping['declared'])
? $reflService->getAccessibleProperty($mapping['declared'], $field)
: $reflService->getAccessibleProperty($this->name, $field);
}
}
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
*
* @param string $entityName The name of the entity class the new instance is used for.
*/
public function initializeReflection($reflService)
{
$this->reflClass = $reflService->getClass($this->name);
$this->namespace = $reflService->getClassNamespace($this->name);
$this->table['name'] = $reflService->getClassShortName($this->name);
if ($this->reflClass) {
$this->name = $this->rootEntityName = $this->reflClass->getName();
}
}
/**
* Validate Identifier
*
* @return void
*/
public function validateIdentifier()
{
// Verify & complete identifier mapping
if ( ! $this->identifier && ! $this->isMappedSuperclass) {
throw MappingException::identifierRequired($this->name);
}
if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
}
}
/**
* Validate association targets actually exist.
*
* @return void
*/
public function validateAssocations()
{
foreach ($this->associationMappings as $field => $mapping) {
if ( ! \Doctrine\Common\ClassLoader::classExists($mapping['targetEntity']) ) {
throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
}
}
}
/**
* Validate lifecycle callbacks
*
* @param ReflectionService $reflService
* @return void
*/
public function validateLifecycleCallbacks($reflService)
{
foreach ($this->lifecycleCallbacks as $event => $callbacks) {
foreach ($callbacks as $callbackFuncName) {
if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
}
}
}
}
/**
* Gets the ReflectionClass instance of the mapped class.
*
@@ -513,9 +830,6 @@ class ClassMetadataInfo
*/
public function getReflectionClass()
{
if ( ! $this->reflClass) {
$this->reflClass = new ReflectionClass($this->name);
}
return $this->reflClass;
}
@@ -791,7 +1105,7 @@ class ClassMetadataInfo
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
if ( ($mapping['type'] & (self::MANY_TO_ONE|self::MANY_TO_MANY)) > 0 &&
if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
isset($mapping['orphanRemoval']) &&
$mapping['orphanRemoval'] == true) {
@@ -893,9 +1207,11 @@ class ClassMetadataInfo
$uniqueContraintColumns = array();
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE) {
if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
if (count($mapping['joinColumns']) == 1) {
$joinColumn['unique'] = true;
if (! isset($mapping['id']) || ! $mapping['id']) {
$joinColumn['unique'] = true;
}
} else {
$uniqueContraintColumns[] = $joinColumn['name'];
}
@@ -1022,6 +1338,8 @@ class ClassMetadataInfo
}
}
$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']));
@@ -1378,7 +1696,7 @@ class ClassMetadataInfo
{
if (isset($table['name'])) {
if ($table['name'][0] == '`') {
$this->table['name'] = trim($table['name'], '`');
$this->table['name'] = str_replace("`", "", $table['name']);
$this->table['quoted'] = true;
} else {
$this->table['name'] = $table['name'];
@@ -1675,7 +1993,7 @@ class ClassMetadataInfo
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name)) {
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
$this->subClasses[] = $className;
}
}
@@ -134,11 +134,15 @@ class AnnotationDriver implements Driver
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
{
$class = $metadata->getReflectionClass();
if (!$class) {
// this happens when running annotation driver in combination with
// static reflection services. This is not the nicest fix
$class = new \ReflectionClass($metadata->name);
}
$classAnnotations = $this->_reader->getClassAnnotations($class);
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
$classAnnotations[get_class($annot)] = $annot;
}
@@ -171,17 +175,25 @@ class AnnotationDriver implements Driver
if ($tableAnnot->indexes !== null) {
foreach ($tableAnnot->indexes as $indexAnnot) {
$primaryTable['indexes'][$indexAnnot->name] = array(
'columns' => $indexAnnot->columns
);
$index = array('columns' => $indexAnnot->columns);
if ( ! empty($indexAnnot->name)) {
$primaryTable['indexes'][$indexAnnot->name] = $index;
} else {
$primaryTable['indexes'][] = $index;
}
}
}
if ($tableAnnot->uniqueConstraints !== null) {
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraint) {
$primaryTable['uniqueConstraints'][$uniqueConstraint->name] = array(
'columns' => $uniqueConstraint->columns
);
foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
$uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns);
if ( ! empty($uniqueConstraintAnnot->name)) {
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
} else {
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
}
}
}
@@ -402,6 +414,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
@@ -419,8 +432,7 @@ class AnnotationDriver implements Driver
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->_reader->getMethodAnnotations($method);
// Compatibility with Doctrine Common 3.x
if ($annotations && is_int(key($annotations))) {
if ($annotations && is_numeric(key($annotations))) {
foreach ($annotations as $annot) {
$annotations[get_class($annot)] = $annot;
}
@@ -475,8 +487,7 @@ class AnnotationDriver implements Driver
{
$classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className));
// Compatibility with Doctrine Common 3.x
if ($classAnnotations && is_int(key($classAnnotations))) {
if ($classAnnotations && is_numeric(key($classAnnotations))) {
foreach ($classAnnotations as $annot) {
if ($annot instanceof \Doctrine\ORM\Mapping\Entity) {
return false;
@@ -130,6 +130,13 @@ class DatabaseDriver implements Driver
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
@@ -185,12 +192,13 @@ class DatabaseDriver implements Driver
$fieldMappings = array();
foreach ($columns as $column) {
$fieldMapping = array();
if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
} else if (in_array($column->getName(), $allForeignKeyColumns)) {
if (in_array($column->getName(), $allForeignKeyColumns)) {
continue;
} else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) {
$fieldMapping['id'] = true;
}
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
$fieldMapping['columnName'] = $column->getName();
$fieldMapping['type'] = strtolower((string) $column->getType());
@@ -291,13 +299,23 @@ class DatabaseDriver implements Driver
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) {
$associationMapping['id'] = true;
}
for ($i = 0; $i < count($cols); $i++) {
$associationMapping['joinColumns'][] = array(
'name' => $cols[$i],
'referencedColumnName' => $fkCols[$i],
);
}
$metadata->mapManyToOne($associationMapping);
//Here we need to check if $cols are the same as $primaryKeyColums
if (!array_diff($cols,$primaryKeyColumns)) {
$metadata->mapOneToOne($associationMapping);
} else {
$metadata->mapManyToOne($associationMapping);
}
}
}
@@ -397,6 +397,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']);
}
if (isset($manyToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphan-removal'];
}
if (isset($manyToManyElement['mapped-by'])) {
$mapping['mappedBy'] = (string)$manyToManyElement['mapped-by'];
} else if (isset($manyToManyElement->{'join-table'})) {
@@ -402,9 +402,6 @@ class YamlDriver extends AbstractFileDriver
if (isset($manyToManyElement['mappedBy'])) {
$mapping['mappedBy'] = $manyToManyElement['mappedBy'];
} else if (isset($manyToManyElement['joinTable'])) {
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
$joinTableElement = $manyToManyElement['joinTable'];
$joinTable = array(
@@ -434,6 +431,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['joinTable'] = $joinTable;
}
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
if (isset($manyToManyElement['cascade'])) {
$mapping['cascade'] = $manyToManyElement['cascade'];
}
@@ -446,6 +447,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
$metadata->mapManyToMany($mapping);
}
}
+2
View File
@@ -35,6 +35,8 @@ final class ManyToMany implements Annotation
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}
@@ -324,4 +324,9 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("Entity '". $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported.");
}
public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName)
{
return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'.");
}
}
@@ -0,0 +1,107 @@
<?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;
/**
* Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ORMInvalidArgumentException extends \InvalidArgumentException
{
static public function scheduleInsertForManagedEntity($entity)
{
return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion.");
}
static public function scheduleInsertForRemovedEntity($entity)
{
return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion.");
}
static public function scheduleInsertTwice($entity)
{
return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice.");
}
static public function entityWithoutIdentity($className, $entity)
{
throw new self(
"The given entity of type '" . $className . "' (".self::objToStr($entity).") has no identity/no " .
"id values set. It cannot be added to the identity map."
);
}
static public function readOnlyRequiresManagedEntity($entity)
{
return new self("Only managed entities can be marked or checked as read only. But " . self::objToStr($entity) . " is not");
}
static public function newEntityFoundThroughRelationship(array $assoc, $entry)
{
return new self("A new entity was found through the relationship '"
. $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not"
. " configured to cascade persist operations for entity: " . self::objToStr($entry) . "."
. " To solve this issue: Either explicitly call EntityManager#persist()"
. " on this unknown entity or configure cascade persist "
. " this association in the mapping for example @ManyToOne(..,cascade={\"persist\"}). "
. " If you cannot find out which entity causes the problem"
. " implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.");
}
static public function detachedEntityFoundThroughRelationship(array $assoc, $entry)
{
throw new self("A detached entity of type " . $assoc['targetEntity'] . " (" . self::objToStr($entry) . ") "
. " was found through the relationship '" . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' "
. "during cascading a persist operation.");
}
static public function entityNotManaged($entity)
{
throw new self("Entity " . self::objToStr($entity) . " is not managed. An entity is managed if its fetched " .
"from the database or registered as new through EntityManager#persist");
}
static public function entityHasNoIdentity($entity, $operation)
{
throw new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity));
}
static public function entityIsRemoved($entity, $operation)
{
throw new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity));
}
static public function detachedEntityCannot($entity, $operation)
{
throw new self("A detached entity was found during " . $operation . " " . self::objToStr($entity));
}
/**
* Helper method to show an object as string.
*
* @param object $obj
* @return string
*/
private static function objToStr($obj)
{
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
}
}
+27 -3
View File
@@ -305,6 +305,7 @@ final class PersistentCollection implements Collection
if ($this->association !== null &&
$this->association['isOwningSide'] &&
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
$this->owner &&
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
@@ -387,7 +388,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
@@ -425,7 +426,7 @@ final class PersistentCollection implements Collection
$this->changed();
if ($this->association !== null &&
$this->association['type'] === ClassMetadata::ONE_TO_MANY &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -630,7 +631,7 @@ final class PersistentCollection implements Collection
$uow = $this->em->getUnitOfWork();
if ($this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
if ($this->association['type'] & ClassMetadata::TO_MANY && $this->association['orphanRemoval']) {
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
// hence for event listeners we need the objects in memory.
$this->initialize();
@@ -759,4 +760,27 @@ final class PersistentCollection implements Collection
return $this->coll->slice($offset, $length);
}
/**
* Cleanup internal state of cloned persistent collection.
*
* The following problems have to be prevented:
* 1. Added entities are added to old PC
* 2. New collection is not dirty, if reused on other entity nothing
* changes.
* 3. Snapshot leads to invalid diffs being generated.
* 4. Lazy loading grabs entities from old owner object.
* 5. New collection is connected to old owner and leads to duplicate keys.
*/
public function __clone()
{
$this->initialize();
$this->owner = null;
if (is_object($this->coll)) {
$this->coll = clone $this->coll;
}
$this->snapshot = array();
$this->changed();
}
}
@@ -204,4 +204,4 @@ abstract class AbstractCollectionPersister
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
}
@@ -698,16 +698,6 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
$evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
}
}
/**
@@ -1153,6 +1143,7 @@ class BasicEntityPersister
$placeholder = '?';
if (isset($this->_columnTypes[$column]) &&
isset($this->_class->fieldNames[$column]) &&
isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) {
$type = Type::getType($this->_columnTypes[$column]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform);
@@ -192,7 +192,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/
public function count(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
$mapping = $filterMapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
@@ -218,7 +218,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
}
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
if ($filterSql) {
$whereClauses[] = $filterSql;
}
@@ -253,8 +253,15 @@ class ManyToManyPersister extends AbstractCollectionPersister
{
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
// Shortcut for new entities
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// Entity is scheduled for inclusion
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
@@ -275,7 +282,15 @@ class ManyToManyPersister extends AbstractCollectionPersister
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
@@ -295,7 +310,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters)
{
$uow = $this->_em->getUnitOfWork();
$mapping = $coll->getMapping();
$mapping = $filterMapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
@@ -332,7 +347,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
}
if ($addFilters) {
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
if ($filterSql) {
$quotedJoinTable .= ' t ' . $joinTargetEntitySQL;
$whereClauses[] = $filterSql;
@@ -351,13 +366,21 @@ class ManyToManyPersister extends AbstractCollectionPersister
* have to join in the actual entities table leading to additional
* JOIN.
*
* @param array $targetEntity Array containing mapping information.
* @param array $mapping Array containing mapping information.
*
* @return string The SQL query part to add to a query.
*/
public function getFilterSql($mapping)
{
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
if ($mapping['isOwningSide']) {
$joinColumns = $mapping['relationToTargetKeyColumns'];
} else {
$mapping = $targetClass->associationMappings[$mapping['mappedBy']];
$joinColumns = $mapping['relationToSourceKeyColumns'];
}
$targetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
// A join is needed if there is filtering on the target entity
@@ -368,7 +391,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
. ' ON';
$joinTargetEntitySQLClauses = array();
foreach ($mapping['relationToTargetKeyColumns'] as $joinTableColumn => $targetTableColumn) {
foreach ($joinColumns as $joinTableColumn => $targetTableColumn) {
$joinTargetEntitySQLClauses[] = ' t.' . $joinTableColumn . ' = ' . 'te.' . $targetTableColumn;
}
@@ -119,8 +119,9 @@ class OneToManyPersister extends AbstractCollectionPersister
: $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
$filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($targetClass, 't')) {
if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
$whereClauses[] = '(' . $filterExpr . ')';
}
}
@@ -158,7 +159,14 @@ class OneToManyPersister extends AbstractCollectionPersister
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// Entity is scheduled for inclusion
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
@@ -167,7 +175,7 @@ class OneToManyPersister extends AbstractCollectionPersister
// 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()) );
$id = current( $uow->getEntityIdentifier($coll->getOwner()));
return $persister->exists($element, array($mapping['mappedBy'] => $id));
}
@@ -182,7 +190,15 @@ class OneToManyPersister extends AbstractCollectionPersister
$uow = $this->_em->getUnitOfWork();
// shortcut for new entities
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
$entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
if ($entityState === UnitOfWork::STATE_NEW) {
return false;
}
// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
return false;
}
@@ -48,12 +48,13 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$columnList = parent::_getSelectColumnListSQL();
// Append discriminator column
$discrColumn = $this->_class->discriminatorColumn['name'];
$columnList .= ', ' . $discrColumn;
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
// Append discriminator column
$discrColumn = $this->_class->discriminatorColumn['name'];
$columnList .= ', ' . $tableAlias . '.' . $discrColumn;
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
+78
View File
@@ -0,0 +1,78 @@
<?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\Proxy;
/**
* Special Autoloader for Proxy classes because them not being PSR-0 compatible.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class Autoloader
{
/**
* Resolve proxy class name to a filename based on the following pattern.
*
* 1. Remove Proxy namespace from class name
* 2. Remove namespace seperators from remaining class name.
* 3. Return PHP filename from proxy-dir with the result from 2.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param string $className
* @return string
*/
static public function resolveFile($proxyDir, $proxyNamespace, $className)
{
if (0 !== strpos($className, $proxyNamespace)) {
throw ProxyException::notProxyClass($className, $proxyNamespace);
}
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1));
return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php';
}
/**
* Register and return autoloader callback for the given proxy dir and
* namespace.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param Closure $notFoundCallback Invoked when the proxy file is not found.
* @return Closure
*/
static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null)
{
$proxyNamespace = ltrim($proxyNamespace, "\\");
$autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) {
if (0 === strpos($className, $proxyNamespace)) {
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
if ($notFoundCallback && ! file_exists($file)) {
$notFoundCallback($proxyDir, $proxyNamespace, $className);
}
require $file;
}
};
spl_autoload_register($autoloader);
return $autoloader;
}
}
+3 -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
@@ -21,10 +19,12 @@
namespace Doctrine\ORM\Proxy;
use Doctrine\Common\Persistence\Proxy as BaseProxy;
/**
* Interface for proxy classes.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
interface Proxy {}
interface Proxy extends BaseProxy {}
+9 -1
View File
@@ -40,4 +40,12 @@ class ProxyException extends \Doctrine\ORM\ORMException {
return new self("You must configure a proxy namespace. See docs for details");
}
}
public static function notProxyClass($className, $proxyNamespace)
{
return new self(sprintf(
"The class %s is not part of the proxy namespace %s",
$className, $proxyNamespace
));
}
}
+79 -21
View File
@@ -21,7 +21,8 @@ namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\AssociationMapping;
Doctrine\ORM\Mapping\AssociationMapping,
Doctrine\Common\Util\ClassUtils;
/**
* This factory is used to create proxy objects for entities at runtime.
@@ -41,6 +42,14 @@ class ProxyFactory
/** The directory that contains all proxy classes. */
private $_proxyDir;
/**
* Used to match very simple id methods that don't need
* to be proxied since the identifier is known.
*
* @var string
*/
const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i';
/**
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
* connected to the given <tt>EntityManager</tt>.
@@ -74,13 +83,12 @@ class ProxyFactory
*/
public function getProxy($className, $identifier)
{
$proxyClassName = str_replace('\\', '', $className) . 'Proxy';
$fqn = $this->_proxyNamespace . '\\' . $proxyClassName;
$fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace);
if (! class_exists($fqn, false)) {
$fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php';
$fileName = $this->getProxyFileName($className);
if ($this->_autoGenerate) {
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
$this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate);
}
require $fileName;
}
@@ -94,6 +102,22 @@ class ProxyFactory
return new $fqn($entityPersister, $identifier);
}
/**
* Generate the Proxy file name
*
* @param string $className
* @param string $baseDir Optional base directory for proxy file name generation.
* If not specified, the directory configured on the Configuration of the
* EntityManager will be used by this factory.
* @return string
*/
private function getProxyFileName($className, $baseDir = null)
{
$proxyDir = $baseDir ?: $this->_proxyDir;
return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
}
/**
* Generates proxy classes for all given classes.
*
@@ -101,32 +125,37 @@ class ProxyFactory
* @param string $toDir The target directory of the proxy classes. If not specified, the
* directory configured on the Configuration of the EntityManager used
* by this factory is used.
* @return int Number of generated proxies.
*/
public function generateProxyClasses(array $classes, $toDir = null)
{
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
$num = 0;
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass) {
if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) {
continue;
}
$proxyClassName = str_replace('\\', '', $class->name) . 'Proxy';
$proxyFileName = $proxyDir . $proxyClassName . '.php';
$this->_generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate);
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
$num++;
}
return $num;
}
/**
* Generates a proxy class file.
*
* @param $class
* @param $originalClassName
* @param $proxyClassName
* @param $file The path of the file to write to.
*/
private function _generateProxyClass($class, $proxyClassName, $fileName, $file)
private function _generateProxyClass($class, $fileName, $file)
{
$methods = $this->_generateMethods($class);
$sleepImpl = $this->_generateSleep($class);
@@ -138,16 +167,19 @@ class ProxyFactory
'<methods>', '<sleepImpl>', '<cloneImpl>'
);
if(substr($class->name, 0, 1) == "\\") {
$className = substr($class->name, 1);
} else {
$className = $class->name;
}
$className = ltrim($class->name, '\\');
$proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->_proxyNamespace);
$parts = explode('\\', strrev($proxyClassName), 2);
$proxyClassNamespace = strrev($parts[1]);
$proxyClassName = strrev($parts[0]);
$replacements = array(
$this->_proxyNamespace,
$proxyClassName, $className,
$methods, $sleepImpl, $cloneImpl
$proxyClassNamespace,
$proxyClassName,
$className,
$methods,
$sleepImpl,
$cloneImpl
);
$file = str_replace($placeholders, $replacements, $file);
@@ -230,6 +262,14 @@ class ProxyFactory
}
/**
* Check if the method is a short identifier getter.
*
* What does this mean? For proxy objects the identifier is already known,
* however accessing the getter for this identifier usually triggers the
* lazy loading, leading to a query that may not be necessary if only the
* ID is interesting for the userland code (for example in views that
* generate links to the entity, but do not display anything else).
*
* @param ReflectionMethod $method
* @param ClassMetadata $class
* @return bool
@@ -237,7 +277,7 @@ class ProxyFactory
private function isShortIdentifierGetter($method, $class)
{
$identifier = lcfirst(substr($method->getName(), 3));
return (
$cheapCheck = (
$method->getNumberOfParameters() == 0 &&
substr($method->getName(), 0, 3) == "get" &&
in_array($identifier, $class->identifier, true) &&
@@ -245,6 +285,18 @@ class ProxyFactory
(($method->getEndLine() - $method->getStartLine()) <= 4)
&& in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string'))
);
if ($cheapCheck) {
$code = file($method->getDeclaringClass()->getFileName());
$code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1)));
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
if (preg_match($pattern, $code)) {
return true;
}
}
return false;
}
/**
@@ -318,6 +370,12 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
}
}
/** @private */
public function __isInitialized()
{
return $this->__isInitialized__;
}
<methods>
public function __sleep()
+2 -34
View File
@@ -282,7 +282,8 @@ final class Query extends AbstractQuery
}
$sqlPositions = $paramMappings[$key];
$value = array_values($this->processParameterValue($value));
// optimized multi value sql positions away for now, they are not allowed in DQL anyways.
$value = array($value);
$countValue = count($value);
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
@@ -305,39 +306,6 @@ final class Query extends AbstractQuery
return array($sqlParams, $types);
}
/**
* Process an individual parameter value
*
* @param mixed $value
* @return array
*/
private function processParameterValue($value)
{
switch (true) {
case is_array($value):
for ($i = 0, $l = count($value); $i < $l; $i++) {
$paramValue = $this->processParameterValue($value[$i]);
// TODO: What about Entities that have composite primary key?
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
return array($value);
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
}
$class = $this->_em->getClassMetadata(get_class($value));
return array_values($class->getIdentifierValues($value));
default:
return array($value);
}
}
/**
* Defines a cache driver to be used for caching queries.
*
+4 -2
View File
@@ -75,7 +75,7 @@ class Expr
* Creates an ASCending order expression.
*
* @param $sort
* @return OrderBy
* @return Expr\OrderBy
*/
public function asc($expr)
{
@@ -86,7 +86,7 @@ class Expr
* Creates a DESCending order expression.
*
* @param $sort
* @return OrderBy
* @return Expr\OrderBy
*/
public function desc($expr)
{
@@ -562,6 +562,8 @@ class Expr
{
if (is_numeric($literal) && !is_string($literal)) {
return (string) $literal;
} else if (is_bool($literal)) {
return $literal ? "true" : "false";
} else {
return "'" . str_replace("'", "''", $literal) . "'";
}
+11 -1
View File
@@ -71,6 +71,12 @@ class ResultSetMapping
*/
public $scalarMappings = array();
/**
* @ignore
* @var array Maps column names in the result set to the alias/field type to use in the mapped result.
*/
public $typeMappings = array();
/**
* @ignore
* @var array Maps entities in the result set to the alias name to use in the mapped result.
@@ -296,12 +302,16 @@ class ResultSetMapping
*
* @param string $columnName The name of the column in the SQL result set.
* @param string $alias The result alias with which the scalar result should be placed in the result structure.
* @param string $type The column type
*
* @return ResultSetMapping This ResultSetMapping instance.
*
* @todo Rename: addScalar
*/
public function addScalarResult($columnName, $alias)
public function addScalarResult($columnName, $alias, $type = null)
{
$this->scalarMappings[$columnName] = $alias;
$this->typeMappings[$columnName] = $type ?: 'string';
if ( ! $this->isMixed && $this->fieldMappings) {
$this->isMixed = true;
+39 -12
View File
@@ -39,6 +39,11 @@ use Doctrine\DBAL\LockMode,
*/
class SqlWalker implements TreeWalker
{
/**
* @var string
*/
const HINT_DISTINCT = 'doctrine.distinct';
/**
* @var ResultSetMapping
*/
@@ -435,13 +440,15 @@ class SqlWalker implements TreeWalker
$sql .= ' ' . $this->_platform->getWriteLockSQL();
break;
case LockMode::PESSIMISTIC_OPTIMISTIC:
case LockMode::OPTIMISTIC:
foreach ($this->_selectedClasses AS $selectedClass) {
if ( ! $class->isVersioned) {
if ( ! $selectedClass['class']->isVersioned) {
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
break;
case LockMode::NONE:
break;
default:
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
@@ -590,6 +597,10 @@ class SqlWalker implements TreeWalker
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
$this->_query->setHint(self::HINT_DISTINCT, true);
}
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
||
@@ -627,11 +638,17 @@ class SqlWalker implements TreeWalker
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) continue;
if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) {
continue;
}
// Add foreign key columns of class and also parent classes
foreach ($class->associationMappings as $assoc) {
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
continue;
} else if ( !$addMetaColumns && !isset($assoc['id'])) {
continue;
}
$owningClass = (isset($assoc['inherited'])) ? $this->_em->getClassMetadata($assoc['inherited']) : $class;
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
@@ -645,6 +662,11 @@ class SqlWalker implements TreeWalker
}
}
// Add foreign key columns to SQL, if necessary
if ( ! $addMetaColumns) {
continue;
}
// Add foreign key columns of subclasses
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
@@ -811,9 +833,10 @@ class SqlWalker implements TreeWalker
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $relation['type'] & ClassMetadata::TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) {
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
}
}
if ($joinVarDecl->indexBy) {
@@ -1080,12 +1103,14 @@ class SqlWalker implements TreeWalker
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
$col = $sqlTableAlias . '.' . $columnName;
$fieldType = $class->getTypeOfField($fieldName);
if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($fieldName));
$type = Type::getType($fieldType);
$col = $type->convertToPHPValueSQL($col, $this->_conn->getDatabasePlatform());
}
@@ -1094,7 +1119,7 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
$this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias;
}
break;
@@ -1118,7 +1143,8 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
// We cannot resolve field type here; assume 'string'.
$this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string');
}
break;
@@ -1131,7 +1157,8 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
// We cannot resolve field type here; assume 'string'.
$this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string');
}
break;
+26 -4
View File
@@ -50,6 +50,7 @@ class QueryBuilder
* @var array The array of DQL parts collected.
*/
private $_dqlParts = array(
'distinct' => false,
'select' => array(),
'from' => array(),
'join' => array(),
@@ -119,7 +120,7 @@ class QueryBuilder
* For more complex expression construction, consider storing the expression
* builder object in a local variable.
*
* @return Expr
* @return Query\Expr
*/
public function expr()
{
@@ -346,7 +347,7 @@ class QueryBuilder
* ->setParameters(array(
* 'user_id1' => 1,
* 'user_id2' => 2
* ));
));
* </code>
*
* @param array $params The query parameters to set.
@@ -503,6 +504,25 @@ class QueryBuilder
return $this->add('select', new Expr\Select($selects), false);
}
/**
* Add a DISTINCT flag to this query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->distinct()
* ->from('User', 'u');
* </code>
*
* @param bool
* @return QueryBuilder
*/
public function distinct($flag = true)
{
$this->_dqlParts['distinct'] = (bool) $flag;
return $this;
}
/**
* Adds an item that is to be returned in the query result.
*
@@ -981,7 +1001,9 @@ class QueryBuilder
private function _getDQLForSelect()
{
$dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$dql = 'SELECT'
. ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '')
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$fromParts = $this->getDQLPart('from');
$joinParts = $this->getDQLPart('join');
@@ -1090,4 +1112,4 @@ class QueryBuilder
}
}
}
}
}
@@ -38,36 +38,8 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo;
*/
class DisconnectedClassMetadataFactory extends ClassMetadataFactory
{
/**
* @override
*/
protected function newClassMetadataInstance($className)
public function getReflectionService()
{
$metadata = new ClassMetadataInfo($className);
if (strpos($className, "\\") !== false) {
$metadata->namespace = strrev(substr( strrev($className), strpos(strrev($className), "\\")+1 ));
} else {
$metadata->namespace = "";
}
return $metadata;
return new \Doctrine\Common\Persistence\Mapping\StaticReflectionService;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
// validate nothing
}
/**
* @override
*/
protected function getParentClasses($name)
{
return array();
}
}
}
+39 -1
View File
@@ -128,10 +128,12 @@ public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
* <description>
*
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
{
<spaces>$this-><fieldName>[] = $<variableName>;
<spaces>return $this;
}';
private static $_lifecycleCallbackMethodTemplate =
@@ -191,6 +193,8 @@ public function <methodName>()
if ( ! $this->_isNew) {
$this->_parseTokensInEntityFile(file_get_contents($path));
} else {
$this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
}
if ($this->_backupExisting && file_exists($path)) {
@@ -581,9 +585,32 @@ public function <methodName>()
$table[] = 'name="' . $metadata->table['name'] . '"';
}
if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
$constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
$table[] = 'uniqueConstraints={' . $constraints . '}';
}
if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
$constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']);
$table[] = 'indexes={' . $constraints . '}';
}
return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
}
private function _generateTableConstraints($constraintName, $constraints)
{
$annotations = array();
foreach ($constraints as $name => $constraint) {
$columns = array();
foreach ($constraint['columns'] as $column) {
$columns[] = '"' . $column . '"';
}
$annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
}
return implode(', ', $annotations);
}
private function _generateInheritanceAnnotation($metadata)
{
if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
@@ -656,6 +683,9 @@ public function <methodName>()
private function _isAssociationIsNullable($associationMapping)
{
if (isset($associationMapping['id']) && $associationMapping['id']) {
return false;
}
if (isset($associationMapping['joinColumns'])) {
$joinColumns = $associationMapping['joinColumns'];
} else {
@@ -832,6 +862,14 @@ public function <methodName>()
if ($this->_generateAnnotations) {
$lines[] = $this->_spaces . ' *';
if (isset($associationMapping['id']) && $associationMapping['id']) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
}
}
$type = null;
switch ($associationMapping['type']) {
@@ -1088,4 +1126,4 @@ public function <methodName>()
throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
}
}
}
}
@@ -111,16 +111,18 @@ abstract class AbstractExporter
}
foreach ($this->_metadata as $metadata) {
$output = $this->exportClassMetadata($metadata);
$path = $this->_generateOutputPath($metadata);
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
//In case output is returned, write it to a file, skip otherwise
if($output = $this->exportClassMetadata($metadata)){
$path = $this->_generateOutputPath($metadata);
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
}
if (file_exists($path) && !$this->_overwriteExistingFiles) {
throw ExportException::attemptOverwriteExistingFile($path);
}
file_put_contents($path, $output);
}
if (file_exists($path) && !$this->_overwriteExistingFiles) {
throw ExportException::attemptOverwriteExistingFile($path);
}
file_put_contents($path, $output);
}
}
@@ -0,0 +1,80 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a COUNT statement
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class CountWalker extends TreeWalkerAdapter
{
/**
* Distinct mode hint name
*/
const HINT_DISTINCT = 'doctrine_paginator.distinct';
/**
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
$parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT);
$AST->selectClause->selectExpressions = array(
new SelectExpression(
new AggregateExpression('count', $pathExpression, $distinct), null
)
);
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
$AST->orderByClause = null;
}
}
@@ -0,0 +1,115 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\SelectExpression,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\AggregateExpression;
/**
* Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class LimitSubqueryWalker extends TreeWalkerAdapter
{
/**
* ID type hint
*/
const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';
/**
* @var int Counter for generating unique order column aliases
*/
private $_aliasCounter = 0;
/**
* Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
* of the root Entity
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
$selectExpressions = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
// preserve mixed data in query for ordering
if (isset($qComp['resultVariable'])) {
$selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
continue;
}
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
continue;
}
}
$identifier = $parent['metadata']->getSingleIdentifierFieldName();
$this->_getQuery()->setHint(
self::IDENTIFIER_TYPE,
Type::getType($parent['metadata']->getTypeOfField($identifier))
);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$parentName,
$identifier
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id'));
$AST->selectClause->selectExpressions = $selectExpressions;
if (isset($AST->orderByClause)) {
foreach ($AST->orderByClause->orderByItems as $item) {
if ($item->expression instanceof PathExpression) {
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$item->expression->identificationVariable,
$item->expression->field
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$AST->selectClause->selectExpressions[] = new SelectExpression(
$pathExpression,
'_dctrn_ord' . $this->_aliasCounter++
);
}
}
}
$AST->selectClause->isDistinct = true;
}
}
@@ -0,0 +1,180 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Tools\Pagination\WhereInWalker;
use Doctrine\ORM\Tools\Pagination\CountWalker;
use Countable;
use IteratorAggregate;
use ArrayIterator;
/**
* Paginator
*
* The paginator can handle various complex scenarios with DQL.
*
* @author Pablo Díez <pablodip@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license New BSD
*/
class Paginator implements \Countable, \IteratorAggregate
{
/**
* @var Query
*/
private $query;
/**
* @var bool
*/
private $fetchJoinCollection;
/**
* @var int
*/
private $count;
/**
* Constructor.
*
* @param Query|QueryBuilder $query A Doctrine ORM query or query builder.
* @param Boolean $fetchJoinCollection Whether the query joins a collection (true by default).
*/
public function __construct($query, $fetchJoinCollection = true)
{
if ($query instanceof QueryBuilder) {
$query = $query->getQuery();
}
$this->query = $query;
$this->fetchJoinCollection = (Boolean) $fetchJoinCollection;
}
/**
* Returns the query
*
* @return Query
*/
public function getQuery()
{
return $this->query;
}
/**
* Returns whether the query joins a collection.
*
* @return Boolean Whether the query joins a collection.
*/
public function getFetchJoinCollection()
{
return $this->fetchJoinCollection;
}
/**
* {@inheritdoc}
*/
public function count()
{
if ($this->count === null) {
/* @var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
$countQuery->setFirstResult(null)->setMaxResults(null);
try {
$data = $countQuery->getScalarResult();
$data = array_map('current', $data);
$this->count = array_sum($data);
} catch(NoResultException $e) {
$this->count = 0;
}
}
return $this->count;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$offset = $this->query->getFirstResult();
$length = $this->query->getMaxResults();
if ($this->fetchJoinCollection) {
$subQuery = $this->cloneQuery($this->query);
$subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'))
->setFirstResult($offset)
->setMaxResults($length);
$ids = array_map('current', $subQuery->getScalarResult());
$whereInQuery = $this->cloneQuery($this->query);
// don't do this for an empty id array
if (count($ids) > 0) {
$namespace = WhereInWalker::PAGINATOR_ID_ALIAS;
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
$whereInQuery->setFirstResult(null)->setMaxResults(null);
foreach ($ids as $i => $id) {
$i++;
$whereInQuery->setParameter("{$namespace}_{$i}", $id);
}
}
$result = $whereInQuery->getResult($this->query->getHydrationMode());
} else {
$result = $this->cloneQuery($this->query)
->setMaxResults($length)
->setFirstResult($offset)
->getResult($this->query->getHydrationMode())
;
}
return new \ArrayIterator($result);
}
/**
* Clones a query.
*
* @param Query $query The query.
*
* @return Query The cloned query.
*/
private function cloneQuery(Query $query)
{
/* @var $cloneQuery Query */
$cloneQuery = clone $query;
$cloneQuery->setParameters($query->getParameters());
foreach ($query->getHints() as $name => $value) {
$cloneQuery->setHint($name, $value);
}
return $cloneQuery;
}
}
@@ -0,0 +1,142 @@
<?php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. This license can also be viewed
* at http://hobodave.com/license.txt
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
namespace Doctrine\ORM\Tools\Pagination;
use Doctrine\ORM\Query\AST\ArithmeticExpression,
Doctrine\ORM\Query\AST\SimpleArithmeticExpression,
Doctrine\ORM\Query\TreeWalkerAdapter,
Doctrine\ORM\Query\AST\SelectStatement,
Doctrine\ORM\Query\AST\PathExpression,
Doctrine\ORM\Query\AST\InExpression,
Doctrine\ORM\Query\AST\NullComparisonExpression,
Doctrine\ORM\Query\AST\InputParameter,
Doctrine\ORM\Query\AST\ConditionalPrimary,
Doctrine\ORM\Query\AST\ConditionalTerm,
Doctrine\ORM\Query\AST\ConditionalExpression,
Doctrine\ORM\Query\AST\ConditionalFactor,
Doctrine\ORM\Query\AST\WhereClause;
/**
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent
*
* @category DoctrineExtensions
* @package DoctrineExtensions\Paginate
* @author David Abdemoulaie <dave@hobodave.com>
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
* @license http://hobodave.com/license.txt New BSD License
*/
class WhereInWalker extends TreeWalkerAdapter
{
/**
* ID Count hint name
*/
const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count';
/**
* Primary key alias for query
*/
const PAGINATOR_ID_ALIAS = 'dpid';
/**
* Replaces the whereClause in the AST
*
* Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
*
* The parameter namespace (dpid) is defined by
* the PAGINATOR_ID_ALIAS
*
* The total number of parameters is retrieved from
* the HINT_PAGINATOR_ID_COUNT query hint
*
* @param SelectStatement $AST
* @return void
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName()
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
if ($count > 0) {
$arithmeticExpression = new ArithmeticExpression();
$arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(
array($pathExpression)
);
$expression = new InExpression($arithmeticExpression);
$ns = self::PAGINATOR_ID_ALIAS;
for ($i = 1; $i <= $count; $i++) {
$expression->literals[] = new InputParameter(":{$ns}_$i");
}
} else {
$expression = new NullComparisonExpression($pathExpression);
$expression->not = false;
}
$conditionalPrimary = new ConditionalPrimary;
$conditionalPrimary->simpleConditionalExpression = $expression;
if ($AST->whereClause) {
if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
$AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
$AST->whereClause->conditionalExpression = new ConditionalExpression(array(
new ConditionalTerm(array(
$AST->whereClause->conditionalExpression,
$conditionalPrimary
))
));
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
$tmpPrimary = new ConditionalPrimary;
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
$tmpPrimary,
$conditionalPrimary
));
}
} else {
$AST->whereClause = new WhereClause(
new ConditionalExpression(array(
new ConditionalTerm(array(
$conditionalPrimary
))
))
);
}
}
}
+13 -3
View File
@@ -21,6 +21,8 @@ namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException,
Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Schema\Schema,
Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Internal\CommitOrderCalculator,
@@ -127,7 +129,7 @@ class SchemaTool
$sm = $this->_em->getConnection()->getSchemaManager();
$metadataSchemaConfig = $sm->createSchemaConfig();
$metadataSchemaConfig->setExplicitForeignKeyIndexes(false);
$schema = new \Doctrine\DBAL\Schema\Schema(array(), array(), $metadataSchemaConfig);
$schema = new Schema(array(), array(), $metadataSchemaConfig);
$evm = $this->_em->getEventManager();
@@ -223,13 +225,13 @@ class SchemaTool
if (isset($class->table['indexes'])) {
foreach ($class->table['indexes'] AS $indexName => $indexData) {
$table->addIndex($indexData['columns'], $indexName);
$table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] AS $indexName => $indexData) {
$table->addUniqueIndex($indexData['columns'], $indexName);
$table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
@@ -252,6 +254,10 @@ class SchemaTool
}
}
if ( ! $this->_platform->supportsSchemas() && ! $this->_platform->canEmulateSchemas() ) {
$schema->visit(new RemoveNamespacedAssets());
}
if ($evm->hasListeners(ToolEvents::postGenerateSchema)) {
$evm->dispatchEvent(ToolEvents::postGenerateSchema, new GenerateSchemaEventArgs($this->_em, $schema));
}
@@ -360,6 +366,10 @@ class SchemaTool
$options['columnDefinition'] = $mapping['columnDefinition'];
}
if (isset($mapping['options'])) {
$options['customSchemaOptions'] = $mapping['options'];
}
if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) {
$options['autoincrement'] = true;
}
+24 -34
View File
@@ -95,8 +95,8 @@ class SchemaValidator
}
foreach ($class->associationMappings AS $fieldName => $assoc) {
if (!$cmf->hasMetadataFor($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.';
return $ce;
}
@@ -106,6 +106,11 @@ class SchemaValidator
$targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']);
if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) {
$ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " .
"the target entity '". $targetMetadata->name . "' also maps an association as identifier.";
}
/* @var $assoc AssociationMapping */
if ($assoc['mappedBy']) {
if ($targetMetadata->hasField($assoc['mappedBy'])) {
@@ -119,7 +124,7 @@ class SchemaValidator
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ".
"'inversedBy' attribute.";
"'inversedBy=".$fieldName."' attribute.";
} else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) {
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
$assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ".
@@ -162,30 +167,21 @@ class SchemaValidator
if ($assoc['isOwningSide']) {
if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) {
$identifierColumns = $class->getIdentifierColumnNames();
foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) {
if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $class->name . "'.";
break;
}
$fieldName = $class->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $class->identifier)) {
if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
"has to be a primary key column on the target entity class '".$class->name."'.";
break;
}
}
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
if (!isset($targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
$ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
$identifierColumns = $targetMetadata->getIdentifierColumnNames();
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
break;
}
}
@@ -204,29 +200,23 @@ class SchemaValidator
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
$identifierColumns = $targetMetadata->getIdentifierColumnNames();
foreach ($assoc['joinColumns'] AS $joinColumn) {
if (!isset($targetMetadata->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetMetadata->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
"has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
}
}
if (count($class->getIdentifierColumnNames()) != count($assoc['joinColumns'])) {
if (count($identifierColumns) != count($assoc['joinColumns'])) {
$ids = array();
foreach ($assoc['joinColumns'] AS $joinColumn) {
$ids[] = $joinColumn['name'];
}
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), $ids)) .
"have to match to ALL identifier columns of the target entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) .
"' are missing.";
}
}
+74 -30
View File
@@ -390,7 +390,7 @@ class UnitOfWork implements PropertyChangedListener
*/
private function computeSingleEntityChangeSet($entity)
{
if ( ! $this->isInIdentityMap($entity) ) {
if ( $this->getEntityState($entity) !== self::STATE_MANAGED) {
throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity));
}
@@ -584,6 +584,23 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->associationMappings[$propName];
// Persistent collection was exchanged with the "originally"
// created one. This can only mean it was cloned and replaced
// on another entity.
if ($actualValue instanceof PersistentCollection) {
$owner = $actualValue->getOwner();
if ($owner === null) { // cloned
$actualValue->setOwner($entity, $assoc);
} else if ($owner !== $entity) { // no clone, we have to fix
if (!$actualValue->isInitialized()) {
$actualValue->initialize(); // we have to do this otherwise the cols share state
}
$newValue = clone $actualValue;
$newValue->setOwner($entity, $assoc);
$class->reflFields[$propName]->setValue($entity, $newValue);
}
}
if ($orgValue instanceof PersistentCollection) {
// A PersistentCollection was de-referenced, so delete it.
$coid = spl_object_hash($orgValue);
@@ -620,6 +637,15 @@ class UnitOfWork implements PropertyChangedListener
foreach ($class->associationMappings as $field => $assoc) {
if (($val = $class->reflFields[$field]->getValue($entity)) !== null) {
$this->computeAssociationChanges($assoc, $val);
if (!isset($this->entityChangeSets[$oid]) &&
$assoc['isOwningSide'] &&
$assoc['type'] == ClassMetadata::MANY_TO_MANY &&
$val instanceof PersistentCollection &&
$val->isDirty()) {
$this->entityChangeSets[$oid] = array();
$this->originalEntityData[$oid] = $actualData;
$this->entityUpdates[$oid] = $entity;
}
}
}
}
@@ -707,17 +733,19 @@ class UnitOfWork implements PropertyChangedListener
$state = $this->getEntityState($entry, self::STATE_NEW);
$oid = spl_object_hash($entry);
if (!($entry instanceof $assoc['targetEntity'])) {
throw new ORMException(sprintf("Found entity of type %s on association %s#%s, but expecting %s",
get_class($entry),
$assoc['sourceEntity'],
$assoc['fieldName'],
$targetClass->name
));
}
switch ($state) {
case self::STATE_NEW:
if ( ! $assoc['isCascadePersist']) {
$message = "A new entity was found through the relationship '%s#%s' that was not configured " .
' to cascade persist operations for entity: %s. Explicitly persist the new entity or ' .
'configure cascading persist operations on the relationship. If you cannot find out ' .
'which entity causes the problem, implement %s#__toString() to get a clue.';
throw new InvalidArgumentException(sprintf(
$message, $assoc['sourceEntity'], $assoc['fieldName'], self::objToStr($entry), $assoc['targetEntity']
));
throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
}
$this->persistNew($targetClass, $entry);
@@ -735,9 +763,7 @@ class UnitOfWork implements PropertyChangedListener
case self::STATE_DETACHED:
// Can actually not happen right now as we assume STATE_NEW,
// so the exception will be raised from the DBAL layer (constraint violation).
$message = 'A detached entity was found through a relationship during cascading a persist operation.';
throw new InvalidArgumentException($message);
throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
break;
default:
@@ -797,7 +823,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity);
if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
throw new InvalidArgumentException('Entity must be managed.');
throw ORMInvalidArgumentException::entityNotManaged($entity);
}
// skip if change tracking is "NOTIFY"
@@ -1077,11 +1103,14 @@ class UnitOfWork implements PropertyChangedListener
}
if (isset($this->entityDeletions[$oid])) {
throw new InvalidArgumentException("Removed entity can not be scheduled for insertion.");
throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
}
if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
}
if (isset($this->entityInsertions[$oid])) {
throw new InvalidArgumentException("Entity can not be scheduled for insertion twice.");
throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
}
$this->entityInsertions[$oid] = $entity;
@@ -1112,11 +1141,11 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity);
if ( ! isset($this->entityIdentifiers[$oid])) {
throw new InvalidArgumentException("Entity has no identity.");
throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
}
if (isset($this->entityDeletions[$oid])) {
throw new InvalidArgumentException("Entity is removed.");
throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
}
if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
@@ -1256,7 +1285,7 @@ class UnitOfWork implements PropertyChangedListener
$idHash = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]);
if ($idHash === '') {
throw new InvalidArgumentException('The given entity has no identity.');
throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);
}
$className = $classMetadata->rootEntityName;
@@ -1366,13 +1395,14 @@ class UnitOfWork implements PropertyChangedListener
$idHash = implode(' ', $this->entityIdentifiers[$oid]);
if ($idHash === '') {
throw new InvalidArgumentException('The given entity has no identity.');
throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
}
$className = $classMetadata->rootEntityName;
if (isset($this->identityMap[$className][$idHash])) {
unset($this->identityMap[$className][$idHash]);
unset($this->readOnlyObjects[$oid]);
//$this->entityStates[$oid] = self::STATE_DETACHED;
@@ -1513,10 +1543,10 @@ class UnitOfWork implements PropertyChangedListener
case self::STATE_DETACHED:
// Can actually not happen right now since we assume STATE_NEW.
throw new InvalidArgumentException('Detached entity passed to persist().');
throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
default:
throw new UnexpectedValueException(sprintf('Unexpected entity state: %s', $entityState));
throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
}
$this->cascadePersist($entity, $visited);
@@ -1580,10 +1610,9 @@ class UnitOfWork implements PropertyChangedListener
break;
case self::STATE_DETACHED:
throw new InvalidArgumentException('A detached entity can not be removed.');
throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
default:
throw new UnexpectedValueException(sprintf('Unexpected entity state: %s', $entityState));
throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
}
}
@@ -1665,8 +1694,7 @@ class UnitOfWork implements PropertyChangedListener
if ($managedCopy) {
// We have the entity in-memory already, just make sure its not removed.
if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) {
throw new InvalidArgumentException('Removed entity detected during merge.'
. ' Can not merge with a removed entity.');
throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge");
}
} else {
// We need to fetch the managed copy in order to merge.
@@ -1684,6 +1712,10 @@ class UnitOfWork implements PropertyChangedListener
$class->setIdentifierValues($managedCopy, $id);
$this->persistNew($class, $managedCopy);
} else {
if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized__) {
$managedCopy->__load();
}
}
}
@@ -1883,7 +1915,7 @@ class UnitOfWork implements PropertyChangedListener
$class = $this->em->getClassMetadata(get_class($entity));
if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
throw ORMInvalidArgumentException::entityNotManaged($entity);
}
$this->getEntityPersister($class->name)->refresh(
@@ -2107,7 +2139,7 @@ class UnitOfWork implements PropertyChangedListener
public function lock($entity, $lockMode, $lockVersion = null)
{
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
throw ORMInvalidArgumentException::entityNotManaged($entity);
}
$entityName = get_class($entity);
@@ -2184,6 +2216,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionDeletions =
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
@@ -2493,6 +2526,17 @@ class UnitOfWork implements PropertyChangedListener
}
}
if ($overrideLocalValues) {
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
if ($this->evm->hasListeners(Events::postLoad)) {
$this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
}
}
return $entity;
}
@@ -2881,7 +2925,7 @@ class UnitOfWork implements PropertyChangedListener
public function markReadOnly($object)
{
if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
throw new InvalidArgumentException("Managed entity required");
throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
}
$this->readOnlyObjects[spl_object_hash($object)] = true;
@@ -2897,7 +2941,7 @@ class UnitOfWork implements PropertyChangedListener
public function isReadOnly($object)
{
if ( ! is_object($object) ) {
throw new InvalidArgumentException("Managed entity required");
throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
}
return isset($this->readOnlyObjects[spl_object_hash($object)]);
+1 -1
View File
@@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.2.0-BETA1';
const VERSION = '2.2.2';
/**
* Compares a Doctrine version with the current one.
@@ -14,4 +14,4 @@ class CompanyAuction extends CompanyEvent {
public function getData() {
return $this->data;
}
}
}
@@ -21,7 +21,7 @@ abstract class CompanyContract
private $id;
/**
* @ManyToOne(targetEntity="CompanyEmployee")
* @ManyToOne(targetEntity="CompanyEmployee", inversedBy="soldContracts")
*/
private $salesPerson;
@@ -32,7 +32,7 @@ abstract class CompanyContract
private $completed = false;
/**
* @ManyToMany(targetEntity="CompanyEmployee")
* @ManyToMany(targetEntity="CompanyEmployee", inversedBy="contracts")
* @JoinTable(name="company_contract_employees",
* joinColumns={@JoinColumn(name="contract_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={@JoinColumn(name="employee_id", referencedColumnName="id")}
@@ -86,4 +86,4 @@ abstract class CompanyContract
}
abstract public function calculatePrice();
}
}
@@ -23,6 +23,16 @@ class CompanyEmployee extends CompanyPerson
*/
private $startDate;
/**
* @ManyToMany(targetEntity="CompanyContract", mappedBy="engineers", fetch="EXTRA_LAZY")
*/
public $contracts;
/**
* @OneToMany(targetEntity="CompanyFlexUltraContract", mappedBy="salesPerson", fetch="EXTRA_LAZY")
*/
public $soldContracts;
public function getSalary() {
return $this->salary;
}
@@ -46,4 +56,4 @@ class CompanyEmployee extends CompanyPerson
public function setStartDate($date) {
$this->startDate = $date;
}
}
}
@@ -18,6 +18,15 @@ class CompanyFlexContract extends CompanyContract
*/
private $pricePerHour = 0;
/**
* @ManyToMany(targetEntity="CompanyManager", inversedBy="managedContracts", fetch="EXTRA_LAZY")
* @JoinTable(name="company_contract_managers",
* joinColumns={@JoinColumn(name="contract_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={@JoinColumn(name="employee_id", referencedColumnName="id")}
* )
*/
public $managers;
public function calculatePrice()
{
return $this->hoursWorked * $this->pricePerHour;
@@ -42,4 +51,18 @@ class CompanyFlexContract extends CompanyContract
{
$this->pricePerHour = $pricePerHour;
}
}
public function getManagers()
{
return $this->managers;
}
public function addManager(CompanyManager $manager)
{
$this->managers[] = $manager;
}
public function removeManager(CompanyManager $manager)
{
$this->managers->removeElement($manager);
}
}
@@ -19,6 +19,11 @@ class CompanyManager extends CompanyEmployee
*/
private $car;
/**
* @ManyToMany(targetEntity="CompanyFlexContract", mappedBy="managers", fetch="EXTRA_LAZY")
*/
public $managedContracts;
public function getTitle() {
return $this->title;
}
@@ -34,4 +39,4 @@ class CompanyManager extends CompanyEmployee
public function setCar(CompanyCar $car) {
$this->car = $car;
}
}
}
@@ -11,9 +11,9 @@ class CompanyOrganization {
private $id;
/**
* @OneToMany(targetEntity="CompanyEvent", mappedBy="organization", cascade={"persist"})
* @OneToMany(targetEntity="CompanyEvent", mappedBy="organization", cascade={"persist"}, fetch="EXTRA_LAZY")
*/
private $events;
public $events;
public function getId() {
return $this->id;
@@ -41,4 +41,4 @@ class CompanyOrganization {
public function setMainEvent($event) {
$this->mainevent = $event;
}
}
}
@@ -28,7 +28,7 @@ class CompanyPerson
*/
private $name;
/**
* @OneToOne(targetEntity="CompanyPerson", mappedBy="spouse")
* @OneToOne(targetEntity="CompanyPerson")
* @JoinColumn(name="spouse_id", referencedColumnName="id")
*/
private $spouse;
@@ -16,7 +16,7 @@ class DDC117Reference
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article", inversedBy="references")
* @ManyToOne(targetEntity="DDC117Article")
* @JoinColumn(name="target_id", referencedColumnName="article_id")
*/
private $target;
@@ -61,4 +61,4 @@ class DDC117Reference
{
return $this->description;
}
}
}
@@ -9,7 +9,7 @@ class DDC117Translation
{
/**
* @Id
* @ManyToOne(targetEntity="DDC117Article")
* @ManyToOne(targetEntity="DDC117Article", inversedBy="translations")
* @JoinColumn(name="article_id", referencedColumnName="article_id")
*/
private $article;
@@ -62,4 +62,4 @@ class DDC117Translation
{
return $this->reviewedByEditors;
}
}
}
@@ -17,7 +17,7 @@ class LegacyUserReference
/**
* @Id
* @ManyToOne(targetEntity="LegacyUser", inversedBy="_references")
* @ManyToOne(targetEntity="LegacyUser")
* @JoinColumn(name="iUserIdTarget", referencedColumnName="iUserId")
*/
private $_target;
@@ -26,7 +26,7 @@ class NavPointOfInterest
private $name;
/**
* @ManyToOne(targetEntity="NavCountry")
* @ManyToOne(targetEntity="NavCountry", inversedBy="pois")
*/
private $country;
@@ -53,4 +53,4 @@ class NavPointOfInterest
public function getCountry() {
return $this->country;
}
}
}
@@ -1141,6 +1141,21 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush($user);
}
/**
* @group DDC-720
* @group DDC-1612
*/
public function testFlushSingleNewEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush($user);
}
/**
* @group DDC-720
*/
@@ -1197,4 +1212,21 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user2 = $this->_em->find(get_class($user2), $user2->id);
$this->assertEquals('developer', $user2->status);
}
/**
* @group DDC-1585
*/
public function testWrongAssocationInstance()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$user->address = $user;
$this->_em->persist($user);
$this->setExpectedException("Doctrine\ORM\ORMException", "Found entity of type Doctrine\Tests\Models\CMS\CmsUser on association Doctrine\Tests\Models\CMS\CmsUser#address, but expecting Doctrine\Tests\Models\CMS\CmsAddress");
$this->_em->flush();
}
}
@@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Navigation\NavCountry;
use Doctrine\Tests\Models\Navigation\NavPointOfInterest;
use Doctrine\Tests\Models\Navigation\NavTour;
use Doctrine\Tests\Models\Navigation\NavPhotos;
require_once __DIR__ . '/../../TestInit.php';
@@ -51,6 +52,25 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Brandenburger Tor', $poi->getName());
}
/**
* @group DDC-1651
*/
public function testSetParameterCompositeKeyObject()
{
$this->putGermanysBrandenburderTor();
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('lat' => 100, 'long' => 200));
$photo = new NavPhotos($poi, "asdf");
$this->_em->persist($photo);
$this->_em->flush();
$this->_em->clear();
$dql = 'SELECT t FROM Doctrine\Tests\Models\Navigation\NavPhotos t WHERE t.poi = ?1';
$this->setExpectedException('Doctrine\ORM\Query\QueryException', 'A single-valued association path expression to an entity with a composite primary key is not supported.');
$sql = $this->_em->createQuery($dql)->getSQL();
}
public function testManyToManyCompositeRelation()
{
$this->putGermanysBrandenburderTor();
@@ -98,4 +118,4 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->setExpectedException('Doctrine\ORM\ORMException', 'The identifier long is missing for a query of Doctrine\Tests\Models\Navigation\NavPointOfInterest');
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('key1' => 100));
}
}
}
@@ -64,7 +64,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
/**
* @group DDC-546
*/
public function testCountWhenNewEntitysPresent()
public function testCountWhenNewEntityPresent()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
@@ -223,13 +223,15 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
// Test One to Many existance retrieved from DB
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
$queryCount = $this->getCurrentQueryCount();
$this->assertTrue($user->articles->contains($article));
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
// Test One to Many existance with state new
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article->topic = "Testnew";
$article->text = "blub";
@@ -238,12 +240,26 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->articles->contains($article));
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed.");
// Test One to Many existance with state clear
$this->_em->persist($article);
$this->_em->flush();
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->articles->contains($article));
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of persisted entity should cause one query to be executed.");
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test One to Many existance with state managed
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article->topic = "How to not fail anymore on tests";
$article->text = "That is simple! Just write more tests!";
$this->_em->persist($article);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->articles->contains($article));
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of managed entity (but not persisted) should cause no query to be executed.");
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
}
@@ -255,6 +271,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
// Test Many to Many existance retrieved from DB
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
@@ -262,6 +279,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many existance with state new
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
$group->name = "A New group!";
@@ -271,13 +289,26 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many existance with state clear
$this->_em->persist($group);
$this->_em->flush();
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->groups->contains($group));
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of persisted entity should cause one query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many existance with state managed
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
$group->name = "My managed group";
$this->_em->persist($group);
$queryCount = $this->getCurrentQueryCount();
$this->assertFalse($user->groups->contains($group));
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of managed entity (but not persisted) should cause no query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
}
@@ -313,6 +344,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
// Test One to Many removal with Entity retrieved from DB
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
$queryCount = $this->getCurrentQueryCount();
@@ -321,6 +353,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
// Test One to Many removal with Entity state as new
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article->topic = "Testnew";
$article->text = "blub";
@@ -331,6 +364,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a new entity should cause no query to be executed.");
// Test One to Many removal with Entity state as clean
$this->_em->persist($article);
$this->_em->flush();
@@ -338,8 +372,21 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->articles->removeElement($article);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a persisted entity should cause one query to be executed.");
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test One to Many removal with Entity state as managed
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
$article->topic = "How to not fail anymore on tests";
$article->text = "That is simple! Just write more tests!";
$this->_em->persist($article);
$queryCount = $this->getCurrentQueryCount();
$user->articles->removeElement($article);
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a managed entity should cause no query to be executed.");
}
/**
@@ -350,14 +397,16 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
// Test Many to Many removal with Entity retrieved from DB
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
$user->groups->removeElement($group);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a persisted entity should cause one query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many removal with Entity state as new
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
$group->name = "A New group!";
@@ -368,6 +417,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing new entity should cause no query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many removal with Entity state as clean
$this->_em->persist($group);
$this->_em->flush();
@@ -375,7 +425,20 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->groups->removeElement($group);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a persisted entity should cause one query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
// Test Many to Many removal with Entity state as managed
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
$group->name = "A New group!";
$this->_em->persist($group);
$queryCount = $this->getCurrentQueryCount();
$user->groups->removeElement($group);
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a managed entity should cause no query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
}
@@ -0,0 +1,86 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\Tests\Models\Cms\CmsUser;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\Common\Cache\ArrayCache;
/**
* @group DDC-1766
*/
class HydrationCacheTest extends OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user = new CmsUser;
$user->name = "Benjamin";
$user->username = "beberlei";
$user->status = 'active';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
}
public function testHydrationCache()
{
$cache = new ArrayCache();
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u";
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getResult();
$c = $this->getCurrentQueryCount();
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getResult();
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getArrayResult();
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration is part of cache key.");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache))
->getArrayResult();
$this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached");
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
->getArrayResult();
$this->assertTrue($cache->contains('cachekey'), 'Explicit cache key');
$users = $this->_em->createQuery($dql)
->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache))
->getArrayResult();
$this->assertEquals($c + 2, $this->getCurrentQueryCount(), "Hydration now cached");
}
public function testHydrationParametersSerialization()
{
$cache = new ArrayCache();
$user = new CmsUser();
$user->id = 1;
$dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1";
$query = $this->_em->createQuery($dql)
->setParameter(1, $user)
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
$query->getResult();
$c = $this->getCurrentQueryCount();
$query->getResult();
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
}
}
@@ -9,6 +9,9 @@ use Doctrine\Tests\Models\CMS\CmsArticle,
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group locking
*/
class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
protected function setUp() {
$this->useModelSet('cms');
@@ -71,7 +74,7 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
public function testLockUnmanagedEntity_ThrowsException() {
$article = new CmsArticle();
$this->setExpectedException('InvalidArgumentException', 'Entity is not MANAGED.');
$this->setExpectedException('InvalidArgumentException', 'Entity Doctrine\Tests\Models\CMS\CmsArticle');
$this->_em->lock($article, LockMode::OPTIMISTIC, $article->version + 1);
}
@@ -139,7 +142,6 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
/**
* @group DDC-178
* @group locking
*/
public function testLockPessimisticRead() {
$readLockSql = $this->_em->getConnection()->getDatabasePlatform()->getReadLockSql();
@@ -166,4 +168,17 @@ class LockTest extends \Doctrine\Tests\OrmFunctionalTestCase {
$query = array_pop( $this->_sqlLoggerStack->queries );
$this->assertContains($readLockSql, $query['sql']);
}
}
/**
* @group DDC-1693
*/
public function testLockOptimisticNonVersionedThrowsExceptionInDQL()
{
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'";
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock on an entity failed.');
$sql = $this->_em->createQuery($dql)->setHint(
\Doctrine\ORM\Query::HINT_LOCK_MODE, \Doctrine\DBAL\LockMode::OPTIMISTIC
)->getSQL();
}
}
@@ -0,0 +1,76 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* ManyToManyEventTest
*
* @author Francisco Facioni <fran6co@gmail.com>
*/
class ManyToManyEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var PostUpdateListener
*/
private $listener;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new PostUpdateListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::postUpdate, $this->listener);
}
public function testListenerShouldBeNotifiedOnlyWhenUpdating()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->assertFalse($this->listener->wasNotified);
$group = new CmsGroup();
$group->name = "admins";
$user->addGroup($group);
$this->_em->persist($user);
$this->_em->flush();
$this->assertTrue($this->listener->wasNotified);
}
/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'fran6co';
$user->name = 'Francisco Facioni';
$group = new CmsGroup();
$group->name = "users";
$user->addGroup($group);
return $user;
}
}
class PostUpdateListener
{
/**
* @var bool
*/
public $wasNotified = false;
/**
* @param $args
*/
public function postUpdate($args)
{
$this->wasNotified = true;
}
}
@@ -127,14 +127,14 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
$train = $this->_em->find(get_class($train), $train->id);
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5, t0.owner_id AS owner_id6, t7.id AS id8, t7.name AS name9 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id INNER JOIN TrainOwner t7 ON t0.owner_id = t7.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
$this->_em->clear();
$driver = $this->_em->find(get_class($driver), $driver->id);
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
@@ -155,13 +155,13 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
// The last query is the eager loading of the owner of the train
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
// The one before is the fetching of the waggon and train
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery - 1]['sql']
);
@@ -176,7 +176,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear();
$waggon = $this->_em->find(get_class($owner), $owner->id);
$this->assertEquals(
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
@@ -0,0 +1,105 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Query;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsComment;
use Doctrine\ORM\Tools\Pagination\Paginator;
/**
* @group DDC-1613
*/
class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->populate();
}
public function testCountSimpleWithoutJoin()
{
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
$query = $this->_em->createQuery($dql);
$paginator = new Paginator($query);
$this->assertEquals(3, count($paginator));
}
public function testCountWithFetchJoin()
{
$dql = "SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g";
$query = $this->_em->createQuery($dql);
$paginator = new Paginator($query);
$this->assertEquals(3, count($paginator));
}
public function testIterateSimpleWithoutJoinFetchJoinHandlingOff()
{
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
$query = $this->_em->createQuery($dql);
$paginator = new Paginator($query, false);
$data = array();
foreach ($paginator as $user) {
$data[] = $user;
}
$this->assertEquals(3, count($data));
}
public function testIterateSimpleWithoutJoinFetchJoinHandlingOn()
{
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
$query = $this->_em->createQuery($dql);
$paginator = new Paginator($query, true);
$data = array();
foreach ($paginator as $user) {
$data[] = $user;
}
$this->assertEquals(3, count($data));
}
public function testIterateWithFetchJoin()
{
$dql = "SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g";
$query = $this->_em->createQuery($dql);
$paginator = new Paginator($query, true);
$data = array();
foreach ($paginator as $user) {
$data[] = $user;
}
$this->assertEquals(3, count($data));
}
public function populate()
{
for ($i = 0; $i < 3; $i++) {
$user = new CmsUser();
$user->name = "Name$i";
$user->username = "username$i";
$user->status = "active";
$this->_em->persist($user);
for ($j = 0; $j < 3; $j++) {;
$group = new CmsGroup();
$group->name = "group$j";
$user->addGroup($group);
$this->_em->persist($group);
}
}
$this->_em->flush();
}
}
@@ -599,4 +599,24 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(3, count($users));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
}
}
/**
* @group DDC-1651
*/
public function testSetParameterBindingSingleIdentifierObjectConverted()
{
$userC = new CmsUser;
$userC->name = 'Jonathan';
$userC->username = 'jwage';
$userC->status = 'developer';
$this->_em->persist($userC);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1");
$q->setParameter(1, $userC);
$this->assertEquals($userC->id, $q->getParameter(1));
}
}
@@ -15,9 +15,12 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
} catch(\Exception $e) {
}
}
public function testReadOnlyEntityNeverChangeTracked()
@@ -36,6 +39,36 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->numericValue);
}
/**
* @group DDC-1659
*/
public function testClearReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear();
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
/**
* @group DDC-1659
*/
public function testClearEntitiesReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear(get_class($readOnly));
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
}
/**
@@ -58,4 +91,4 @@ class ReadOnlyEntity
$this->name = $name;
$this->numericValue = $number;
}
}
}
@@ -6,6 +6,7 @@ use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Proxy\ProxyClassGenerator;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
use Doctrine\Tests\Models\Company\CompanyAuction;
require_once __DIR__ . '/../../TestInit.php';
@@ -39,6 +40,18 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
return $product->getId();
}
public function createAuction()
{
$event = new CompanyAuction();
$event->setData('Doctrine Cookbook');
$this->_em->persist($event);
$this->_em->flush();
$this->_em->clear();
return $event->getId();
}
public function testLazyLoadsFieldValuesFromDatabase()
{
$id = $this->createProduct();
@@ -161,6 +174,21 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
/**
* @group DDC-1625
*/
public function testDoNotInitializeProxyOnGettingTheIdentifier_DDC_1625()
{
$id = $this->createAuction();
/* @var $entity Doctrine\Tests\Models\Company\CompanyAuction */
$entity = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyAuction' , $id);
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals($id, $entity->getId());
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy when extending.");
}
public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType()
{
$product = new ECommerceProduct();
@@ -195,4 +223,28 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Doctrine Cookbook', $entity->getName());
$this->assertTrue($entity->__isInitialized__, "Getting something other than the identifier initializes the proxy.");
}
/**
* @group DDC-1604
*/
public function testCommonPersistenceProxy()
{
$id = $this->createProduct();
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$className = \Doctrine\Common\Util\ClassUtils::getClass($entity);
$this->assertInstanceOf('Doctrine\Common\Persistence\Proxy', $entity);
$this->assertFalse($entity->__isInitialized());
$this->assertEquals('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $className);
$restName = str_replace($this->_em->getConfiguration()->getProxyNamespace(), "", get_class($entity));
$restName = substr(get_class($entity), strlen($this->_em->getConfiguration()->getProxyNamespace()) +1);
$proxyFileName = $this->_em->getConfiguration()->getProxyDir() . DIRECTORY_SEPARATOR . str_replace("\\", "", $restName) . ".php";
$this->assertTrue(file_exists($proxyFileName), "Proxy file name cannot be found generically.");
$entity->__load();
$this->assertTrue($entity->__isInitialized());
}
}
@@ -19,6 +19,8 @@ use Doctrine\Tests\Models\CMS\CmsComment;
use Doctrine\Tests\Models\Company\CompanyPerson;
use Doctrine\Tests\Models\Company\CompanyManager;
use Doctrine\Tests\Models\Company\CompanyEmployee;
use Doctrine\Tests\Models\Company\CompanyOrganization;
use Doctrine\Tests\Models\Company\CompanyAuction;
use Doctrine\Tests\Models\Company\CompanyFlexContract;
use Doctrine\Tests\Models\Company\CompanyFlexUltraContract;
@@ -34,6 +36,8 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $userId, $userId2, $articleId, $articleId2;
private $groupId, $groupId2;
private $managerId, $managerId2, $contractId1, $contractId2;
private $organizationId, $eventId1, $eventId2;
public function setUp()
{
@@ -552,11 +556,7 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(2, count($this->_em->createQuery("SELECT cm FROM Doctrine\Tests\Models\Company\CompanyManager cm")->getResult()));
// Enable the filter
$conf = $this->_em->getConfiguration();
$conf->addFilter("person_name", "\Doctrine\Tests\ORM\Functional\CompanyPersonNameFilter");
$this->_em->getFilters()
->enable("person_name")
->setParameter("name", "Guilh%", DBALType::STRING);
$this->usePersonNameFilter('Guilh%');
$managers = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyManager')->findAll();
$this->assertEquals(1, count($managers));
@@ -572,11 +572,7 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(3, count($this->_em->createQuery("SELECT cp FROM Doctrine\Tests\Models\Company\CompanyPerson cp")->getResult()));
// Enable the filter
$conf = $this->_em->getConfiguration();
$conf->addFilter("person_name", "\Doctrine\Tests\ORM\Functional\CompanyPersonNameFilter");
$this->_em->getFilters()
->enable("person_name")
->setParameter("name", "Guilh%", DBALType::STRING);
$this->usePersonNameFilter('Guilh%');
$persons = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyPerson')->findAll();
$this->assertEquals(1, count($persons));
@@ -655,12 +651,302 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
$contract4 = new CompanyFlexContract;
$contract4->markCompleted();
$manager = new CompanyManager;
$manager->setName('Alexander');
$manager->setSalary(42);
$manager->setDepartment('Doctrine');
$manager->setTitle('Filterer');
$manager2 = new CompanyManager;
$manager2->setName('Benjamin');
$manager2->setSalary(1337);
$manager2->setDepartment('Doctrine');
$manager2->setTitle('Maintainer');
$contract1->addManager($manager);
$contract2->addManager($manager);
$contract3->addManager($manager);
$contract4->addManager($manager);
$contract1->addManager($manager2);
$contract1->setSalesPerson($manager);
$contract2->setSalesPerson($manager);
$this->_em->persist($manager);
$this->_em->persist($manager2);
$this->_em->persist($contract1);
$this->_em->persist($contract2);
$this->_em->persist($contract3);
$this->_em->persist($contract4);
$this->_em->flush();
$this->_em->clear();
$this->managerId = $manager->getId();
$this->managerId2 = $manager2->getId();
$this->contractId1 = $contract1->getId();
$this->contractId2 = $contract2->getId();
}
private function useCompletedContractFilter()
{
$conf = $this->_em->getConfiguration();
$conf->addFilter("completed_contract", "\Doctrine\Tests\ORM\Functional\CompletedContractFilter");
$this->_em->getFilters()
->enable("completed_contract")
->setParameter("completed", true, DBALType::BOOLEAN);
}
public function testManyToMany_ExtraLazyCountWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertEquals(4, count($manager->managedContracts));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertEquals(2, count($manager->managedContracts));
}
public function testManyToMany_ExtraLazyContainsWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$contract1 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyContract', $this->contractId1);
$contract2 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyContract', $this->contractId2);
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertTrue($manager->managedContracts->contains($contract1));
$this->assertTrue($manager->managedContracts->contains($contract2));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertFalse($manager->managedContracts->contains($contract1));
$this->assertTrue($manager->managedContracts->contains($contract2));
}
public function testManyToMany_ExtraLazySliceWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertEquals(4, count($manager->managedContracts->slice(0, 10)));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->managedContracts->isInitialized());
$this->assertEquals(2, count($manager->managedContracts->slice(0, 10)));
}
private function usePersonNameFilter($name)
{
// Enable the filter
$conf = $this->_em->getConfiguration();
$conf->addFilter("person_name", "\Doctrine\Tests\ORM\Functional\CompanyPersonNameFilter");
$this->_em->getFilters()
->enable("person_name")
->setParameter("name", $name, DBALType::STRING);
}
public function testManyToMany_ExtraLazyCountWithFilterOnCTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$contract = $this->_em->find('Doctrine\Tests\Models\Company\CompanyFlexUltraContract', $this->contractId1);
$this->assertFalse($contract->managers->isInitialized());
$this->assertEquals(2, count($contract->managers));
// Enable the filter
$this->usePersonNameFilter('Benjamin');
$this->assertFalse($contract->managers->isInitialized());
$this->assertEquals(1, count($contract->managers));
}
public function testManyToMany_ExtraLazyContainsWithFilterOnCTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$contract = $this->_em->find('Doctrine\Tests\Models\Company\CompanyFlexUltraContract', $this->contractId1);
$manager1 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$manager2 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId2);
$this->assertFalse($contract->managers->isInitialized());
$this->assertTrue($contract->managers->contains($manager1));
$this->assertTrue($contract->managers->contains($manager2));
// Enable the filter
$this->usePersonNameFilter('Benjamin');
$this->assertFalse($contract->managers->isInitialized());
$this->assertFalse($contract->managers->contains($manager1));
$this->assertTrue($contract->managers->contains($manager2));
}
public function testManyToMany_ExtraLazySliceWithFilterOnCTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$contract = $this->_em->find('Doctrine\Tests\Models\Company\CompanyFlexUltraContract', $this->contractId1);
$this->assertFalse($contract->managers->isInitialized());
$this->assertEquals(2, count($contract->managers->slice(0, 10)));
// Enable the filter
$this->usePersonNameFilter('Benjamin');
$this->assertFalse($contract->managers->isInitialized());
$this->assertEquals(1, count($contract->managers->slice(0, 10)));
}
public function testOneToMany_ExtraLazyCountWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertEquals(2, count($manager->soldContracts));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertEquals(1, count($manager->soldContracts));
}
public function testOneToMany_ExtraLazyContainsWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$contract1 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyContract', $this->contractId1);
$contract2 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyContract', $this->contractId2);
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertTrue($manager->soldContracts->contains($contract1));
$this->assertTrue($manager->soldContracts->contains($contract2));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertFalse($manager->soldContracts->contains($contract1));
$this->assertTrue($manager->soldContracts->contains($contract2));
}
public function testOneToMany_ExtraLazySliceWithFilterOnSTI()
{
$this->loadCompanySingleTableInheritanceFixtureData();
$manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $this->managerId);
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertEquals(2, count($manager->soldContracts->slice(0, 10)));
// Enable the filter
$this->useCompletedContractFilter();
$this->assertFalse($manager->soldContracts->isInitialized());
$this->assertEquals(1, count($manager->soldContracts->slice(0, 10)));
}
private function loadCompanyOrganizationEventJoinedSubclassFixtureData()
{
$organization = new CompanyOrganization;
$event1 = new CompanyAuction;
$event1->setData('foo');
$event2 = new CompanyAuction;
$event2->setData('bar');
$organization->addEvent($event1);
$organization->addEvent($event2);
$this->_em->persist($organization);
$this->_em->flush();
$this->_em->clear();
$this->organizationId = $organization->getId();
$this->eventId1 = $event1->getId();
$this->eventId2 = $event2->getId();
}
private function useCompanyEventIdFilter()
{
// Enable the filter
$conf = $this->_em->getConfiguration();
$conf->addFilter("event_id", "\Doctrine\Tests\ORM\Functional\CompanyEventFilter");
$this->_em->getFilters()
->enable("event_id")
->setParameter("id", $this->eventId2);
}
public function testOneToMany_ExtraLazyCountWithFilterOnCTI()
{
$this->loadCompanyOrganizationEventJoinedSubclassFixtureData();
$organization = $this->_em->find('Doctrine\Tests\Models\Company\CompanyOrganization', $this->organizationId);
$this->assertFalse($organization->events->isInitialized());
$this->assertEquals(2, count($organization->events));
// Enable the filter
$this->useCompanyEventIdFilter();
$this->assertFalse($organization->events->isInitialized());
$this->assertEquals(1, count($organization->events));
}
public function testOneToMany_ExtraLazyContainsWithFilterOnCTI()
{
$this->loadCompanyOrganizationEventJoinedSubclassFixtureData();
$organization = $this->_em->find('Doctrine\Tests\Models\Company\CompanyOrganization', $this->organizationId);
$event1 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyEvent', $this->eventId1);
$event2 = $this->_em->find('Doctrine\Tests\Models\Company\CompanyEvent', $this->eventId2);
$this->assertFalse($organization->events->isInitialized());
$this->assertTrue($organization->events->contains($event1));
$this->assertTrue($organization->events->contains($event2));
// Enable the filter
$this->useCompanyEventIdFilter();
$this->assertFalse($organization->events->isInitialized());
$this->assertFalse($organization->events->contains($event1));
$this->assertTrue($organization->events->contains($event2));
}
public function testOneToMany_ExtraLazySliceWithFilterOnCTI()
{
$this->loadCompanyOrganizationEventJoinedSubclassFixtureData();
$organization = $this->_em->find('Doctrine\Tests\Models\Company\CompanyOrganization', $this->organizationId);
$this->assertFalse($organization->events->isInitialized());
$this->assertEquals(2, count($organization->events->slice(0, 10)));
// Enable the filter
$this->useCompanyEventIdFilter();
$this->assertFalse($organization->events->isInitialized());
$this->assertEquals(1, count($organization->events->slice(0, 10)));
}
}
@@ -747,3 +1033,15 @@ class CompletedContractFilter extends SQLFilter
return $targetTableAlias.'.completed = ' . $this->getParameter('completed');
}
}
class CompanyEventFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias, $targetTable = '')
{
if ($targetEntity->name != "Doctrine\Tests\Models\Company\CompanyEvent") {
return "";
}
return $targetTableAlias.'.id = ' . $this->getParameter('id');
}
}
@@ -63,6 +63,6 @@ class CompanySchemaTest extends \Doctrine\Tests\OrmFunctionalTestCase
$sql = $this->_schemaTool->getDropSchemaSQL(array(
$this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyManager'),
));
$this->assertEquals(3, count($sql));
$this->assertEquals(4, count($sql));
}
}
}
@@ -27,16 +27,14 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, UNIQUE INDEX UNIQ_ACAC157BA76ED395 (user_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]);
$this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, email_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_3AF03EC5F85E0677 (username), UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 (email_id), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, INDEX IDX_7EA9409AA76ED395 (user_id), INDEX IDX_7EA9409AFE54D947 (group_id), PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]);
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, INDEX IDX_F21F790FA76ED395 (user_id), PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]);
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[4]);
$this->assertEquals("ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[6]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id)", $sql[7]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[8]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[5]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id)", $sql[6]);
$this->assertEquals(9, count($sql));
$this->assertEquals(7, count($sql));
}
public function testGetCreateSchemaSql2()
@@ -64,4 +62,31 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(1, count($sql));
$this->assertEquals("CREATE TABLE boolean_model (id INT AUTO_INCREMENT NOT NULL, booleanField TINYINT(1) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]);
}
/**
* @group DBAL-204
*/
public function testGetCreateSchemaSql4()
{
$classes = array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\MysqlSchemaNamespacedEntity')
);
$tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes);
$this->assertEquals(0, count($sql));
}
}
/**
* @Entity
* @Table("namespace.entity")
*/
class MysqlSchemaNamespacedEntity
{
/** @Column(type="integer") @Id @GeneratedValue */
public $id;
}
@@ -0,0 +1,50 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Tools\SchemaValidator;
/**
* Test the validity of all modelsets
*
* @group DDC-1601
*/
class SchemaValidatorTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
static public function dataValidateModelSets()
{
$modelSets = array();
foreach (self::$_modelSets as $modelSet => $classes) {
if ($modelSet == "customtype") {
continue;
}
$modelSets[] = array($modelSet);
}
return $modelSets;
}
/**
* @dataProvider dataValidateModelSets
*/
public function testValidateModelSets($modelSet)
{
$validator = new SchemaValidator($this->_em);
$classes = array();
foreach (self::$_modelSets[$modelSet] as $className) {
$classes[] = $this->_em->getClassMetadata($className);
}
foreach ($classes as $class) {
$ce = $validator->validateClass($class);
foreach ($ce as $key => $error) {
if (strpos($error, "must be private or protected. Public fields may break lazy-loading.") !== false) {
unset($ce[$key]);
}
}
$this->assertEquals(0, count($ce), "Invalid Modelset: " . $modelSet . " class " . $class->name . ": ". implode("\n", $ce));
}
}
}
@@ -433,4 +433,32 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($this->article1->id(), $refRep->source()->id());
$this->assertEquals($this->article2->id(), $refRep->target()->id());
}
/**
* @group DDC-1652
*/
public function testArrayHydrationWithCompositeKey()
{
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$before = count($this->_em->createQuery($dql)->getResult());
$this->article1 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article1->id());
$this->article2 = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Article", $this->article2->id());
$this->reference = new DDC117Reference($this->article2, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article1, $this->article1, "Test-Description");
$this->_em->persist($this->reference);
$this->reference = new DDC117Reference($this->article2, $this->article2, "Test-Description");
$this->_em->persist($this->reference);
$this->_em->flush();
$dql = "SELECT r,s,t FROM Doctrine\Tests\Models\DDC117\DDC117Reference r INNER JOIN r.source s INNER JOIN r.target t";
$data = $this->_em->createQuery($dql)->getArrayResult();
$this->assertEquals($before + 3, count($data));
}
}
@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group DDC-1360
*/
class DDC1360Test extends OrmFunctionalTestCase
{
public function testSchemaDoubleQuotedCreate()
{
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != "postgresql") {
$this->markTestSkipped("PostgreSQL only test.");
}
$sql = $this->_schemaTool->getCreateSchemaSQL(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1360DoubleQuote')
));
$this->assertEquals(array(
'CREATE TABLE "user"."user" (id INT NOT NULL, PRIMARY KEY(id))',
'CREATE SEQUENCE "user".user_id_seq INCREMENT BY 1 MINVALUE 1 START 1',
), $sql);
}
}
/**
* @Entity @Table(name="`user`.`user`")
*/
class DDC1360DoubleQuote
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
}
@@ -36,7 +36,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
->getQuery();
$this->assertEquals('SELECT o.id, o.date, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, COUNT(d1_.id) AS sclr2 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -68,7 +68,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o.id, o.date, o.status ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -97,7 +97,7 @@ class DDC1430Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('SELECT o, COUNT(p.id) AS p_count FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1430Order o LEFT JOIN o.products p GROUP BY o ORDER BY o.id ASC', $query->getDQL());
$this->assertEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$this->assertSQLEquals('SELECT d0_.order_id AS order_id0, d0_.created_at AS created_at1, d0_.order_status AS order_status2, COUNT(d1_.id) AS sclr3 FROM DDC1430Order d0_ LEFT JOIN DDC1430OrderProduct d1_ ON d0_.order_id = d1_.order_id GROUP BY d0_.order_id, d0_.created_at, d0_.order_status ORDER BY d0_.order_id ASC', $query->getSQL());
$result = $query->getResult();
@@ -294,4 +294,4 @@ class DDC1430OrderProduct
$this->value = $value;
}
}
}
@@ -57,6 +57,8 @@ class DDC1514Test extends \Doctrine\Tests\OrmFunctionalTestCase
$dql = "SELECT a, b, ba, c FROM " . __NAMESPACE__ . "\DDC1514EntityA AS a LEFT JOIN a.entitiesB AS b LEFT JOIN b.entityATo AS ba LEFT JOIN a.entityC AS c";
$results = $this->_em->createQuery($dql)->getResult();
$this->assertInternalType('array', $results);
$this->assertTrue(count($results) >= 1, "At least one result expected in array");
$this->assertEquals($c->title, $results[1]->entityC->title);
}
}
@@ -0,0 +1,67 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1526
*/
class DDC1526Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1526Menu'),
));
}
public function testIssue()
{
$parents = array();
for ($i = 0; $i < 9; $i++) {
$entity = new DDC1526Menu;
if (isset ($parents[($i % 3)])) {
$entity->parent = $parents[($i%3)];
}
$this->_em->persist($entity);
$parents[$i] = $entity;
}
$this->_em->flush();
$this->_em->clear();
$dql = "SELECT m, c
FROM " . __NAMESPACE__ . "\DDC1526Menu m
LEFT JOIN m.children c";
$menus = $this->_em->createQuery($dql)->getResult();
// All Children collection now have to be initiailzed
foreach ($menus as $menu) {
$this->assertTrue($menu->children->isInitialized());
}
}
}
/**
* @Entity
*/
class DDC1526Menu
{
/**
* @Column(type="integer")
* @Id
* @GeneratedValue
*/
public $id;
/**
* @ManyToOne(targetEntity="DDC1526Menu", inversedBy="children")
*/
public $parent;
/**
* @OneToMany(targetEntity="DDC1526Menu", mappedBy="parent")
*/
public $children;
}
@@ -0,0 +1,45 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsComment;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsUser;
/**
* @group DDC-1594
*/
class DDC1594Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$user = new CmsUser();
$user->status = 'foo';
$user->username = 'foo';
$user->name = 'foo';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$detachedUser = clone $user;
$detachedUser->name = 'bar';
$detachedUser->status = 'bar';
$newUser = $this->_em->getReference(get_class($user), $user->id);
$mergedUser = $this->_em->merge($detachedUser);
$this->assertNotSame($mergedUser, $detachedUser);
$this->assertEquals('bar', $detachedUser->getName());
$this->assertEquals('bar', $mergedUser->getName());
}
}
@@ -0,0 +1,111 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1595
* @group DDC-1596
*/
class DDC1595Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\DebugStack);
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1595BaseInheritance'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1595InheritedEntity1'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1595InheritedEntity2'),
));
}
public function testIssue()
{
$e1 = new DDC1595InheritedEntity1();
$this->_em->persist($e1);
$this->_em->flush();
$this->_em->clear();
$sqlLogger = $this->_em->getConnection()->getConfiguration()->getSQLLogger();
$repository = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1595InheritedEntity1');
$entity1 = $repository->find($e1->id);
// DDC-1596
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.type FROM base t0 WHERE t0.id = ? AND t0.type IN ('Entity1')",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
$entities = $entity1->getEntities()->getValues();
$this->assertSQLEquals(
"SELECT t0.id AS id1, t0.type FROM base t0 INNER JOIN entity1_entity2 ON t0.id = entity1_entity2.item WHERE entity1_entity2.parent = ? AND t0.type IN ('Entity2')",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
$this->_em->clear();
$entity1 = $repository->find($e1->id);
$entities = $entity1->getEntities()->count();
$this->assertSQLEquals(
"SELECT COUNT(*) FROM entity1_entity2 t WHERE parent = ?",
$sqlLogger->queries[count($sqlLogger->queries)]['sql']
);
}
}
/**
* @Entity
* @Table(name="base")
*
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="type", type="string")
* @DiscriminatorMap({
* "Entity1" = "DDC1595InheritedEntity1",
* "Entity2" = "DDC1595InheritedEntity2"
* })
*/
abstract class DDC1595BaseInheritance
{
/**
* @Id @GeneratedValue
* @Column(type="integer")
*
* @var integer
*/
public $id;
}
/**
* @Entity
* @Table(name="entity1")
*/
class DDC1595InheritedEntity1 extends DDC1595BaseInheritance
{
/**
* @ManyToMany(targetEntity="DDC1595InheritedEntity2", fetch="EXTRA_LAZY")
* @JoinTable(name="entity1_entity2",
* joinColumns={@JoinColumn(name="parent", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="item", referencedColumnName="id")}
* )
*/
protected $entities;
public function getEntities()
{
return $this->entities;
}
}
/**
* @Entity
* @Table(name="entity2")
*/
class DDC1595InheritedEntity2 extends DDC1595BaseInheritance
{
}
@@ -0,0 +1,121 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
/**
* @group DDC-1643
*/
class DDC1643Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $user1;
private $user2;
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$user1 = new CmsUser();
$user1->username = "beberlei";
$user1->name = "Benjamin";
$user1->status = "active";
$group1 = new CmsGroup();
$group1->name = "test";
$group2 = new CmsGroup();
$group2->name = "test";
$user1->addGroup($group1);
$user1->addGroup($group2);
$user2 = new CmsUser();
$user2->username = "romanb";
$user2->name = "Roman";
$user2->status = "active";
$this->_em->persist($user1);
$this->_em->persist($user2);
$this->_em->persist($group1);
$this->_em->persist($group2);
$this->_em->flush();
$this->_em->clear();
$this->user1 = $this->_em->find(get_class($user1), $user1->id);
$this->user2 = $this->_em->find(get_class($user1), $user2->id);
}
public function testClonePersistentCollectionAndReuse()
{
$user1 = $this->user1;
$user1->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$this->assertEquals(2, count($user1->groups));
}
public function testClonePersistentCollectionAndShare()
{
$user1 = $this->user1;
$user2 = $this->user2;
$user2->groups = clone $user1->groups;
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(2, count($user1->groups));
$this->assertEquals(2, count($user2->groups));
}
public function testCloneThenDirtyPersistentCollection()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = clone $user1->groups;
$user2->groups->add($group3);
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(2, count($user1->groups));
}
public function testNotCloneAndPassAroundFlush()
{
$user1 = $this->user1;
$user2 = $this->user2;
$group3 = new CmsGroup();
$group3->name = "test";
$user2->groups = $user1->groups;
$user2->groups->add($group3);
$this->assertEQuals(1, count($user1->groups->getInsertDiff()));
$this->_em->persist($group3);
$this->_em->flush();
$this->_em->clear();
$user1 = $this->_em->find(get_class($user1), $user1->id);
$user2 = $this->_em->find(get_class($user1), $user2->id);
$this->assertEquals(3, count($user2->groups));
$this->assertEquals(3, count($user1->groups));
}
}
@@ -0,0 +1,103 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1654
*/
class DDC1654Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->setUpEntitySchema(array(
__NAMESPACE__ . '\\DDC1654Post',
__NAMESPACE__ . '\\DDC1654Comment',
));
}
public function testManyToManyRemoveFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->remove(0);
$post->comments->remove(1);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyRemoveElementFromCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->removeElement($post->comments[0]);
$post->comments->removeElement($post->comments[1]);
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
public function testManyToManyClearCollectionOrphanRemoval()
{
$post = new DDC1654Post();
$post->comments[] = new DDC1654Comment();
$post->comments[] = new DDC1654Comment();
$this->_em->persist($post);
$this->_em->flush();
$post->comments->clear();
$this->_em->flush();
$this->_em->clear();
$comments = $this->_em->getRepository(__NAMESPACE__ . '\\DDC1654Comment')->findAll();
$this->assertEquals(0, count($comments));
}
}
/**
* @Entity
*/
class DDC1654Post
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
/**
* @ManyToMany(targetEntity="DDC1654Comment", orphanRemoval=true,
* cascade={"persist"})
*/
public $comments = array();
}
/**
* @Entity
*/
class DDC1654Comment
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
public $id;
}
@@ -0,0 +1,144 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1655
* @group DDC-1640
* @group DDC-1556
*/
class DDC1655Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Foo'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Bar'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1655Baz'),
));
} catch(\Exception $e) {
}
}
public function testPostLoadOneToManyInheritance()
{
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Foo');
$this->assertEquals(array("postLoad" => array("postLoad")), $cm->lifecycleCallbacks);
$cm = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1655Bar');
$this->assertEquals(array("postLoad" => array("postLoad", "postSubLoaded")), $cm->lifecycleCallbacks);
$baz = new DDC1655Baz();
$foo = new DDC1655Foo();
$foo->baz = $baz;
$bar = new DDC1655Bar();
$bar->baz = $baz;
$this->_em->persist($foo);
$this->_em->persist($bar);
$this->_em->persist($baz);
$this->_em->flush();
$this->_em->clear();
$baz = $this->_em->find(get_class($baz), $baz->id);
foreach ($baz->foos as $foo) {
$this->assertEquals(1, $foo->loaded, "should have loaded callback counter incremented for " . get_class($foo));
}
}
/**
* Check that post load is not executed several times when the entity
* is rehydrated again although its already known.
*/
public function testPostLoadInheritanceChild()
{
$bar = new DDC1655Bar();
$this->_em->persist($bar);
$this->_em->flush();
$this->_em->clear();
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$bar = $this->_em->find(get_class($bar), $bar->id);
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$dql = "SELECT b FROM " . __NAMESPACE__ . "\DDC1655Bar b WHERE b.id = ?1";
$bar = $this->_em->createQuery($dql)->setParameter(1, $bar->id)->getSingleResult();
$this->assertEquals(1, $bar->loaded);
$this->assertEquals(1, $bar->subLoaded);
$this->_em->refresh($bar);
$this->assertEquals(2, $bar->loaded);
$this->assertEquals(2, $bar->subLoaded);
}
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({
* "foo" = "DDC1655Foo",
* "bar" = "DDC1655Bar"
* })
* @HasLifecycleCallbacks
*/
class DDC1655Foo
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
public $loaded = 0;
/**
* @ManyToOne(targetEntity="DDC1655Baz", inversedBy="foos")
*/
public $baz;
/**
* @PostLoad
*/
public function postLoad()
{
$this->loaded++;
}
}
/**
* @Entity
* @HasLifecycleCallbacks
*/
class DDC1655Bar extends DDC1655Foo
{
public $subLoaded;
/**
* @PostLoad
*/
public function postSubLoaded()
{
$this->subLoaded++;
}
}
/**
* @Entity
*/
class DDC1655Baz
{
/** @Id @GeneratedValue @Column(type="integer") */
public $id;
/**
* @OneToMany(targetEntity="DDC1655Foo", mappedBy="baz")
*/
public $foos = array();
}
@@ -61,4 +61,4 @@ class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $theEmployee);
$this->assertInstanceOf('Doctrine\Tests\Models\Company\CompanyEmployee', $theEmployee->getSpouse());
}
}
}
@@ -0,0 +1,158 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-1695
*/
class DDC1695Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function testIssue()
{
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != "sqlite") {
$this->markTestSkipped("Only with sqlite");
}
$dql = "SELECT n.smallText, n.publishDate FROM " . __NAMESPACE__ . "\\DDC1695News n";
$sql = $this->_em->createQuery($dql)->getSQL();
$this->assertEquals(
'SELECT d0_."SmallText" AS SmallText0, d0_."PublishDate" AS PublishDate1 FROM "DDC1695News" d0_',
$sql
);
}
}
/**
* @Table(name="`DDC1695News`")
* @Entity
*/
class DDC1695News
{
/**
* @var integer $idNews
*
* @Column(name="`IdNews`", type="integer", nullable=false)
* @Id
* @GeneratedValue
*/
private $idNews;
/**
* @var bigint $iduser
*
* @Column(name="`IdUser`", type="bigint", nullable=false)
*/
private $idUser;
/**
* @var integer $idLanguage
*
* @Column(name="`IdLanguage`", type="integer", nullable=false)
*/
private $idLanguage;
/**
* @var integer $idCondition
*
* @Column(name="`IdCondition`", type="integer", nullable=true)
*/
private $idCondition;
/**
* @var integer $idHealthProvider
*
* @Column(name="`IdHealthProvider`", type="integer", nullable=true)
*/
private $idHealthProvider;
/**
* @var integer $idSpeciality
*
* @Column(name="`IdSpeciality`", type="integer", nullable=true)
*/
private $idSpeciality;
/**
* @var integer $idMedicineType
*
* @Column(name="`IdMedicineType`", type="integer", nullable=true)
*/
private $idMedicineType;
/**
* @var integer $idTreatment
*
* @Column(name="`IdTreatment`", type="integer", nullable=true)
*/
private $idTreatment;
/**
* @var string $title
*
* @Column(name="`Title`", type="string", nullable=true)
*/
private $title;
/**
* @var string $smallText
*
* @Column(name="`SmallText`", type="string", nullable=true)
*/
private $smallText;
/**
* @var string $longText
*
* @Column(name="`LongText`", type="string", nullable=true)
*/
private $longText;
/**
* @var datetimetz $publishDate
*
* @Column(name="`PublishDate`", type="datetimetz", nullable=true)
*/
private $publishDate;
/**
* @var tsvector $idxNews
*
* @Column(name="`IdxNews`", type="tsvector", nullable=true)
*/
private $idxNews;
/**
* @var boolean $highlight
*
* @Column(name="`Highlight`", type="boolean", nullable=false)
*/
private $highlight;
/**
* @var integer $order
*
* @Column(name="`Order`", type="integer", nullable=false)
*/
private $order;
/**
* @var boolean $deleted
*
* @Column(name="`Deleted`", type="boolean", nullable=false)
*/
private $deleted;
/**
* @var boolean $active
*
* @Column(name="`Active`", type="boolean", nullable=false)
*/
private $active;
/**
* @var boolean $updateToHighlighted
*
* @Column(name="`UpdateToHighlighted`", type="boolean", nullable=true)
*/
private $updateToHighlighted;
}
@@ -0,0 +1,118 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
use Doctrine\Tests\Models\Generic\DateTimeModel;
/**
* @group DDC-657
*/
class DDC657Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
const NS = 'Doctrine\Tests\Models\Generic';
protected function setUp()
{
$this->useModelSet('generic');
parent::setUp();
$this->loadFixtures();
}
public function testEntitySingleResult()
{
$query = $this->_em->createQuery('SELECT d FROM ' . self::NS . '\DateTimeModel d');
$datetime = $query->setMaxResults(1)->getSingleResult();
$this->assertTrue($datetime instanceof DateTimeModel);
$this->assertTrue($datetime->datetime instanceof \DateTime);
$this->assertTrue($datetime->time instanceof \DateTime);
$this->assertTrue($datetime->date instanceof \DateTime);
}
public function testScalarResult()
{
$query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC');
$result = $query->getScalarResult();
$this->assertCount(2,$result);
$this->assertContains('11:11:11', $result[0]['time']);
$this->assertContains('2010-01-01', $result[0]['date']);
$this->assertContains('2010-01-01 11:11:11', $result[0]['datetime']);
$this->assertContains('12:12:12', $result[1]['time']);
$this->assertContains('2010-02-02', $result[1]['date']);
$this->assertContains('2010-02-02 12:12:12', $result[1]['datetime']);
}
public function testaTicketEntityArrayResult()
{
$query = $this->_em->createQuery('SELECT d FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC');
$result = $query->getArrayResult();
$this->assertCount(2,$result);
$this->assertTrue($result[0]['datetime'] instanceof \DateTime);
$this->assertTrue($result[0]['time'] instanceof \DateTime);
$this->assertTrue($result[0]['date'] instanceof \DateTime);
$this->assertTrue($result[1]['datetime'] instanceof \DateTime);
$this->assertTrue($result[1]['time'] instanceof \DateTime);
$this->assertTrue($result[1]['date'] instanceof \DateTime);
}
public function testTicketSingleResult()
{
$query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC');
$datetime = $query->setMaxResults(1)->getSingleResult();
$this->assertTrue(is_array($datetime));
$this->assertTrue($datetime['datetime'] instanceof \DateTime);
$this->assertTrue($datetime['time'] instanceof \DateTime);
$this->assertTrue($datetime['date'] instanceof \DateTime);
}
public function testTicketResult()
{
$query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC');
$result = $query->getResult();
$this->assertCount(2,$result);
$this->assertTrue($result[0]['time'] instanceof \DateTime);
$this->assertTrue($result[0]['date'] instanceof \DateTime);
$this->assertTrue($result[0]['datetime'] instanceof \DateTime);
$this->assertEquals('2010-01-01 11:11:11', $result[0]['datetime']->format('Y-m-d G:i:s'));
$this->assertTrue($result[1]['time'] instanceof \DateTime);
$this->assertTrue($result[1]['date'] instanceof \DateTime);
$this->assertTrue($result[1]['datetime'] instanceof \DateTime);
$this->assertEquals('2010-02-02 12:12:12', $result[1]['datetime']->format('Y-m-d G:i:s'));
}
public function loadFixtures()
{
$timezone = new \DateTimeZone('America/Sao_Paulo');
$dateTime1 = new DateTimeModel();
$dateTime2 = new DateTimeModel();
$dateTime1->date = new \DateTime('2010-01-01', $timezone);
$dateTime1->time = new \DateTime('2010-01-01 11:11:11', $timezone);
$dateTime1->datetime= new \DateTime('2010-01-01 11:11:11', $timezone);
$dateTime2->date = new \DateTime('2010-02-02', $timezone);
$dateTime2->time = new \DateTime('2010-02-02 12:12:12', $timezone);
$dateTime2->datetime= new \DateTime('2010-02-02 12:12:12', $timezone);
$this->_em->persist($dateTime1);
$this->_em->persist($dateTime2);
$this->_em->flush();
}
}
@@ -8,6 +8,9 @@ require_once __DIR__ . '/../../../TestInit.php';
class DDC742Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
private $userCm;
private $commentCm;
protected function setUp()
{
parent::setUp();
@@ -15,6 +18,7 @@ class DDC742Test extends \Doctrine\Tests\OrmFunctionalTestCase
if (\extension_loaded('memcache')) {
$memcache = new \Memcache();
$memcache->addServer('localhost');
$memcache->flush();
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
$cacheDriver->setMemcache($memcache);
@@ -123,4 +127,4 @@ class DDC742Comment
* @var string
*/
public $content;
}
}
@@ -0,0 +1,71 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
class UnitOfWorkLifecycleTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testScheduleInsertManaged()
{
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "Benjamin";
$user->status = "active";
$this->_em->persist($user);
$this->_em->flush();
$this->setExpectedException("Doctrine\ORM\ORMInvalidArgumentException", "A managed+dirty entity Doctrine\Tests\Models\CMS\CmsUser");
$this->_em->getUnitOfWork()->scheduleForInsert($user);
}
public function testScheduleInsertDeleted()
{
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "Benjamin";
$user->status = "active";
$this->_em->persist($user);
$this->_em->flush();
$this->_em->remove($user);
$this->setExpectedException("Doctrine\ORM\ORMInvalidArgumentException", "Removed entity Doctrine\Tests\Models\CMS\CmsUser");
$this->_em->getUnitOfWork()->scheduleForInsert($user);
}
public function testScheduleInsertTwice()
{
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "Benjamin";
$user->status = "active";
$this->_em->getUnitOfWork()->scheduleForInsert($user);
$this->setExpectedException("Doctrine\ORM\ORMInvalidArgumentException", "Entity Doctrine\Tests\Models\CMS\CmsUser");
$this->_em->getUnitOfWork()->scheduleForInsert($user);
}
public function testAddToIdentityMapWithoutIdentity()
{
$user = new CmsUser();
$this->setExpectedException("Doctrine\ORM\ORMInvalidArgumentException", "The given entity of type 'Doctrine\Tests\Models\CMS\CmsUser' (Doctrine\Tests\Models\CMS\CmsUser@");
$this->_em->getUnitOfWork()->registerManaged($user, array(), array());
}
public function testMarkReadOnlyNonManaged()
{
$user = new CmsUser();
$this->setExpectedException("Doctrine\ORM\ORMInvalidArgumentException", "Only managed entities can be marked or checked as read only. But Doctrine\Tests\Models\CMS\CmsUser@");
$this->_em->getUnitOfWork()->markReadOnly($user);
}
}
@@ -18,6 +18,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$mappingDriver = $this->_loadDriver();
$class = new ClassMetadata($entityClassName);
$class->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$mappingDriver->loadMetadataForClass($entityClassName, $class);
return $class;
@@ -677,4 +678,8 @@ class DDC1170Entity
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);
}
}
}
class Address {}
class Phonenumber {}
class Group {}
@@ -15,6 +15,7 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
public function testLoadMetadataForNonEntityThrowsException()
{
$cm = new ClassMetadata('stdClass');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$reader = new \Doctrine\Common\Annotations\AnnotationReader(new \Doctrine\Common\Cache\ArrayCache());
$annotationDriver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader);
@@ -28,6 +29,7 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
public function testColumnWithMissingTypeDefaultsToString()
{
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\ColumnWithoutType');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$annotationDriver = $this->_loadDriver();
$annotationDriver->loadMetadataForClass('Doctrine\Tests\ORM\Mapping\InvalidColumn', $cm);
@@ -91,6 +91,7 @@ class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase
$class = $this->_factory->getMetadataFor(__NAMESPACE__ . '\\EntitySubClass2');
$class2 = unserialize(serialize($class));
$class2->wakeupReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertTrue(isset($class2->reflFields['mapped1']));
$this->assertTrue(isset($class2->reflFields['mapped2']));
@@ -197,6 +198,7 @@ class MappedSuperclassBase {
private $mappedRelated1;
private $transient;
}
class MappedSuperclassRelated1 {}
/** @Entity */
class EntitySubClass2 extends MappedSuperclassBase {
@@ -314,4 +316,4 @@ class MediumSuperclassEntity extends MediumSuperclassBase
class SubclassWithRepository extends \Doctrine\Tests\Models\DDC869\DDC869Payment
{
}
}
@@ -40,6 +40,7 @@ class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
public function setUp()
{
$this->cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$this->cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->builder = new ClassMetadataBuilder($this->cm);
}
@@ -376,6 +377,7 @@ class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
array(
'user_id' => 'id',
),
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}
@@ -27,25 +27,26 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
// Self-made metadata
$cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1');
$cm1->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm1->setPrimaryTable(array('name' => '`group`'));
// Add a mapped field
$cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar'));
// Add a mapped field
$cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
// and a mapped association
$cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this'));
$cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'TestEntity1', 'mappedBy' => 'this'));
// and an association on the owning side
$joinColumns = array(
array('name' => 'other_id', 'referencedColumnName' => 'id')
);
$cm1->mapOneToOne(array('fieldName' => 'association', 'targetEntity' => 'Other', 'joinColumns' => $joinColumns));
$cm1->mapOneToOne(array('fieldName' => 'association', 'targetEntity' => 'TestEntity1', 'joinColumns' => $joinColumns));
// and an id generator type
$cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
// SUT
$cmf = new ClassMetadataFactoryTestSubject();
$cmf = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$cmf->setEntityManager($entityManager);
$cmf->setMetadataForClass('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1);
$cmf->setMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1', $cm1);
// Prechecks
$this->assertEquals(array(), $cm1->parentClasses);
@@ -53,15 +54,16 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($cm1->hasField('name'));
$this->assertEquals(2, count($cm1->associationMappings));
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $cm1->generatorType);
$this->assertEquals('group', $cm1->table['name']);
// Go
$cm1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
$cmMap1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
$this->assertEquals('group', $cm1->table['name']);
$this->assertTrue($cm1->table['quoted']);
$this->assertEquals(array(), $cm1->parentClasses);
$this->assertTrue($cm1->hasField('name'));
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $cm1->generatorType);
$this->assertSame($cm1, $cmMap1);
$this->assertEquals('group', $cmMap1->table['name']);
$this->assertTrue($cmMap1->table['quoted']);
$this->assertEquals(array(), $cmMap1->parentClasses);
$this->assertTrue($cmMap1->hasField('name'));
}
public function testHasGetMetadata_NamespaceSeperatorIsNotNormalized()
@@ -9,6 +9,9 @@ require_once __DIR__ . '/../../TestInit.php';
class ClassMetadataLoadEventTest extends \Doctrine\Tests\OrmTestCase
{
/**
* @group DDC-1610
*/
public function testEvent()
{
$em = $this->_getTestEntityManager();
@@ -17,6 +20,8 @@ class ClassMetadataLoadEventTest extends \Doctrine\Tests\OrmTestCase
$evm->addEventListener(Events::loadClassMetadata, $this);
$classMetadata = $metadataFactory->getMetadataFor('Doctrine\Tests\ORM\Mapping\LoadEventTestEntity');
$this->assertTrue($classMetadata->hasField('about'));
$this->assertArrayHasKey('about', $classMetadata->reflFields);
$this->assertInstanceOf('ReflectionProperty', $classMetadata->reflFields['about']);
}
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
@@ -48,4 +53,4 @@ class LoadEventTestEntity
private $name;
private $about;
}
}
@@ -13,6 +13,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testClassMetadataInstanceSerialization()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// Test initial state
$this->assertTrue(count($cm->getReflectionProperties()) == 0);
@@ -29,13 +30,14 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$cm->setParentClasses(array("UserParent"));
$cm->setCustomRepositoryClass("UserRepository");
$cm->setDiscriminatorColumn(array('name' => 'disc', 'type' => 'integer'));
$cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'Bar', 'mappedBy' => 'foo'));
$cm->mapOneToOne(array('fieldName' => 'phonenumbers', 'targetEntity' => 'CmsAddress', 'mappedBy' => 'foo'));
$cm->markReadOnly();
$cm->addNamedQuery(array('name' => 'dql', 'query' => 'foo'));
$this->assertEquals(1, count($cm->associationMappings));
$serialized = serialize($cm);
$cm = unserialize($serialized);
$cm->wakeupReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// Check state
$this->assertTrue(count($cm->getReflectionProperties()) > 0);
@@ -52,7 +54,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$oneOneMapping = $cm->getAssociationMapping('phonenumbers');
$this->assertTrue($oneOneMapping['fetch'] == ClassMetadata::FETCH_LAZY);
$this->assertEquals('phonenumbers', $oneOneMapping['fieldName']);
$this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']);
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddress', $oneOneMapping['targetEntity']);
$this->assertTrue($cm->isReadOnly);
$this->assertEquals(array('dql' => array('name'=>'dql','query'=>'foo','dql'=>'foo')), $cm->namedQueries);
}
@@ -60,6 +62,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testFieldIsNullable()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// Explicit Nullable
$cm->mapField(array('fieldName' => 'status', 'nullable' => true, 'type' => 'string', 'length' => 50));
@@ -82,6 +85,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
$cm = new ClassMetadata('DoctrineGlobal_Article');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToMany(array(
'fieldName' => 'author',
'targetEntity' => 'DoctrineGlobal_User',
@@ -98,6 +102,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testMapManyToManyJoinTableDefaults()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToMany(
array(
'fieldName' => 'groups',
@@ -117,6 +122,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testSerializeManyToManyJoinTableCascade()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToMany(
array(
'fieldName' => 'groups',
@@ -138,6 +144,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
$cm = new ClassMetadata('DoctrineGlobal_User');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setDiscriminatorMap(array('descr' => 'DoctrineGlobal_Article', 'foo' => 'DoctrineGlobal_User'));
$this->assertEquals("DoctrineGlobal_Article", $cm->discriminatorMap['descr']);
@@ -152,6 +159,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
require_once __DIR__."/../../Models/Global/GlobalNamespaceModel.php";
$cm = new ClassMetadata('DoctrineGlobal_User');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setSubclasses(array('DoctrineGlobal_Article'));
$this->assertEquals("DoctrineGlobal_Article", $cm->subClasses[0]);
@@ -167,6 +175,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$field['type'] = 'string';
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
$cm->setVersionMapping($field);
@@ -175,6 +184,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testGetSingleIdentifierFieldName_MultipleIdentifierEntity_ThrowsException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->isIdentifierComposite = true;
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -184,6 +194,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateAssociationMappingException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$a1 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo');
$a2 = array('fieldName' => 'foo', 'sourceEntity' => 'stdClass', 'targetEntity' => 'stdClass', 'mappedBy' => 'foo');
@@ -195,6 +207,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateColumnName_ThrowsMappingException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -204,6 +218,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateColumnName_DiscriminatorColumn_ThrowsMappingException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -213,6 +229,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateColumnName_DiscriminatorColumn2_ThrowsMappingException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setDiscriminatorColumn(array('name' => 'name'));
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -222,6 +240,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateFieldAndAssocationMapping1_ThrowsException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapField(array('fieldName' => 'name', 'columnName' => 'name'));
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -231,6 +251,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDuplicateFieldAndAssocationMapping2_ThrowsException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapOneToOne(array('fieldName' => 'name', 'targetEntity' => 'CmsUser'));
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -243,6 +265,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testGetTemporaryTableNameSchema()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->setTableName('foo.bar');
$this->assertEquals('foo_bar_id_tmp', $cm->getTemporaryIdTableName());
@@ -251,6 +275,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDefaultTableName()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// When table's name is not given
$primaryTable = array();
@@ -260,6 +285,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('CmsUser', $cm->table['name']);
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// When joinTable's name is not given
$cm->mapManyToMany(array(
'fieldName' => 'user',
@@ -273,6 +299,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testDefaultJoinColumnName()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
// this is really dirty, but it's the simpliest way to test whether
// joinColumn's name will be automatically set to user_id
$cm->mapOneToOne(array(
@@ -282,6 +310,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('user_id', $cm->associationMappings['user']['joinColumns'][0]['name']);
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToMany(array(
'fieldName' => 'user',
'targetEntity' => 'CmsUser',
@@ -299,6 +328,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testSetMultipleIdentifierSetsComposite()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapField(array('fieldName' => 'name'));
$cm->mapField(array('fieldName' => 'username'));
@@ -312,6 +343,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testMappingNotFound()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException', "No mapping found for field 'foo' on class 'Doctrine\Tests\Models\CMS\CmsUser'.");
$cm->getFieldMapping('foo');
@@ -323,6 +355,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testJoinTableMappingDefaults()
{
$cm = new ClassMetadata('DoctrineGlobal_Article');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToMany(array('fieldName' => 'author', 'targetEntity' => 'Doctrine\Tests\Models\CMS\CmsUser'));
$this->assertEquals('doctrineglobal_article_cmsuser', $cm->associationMappings['author']['joinTable']['name']);
@@ -334,6 +368,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testMapIdentifierAssociation()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapOneToOne(array(
'fieldName' => 'article',
'id' => true,
@@ -351,6 +387,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testOrphanRemovalIdentifierAssociation()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'The orphan removal option is not allowed on an association that');
$cm->mapOneToOne(array(
@@ -368,6 +405,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testInverseIdentifierAssocation()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'An inverse association is not allowed to be identifier in');
$cm->mapOneToOne(array(
@@ -385,6 +424,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testIdentifierAssocationManyToMany()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\DDC117\DDC117ArticleDetails');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException', 'Many-to-many or one-to-many associations are not allowed to be identifier in');
$cm->mapManyToMany(array(
@@ -403,12 +444,16 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException',
"The field or association mapping misses the 'fieldName' attribute in entity 'Doctrine\Tests\Models\CMS\CmsUser'.");
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapField(array('fieldName' => ''));
}
public function testRetrievalOfNamedQueries()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertEquals(0, count($cm->getNamedQueries()));
@@ -423,6 +468,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testExistanceOfNamedQuery()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->addNamedQuery(array(
'name' => 'all',
@@ -436,6 +483,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testRetrieveOfNamedQuery()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->addNamedQuery(array(
'name' => 'userById',
@@ -448,6 +497,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testNamingCollisionNamedQueryShouldThrowException()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException');
@@ -469,6 +520,8 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
{
$user = new \Doctrine\Tests\Models\CMS\CmsUser();
$cm = new ClassMetadata('DOCTRINE\TESTS\MODELS\CMS\CMSUSER');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name);
}
@@ -478,8 +531,23 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
public function testLifecycleCallbackNotFound()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->addLifecycleCallback('notfound', 'postLoad');
$this->setExpectedException("Doctrine\ORM\Mapping\MappingException", "Entity 'Doctrine\Tests\Models\CMS\CmsUser' has no method 'notfound' to be registered as lifecycle callback.");
$cm->addLifecycleCallback('notfound', 'postLoad');
$cm->validateLifecycleCallbacks(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
}
/**
* @group ImproveErrorMessages
*/
public function testTargetEntityNotFound()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$cm->mapManyToOne(array('fieldName' => 'address', 'targetEntity' => 'UnknownClass'));
$this->setExpectedException("Doctrine\ORM\Mapping\MappingException", "The target-entity Doctrine\Tests\Models\CMS\UnknownClass cannot be found in 'Doctrine\Tests\Models\CMS\CmsUser#address'.");
$cm->validateAssocations();
}
}
@@ -21,6 +21,7 @@ class XmlMappingDriverTest extends AbstractMappingDriverTest
$mappingDriver = $this->_loadDriver();
$class = new ClassMetadata($className);
$class->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$mappingDriver->loadMetadataForClass($className, $class);
$expectedMap = array(
@@ -92,4 +93,4 @@ class CTI
class CTIFoo extends CTI {}
class CTIBar extends CTI {}
class CTIBaz extends CTI {}
class CTIBaz extends CTI {}
@@ -0,0 +1,62 @@
<?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\Tests\ORM\Proxy;
use Doctrine\Tests\OrmTestCase;
use Doctrine\ORM\Proxy\Autoloader;
/**
* @group DDC-1698
*/
class AutoloaderTest extends OrmTestCase
{
static public function dataResolveFile()
{
return array(
array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp/__CG__RealClass.php'),
array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp/__CG__OtherRealClass.php'),
);
}
/**
* @dataProvider dataResolveFile
*/
public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile)
{
$actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
$this->assertEquals($expectedProxyFile, $actualProxyFile);
}
public function testAutoload()
{
if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) {
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
$autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) {
file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ");
});
$this->assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true));
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
}
@@ -52,7 +52,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
public function testReferenceProxyDelegatesLoadingToThePersister()
{
$identifier = array('id' => 42);
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$persister = $this->_getMockPersister();
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
@@ -69,7 +69,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
public function testReferenceProxyExecutesLoadingOnlyOnce()
{
$identifier = array('id' => 42);
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$persister = $this->_getMockPersister();
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
$proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier);
@@ -108,7 +108,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
public function testCreatesAssociationProxyAsSubclassOfTheOriginalOne()
{
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$this->assertTrue(is_subclass_of($proxyClass, 'Doctrine\Tests\Models\ECommerce\ECommerceFeature'));
}
@@ -125,34 +125,50 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
require_once dirname(__FILE__)."/fixtures/NonNamespacedProxies.php";
$className = "\DoctrineOrmTestEntity";
$proxyName = "DoctrineOrmTestEntityProxy";
$proxyName = "DoctrineOrmTestEntity";
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
$classMetadata->setIdentifier(array('id'));
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
$classCode = file_get_contents(dirname(__FILE__)."/generated/".$proxyName.".php");
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
$this->assertNotContains("class DoctrineOrmTestEntityProxy extends \\\\DoctrineOrmTestEntity", $classCode);
$this->assertContains("class DoctrineOrmTestEntityProxy extends \\DoctrineOrmTestEntity", $classCode);
$this->assertNotContains("class DoctrineOrmTestEntity extends \\\\DoctrineOrmTestEntity", $classCode);
$this->assertContains("class DoctrineOrmTestEntity extends \\DoctrineOrmTestEntity", $classCode);
}
public function testClassWithSleepProxyGeneration()
{
$className = "\Doctrine\Tests\ORM\Proxy\SleepClass";
$proxyName = "DoctrineTestsORMProxySleepClassProxy";
$proxyName = "DoctrineTestsORMProxySleepClass";
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
$classMetadata->setIdentifier(array('id'));
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
$classCode = file_get_contents(dirname(__FILE__)."/generated/".$proxyName.".php");
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
$this->assertEquals(1, substr_count($classCode, 'function __sleep'));
}
/**
* @group DDC-1771
*/
public function testSkipAbstractClassesOnGeneration()
{
$cm = new \Doctrine\ORM\Mapping\ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertNotNull($cm->reflClass);
$num = $this->_proxyFactory->generateProxyClasses(array($cm));
$this->assertEquals(0, $num, "No proxies generated.");
}
public function testNoConfigDir_ThrowsException()
{
$this->setExpectedException('Doctrine\ORM\Proxy\ProxyException');
@@ -181,3 +197,8 @@ class SleepClass
return array('id');
}
}
abstract class AbstractClass
{
}
+10 -1
View File
@@ -336,4 +336,13 @@ class ExprTest extends \Doctrine\Tests\OrmTestCase
$orExpr = $this->_expr->orx();
$orExpr->add($this->_expr->quot(5, 2));
}
}
/**
* @group DDC-1683
*/
public function testBooleanLiteral()
{
$this->assertEquals('true', $this->_expr->literal(true));
$this->assertEquals('false', $this->_expr->literal(false));
}
}

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