Compare commits

...

750 Commits
2.1.6 ... 2.2.2

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
6136654dad Release 2.2.0-BETA1 2011-12-20 22:39:27 +01:00
Benjamin Eberlei
9d906fa31e Update build common 2011-12-20 22:39:12 +01:00
Benjamin Eberlei
dcaf1b5891 Prepare 2.2 beta 2011-12-20 22:38:38 +01:00
Benjamin Eberlei
9f81d5d077 Fix PEAR path 2011-12-20 21:55:58 +01:00
Alexander
d3e697143f Merge pull request #234 from FabioBatSilva/testJoinQueries
Fix QueryTest#testJoinQueries order
2011-12-20 11:25:04 -08:00
Fabio B. Silva
e90545cef5 ORDER BY CmsArticle#topic 2011-12-20 17:22:14 -02:00
Fabio B. Silva
0a01b14830 fix QueryTest#testJoinQueries order 2011-12-20 16:14:01 -02:00
Guilherme Blanco
4bc014c696 Merge pull request #233 from FabioBatSilva/DDC-1539
[DDC-1539] Fix DDC-1539
2011-12-20 08:36:18 -08:00
Fabio B. Silva
e45ebbac46 remove white spaces 2011-12-20 10:40:29 -02:00
Fabio B. Silva
24dc74a800 Fixed DDC-1539 2011-12-20 10:31:00 -02:00
Guilherme Blanco
772f58a95b Removed test since I'm unable to test now. 2011-12-20 00:48:19 -05:00
Guilherme Blanco
f6eb83705a Added coverage to DDC-1521. Small CS changes. 2011-12-20 00:05:14 -05:00
Benjamin Eberlei
6c4aaab2d6 Merge branch 'TrailingWhitespaces' 2011-12-19 22:56:52 +01:00
Benjamin Eberlei
cd6131c9b8 Remove all trailing whitespaces 2011-12-19 22:56:19 +01:00
Benjamin Eberlei
f2d8102bbf Fix bug in test, removed an assertion that is not necessary and violates sqlites autoincrement assumptions 2011-12-19 22:32:50 +01:00
Benjamin Eberlei
86f67788c5 Merge pull request #231 from FabioBatSilva/tmp2
[DDC 1213]  bit comparison ( BIT_AND() , BIT_OR() )
2011-12-19 11:08:05 -08:00
Fabio B. Silva
ea5108ea0f rebase upstream/master 2011-12-19 16:25:31 -02:00
Fabio B. Silva
5c89d7ffcb support for bit comparison 2011-12-19 16:24:16 -02:00
Benjamin Eberlei
568698e321 Add dependency to doctrine-build-common and refactor build.xml and build.properties 2011-12-19 19:16:26 +01:00
Benjamin Eberlei
b545525e13 Update ORM to Doctrine Common master 2011-12-19 18:03:53 +01:00
Benjamin Eberlei
bf32125bad DDC-1545 - Fix issue with changing values from null to something new.
This issue was introduced by a side-effect in 2.1.3 with
d9f9228d95. In this commit read-only
objects where prevented to be updated. This lead to an invalid check not
being performed in UnitOfWork#computeChangeSet which was present before
where an association that was null would be injected into the
originalEntityData using the UnitOfWork#setOriginalEntityProperty()
method in the AbstractHydrator.

This commit now explicitly sets this field to null using the same API so
that is present during UnitOfWork#computeChangeSet.
2011-12-19 17:55:19 +01:00
Benjamin Eberlei
a9035e1533 Merge remote-tracking branch 'origin/master' 2011-12-19 17:11:19 +01:00
Guilherme Blanco
68663fac4b Fixed issue when one to one badly populated when containing null values. Fixed DDC-1548. 2011-12-19 10:55:29 -05:00
Benjamin Eberlei
b1b10042d2 Revert "Fixed issue with fetched association not being considered during changeSet calculation. Fixes DDC-1545."
This reverts commit a8478d5766.
2011-12-19 16:31:26 +01:00
Alexander
108cb53eef Merge pull request #229 from bschussek/DDC-1545
DDC-1545
2011-12-19 03:22:49 -08:00
Bernhard Schussek
e035fe7949 Fixed class name of test for DDC-1545 2011-12-19 11:20:37 +01:00
Benjamin Eberlei
40800bd3cd DDC-1530 - Validate field types in SchemaValidator 2011-12-19 10:11:11 +01:00
Alexander
4f67ea3869 Merge pull request #228 from asm89/ddc-551-parameter-inference
Fixed testsuite
2011-12-19 01:02:30 -08:00
Alexander
8c6c49a6ee Fixed testsuite 2011-12-19 09:55:49 +01:00
Benjamin Eberlei
7f8f39168a Merge pull request #227 from asm89/ddc-551-parameter-inference
[DDC-551] Add type inference to SQLFilter::setParameter() + cleaned tests
2011-12-19 00:47:58 -08:00
Alexander
bd07f8d3dd [DDC-551] Add type inference to SQLFilter::setParameter() + cleaned tests 2011-12-19 08:43:42 +01:00
Guilherme Blanco
a8478d5766 Fixed issue with fetched association not being considered during changeSet calculation. Fixes DDC-1545. 2011-12-19 01:39:48 -05:00
Guilherme Blanco
f6d9344d89 Merge pull request #219 from FabioBatSilva/DDC-1468
Fixed DDC-1468
2011-12-18 19:20:33 -08:00
Benjamin Eberlei
91e700a70e Merge pull request #226 from asm89/ddc-1505
[DDC-1505] joinColumn "nullable" should be handled true by default
2011-12-18 12:59:49 -08:00
Alexander
de769c6c3c [DDC-1505] joinColumn "nullable" should be handled true by default 2011-12-18 21:33:38 +01:00
Benjamin Eberlei
29fabbd81f Merge pull request #225 from asm89/fix-pgsql-testsuite
[DDC-551] Fix testcase on pgsql
2011-12-18 10:59:35 -08:00
Alexander
5160bdc1a8 [DDC-551] Fix testcase on pgsql 2011-12-18 19:58:32 +01:00
Benjamin Eberlei
003d1410b0 Merge pull request #224 from doctrine/DDC-551
DDC-551 - Filter branch
2011-12-18 08:03:23 -08:00
Bernhard Schussek
9b877499c7 Added test case for DDC-1545 2011-12-18 12:13:58 +01:00
Benjamin Eberlei
9cd8f85a8c DDC-1456 - Disallow setting id generators on composite identifiers. 2011-12-18 00:32:35 +01:00
Benjamin Eberlei
072094f722 DDC-1368 - Fix tests 2011-12-17 23:38:39 +01:00
Benjamin Eberlei
170271fd72 DDC-1368 - Improve schema validator 2011-12-17 23:27:39 +01:00
Benjamin Eberlei
bb47e90696 Merge pull request #223 from doctrine/DDC-1544
DDC-1544
2011-12-17 14:04:54 -08:00
Benjamin Eberlei
20c859611a Merge pull request #222 from EvanDotPro/DDC-1544
DDC-1544 Unit test / assertions for ResolveTargetEntityListener
2011-12-17 14:03:33 -08:00
Evan Coury
36a47e391c DDC-1544 - Add unit test and assertions for ResolveTargetEntityListener 2011-12-17 15:00:05 -07:00
Benjamin Eberlei
267ce7df88 DDC-1544 - Add ResolveTargetEntityListener 2011-12-17 19:35:10 +01:00
Benjamin Eberlei
cfe1259400 DDC-1541 - Fix wrong references in ClassMetadataBuilder 2011-12-17 12:39:44 +01:00
Benjamin Eberlei
6015253064 DDC-1524 - Add validation and error messages for annotation named query code. 2011-12-17 12:35:22 +01:00
Benjamin Eberlei
98bd5cae64 Revert "Incorporated setAssociationTargetClass, which solves biggest problem of modular system in ZF. Any questions, pelase forward to @EvanDotPro."
This reverts commit cac9928f28.
2011-12-17 11:36:23 +01:00
Guilherme Blanco
cac9928f28 Incorporated setAssociationTargetClass, which solves biggest problem of modular system in ZF. Any questions, pelase forward to @EvanDotPro. 2011-12-16 16:16:22 -05:00
Benjamin Eberlei
cd04cbc7c4 Merge branch 'DDC-1514' 2011-12-15 23:00:13 +01:00
Benjamin Eberlei
5b5fb2b732 DDC-1514 - Fix complex self-referencing + proxy hydration problem. 2011-12-15 23:00:01 +01:00
Benjamin Eberlei
e8a47b3921 DDC-1519 - Fix bug in merging of entities that contain foreign identifiers 2011-12-15 20:49:25 +01:00
Fabio B. Silva
017a7d889f Fixed DDC-1468 2011-12-15 17:12:01 -02:00
Guilherme Blanco
41a3d90a57 Merge pull request #214 from yethee/xml_driver
Fixed typo in the XmlDriver
2011-12-13 20:07:43 -08:00
Benjamin Eberlei
99e46a23c6 Bugfix in UnitOfWorklib/Doctrine/ORM/UnitOfWork.phptriggerEagerLoads() 2011-12-13 21:26:04 +01:00
Guilherme Blanco
65c2b498ad Removed broken test. 2011-12-12 23:40:48 -05:00
Benjamin Eberlei
f7ede572e0 DDC-1415 - Remove EntityEventDelegatee, the API sucks and we need another approach. 2011-12-12 16:46:53 +01:00
Benjamin Eberlei
43ef8765fd DDC-1527 - Port bugfix for master branch 2011-12-12 16:39:52 +01:00
Benjamin Eberlei
bda98150c4 Merge pull request #216 from andrewmackrodt/DDC-1025
DDC-1025 - Absolute Namespace Annotations
2011-12-11 14:32:02 -08:00
Andrew Mackrodt
ef12a09ae0 Added remaining absolute namespace paths to phpdoc annotations - issue DDC-1025. 2011-12-11 21:56:27 +00:00
Andrew Mackrodt
2fdb55a878 Added absolute namespace paths to phpdoc annotations - issue DDC-1025. 2011-12-11 21:56:26 +00:00
Benjamin Eberlei
b6d776f75d DDC-551 - rework walker filtering 2011-12-11 21:14:09 +01:00
Benjamin Eberlei
ad6130b02d DDC-551 - Cleanup filters branch, especially inheritance related code and yoda conditions and some inconsistencies 2011-12-11 19:29:36 +01:00
Benjamin Eberlei
ca5dbb182a DDC-551 - Make filters case-sensitive everywhere 2011-12-11 19:27:50 +01:00
Benjamin Eberlei
69b1eb5c64 DDC-551 - Fix locking mess with filters 2011-12-11 18:46:57 +01:00
Benjamin Eberlei
0f501114eb Merge branch 'master' into DDC-551 2011-12-11 18:39:11 +01:00
Benjamin Eberlei
f7175c229e DDC-551 - Fix some ugly yoda conditions and a wrong nesting. 2011-12-11 18:39:04 +01:00
Benjamin Eberlei
853a0ee865 Merge branch 'DDC-1515' 2011-12-11 17:09:11 +01:00
Benjamin Eberlei
40d094fea2 DDC-1515 - Now the real bugfix 2011-12-11 17:08:58 +01:00
Benjamin Eberlei
f6d2b00d5c DDC-1400 - var_dump(), seriously? 2011-12-11 16:42:59 +01:00
Benjamin Eberlei
57970499fd Revert "DDC-1515 - Merge from 2.1.x"
This reverts commit bd0fb574e3.
2011-12-11 16:11:16 +01:00
Benjamin Eberlei
bd0fb574e3 DDC-1515 - Merge from 2.1.x 2011-12-11 16:07:35 +01:00
Deni
2ce9246733 Fixed typo in the XmlDriver 2011-12-11 17:24:38 +04:00
Guilherme Blanco
954b5077e4 Fixed nesting recursion error in some situations. Complement to DDC-1276. 2011-12-09 10:59:53 -05:00
Guilherme Blanco
0febf06114 Made ClassMetadataBuilder support ClassMetadataInfo instead of ClassMetadata. Fixed DDC-1508. 2011-12-09 00:04:47 -05:00
Guilherme Blanco
33c5b639b0 Merge pull request #211 from FabioBatSilva/DDC-1057
DDC-1057
2011-12-07 07:25:45 -08:00
Fabio B. Silva
8e50a31b98 trying remove whitespaces 2011-12-07 12:31:23 -02:00
Fabio B. Silva
c6a89c64f3 put return after comment 2011-12-07 12:16:27 -02:00
Fabio B. Silva
df19e68a86 Fixed DDC-1057 2011-12-07 11:23:15 -02:00
Alexander
5e91f0c1ca [DDC-551] Update SQLWalker to reflect filter requirements for inheritance 2011-12-07 10:02:15 +01:00
Alexander
efe7a01482 [DDC-551] Fixed CS, comments by @stof 2011-12-05 23:00:52 +01:00
Alexander
f4663f4512 [DDC-551] Another batch of small refactorings 2011-12-05 22:19:54 +01:00
Alexander
e8d30068e2 [DDC-551] Various refactorings 2011-12-05 22:05:42 +01:00
Alexander
04635ad4ff Merge remote-tracking branch 'upstream/master' into DDC-551
Conflicts:
	lib/Doctrine/ORM/Persisters/ManyToManyPersister.php
	lib/Doctrine/ORM/Persisters/OneToManyPersister.php
2011-12-05 21:53:34 +01:00
Guilherme Blanco
eaec259186 Merge pull request #209 from FabioBatSilva/DDC1170
DDC-1170
2011-12-05 12:16:39 -08:00
Alexander
3b7d16c60f [DDC-551] General cleanup of the code. 2011-12-05 21:14:31 +01:00
Fabio B. Silva
33c68df3ba Fixed DDC-1170 2011-12-05 17:35:49 -02:00
Alexander
4c842974b4 [DDC-551] Add filters only on root entities in SingleTablePersister 2011-12-05 18:56:44 +01:00
Alexander
752b502326 [DDC-551] Add filters only on root entities in JoinedSubclassPersister 2011-12-05 18:26:56 +01:00
Alexander
e98c775f0d Revert "[DDC-551] Initial support for filters in the JoinedSubclassPersister"
This reverts commit f6d5f0481e.
2011-12-05 16:14:04 +01:00
Guilherme Blanco
0380d5ae58 Implemented multiple enhancements in InExpression support for DQL. Fixed DDC-1472 and DDC-1416. 2011-12-04 02:41:54 -05:00
Guilherme Blanco
a26990c3e8 DDC-1457: Fixed wrong docblock. 2011-12-04 02:14:47 -05:00
Guilherme Blanco
2f6b930a8d Implemented missing support in CollectionMemberComparison. Removed old todo in ArrayHydrator. Finished implementation of IdentificationVariable in ArithmeticPrimary. 2011-12-03 15:19:21 -05:00
Guilherme Blanco
2642daa438 Fixed DDC-1236: GROUP BY now supports ResultVariable and IdentificationVariable. Composite PK is also supported. If you are willing to group by an aggregate function or a function itself, just place it in SELECT expression then refer to it in the GROUP BY clause. If you are not willing to have the function being part of your resultset, just mark the column as HIDDEN and you are done. 2011-12-01 23:52:35 -05:00
Benjamin Eberlei
619a31913a DDC-1517 - Fix EntityRepository#find() and EntityManager#getReference() breaking on invalid or missing identifiers. 2011-12-01 21:18:39 +01:00
Guilherme Blanco
5e3e8b3957 More refactorings and optimizations. 2011-12-01 10:00:26 -05:00
Alexander
bf1cc29a2a [DDC-551] Fixed some comments 2011-12-01 09:46:02 +01:00
Alexander
f6d5f0481e [DDC-551] Initial support for filters in the JoinedSubclassPersister
Still some things to do.
2011-11-30 23:01:10 +01:00
Alexander
4c94a7ccc5 [DDC-551] Various minor fixes after merge and cleanup 2011-11-30 16:40:55 +01:00
Guilherme Blanco
5b73f1bd82 Improved code readability. Improved performance. 2011-11-30 09:57:54 -05:00
Guilherme Blanco
356f5874bf Added support to removeElement remove items without initializing the PersistentCollection. 2011-11-29 11:29:17 -05:00
Guilherme Blanco
24f6b74427 Refactored UnitOfWork::createEntity, improving its performance. 2011-11-29 10:36:32 -05:00
Benjamin Eberlei
62ec98a9fc Merge pull request #205 from Seldaek/psr0
PSR-0 compliance
2011-11-28 05:05:50 -08:00
Benjamin Eberlei
f2467dd30e Merge branch 'DDC-1512' 2011-11-28 11:16:35 +01:00
Benjamin Eberlei
0c12d3ed5a DDC-1512 - Make ClassMetadataFactory::isTransient() entity namespace aware. 2011-11-28 11:16:23 +01:00
Benjamin Eberlei
f2f32ca70f DDC-1509 - Fix regression in doMerge() introduced with the DDC-1383 bugfix 2011-11-28 10:04:33 +01:00
Jordi Boggiano
b8ac2fb416 Add requires to new files for BC 2011-11-27 18:16:18 +01:00
Jordi Boggiano
10e74040af PSR-0 compliance 2011-11-27 12:59:17 +01:00
Benjamin Eberlei
2b7360e738 Merge pull request #200 from velovint/StandaloneTestsFailing
Register DoctrineAnnotations on call to OrmTestCase::createAnnotationDri...
2011-11-26 00:17:12 -08:00
Alexander
be48821e86 Merge remote-tracking branch 'origin/master' into DDC-551
Conflicts:
	lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
	lib/Doctrine/ORM/Query.php
2011-11-23 22:43:42 +01:00
Alexander
68027e1b5f Merge remote-tracking branch 'upstream/master' 2011-11-23 22:31:17 +01:00
Guilherme Blanco
ef33454301 Reverted PR which broke suite. Issue is still valid, but it requires more investigation. 2011-11-23 08:40:47 -05:00
Guilherme Blanco
bfcea9460b Merge pull request #202 from realestateconz/master
Fixed array flip breaking discriminator map SQL generation
2011-11-22 18:36:30 -08:00
warezthebeef
b80ef58cab Fixed array_flip breaking discriminator map SQL generation 2011-11-23 12:15:23 +13:00
Benjamin Eberlei
be4eb63c62 Merge branch 'DBAL-15' 2011-11-21 21:38:05 +01:00
jsor
16aa558292 Remove sql conversion from where clauses and update statements 2011-11-21 15:08:36 +01:00
Benjamin Eberlei
135e515e7f DDC-1500 - Fix potential security problem in EntityRepository ORDER BY orientations 2011-11-21 15:04:46 +01:00
Guilherme Blanco
7ca43b72c9 Merge pull request #201 from jonathaningram/patch-1
Fixed typo
2011-11-21 05:16:40 -08:00
Jonathan Ingram
248c9bdeff Fixed typo 2011-11-21 12:34:20 +11:00
Jan Sorgalla
4042bc53ce Fix argument on wrong method call 2011-11-20 19:57:04 +01:00
Jan Sorgalla
841d12e9b6 Move check for conversion SQL to ClassMetadataInfo 2011-11-20 19:50:51 +01:00
Vitali
507c8f45b4 Register DoctrineAnnotations on call to OrmTestCase::createAnnotationDriver() 2011-11-20 17:02:15 +03:00
Benjamin Eberlei
bda593a66d DDC-1448 - Add support for ObjectManagerAware interface and PersistentObject in ORM 2011-11-19 13:06:24 +01:00
Benjamin Eberlei
8eaf160ead Update Doctrine Common Vendor 2011-11-19 09:29:32 +01:00
Benjamin Eberlei
53b3030aa2 Clarify EntityManager#transactional() docblock 2011-11-19 08:58:58 +01:00
Benjamin Eberlei
dc0a03ab30 DDC-1400 working testcase 2011-11-19 08:50:49 +01:00
Jan Sorgalla
6f35679911 Initial implementation of Doctrine\DBAL\Types\Type::convertToDatabaseValueSQL() and Doctrine\DBAL\Types\Type::convertToPHPValueSQL() integration 2011-11-19 00:35:29 +01:00
Benjamin Eberlei
f9a4dcb2d0 Remove code that could allow users of xml and yaml to define orphan removal on the wrong association sides. 2011-11-18 18:33:03 +01:00
Benjamin Eberlei
a0d6d84a4f Merge branch 'DBAL-171' 2011-11-18 17:42:38 +01:00
Benjamin Eberlei
166c05dfaa Fix tests to be more stable 2011-11-18 17:42:32 +01:00
Benjamin Eberlei
ab4d3c3a09 Merge branch 'DBAL-171' 2011-11-18 17:30:26 +01:00
Benjamin Eberlei
9e8a950f2e DBAL-171 - Fix bug where params where resorted but types where not in DQL Query 2011-11-18 17:29:31 +01:00
Benjamin Eberlei
0d4e0626cf Merge pull request #158 from goetas/cascade-all
Collapsed cascade elements, if cascade-all.
2011-11-18 06:49:40 -08:00
Benjamin Eberlei
ef0a901cf5 Merge branch 'DDC-1496' 2011-11-18 15:44:19 +01:00
Benjamin Eberlei
ceadc95439 DDC-1496 - Fix bug with OneToMany collections having orphanRemoval=true and Collection#clear() being called. 2011-11-18 15:44:06 +01:00
Benjamin Eberlei
61e371cbdc DDC-1069 - Fix error in docblocks of query builder 2011-11-18 14:43:47 +01:00
Benjamin Eberlei
b44ff9b849 Merge pull request #194 from FabioBatSilva/DDC-1430-PGSQL-TEST
DDC-1430 - fix broken test on postgres
2011-11-18 05:14:15 -08:00
Benjamin Eberlei
2187f33477 Add test for DDC-1436 and DDC-1452 showing they are the same issues 2011-11-18 13:28:56 +01:00
Fabio B. Silva
f4da4591fa fix broken test on postgres 2011-11-16 09:48:11 -02:00
Benjamin Eberlei
43b1e79ec4 Merge pull request #193 from FabioBatSilva/DDC-1430
DDC 1430
2011-11-15 14:47:45 -08:00
Fabio B. Silva
aeb2ab132b group by all fields when entity has foreign keys 2011-11-15 20:27:45 -02:00
Fabio B. Silva
4cbd5eac95 Test Foreign Keys 2011-11-15 18:54:53 -02:00
Benjamin Eberlei
2d14be86f3 Merge pull request #192 from FabioBatSilva/DDC-1474
DDC-1474
2011-11-15 12:10:19 -08:00
Fabio B. Silva
08edf34057 move tests to SelectSqlGenerationTest 2011-11-15 17:27:17 -02:00
Benjamin Eberlei
9916f34262 Merge pull request #191 from FabioBatSilva/DDC1430
DDC-1430
2011-11-15 11:07:30 -08:00
Benjamin Eberlei
a0ee72f264 Fix bug introduced in recent XmlDriver commit 2011-11-15 20:03:56 +01:00
Fabio B. Silva
3f8347a4d9 fixed DDC-1474 2011-11-15 17:00:19 -02:00
Guilherme Blanco
14f20c16bc Changed the RSM to make is behavior as mixed if you alias an entity. 2011-11-15 15:14:57 -02:00
Fabio B. Silva
3dd5d14977 Fixed DDC-1430 2011-11-15 14:28:57 -02:00
Guilherme Blanco
77e076f1fd Fixed DDC-1492. 2011-11-15 01:10:27 -02:00
Guilherme Blanco
45d95ad130 Fixed wrong indentation by my previous commit. 2011-11-15 01:09:48 -02:00
Benjamin Eberlei
9b32a2d87a DDC-1452 - Fix missing fetched parameter in BasicEntityPersister 2011-11-14 23:37:02 +01:00
Benjamin Eberlei
3e95eed76e Merge branch 'DDC-1452' 2011-11-14 23:06:00 +01:00
Benjamin Eberlei
34c94dbd94 DDC-1452 - Fixed bug with multiple fetch joins of the same "propery-path" of Class+field name combinations 2011-11-14 23:05:33 +01:00
Benjamin Eberlei
98033cc878 Merge branch 'master' into DDC-1452 2011-11-14 21:53:36 +01:00
Benjamin Eberlei
909504c074 DDC-1461 - Fix test failures 2011-11-14 21:05:44 +01:00
Benjamin Eberlei
4c7e4296c9 Merge pull request #189 from FabioBatSilva/DDC-1404
DDC-1404
2011-11-14 10:32:31 -08:00
Benjamin Eberlei
1b7eacd9fa Merge pull request #188 from FabioBatSilva/DDC-1476
DDC-1476
2011-11-14 10:31:30 -08:00
Fabio B. Silva
8af0f9d071 added support for Inherited Named Queries 2011-11-14 16:07:37 -02:00
Fabio B. Silva
0632b37492 fix default field type 2011-11-14 13:17:56 -02:00
Guilherme Blanco
81cc6d9da8 Implemented alias support for EntityResult. This addresses DDC-1096 and DDC-1424. Improved DQL Parser, SQL Walker and Hydrators in general. Performance is generally improved by a factor of 20%. There is still more to be done, like remove the isMixed in ResultSetMapping, mainly because this query - SELECT u AS user FROM User u -, it should return an array('user' => [User object]), while currently it doesn't due to this before mentioned 'bug' in RSM. Will open a separate ticket for this. Also, UnitOfWork and Hydrators share code that could be abstracted/improved. 2011-11-14 01:36:39 -02:00
Benjamin Eberlei
e8eda4aeae Merge branch 'DDC-1461' 2011-11-13 23:14:40 +01:00
Benjamin Eberlei
c648981f28 DDC-1461 - Verified deferred explicit works 2011-11-13 23:14:31 +01:00
Benjamin Eberlei
c87d5af243 Specialize build status on versions 2011-11-13 22:40:27 +01:00
Benjamin Eberlei
077d4a0e15 Fix travis configuration files 2011-11-13 22:04:48 +01:00
Benjamin Eberlei
0fdffb9dbc Fix postgresql travis phpunit configuration file 2011-11-13 17:57:54 +01:00
Benjamin Eberlei
c2bb281e80 Merge travis support from @pborreli - thanks! 2011-11-13 17:44:37 +01:00
Benjamin Eberlei
6b0cd7b604 Merge branch 'master' of github.com:doctrine/doctrine2 2011-11-13 17:17:06 +01:00
Benjamin Eberlei
bb853722dc Merge branch 'thiagofesta-master' 2011-11-13 17:16:51 +01:00
Benjamin Eberlei
4571e498b4 DDC-1477 - Adjust patch to really fix bug in Proxy generation 2011-11-13 17:16:43 +01:00
Benjamin Eberlei
4e10a95dca Merge branch 'master' of https://github.com/thiagofesta/doctrine2 into thiagofesta-master 2011-11-13 17:05:23 +01:00
Benjamin Eberlei
e9068a1552 Merge pull request #175 from lsmith77/get_class_name
added EntityRepository::getClassName()
2011-11-13 07:57:58 -08:00
Benjamin Eberlei
64b649ef61 Merge pull request #160 from goetas/generation
Allow to unset one-to-one relation with generated class
2011-11-13 07:53:58 -08:00
Benjamin Eberlei
70f0136c8a Merge branch 'everzet-PreFlush-event' 2011-11-13 16:51:40 +01:00
Benjamin Eberlei
6520211df3 Merge everzet/PreFlush-event 2011-11-13 16:51:23 +01:00
Benjamin Eberlei
056b34d847 Merge branch 'DDC-1491' 2011-11-13 15:45:15 +01:00
Benjamin Eberlei
f7c46c7b33 DDC-1491 - Fix Schema Validator bug 2011-11-13 15:45:06 +01:00
Benjamin Eberlei
146f8f81f7 Merge branch 'DDC-1490' 2011-11-13 15:37:04 +01:00
Benjamin Eberlei
5aeabcb445 DDC-1490 - Fix id generation of sequence and identity to cast values to int 2011-11-13 15:36:48 +01:00
Benjamin Eberlei
01697fee3d Fix failing PostgreSQL tests 2011-11-12 22:16:39 +01:00
Benjamin Eberlei
450d92872a Forward compatibility with DBAL master 2011-11-12 12:56:44 +01:00
Benjamin Eberlei
a14ba1e561 DDC-1237 - Remove dependency to mbstring 2011-11-12 09:43:37 +01:00
Benjamin Eberlei
4d425317b4 Merge pull request #187 from asm89/DDC-1458
Fix DDC-1458
2011-11-10 07:19:46 -08:00
Alexander
1f55351f19 Cleanup 2011-11-10 16:16:55 +01:00
Alexander
9c9f85ed4b Only refresh the given entity if an entity is specified in the query hints 2011-11-09 22:52:48 +01:00
Alexander
3131103801 Failing test case 2011-11-09 22:15:22 +01:00
Benjamin Eberlei
d9ec0a59ec Merge pull request #186 from asm89/fix-tests-phpunit-3.6
Fixed failing tests in PHPUnit 3.6.2 (expecting \Exception was deprecated)
2011-11-09 01:04:38 -08:00
Alexander
2ddfc6af5a Fixed failing tests in PHPUnit 3.6.2 (expecting \Exception was deprecated) 2011-11-09 08:20:35 +01:00
Guilherme Blanco
c391287cc4 More optimizations and increased code readability in Id Generators. 2011-11-08 18:36:18 -02:00
Benjamin Eberlei
d943e67c65 Merge pull request #185 from asm89/indexby-exception
Remove invalid(?) exception for now
2011-11-08 04:17:01 -08:00
Alexander
39ed719c4c Remove invalid(?) exception for now 2011-11-08 13:16:33 +01:00
Benjamin Eberlei
3914e4a5d0 Merge pull request #184 from asm89/InvalidFetchMode-exception
Throw exception on invalid fetch mode in annotations
2011-11-08 01:28:32 -08:00
Benjamin Eberlei
cce3798b4b Merge pull request #183 from asm89/ProxyIdentifier-types
Proxy identifier type casting
2011-11-08 01:27:30 -08:00
Alexander
e99b800406 Throw exception on invalid fetch mode in annotations 2011-11-08 10:01:22 +01:00
Alexander
0cc176aae2 Do not cast BigInt to (int) 2011-11-08 09:47:33 +01:00
Alexander
6707129a3e Added type casts to 'non-lazy' identifiers in proxies. 2011-11-08 09:43:06 +01:00
Guilherme Blanco
32b8d77580 Fixed CS issues. More tiny optimizations in UnitOfWork. 2011-11-07 01:27:20 -02:00
Guilherme Blanco
96aa25fb3e Optimized more pieces of code in UnitOfWork. 2011-11-06 02:03:34 -02:00
Guilherme Blanco
ea69d9ca0c Fixed wrong optimization. Optimized more pieces of code in UnitOfWork. 2011-11-05 19:21:35 -02:00
Guilherme Blanco
c6a3ff4da5 Optimizations in UnitOfWork. 2011-11-05 03:09:14 -02:00
everzet
9c4c06c422 optimized PreFlush (moved into computeChangeSet function) 2011-11-03 16:24:47 +02:00
Benjamin Eberlei
793a1032e6 Merge pull request #182 from asm89/innerjoin-fetch-eager
Eagerly fetched entities should only be inner joined if they are loaded
2011-11-03 03:58:21 -07:00
Alexander
ca438fa110 Eagerly fetched entities should only be inner joined if they are loaded
from the owning side.
2011-11-03 09:20:41 +01:00
Guilherme Blanco
305da5b8ff Added missing indexBy. 2011-11-03 02:49:50 -02:00
Guilherme Blanco
3c31d88810 Major optimizations in SqlWalker code, reducing overhead, reducing lookahead checks. 2011-11-03 02:44:50 -02:00
Guilherme Blanco
058242fa27 Fixed missing changes. 2011-11-03 02:37:54 -02:00
Guilherme Blanco
d1bfd57fd9 Initial code optimization in Hydrators. 2011-11-02 22:08:24 -02:00
Alexander
53055f1fb2 [DDC-551] Fixed a bug in the sql generation for filters 2011-11-02 15:15:22 +01:00
Alexander
9ccce8ed74 [DDC-551] Add filters to eagerly joined entities in the persisters 2011-11-02 15:15:14 +01:00
Benjamin Eberlei
d532de9da3 Fix docblocks ObjectHydratorTest 2011-11-01 17:43:23 +01:00
Benjamin Eberlei
afb8d63fcb Fixed some AbstractHydrator docblocks 2011-11-01 16:42:03 +01:00
Benjamin Eberlei
98e3e04477 Add autoload section to composer.json 2011-11-01 15:05:38 +01:00
Benjamin Eberlei
4b316ec54f DDC-1389 - Add validation for empty discriminator map values 2011-10-31 23:35:41 +01:00
Benjamin Eberlei
c965d231b1 Rename method and refactor code a bit 2011-10-31 23:21:11 +01:00
Benjamin Eberlei
abb129028a Merge branch 'DBAL-1420' 2011-10-31 23:13:33 +01:00
Benjamin Eberlei
e31e164896 DBAL-1420 - Use safe mode for schema validation. Dropping stuff isnt necessary here. 2011-10-31 23:12:52 +01:00
Benjamin Eberlei
c38d273c1f Merge pull request #151 from doctrine/DDC-1385
DDC-1385
2011-10-31 15:10:54 -07:00
Benjamin Eberlei
88bda9b0d5 Merge pull request #180 from asm89/innerjoin-on-fetch-eager
[DDC-1463] Inner join eagerly loaded entities if possible
2011-10-31 14:24:47 -07:00
Alexander
53386e5247 Merge branch 'master' into innerjoin-on-fetch-eager
Conflicts:
	lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
2011-10-31 22:24:16 +01:00
Benjamin Eberlei
231d84b625 Fix xml fix again 2011-10-31 22:15:08 +01:00
Alexander
22b3b46b61 Removed unnecessary spaces in generated SQL 2011-10-31 22:08:40 +01:00
Alexander
0f938b8c1d Added tests for inner join generation with eager loading 2011-10-31 21:53:46 +01:00
Alexander
3994b80aa4 Fix TODO: Inner join when all join columns are NOT nullable. 2011-10-31 21:36:55 +01:00
Benjamin Eberlei
54217bd4c6 Merge branch 'DDC-1462' 2011-10-31 21:34:31 +01:00
Benjamin Eberlei
1dc5b7fba4 DDC-1462 - Fix bug in slice when calling on a dirty collection that is marked extra lazy 2011-10-31 21:34:22 +01:00
Benjamin Eberlei
3b819b52ca Merge branch 'DDC-1399' 2011-10-31 21:17:09 +01:00
Benjamin Eberlei
95193ab5f8 DDC-1399 - Fix extra lazy collections when inner collection contains values but persistent collection is marked not dirty because of flush() 2011-10-31 21:17:01 +01:00
Benjamin Eberlei
11f82bd41f DDC-1439 - Fix validate mapping some more 2011-10-31 20:49:59 +01:00
Guilherme Blanco
d444f0e06b Micro optimization in SqlWalker. 2011-10-30 16:22:45 -02:00
Guilherme Blanco
c246c6b28b AssignedGenerator optimization. 2011-10-30 16:07:43 -02:00
Guilherme Blanco
3cfa479c01 Micro optimization in computeChangeSet when using readOnly entities. 2011-10-30 15:46:07 -02:00
Benjamin Eberlei
67497ed834 Merge branch 'DDC-1384' 2011-10-30 16:48:00 +01:00
Benjamin Eberlei
64d405f7dd Merge origin/master into DDC-1384 2011-10-30 12:12:18 +01:00
Benjamin Eberlei
94c288a6a8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-30 08:26:03 +01:00
Benjamin Eberlei
2c036b3185 Fix glitch in testsuites tearDown() 2011-10-30 08:25:19 +01:00
Benjamin Eberlei
1bbec8dd33 Clarify error message 2011-10-30 00:25:49 +02:00
Benjamin Eberlei
30731e0727 DDC-1384 - Fix all tests on Oracle 2011-10-29 23:58:09 +02:00
Benjamin Eberlei
50e028212d DDC-1384 - Fix a bunch of Oracle test failures 2011-10-29 20:42:44 +02:00
Benjamin Eberlei
fb55e8094b DDC-1384 - Made tests case-insenstive to work with latest change regarding column sizes 2011-10-29 18:48:34 +02:00
Guilherme Blanco
96cb3d8004 Merge pull request #178 from adrienbrault/master
Fix Query iterate() doc return type
2011-10-29 06:39:27 -07:00
Adrien BRAULT
e39bfced4a Fix iterate method doc return type 2011-10-29 13:40:01 +02:00
Guilherme Blanco
15562d030e Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-29 02:00:55 -02:00
Guilherme Blanco
0ec2cc557f Implemented support to entities with association marked as @Id support in many situations. Fixed DDC-1435. 2011-10-29 02:00:35 -02:00
Benjamin Eberlei
7d921a8220 DDC-1452 - Attach working testcase 2011-10-29 00:22:45 +02:00
Benjamin Eberlei
f232e45efe Merge branch 'DDC-1410' 2011-10-28 23:45:38 +02:00
Benjamin Eberlei
7be98f475e DDC-1410 - Remove code-inlining that caused problems 2011-10-28 23:45:23 +02:00
Thiago Festa
66d2b9e0fb The ProxyFactory was redeclaring methods serialize and unserialize on the cache file on some OSs. 2011-10-28 17:54:15 -02:00
Guilherme Blanco
3745e948c6 Made SimpleSelectExpression (Literal) be included as a scalar result. More general SQL Walker optimizations. 2011-10-28 14:25:12 -02:00
Guilherme Blanco
1f06e9fca5 Fixed issue with SimpleSelectExpression containing Literals. This issue is related to a previously fixed ticket DDC-1079. 2011-10-28 12:56:14 -02:00
Guilherme Blanco
7841ccb7c0 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-28 12:49:21 -02:00
Guilherme Blanco
1579c43433 Code beautification and docblocks enhancements. 2011-10-28 12:49:01 -02:00
Benjamin Eberlei
fbfd59afa0 Merge branch 'DDC-1454' 2011-10-28 00:50:30 +02:00
Benjamin Eberlei
f34eb83a7c DDC-1454 - Fix exists() for Joined table inheritance 2011-10-28 00:50:10 +02:00
Benjamin Eberlei
8db1a09001 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-28 00:26:54 +02:00
Benjamin Eberlei
4a0227e5f2 Revert Expr\Base patch 2011-10-28 00:24:41 +02:00
Benjamin Eberlei
a98c7df2f1 Merge branch 'DDC-217' 2011-10-27 22:36:25 +02:00
Benjamin Eberlei
5918058d86 Merge origin/master 2011-10-27 22:36:06 +02:00
Guilherme Blanco
c5ef21864f Fixed bug with fetch=EAGER associations that have already been hydrated during querying. 2011-10-26 15:04:49 -02:00
Asmir Mustafic
d09285e9d3 Collapse cascade all test (YAML too) 2011-10-26 10:59:15 +02:00
Asmir Mustafic
035ca8e500 Collapse cascade all test 2011-10-26 10:14:59 +02:00
Lukas Kahwe Smith
0a5a23628f added EntityRepository::getClassName() to fullfill the ObjectRepository interface
see https://github.com/doctrine/common/pull/70
2011-10-25 23:21:39 +02:00
Benjamin Eberlei
cfe7ab46f2 Merge branch 'master' of git://github.com/doctrine/doctrine2 2011-10-25 22:55:08 +02:00
Benjamin Eberlei
3b9312e291 Bump DBAL version and make TestUtil more lenient 2011-10-25 22:54:20 +02:00
Benjamin Eberlei
a02b0c9269 Merge pull request #165 from armetiz/patch-3
Update lib/Doctrine/ORM/Query/Expr/Base.php
2011-10-24 03:42:04 -07:00
Benjamin Eberlei
0cdf1e042c Merge pull request #173 from jaikdean/master
Fixed a couple of typos of 'discriminator'
2011-10-24 03:41:19 -07:00
Project
1873302ee9 Merge branch 'generation' of https://github.com/goetas/doctrine2 into generation 2011-10-24 12:15:01 +02:00
Asmir Mustafic
7efd615b8c Coding standards 2011-10-24 12:00:11 +02:00
Jaik Dean
cdb452b27b Fixed typos of 'discriminator' 2011-10-24 10:01:27 +01:00
Asmir Mustafic
f5330741ac Coding standards 2011-10-24 10:59:50 +02:00
Asmir Mustafic
5f80b57554 Improoved coding standards 2011-10-24 10:19:01 +02:00
Asmir Mustafic
cb76222e63 Collapse cascade persist, remove, refresh, detach, merge into
cascade-all (implemented currently only for XML annotation)
2011-10-24 09:54:31 +02:00
Asmir Mustafic
1b83fcc46d Coding standards 2011-10-24 09:20:24 +02:00
Benjamin Eberlei
a8052dec37 Added note about result cache changes 2011-10-23 23:35:04 +02:00
Benjamin Eberlei
adc4840cce DDC-217 - Add support for QueryCacheProfiles and remove the old result caching code from ORM. Deprecate a bunch of methods in favor of using the AbstractQuery#getQueryCacheProfile method. 2011-10-23 23:28:23 +02:00
Benjamin Eberlei
993d6f5ac8 Merge pull request #171 from asm89/fix-mysql-tests
Fix mysql testsuite
2011-10-23 13:12:09 -07:00
Alexander
80c9690926 Fix mysql testsuite 2011-10-23 22:06:03 +02:00
Benjamin Eberlei
d9740e2028 Merge pull request #170 from asm89/fix-tests
Fix testsuite
2011-10-23 12:39:00 -07:00
Alexander
f1df4ffca4 Fix testsuite 2011-10-23 21:37:29 +02:00
everzet
91d8829c43 removed non-used code 2011-10-23 18:50:24 +03:00
everzet
20ed8869e4 added test for PreFlush lifetime event 2011-10-23 18:41:41 +03:00
everzet
66e2a9260e added PreFlush lifetime event and lifecycle callback 2011-10-23 18:39:16 +03:00
Benjamin Eberlei
9b8d2d512b Merge pull request #168 from dfreudenberger/master
postFlush event implementation
2011-10-23 01:12:27 -07:00
Benjamin Eberlei
fd00f2d371 Merge pull request #166 from doctrine/DDC-720
DDC-720 - Add support to flush only one entity through EntityManager#flush()
2011-10-23 01:07:14 -07:00
Benjamin Eberlei
5d3298e706 DDC-720 - Correct mentioned issues by @asm89 2011-10-23 10:05:46 +02:00
Daniel Freudenberger
fa8000fa5c dispatch the event after the snapshot was taken 2011-10-23 01:27:09 +02:00
Daniel Freudenberger
5e28273548 fixed typo in docblock 2011-10-22 18:57:48 +02:00
Daniel Freudenberger
46a3fecb4f added the postFlush event 2011-10-22 18:38:51 +02:00
Alexander
079e2b1302 [DDC-1384] Fix for generating sql with aliases not longer than MaxIdentifierLength 2011-10-22 16:27:56 +02:00
Benjamin Eberlei
b910a487c5 DDC-720 - Wait, we should really test it only changes the passed entity. 2011-10-22 14:31:23 +02:00
Benjamin Eberlei
f569a2a389 DDC-720 - Add support to flush only one entity (within cascade rules) through EntityManager#flush() 2011-10-22 13:44:33 +02: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
23560038b4 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-22 10:37:32 +02:00
Asmir Mustafic
d4059b88ca Nullable relations, fixing join condition 2011-10-21 15:30:21 +02:00
armetiz
baef4f735f Update lib/Doctrine/ORM/Query/Expr/Base.php 2011-10-21 16:30:06 +03:00
Benjamin Eberlei
01f1924a89 Merge pull request #163 from armetiz/patch-2
Update lib/Doctrine/ORM/Query/Expr.php
2011-10-21 05:18:01 -07:00
Alexander
07ce4092cd Merge branch 'master' into DDC-551 2011-10-21 12:04:29 +02:00
armetiz
4a50eb4fa7 Update lib/Doctrine/ORM/Query/Expr.php 2011-10-21 11:55:54 +03:00
Asmir Mustafic
fe84a61d0b Better code generation when association is nullable 2011-10-21 09:38:37 +02:00
Asmir Mustafic
596ba3d5b1 Collection inteface instead of ArrayCollection 2011-10-19 15:04:16 +02:00
Benjamin Eberlei
ca01065c6a Bugfix in short identifier shortcut with association ids 2011-10-19 11:58:59 +02:00
Asmir Mustafic
be3adfb35e With TO_MANY relations, class filed is instanceof ArrayCollection,
instead of targetEntity class type.
2011-10-19 09:25:40 +02:00
Asmir Mustafic
8f092812c4 Spaces 2011-10-19 09:17:30 +02:00
Asmir Mustafic
9c1202a766 Added <variableDefault> on generated class.
This allow to unset many-to-one and one-to-one relations


Example: $user->setGroup(null);
2011-10-19 09:15:41 +02:00
Asmir Mustafic
e19fd756cb Better indentation for generated class 2011-10-19 09:07:18 +02:00
Benjamin Eberlei
7345c795ac Merge pull request #154 from Partugal/master
Fix isTransient call on uninitialized ClassMetadataFactory
2011-10-19 00:04:14 -07:00
Benjamin Eberlei
753f75cfb9 Merge pull request #156 from goetas/xmlassoc
Set association-key attribute in xml mapping
2011-10-19 00:03:55 -07:00
Asmir Mustafic
97321a1ff2 Collapsed cascade elements, if cascade-all.
(better readability for generated xml)
2011-10-18 16:18:25 +02:00
Asmir Mustafic
0d57ffbc3b Set association-key attribute in xml mapping 2011-10-18 15:48:56 +02:00
Sergey Linnik
c3ec6e383c Fix isTransient call on uninitialized ClassMetadataFactory 2011-10-18 01:14:07 +04:00
Benjamin Eberlei
0b1f6e3539 Merge pull request #153 from asm89/shesek-patch-1
Support NULL in EntityRepository's magic findBy and findOneBy methods
2011-10-17 12:05:07 -07:00
Alexander
b8af241504 Added a testcase for findBy(.. => null) and renamed 'old' testcase 2011-10-17 20:53:04 +02:00
Alexander
91bc9c0329 Adjusted test to verify that findBy*(null) is now supported 2011-10-17 18:56:06 +02:00
Alexander
fea855004c Merge remote-tracking branch 'doctrine/master' into shesek-patch-1 2011-10-17 18:55:48 +02:00
Guilherme Blanco
83a8ed01e7 Merge pull request #94 from kwiateusz/findByOnePatch
Now findByOne really retrieve only one entity adding limit to query.
2011-10-17 06:24:37 -07:00
Benjamin Eberlei
2b663ff2bc Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-16 22:47:28 +02:00
Benjamin Eberlei
1a602a8cb3 Merge branch 'DDC-1278' 2011-10-16 22:45:50 +02:00
Benjamin Eberlei
939fbf9c24 DDC-1278 - Clean up event handling of new clear functionality. 2011-10-16 22:45:06 +02:00
Benjamin Eberlei
22a04fd6de Merge dominikl/DDC-1278 into doctrine/DDC-1278 2011-10-16 22:41:16 +02:00
Benjamin Eberlei
8466060797 DDC-1385 - Add INDEX BY scalar variables on the top-level 2011-10-16 19:23:20 +02:00
Benjamin Eberlei
ba5e73213b Merge pull request #150 from stof/initialize_object
Added the initializeObject method in the EntityManager
2011-10-16 08:04:46 -07:00
Christophe Coevoet
a8e6131e3b Added the initializeObject method in the EntityManager 2011-10-16 17:00:33 +02:00
Benjamin Eberlei
ee924ffaba DDC-1385 - Fix scalar handling for array hydrator 2011-10-16 16:27:50 +02:00
Benjamin Eberlei
2730f64d90 DDC-1385 - Refactor ObjectHydrator 2011-10-16 16:13:59 +02:00
Benjamin Eberlei
cb0e5dbff3 DDC-1385 - Fixed Mixed result hydration using INDEX BY to work on the top-level indexes, not some weird result. This is a BC break to those that actually use this crazy logic, sorry for that :-) 2011-10-16 12:46:17 +02:00
Benjamin Eberlei
fc738455b6 Merge branch 'DDC-1358' 2011-10-16 11:17:04 +02:00
Benjamin Eberlei
0252d55c67 DDC-1358 - Fix bug where multiple NULL root entity combined with scalar results will break the object and array hydrator.
This case likeli only occurs when doing native queries. A guard clause that prevents hydration from breaking
when RIGHT JOIN queries with null root entities appear has been added aswell.
2011-10-16 11:15:45 +02:00
Guilherme Blanco
eeba947ea7 Code optimizations. Fixed unused argument in OrmTestCase as referred in DDC-766. 2011-10-16 02:10:59 -02:00
Guilherme Blanco
33bcf7ad6f Added coverage to DDC-1161. 2011-10-16 01:42:36 -02:00
Alexander
58b381bf24 [DDC-551] use isClean to check the filterCollection state.. 2011-10-15 22:31:20 +02:00
Benjamin Eberlei
4474d305cb DDC-1210 - Optimize UnitOfWork collection handling internally. 2011-10-15 21:47:16 +02:00
Benjamin Eberlei
7c244abc1c Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-15 21:09:36 +02:00
Benjamin Eberlei
5c78ecaca1 Fix tests in EntityGenerator due to Annotation prefixes 2011-10-15 20:44:25 +02:00
Benjamin Eberlei
7b71b3284d Fix failing test due to EntityGenerator assuming beginning with 2.2 the AnnotationReader is always used. There is still the simple reader though. 2011-10-15 20:41:07 +02:00
Benjamin Eberlei
7e571212a7 Merge pull request #109 from alOneh/patch-1
Remove trailing spaces
2011-10-15 11:39:56 -07:00
Benjamin Eberlei
75e1d17d18 Merge branch 'ValidateJoinColumnsMatching' 2011-10-15 20:33:46 +02:00
Benjamin Eberlei
a82bffbfc9 Make SchemaValidator catch errors such as very invalid schema using only part of the primary key for join columns 2011-10-15 20:31:56 +02:00
Guilherme Blanco
ba38f3e1e9 Merge pull request #148 from asm89/ProxyIdentifer
Do not load entity on retrieving identifier from a proxy
2011-10-15 11:23:06 -07:00
Benjamin Eberlei
46541755ca Merge pull request #128 from alanbem/xml-schema-on-delete-fix
fixed wrong on-delete XML Schema mapping
2011-10-15 11:09:55 -07:00
Benjamin Eberlei
18fd29613c Merge pull request #137 from docteurklein/fluent_query_expr
added fluent pattern to Query\Expr\Base::add* methods
2011-10-15 11:09:37 -07:00
Benjamin Eberlei
1f59001ff7 Merge pull request #130 from bmichotte/master
Fluent entity (master)
2011-10-15 11:01:19 -07:00
lenar
3dc30dee11 use the correct targetEntity 2011-10-15 20:00:02 +02:00
lenar
cab154b873 identifier referencing foreign entity can be defined in parent class too 2011-10-15 19:59:50 +02:00
Benjamin Eberlei
7f5844c209 Merge pull request #147 from asm89/assignid
Goetas: Better error handling on missing assigned id
2011-10-15 10:41:27 -07:00
Alexander
c5e51e6fa9 Merge branch 'master' into assignid 2011-10-15 19:33:42 +02:00
Alexander
fdb9fb1c2b AssignedGenerator can always tell what field is missing an id 2011-10-15 19:33:29 +02:00
Benjamin Eberlei
b6c49863e8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-15 19:14:53 +02:00
Benjamin Eberlei
d0383f93d0 Merge branch 'DDC-1383' 2011-10-15 19:14:40 +02:00
Benjamin Eberlei
08716d9f72 DDC-1383 - Proxy Generation in merge was flawed with inheritance 2011-10-15 19:14:30 +02:00
Benjamin Eberlei
73101be422 Merge pull request #143 from craue/patch-2
added missing type hint
2011-10-15 10:05:53 -07:00
Benjamin Eberlei
6f3667201c Add @ignore and @internal to UnitOfWork#computeChangeSet 2011-10-15 18:11:14 +02:00
Alexander
d46352da01 Fixed tests + added dedicated tests for proxy loading and identifiers 2011-10-15 17:58:00 +02:00
Benjamin Eberlei
30149e48f9 Merge branch 'DDC-1411' 2011-10-15 17:53:11 +02:00
Benjamin Eberlei
52cea01563 DDC-1411 - Fixed onDelete handling in EntityGenerator 2011-10-15 17:53:04 +02:00
Benjamin Eberlei
f54cdf625e Merge branch 'DDC-1414' 2011-10-15 17:47:17 +02:00
Benjamin Eberlei
cb21f3c5ff DDC-1414 - Missing push to $newNodes 2011-10-15 17:47:09 +02:00
Benjamin Eberlei
cb28bfd484 Improve Error Messages in ClassMetadata and UnitOfWork 2011-10-15 17:38:55 +02:00
Alexander
8d1b852aa2 Added tests for not loading the entity + fixed a test 2011-10-15 17:31:09 +02:00
Alexander
f47e1feac6 Merge branch 'master' of git://github.com/doctrine/doctrine2 into ProxyIdentifer
Conflicts:
	lib/Doctrine/ORM/Proxy/ProxyFactory.php
2011-10-15 17:24:13 +02:00
Benjamin Eberlei
84dbb08502 Merge branch 'DDC-1421' 2011-10-15 16:58:05 +02:00
Benjamin Eberlei
e38076c19a DDC-1421 - Fix potential bug and code-smells 2011-10-15 16:57:57 +02:00
Benjamin Eberlei
3b46df68ea Adjust README.markdown 2011-10-15 16:36:20 +02:00
Benjamin Eberlei
dd6f6cb097 Fix notice 2011-10-15 16:03:50 +02:00
Benjamin Eberlei
640facd26a Remove unncessary line 2011-10-15 15:51:11 +02:00
Benjamin Eberlei
291d2fd624 UPGRADE_TO_2_2 2011-10-15 15:49:41 +02:00
Benjamin Eberlei
9ece0fe3c9 Merge branch 'DDC-1386' 2011-10-15 15:45:59 +02:00
Benjamin Eberlei
3801e0c230 Add way to keep track of read only objects in the UnitOfWork which are never updated during flush.
Changed the behavior of EntityManager#getPartialReference to be read-only. No changes are ever
done to this entities. Changed UnitOfWork#computeChangeSet to never create a changeset for
fields that are partially omitted from a DQL or NativeQuery.

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

    if ($entityManager->getUnitOfWork()->isReadOnly($entity))
2011-10-15 15:42:02 +02:00
Benjamin Eberlei
2166a21511 Merge branch 'master' of github.com:doctrine/doctrine2 2011-10-15 11:36:53 +02:00
Benjamin Eberlei
894bbb020c DDC-1394 - Enhance test to verify 2011-10-15 09:57:35 +02:00
Guilherme Blanco
772b413579 Fixed bug with boolean values being converted to string. 2011-10-15 00:23:55 -03:00
Guilherme Blanco
2518f0687c Removed invalid default argument. 2011-10-15 00:21:11 -03:00
Guilherme Blanco
f86e1ba66c Added tests for DDC-1389. Everything is working in 2.2-DEV. 2011-10-15 00:18:57 -03:00
Alexander
097d573d26 Merge branch 'master' into DDC-551
Conflicts:
	lib/Doctrine/ORM/Configuration.php
	lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
	lib/Doctrine/ORM/Persisters/ManyToManyPersister.php
2011-10-14 12:33:39 +02:00
Benjamin Eberlei
4a920d0aad Merge branch 'DDC-1418' 2011-10-13 23:40:11 +02:00
Fabien Potencier
6a72ba5f97 DDC-1418 - Add simplified XML and YAML drivers ported from the Symfony project, thanks Fabien 2011-10-13 23:39:11 +02:00
Benjamin Eberlei
92b16455c8 Merge branch 'DDC-1415' 2011-10-13 22:58:58 +02:00
Benjamin Eberlei
a36a1624fb [DDC-1415] Add EntityEventDelegatee, allowing to restrict emitting events to certain entity classes only. 2011-10-13 22:58:22 +02:00
Benjamin Eberlei
97a6caf059 Update Common to latest to have the current Persistence Interface 2011-10-11 13:22:26 +02:00
Guilherme Blanco
509e5fb12f Merge pull request #145 from stof/EntityGenerator_versionCheck
Fixed the version check in the entity generator
2011-10-10 12:34:01 -07:00
Christophe Coevoet
c7c875a063 Fixed the version check in the entity generator
The 3.0.x branch of Common has been merged to become the incoming
2.2 release.
2011-10-10 19:20:01 +02:00
Benjamin Eberlei
45308d5f79 Merge branch 'DDC-1402' 2011-10-10 17:52:47 +02:00
Benjamin Eberlei
9058bc3f5c Fix DDC-1402 - No caching for SingleTablePersister::_getSelectColumnList 2011-10-10 17:52:37 +02:00
Benjamin Eberlei
89470787d8 Add composer.json 2011-10-10 16:54:57 +02:00
Benjamin Eberlei
de98f3fb30 Added composer.json 2011-10-10 16:30:49 +02:00
Guilherme Blanco
15877e1443 Merge pull request #144 from craue/patch-3
switched 2nd and 3rd argument for SelectExpression's constructor, making the 3rd one optional to keep its signature compatible to previous versions
2011-10-10 05:21:41 -07:00
Christian Raue
1681d8a893 switched 2nd and 3rd argument for SelectExpression's constructor, making the 3rd one optional to keep its signature compatible to previous versions 2011-10-10 14:04:55 +02:00
Guilherme Blanco
f3b6ed03f3 Merge pull request #142 from craue/patch-1
fixed typo
2011-10-10 03:31:34 -07:00
Christian Raue
689aaef4dc added missing type hint 2011-10-10 09:44:17 +03:00
Christian Raue
bf44be86a9 fixed typo 2011-10-10 03:02:55 +03:00
Guilherme Blanco
24042863ac BasicEntityPersister::exists() was not supporting identifiers that are associations. Fixes DDC-1382. 2011-10-03 02:21:14 -03:00
Guilherme Blanco
ebe933810e Implemented HIDDEN support in DQL. Fixes DDC-1363. 2011-10-03 02:07:07 -03:00
Guilherme Blanco
8efae0b232 Fixes DDC-1396. 2011-10-03 01:30:20 -03:00
Guilherme Blanco
cd28051370 Fixes DDC-1395 2011-10-03 01:26:43 -03:00
Asmir Mustafic
d24f288149 Better error handling on missing assigned id 2011-09-29 09:31:06 +02:00
docteurklein
b28af2e527 added fluent pattern to Query\Expr\Base::add* methods 2011-09-27 10:36:32 +02:00
Guilherme Blanco
80284a273d Merge pull request #133 from FabioBatSilva/DDC-1335
Fixes DDC-1335.

Patch looks perfect to me. @beberlei please merge into 2.1 =)
2011-09-25 14:52:56 -07:00
Benjamin Eberlei
d2cd6560c5 DDC-1337 - Adjust MultiTableUpdateExecutor and MultiTableDeleteExecutor to use AbstractPlatform 2011-09-25 19:01:37 +02:00
Benjamin Eberlei
919f5e5ebc Merge branch 'DDC-1392' 2011-09-25 18:08:50 +02:00
Benjamin Eberlei
9f96d4a31a DDC-1392 - Fix bug with merging unitialized proxies 2011-09-25 18:08:41 +02:00
Fabio B. Silva
5fe996baf9 change tests for DDC-1135 2011-09-25 12:20:48 -03:00
Benjamin Eberlei
43c25ea36d Merge branch 'DDC-1367' 2011-09-25 16:39:50 +02:00
Benjamin Eberlei
cd7029d266 DDC-1367 - Bugfix 2011-09-25 16:39:41 +02:00
Benjamin Eberlei
c02920762b Adjust ClassMetadataFactory to forthcoming interface change 2011-09-25 14:41:56 +02:00
Fabio B. Silva
728724bed5 fix DDC-1135 2011-09-24 16:16:25 -03:00
Fabio B. Silva
e94b902a9b tests for DDC-1335 2011-09-24 16:10:10 -03:00
Fabio B. Silva
f4c5c4ba01 branch for DDC-1335 2011-09-23 18:10:58 -03:00
Guilherme Blanco
9795cb1f0d Subqueries should never add items to ResultSetMapping. Fixing possible broken inclusion. 2011-09-21 17:35:38 -03:00
Guilherme Blanco
a86038b484 Fixed issue with CaseExpression not working in Subselects. 2011-09-21 17:30:45 -03:00
Benjamin
01d900d5d7 tab <-> spaces 2011-09-20 15:50:32 +02:00
Benjamin
944f802d79 Correct indentation 2011-09-20 15:35:16 +02:00
Benjamin
2b334977f5 Add "return $this" to generated methods to get a fluent Enttity class 2011-09-20 14:59:32 +02:00
Alan Bem
90725fa529 fixed wrong on-delete XML Schema mapping 2011-09-20 11:10:59 +02:00
Guilherme Blanco
2ae3bb6e3a Fixed bug in Result cache cleaner. 2011-09-19 01:40:30 -03:00
Guilherme Blanco
1fb213760b Refactored Cache cleaners commands to sync with new Common Cache Providers. 2011-09-15 17:46:13 -03:00
Alexander
63a3fb5ad8 [DDC-551] Moved SQLFilter logic to a separate FilterCollection class 2011-09-15 21:38:47 +02:00
Guilherme Blanco
00321e8f70 Imported DBAL-164 fix to ORM. 2011-09-13 02:03:08 -03:00
Guilherme Blanco
918e2d2018 Fixed remaining Doctrine packages upgrade. 2011-09-13 01:14:36 -03:00
Guilherme Blanco
3bb803fd69 Cherry picked FabioBatSilva upgrade of Common lib. 2011-09-13 00:59:24 -03:00
Guilherme Blanco
f8811c4d21 Merge pull request #121 from FabioBatSilva/DDC-753
DDC-753
2011-09-08 14:30:54 -07:00
Fabio B. Silva
3707b34cbb Change repository type for EntityRepository 2011-09-08 17:21:06 -03:00
Fabio B. Silva
43ccd9ead6 tests for DDC-753 2011-09-08 15:54:49 -03:00
Fabio B. Silva
ee80ec4851 Merge remote-tracking branch 'upstream/master' into DDC-753 2011-09-08 14:36:38 -03:00
Fabio B. Silva
84cd22d7f7 new branch for DDC-753 2011-09-08 14:36:13 -03:00
Guilherme Blanco
7ebfc67d5a Merge pull request #120 from FabioBatSilva/DDC-869
Fixed DDC-869
2011-09-08 10:15:24 -07:00
Fabio B. Silva
19d0887bb0 check if parent class is a mapped superclass 2011-09-08 13:41:16 -03:00
Fabio B. Silva
7f0275155d php mapping 2011-09-08 13:17:53 -03:00
Fabio B. Silva
6a89de51e5 change tests 2011-09-08 12:55:55 -03:00
Fabio B. Silva
54a53b1d03 tests for DDC-869 2011-09-08 11:56:05 -03:00
Fabio B. Silva
f6d34bbb9d Merge remote-tracking branch 'upstream/master' into DDC-869 2011-09-08 11:54:04 -03:00
Guilherme Blanco
2b3bee1fa4 Merge pull request #119 from alOneh/patch-2
Fix documentation for metadata driver
2011-09-08 07:29:55 -07:00
Alain Hippolyte
01c9a4ac1c Fix documentation for metadata driver 2011-09-08 11:41:09 +03:00
Guilherme Blanco
a7f3af8328 Added IDENTITY DQL Function. Fixes DDC-1339. 2011-09-08 02:10:48 -03:00
Fabio B. Silva
b2d98495b1 mapping files 2011-09-07 18:29:43 -03:00
Fabio B. Silva
25ecd45fd1 Merge remote-tracking branch 'upstream/master' into DDC-869 2011-09-07 17:27:59 -03:00
Fabio B. Silva
82f7d6cad2 starts work with mapped superclass repository 2011-09-07 17:27:05 -03:00
Guilherme Blanco
bd5393a318 Added coverage for orphanRemoval in OneToOne when unlinking an entity. 2011-09-07 13:12:02 -03:00
Guilherme Blanco
e3d133af04 Added getQuotedTableName() in missing places of Doctrine ORM. Fixes DDC-1365. 2011-09-07 01:48:19 -03:00
Guilherme Blanco
6ec2ae249b Merge branch 'master' of github.com:doctrine/doctrine2 2011-09-06 01:58:34 -03:00
Guilherme Blanco
2cfc61db84 Fixed bug with orphanRemoval not removing associated Entity on OneToMany and OneToOne relationships. As defined in ClassMatedataInfo, in these situations, when orphanRemoval=true, cascade=remove is implicit. This fixes DDC-1321. 2011-09-06 01:58:16 -03:00
Guilherme Blanco
c30e129390 Merge pull request #118 from bauffman/patch-1
Fixed typo
2011-09-05 06:48:28 -07:00
Davy Hellemans
b333ff95fb Fixed typo 2011-09-05 09:42:09 +03:00
Guilherme Blanco
6bbf2d9da3 Added support for ResultVariable referencing in ArithmeticPrimary. Fixes DDC-1346. 2011-09-05 03:16:01 -03:00
Guilherme Blanco
ddfdb37a58 Merge branch 'master' of github.com:doctrine/doctrine2 2011-09-05 02:27:26 -03:00
Guilherme Blanco
666691f84f Small changes for code readability. Added type binding in JoinedSubclassPersister, which was missing. Fixes DDC-1316. 2011-09-05 02:27:06 -03:00
Benjamin Eberlei
b6a7520c64 Merge branch 'DDC-659' 2011-09-04 14:13:45 +02:00
Benjamin Eberlei
09ac3a9eb2 DDC-659 - Add ClassMetadataBuilder for PHP based metadata management 2011-09-04 14:13:20 +02:00
Benjamin Eberlei
342c3ba941 Initial work on ClassMetdataBuilder 2011-09-04 10:08:33 +02:00
Benjamin Eberlei
88574a0de2 Add note about Common Annotation changes 2011-09-04 01:11:19 +02:00
Benjamin Eberlei
f6f6edbd59 Merge branch 'master' of github.com:doctrine/doctrine2 2011-09-04 01:10:37 +02:00
Guilherme Blanco
f29c907f41 Optimized and refactored code for getParameterValue in Doctrine\ORM\Query. 2011-09-02 13:53:53 -03:00
Guilherme Blanco
3b3186ee98 Added support to user provide an array of Entities as a DQL parameter. Fixes DDC-1356. 2011-09-01 19:11:57 -03:00
Guilherme Blanco
ecc556f687 Fixes DDC-1354. 2011-09-01 03:00:53 -03:00
Benjamin Eberlei
53a153bc15 Merge pull request #111 from Gregwar/yaml_entity_repository
[Tools] Added entityRepository support in yaml exporter
2011-08-30 11:43:25 -07:00
Benjamin Eberlei
800628e3cd Merge branch 'DDC-1350' 2011-08-30 20:40:38 +02:00
Benjamin Eberlei
8b38e68e23 DDC-1350 - Bugfixes in Doctrine\ORM\Tools\Setup 2011-08-30 20:40:26 +02:00
Benjamin Eberlei
7ba656f815 Bump DBAL dependency to 2.1.2 2011-08-29 23:01:05 +02:00
Grégoire Passault
240f0ea34c [Tools] Added entityRepository support in yaml exporter 2011-08-29 17:40:54 +02:00
Guilherme Blanco
12f46e936c Merge branch 'master' of github.com:doctrine/doctrine2 2011-08-28 13:48:24 -03:00
Guilherme Blanco
e7f471ef3e Fixed issue with CTI during DQL update that was incorrectly setting parameter types during multi table execution. Fixes DDC-1341. 2011-08-28 13:48:15 -03:00
Benjamin Eberlei
0a6709acba Fix tests with regard to repositoryClass handling and applying the default namespace 2011-08-28 16:00:21 +02:00
Benjamin Eberlei
c948ad852e Merge branch 'FixPostgresFailures' 2011-08-28 15:58:27 +02:00
Benjamin Eberlei
b145f061c9 DDC-1348 - Fix bug with UnitOfWork::getEntityState() 2011-08-28 15:58:08 +02:00
Benjamin Eberlei
e6a73803a4 Merge branch 'DDC-1306' 2011-08-27 20:44:15 +02:00
Benjamin Eberlei
3b4b38e184 DDC-1306, DDC-1113 - Fix issues with inheritance and commit order 2011-08-27 20:41:52 +02:00
Benjamin Eberlei
7da9beed36 Merge branch 'master' of github.com:doctrine/doctrine2 2011-08-27 17:59:11 +02:00
Benjamin Eberlei
3bee695913 Update dependencies to 2.1.1 respectively 2011-08-27 14:49:49 +02:00
Benjamin Eberlei
d3b27dcb1a DDC-1344 - Fix path displaying to not use realpath()ed variables 2011-08-27 13:36:18 +02:00
Benjamin Eberlei
4f22bbbc76 Add support to distribute the XSD to a given directory during build process 2011-08-27 13:23:17 +02:00
Nadav
2e389e00d4 Removed blank line, used empty() instead of the count() check 2011-08-26 08:15:28 +03:00
Nadav
5fc6277d3f Oops, shouldn't have removed the condition completely... checking a parameter is provided 2011-08-26 07:51:29 +03:00
Nadav
d314386060 we can (now) transform it into IS NULL 2011-08-26 07:42:16 +03:00
Benjamin Eberlei
e69e0fcc71 Merge remote-tracking branch 'origin/master' 2011-08-24 20:45:32 +02:00
Benjamin Eberlei
b560551b1d Merge branch 'DDC-1333' 2011-08-21 15:06:21 +02:00
Benjamin Eberlei
b83945d486 DDC-1333 - Fix bug in xsd 2011-08-21 15:06:14 +02:00
Benjamin Eberlei
3c82b49cd3 Merge branch 'DDC-1340' 2011-08-21 15:02:05 +02:00
Benjamin Eberlei
8c2db89f2b DDC-1340 - Fix bug with merge() and optimistic lock exception 2011-08-21 15:01:57 +02:00
Alain Hippolyte
06d56156dd Remove trailing spaces 2011-08-19 06:11:58 +03:00
Guilherme Blanco
736443f6c2 Merge pull request #108 from lenar/patch-1
short classname support for custom repository class
2011-08-18 11:03:43 -07:00
Lenar Lõhmus
d7fbd2cd14 short classname support for custom repository class 2011-08-19 04:44:24 +03:00
Alexander
ed0fb4ece7 Merge branch 'master' into DDC-551
Conflicts:
	lib/Doctrine/ORM/Persisters/OneToManyPersister.php
2011-08-16 16:59:48 +02:00
Guilherme Blanco
7433148f17 Fixed bug in XMLDriver where relation indexes are treathed as elements but documented as attributes. 2011-08-16 11:36:41 -03:00
Alexander
3800581947 [DDC-551] Altered persisters to make filters work with EXTRA_LAZY associations 2011-08-16 16:24:50 +02:00
Alexander
2653d735e2 [DDC-551] Added state of the complete filter collection to the EM
Previously it was sufficient to use the old parser result for a Query if
the DQL didn't change (Query::STATE_CLEAN), but now there must also be
no changes to the filter collection of the EntityManager.

In the old situation each Query object would create a hash of all the
filter objects on it's own. That was not very efficient.

This commit adds the state of the current filter collection to the
EntityManager. The state will be set to FILTERS_STATE_DIRTY as a filter
is enabled, removed or a parameter is set on a filter. The hash is also
computed by the EntityManager, but only if the filter collection is
dirty. This will prevent recalculation of the hash with each query.
2011-08-16 13:34:42 +02:00
Alexander
3b1ddb0346 [DDC-551] Added filters to SQLWalker 2011-08-16 12:21:43 +02:00
Guilherme Blanco
6857134f36 Fixed issue with duplicated commas if Entity has no fields. 2011-08-15 12:47:17 -03:00
Guilherme Blanco
f148912a28 [DDC-934][DDC-1100][DDC-1331] Implemented support to multi-values in INSTANCE OF expression. 2011-08-15 01:53:56 -03:00
Guilherme Blanco
b9a24b5487 Merge pull request #93 from kwiateusz/UnitTestsUpdate
Last change from assertTrue($a instanceof $b) to assertInstanceOf
2011-08-14 21:05:15 -07:00
Benjamin Eberlei
2ee3cdeb0c Merge pull request #106 from FabioBatSilva/master
[DDC-1325] @Target annotation
2011-08-14 10:22:13 -07:00
Benjamin Eberlei
7782780cda Merge branch 'master' of github.com:doctrine/doctrine2 2011-08-14 19:20:30 +02:00
Dominik Liebler
6e47d7b16d DDC-1278 - EntityManager::clear($entity) support
added test case and modified test data CmsUser to cascade detach address and articles (testing collections and single entites)
2011-08-14 16:12:12 +02:00
Guilherme Blanco
a0ca506db7 Added support to SingleValuePathExpression in ORDER BY. 2011-08-14 00:46:02 -03:00
Guilherme Blanco
63a2f02f4d [DDC-408][DDC-1150][DDC-1277] Implemented support to parameter expanding on associations. 2011-08-13 21:28:54 -03:00
Fabio B. Silva
a04d2933fa @Target annotation and support to common 2.2-DEV 2011-08-13 16:28:05 -03:00
Dominik Liebler
25f5ff0ca1 DDC-1278 - EntityManager::clear($entity) support
cascade detach operation only on entity name entities
2011-08-13 20:22:23 +02:00
Dominik Liebler
745535d269 fixed typo 2011-08-12 20:15:32 +02:00
Dominik Liebler
05fb0b913a DDC-1278 - EntityManager::clear($entity) support
added new parameter $entityName for UnitOfWork::clear()
removed not implemented exception in EntityManager:clear()
2011-08-11 23:03:26 +02:00
Guilherme Blanco
e13720c33d Merge pull request #103 from rdohms/patch-1
Add getReader to AnnotationDriver
2011-08-10 11:11:41 -07:00
Rafael Dohms
07e1c1e2e1 Added method to retrieve currently used Reader. This allows projects that use Doctrine to recycle the reader to use with other annotation-driven packages, like DMS\Filter and Symfony\Validator 2011-08-10 15:10:09 -03:00
Guilherme Blanco
816ce41f63 Added support to CaseExpression. Added support to nest AndX and OrX QueryBuilder composite expressions, since they do not mess with generated queries. 2011-08-08 02:09:25 -03:00
Benjamin Eberlei
1666d59da4 Merge branch 'DDC-1300' 2011-08-06 20:25:52 +02:00
Benjamin Eberlei
bcc7bb1c9c DDC-1300 - Fix bug in fetch join hydration of entities with foreign key identifier 2011-08-06 20:25:45 +02:00
Benjamin Eberlei
15c877b8e8 Merge branch 'master' of github.com:doctrine/doctrine2 2011-08-06 18:15:00 +02:00
Guilherme Blanco
19682bf87a Merge pull request #102 from schmittjoh/configUpdate
updated configuration
2011-08-05 07:56:31 -07:00
Johannes Schmitt
7e47a601b0 updated configuration 2011-08-05 16:48:05 +02:00
Benjamin Eberlei
c85de03b66 Add phar packaging target and distribute phar into download folder 2011-08-01 23:09:03 +02:00
Benjamin Eberlei
d3beb7e23e Merge branch 'DDC-1313' 2011-08-01 21:45:39 +02:00
Benjamin Eberlei
ba882be76b DDC-1313 - Optimize behavior of DriverChain::getAllClassNames() 2011-08-01 21:45:21 +02:00
Benjamin Eberlei
e12a5fb328 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-31 11:41:30 +02:00
Benjamin Eberlei
d583b149ac Merge pull request #98 from asm89/master
[DDC-1301] Fixed tests teardown for mysql suite
2011-07-31 02:41:19 -07:00
Benjamin Eberlei
4675f65bea Merge branch 'DDC-1302' 2011-07-31 11:34:27 +02:00
Benjamin Eberlei
816039f23f DDC-1302 - Fix bug in XmlDriver not handling orphan removal 2011-07-31 11:32:57 +02:00
Alexander
953a0f6572 [DDC-1301] Fixed tests teardown for mysql suite 2011-07-29 09:35:59 +02:00
Benjamin Eberlei
c34612b8e9 [DDC-1298] DDC117Link file got lost from 2.1.x to master somehow 2011-07-28 23:44:11 +02:00
Benjamin Eberlei
f8558d7740 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-28 23:26:24 +02:00
Benjamin Eberlei
5c47c07794 Merge pull request #97 from asm89/DDC-1301
[DDC-1301] Fixed count() for fetch="EXTRA_LAZY" on OneToMany association
2011-07-28 14:26:15 -07:00
Benjamin Eberlei
3d788a10cf Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-28 23:19:44 +02:00
Benjamin Eberlei
8f589e5876 Merge pull request #92 from mridgway/DDC-1275
DDC-1275: Added join columns to result set mapping
2011-07-28 14:18:55 -07:00
Michael Ridgway
1250cd7a5a F[DDC-1275] ixed check for owning side of a toOne relationship 2011-07-28 10:50:22 -04:00
Alexander
d439f67df5 [DDC-1301] Prefixed all Legacy models properties with _ 2011-07-28 12:25:23 +02:00
Alexander
d7dbde8f3e [DDC-1301] Fixed count() for fetch="EXTRA_LAZY" on OneToMany association 2011-07-28 11:01:52 +02:00
Alexander
b2951691e2 [DDC-1301] Added tests for fetch="EXTRA_LAZY" count() on a "legacy" database 2011-07-28 11:01:28 +02:00
Benjamin Eberlei
656beb635c Merge branch 'DDC-1298' 2011-07-27 23:26:06 +02:00
Benjamin Eberlei
196632978c DDC-1298 - Fix bug in SQLWalker with derived entities that have no fields of their own. 2011-07-27 23:22:20 +02:00
kwiateusz
d1e9bc6401 Now findByOne really retrieve only one entity adding limit to query. 2011-07-27 15:43:27 +02:00
kwiateusz
a47af43bc1 Last change from assertTrue($a instanceof $b) to assertInstanceOf 2011-07-27 08:54:06 +02:00
Michael Ridgway
7f20a32db3 Removing debug comment 2011-07-26 17:39:57 -04:00
Michael Ridgway
154fd60d85 DDC-1275: Added join columns to result set mapping 2011-07-26 17:35:06 -04:00
Benjamin Eberlei
a0b7c3e76d Add UPGRADE_TO_2_2 file 2011-07-26 23:16:38 +02:00
Benjamin Eberlei
7584ef6ba1 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-26 23:14:57 +02:00
Benjamin Eberlei
57cfcfd20e Merge pull request #79 from mridgway/DDC-725
[DDC-725] Removed onUpdate property on join columns
2011-07-26 14:14:30 -07:00
Benjamin Eberlei
e73605e177 Merge pull request #90 from mweimerskirch/patch-2
Corrected typo
2011-07-26 14:12:19 -07:00
Benjamin Eberlei
6abe8e412c Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-26 23:03:30 +02:00
Benjamin Eberlei
79d99b11ad Merge pull request #91 from kwiateusz/UnitTestsUpdate
Removed deprecated assertType (thanks @kwiateusz)
2011-07-26 14:03:04 -07:00
Benjamin Eberlei
41a9e9689b Merge branch 'DDC-1215' 2011-07-26 23:00:11 +02:00
Benjamin Eberlei
598ab36968 [DDC-1215] Fix EntityGenerator inheritance regenerating properties and methods 2011-07-26 22:59:55 +02:00
Benjamin Eberlei
2e3d2398d9 Merge branch 'DDC-1280' 2011-07-26 22:30:27 +02:00
Benjamin Eberlei
93d2e1bd0a [DDC-1280] Only generate linefeeds in proxies for consistency. 2011-07-26 22:30:20 +02:00
Benjamin Eberlei
cbf210605a [DDC-1290] Allow smallint and bigint for version fields 2011-07-26 22:24:16 +02:00
Benjamin Eberlei
2a59da1f03 Merge branch 'DDC-1276' 2011-07-26 22:15:37 +02:00
Benjamin Eberlei
a99ffc126f DDC-1276 - Fix bug where merge managed and new entitiy share the same collection that is cascaded, cleared during the process and then empty afterwards. 2011-07-26 22:15:27 +02:00
Benjamin Eberlei
423651e45e Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-26 20:37:37 +02:00
Alexander
6cf7bdc2b7 Merge branch 'master' into DDC-551 2011-07-26 17:26:00 +02:00
Guilherme Blanco
1d9016e48f Merge pull request #88 from asm89/DDC-1294
[DDC-1294] Add discriminator information to subselects
2011-07-26 08:14:36 -07:00
Alexander
97a4dbf2cd [DDC-1294] Added more tests for subselects in subselects 2011-07-26 17:15:21 +02:00
kwiateusz
7261060905 Few more converts from assertTrue($a instance of $b) to assertInstanceOf 2011-07-26 15:22:57 +02:00
kwiateusz
1ea3e543ab Correted indentation and variable name 2011-07-26 12:10:30 +02:00
kwiateusz
49c735109c Change from assertType to assertInstanceOf.
Now PHPUnit doesn't show warning about deprecation of assertType.
Also some refractoring from assertTrue($a instanceof $b) to assertInstanceOf.
Leading \ in namespaces is not required so I removed it from few assertions.
2011-07-26 11:38:09 +02:00
Michel Weimerskirch
6ef4ac62d7 Missed another typo 2011-07-26 11:03:23 +03:00
Michel Weimerskirch
b58f573b31 Corrected typo 2011-07-26 10:59:21 +03:00
Alexander
65f7e897b5 [DDC-1294] Add discriminator information to subselects 2011-07-26 00:19:26 +02:00
Alexander
e3dcfa8702 [DDC-551] Added filters to query hash + tests for hash 2011-07-22 17:01:18 +02:00
Guilherme Blanco
4b85d7a683 Reverted PR #86, which broke our suite. 2011-07-22 11:38:20 -03:00
Alexander
6163d9d932 [DDC-551] Added enabled filters to Query hash 2011-07-22 15:26:03 +02:00
Alexander
afd7a540a7 [DDC-551] Removed 'use ..DBAL\..\Type', causing full testsuite to fail 2011-07-22 15:10:31 +02:00
Alexander
4266ab77b2 [DDC-551] Added __toString() method to SQLFilter 2011-07-22 14:55:00 +02:00
Alexander
4cf63a4e83 [DDC-551] Fixed the escaping of filter parameters 2011-07-22 14:51:30 +02:00
Alexander
d1908f7207 [DDC-551] Keep filter parameters and enabled filters sorted for hashing 2011-07-22 14:36:14 +02:00
Alexander
277fc751b6 [DDC-551] Added tests for SQLFilter 2011-07-22 13:51:26 +02:00
Alexander
b867744f15 [DDC-551] Added tests for SQLFilter functionality + small fixes 2011-07-22 13:08:49 +02:00
Alexander
a85902b08d [DDC-551] Initial code for filter functionality 2011-07-22 12:01:33 +02:00
Guilherme Blanco
4be7c3dac0 Merge pull request #85 from FabienPennequin/fix_ClassMetadataFactory
Fixed php notice in ClassMetadataFactory
2011-07-21 10:02:15 -07:00
Guilherme Blanco
01d6ec35c4 Merge pull request #86 from kwiateusz/findByOne-patch
Now findByOne really retrieve only one entity adding limit to query
2011-07-21 10:01:21 -07:00
kwiateusz
570799b48d Restoring the missing comment 2011-07-21 04:13:15 -07:00
kwiateusz
0e6121e8f5 Now findByOne really retrieve only one entity adding limit to query 2011-07-21 12:40:43 +02:00
Fabien Pennequin
99bdf65c10 Fixed php notice in ClassMetadataFactory 2011-07-17 19:39:55 +02:00
Benjamin Eberlei
180dbbad8b Merge branch 'EntityGenerator' 2011-07-12 23:46:13 +02:00
Benjamin Eberlei
d9b1dbbb09 DDC-1244 - Fix bug with entities without namespace 2011-07-12 23:43:24 +02:00
Benjamin Eberlei
c87dedbec5 DDC-1254 - Dont throw exception about missing id in disconnected metadata factory 2011-07-12 23:39:56 +02:00
Benjamin Eberlei
4796452b07 DDC-1268 - Singular add*() method name through using targetEntity shortname 2011-07-12 23:25:15 +02:00
Benjamin Eberlei
fb11268255 Merge branch 'DDC-1240' 2011-07-12 22:50:31 +02:00
Benjamin Eberlei
98fabd98be DDC-1240 - Fix optimistic lock exception loosing the message 2011-07-12 22:50:21 +02:00
Benjamin Eberlei
5733574867 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-12 22:47:33 +02:00
Benjamin Eberlei
409c16b898 Merge branch 'DDC-1250' 2011-07-09 22:12:15 +02:00
Benjamin Eberlei
c261315ea7 DDC-1250 - Fix bug with inverse one to one loading and ambigious column names in certain scenarios 2011-07-09 22:11:49 +02:00
Benjamin Eberlei
8b92dc5265 Merge branch 'DDC-1257' 2011-07-09 15:14:31 +02:00
Benjamin Eberlei
442a2b3a51 DDC-1257 - Fix bug where validation callbacks are added multiple times in EntityGenerator 2011-07-09 15:11:16 +02:00
Benjamin Eberlei
305e0345ef Merge branch 'DDC-1251' 2011-07-09 14:53:39 +02:00
Benjamin Eberlei
a8048af65d DDC-1251 - Fix bug in token parsing of EntityGenerator 2011-07-09 14:53:25 +02:00
Benjamin Eberlei
d7da292f60 Merge remote branch 'origin/master' 2011-07-09 14:43:04 +02:00
Benjamin Eberlei
689eaf3172 Merge branch 'DDC-1022' 2011-07-09 12:12:56 +02:00
Benjamin Eberlei
2b207106a3 DDC-1022 - Call __wakeup() with the same semantics then ClassMetadata::newInstance() does inside UnitOfWork 2011-07-09 12:12:44 +02:00
Guilherme Blanco
9d1a647753 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-08 16:27:20 -03:00
Guilherme Blanco
b6bd46dbed General fix for end point as file extension on Annotation driver. 2011-07-08 16:26:54 -03:00
Benjamin Eberlei
9a68074878 Clarify possible problem 2011-07-08 14:42:15 +02:00
Benjamin Eberlei
ac76dafe62 Add doccomments, refactor into listener and dump method to allow re-use 2011-07-08 14:32:12 +02:00
Benjamin Eberlei
ef663c83f3 DDC-1258 - Add Debug UnitOfWork Listener 2011-07-08 14:27:59 +02:00
Benjamin Eberlei
024b7b7cb6 Bump Dev Version to 2.2.0-DEV 2011-07-04 21:34:59 +00:00
Benjamin Eberlei
5c2a4c0339 Merge branch 'DDC-1238' 2011-07-04 23:19:26 +02:00
Benjamin Eberlei
2858b8290f DDC-1238 - Fixed a bug introduced when refactoring persisters hydration. This occurs when you call $em->clear() and you start accessing a proxy. 2011-07-04 23:19:08 +02:00
Benjamin Eberlei
a638154046 Update tests 2011-07-04 20:59:46 +02:00
Benjamin Eberlei
a947e8a4b0 DDC-1238 - Reproducible case, its correct through 2011-07-04 20:59:46 +02:00
Benjamin Eberlei
8e7c156451 Started trying to reproduce this issue 2011-07-04 20:59:46 +02:00
Benjamin Eberlei
0529efc843 Merge branch 'master' of github.com:doctrine/doctrine2 2011-07-04 19:52:35 +02:00
Michael Ridgway
c19d7fe2eb Missed one 2011-06-28 15:55:43 -04:00
Michael Ridgway
9ae30421dd Removed onUpdate property on join columns 2011-06-28 15:50:14 -04:00
Benjamin Eberlei
bc4e14a99f Prototype for a proxy extension that avoids loads when calling for a getter that is named after an identifier. 2011-05-20 20:50:03 +02:00
501 changed files with 27037 additions and 7291 deletions

4
.gitignore vendored
View File

@@ -1,4 +1,3 @@
build.properties
build/
logs/
reports/
@@ -7,3 +6,6 @@ download/
lib/api/
lib/Doctrine/Common
lib/Doctrine/DBAL
/.settings/
.buildpath
.project

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
[submodule "lib/vendor/Symfony/Component/Yaml"]
path = lib/vendor/Symfony/Component/Yaml
url = git://github.com/symfony/Yaml.git
[submodule "lib/vendor/doctrine-build-common"]
path = lib/vendor/doctrine-build-common
url = https://github.com/doctrine/doctrine-build-common.git

19
.travis.yml Normal file
View File

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

View File

@@ -1,14 +1,18 @@
# Doctrine 2 ORM
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.0+ that provides transparent persistence
Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2)
2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
without requiring unnecessary code duplication.
More resources:
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en)
* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC)
* [Downloads](http://github.com/doctrine/doctrine2/downloads)
* [Downloads](http://github.com/doctrine/doctrine2/downloads)

View File

@@ -9,6 +9,7 @@ The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepo
The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way:
// new call to the AnnotationRegistry
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();

76
UPGRADE_TO_2_2 Normal file
View File

@@ -0,0 +1,76 @@
# ResultCache implementation rewritten
The result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery
anymore. This means that for result cached queries the hydration will now always be performed again, regardless of
the hydration mode. Affected areas are:
1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork
leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.
2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.
The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now
deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile`
instance with access to result cache driver, lifetime and cache key.
# EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they
haven't been in the identity map before. This means objects of this kind never lead to changes
in the UnitOfWork.
# Fields omitted in a partial DQL query or a native query are never updated
Fields of an entity that are not returned from a partial DQL Query or native SQL query
will never be updated through an UPDATE statement.
# Removed support for onUpdate in @JoinColumn
The onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.
# Changes in Annotation Handling
There have been some changes to the annotation handling in Common 2.2 again, that affect how people with old configurations
from 2.0 have to configure the annotation driver if they don't use `Configuration::newDefaultAnnotationDriver()`:
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile('path/to/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
$reader->addNamespace('Doctrine\ORM\Mapping');
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
$driver = new AnnotationDriver($reader, (array)$paths);
$config->setMetadataDriverImpl($driver);
# Scalar mappings can now be ommitted from DQL result
You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore.
Example:
SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10
Your result will be a collection of Users, and not an array with key 0 as User object instance and "numArticles" as the number of articles per user
# Map entities as scalars in DQL result
When hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance.
You are now allowed to alias this, providing more flexibility for you code.
Example:
SELECT u AS user FROM User u
Will now return a collection of arrays with index "user" pointing to the User object instance.
# Performance optimizations
Thousands of lines were completely reviewed and optimized for best performance.
Removed redundancy and improved code readability made now internal Doctrine code easier to understand.
Also, Doctrine 2.2 now is around 10-15% faster than 2.1.

11
build.properties Normal file
View File

@@ -0,0 +1,11 @@
# Project Name
project.name=DoctrineORM
# Dependency minimum versions
dependencies.common=2.2.0beta1
dependencies.dbal=2.2.0beta1
dependencies.sfconsole=2.0.0
# Version class and file
project.version_class = Doctrine\ORM\Version
project.version_file = lib/Doctrine/ORM/Version.php

View File

@@ -8,6 +8,7 @@ report.dir=reports
log.archive.dir=logs
project.pirum_dir=
project.download_dir=
project.xsd_dir=
test.phpunit_configuration_file=
test.phpunit_generate_coverage=0
test.pmd_reports=0

203
build.xml
View File

@@ -1,11 +1,7 @@
<?xml version="1.0"?>
<!--
Doctrine 2 build file.
-->
<project name="Doctrine2" default="build" basedir=".">
<project name="DoctrineORM" default="build" basedir=".">
<taskdef classname="phing.tasks.ext.d51PearPkg2Task" name="d51pearpkg2" />
<import file="${project.basedir}/lib/vendor/doctrine-build-common/packaging.xml" />
<property file="build.properties" />
@@ -14,6 +10,8 @@
-->
<fileset id="shared-artifacts" dir=".">
<include name="LICENSE"/>
<include name="UPGRADE*" />
<include name="doctrine-mapping.xsd" />
</fileset>
<!--
@@ -51,101 +49,34 @@
-->
<fileset id="symfony-sources" dir="./lib/vendor">
<include name="Symfony/Component/**"/>
<exclude name="**/.git/**" />
</fileset>
<!--
Clean the directory for the next build.
-->
<target name="clean">
<available file="./build.properties" property="build_properties_exist" value="true"/>
<fail unless="build_properties_exist" message="The build.properties file is missing." />
<delete dir="${build.dir}" includeemptydirs="true" />
<delete dir="${dist.dir}" includeemptydirs="true" />
<delete dir="${report.dir}" includeemptydirs="true" />
</target>
<!--
Prepare the new build directories after cleaning
-->
<target name="prepare" depends="clean">
<echo msg="Creating build directory: ${build.dir}" />
<mkdir dir="${build.dir}" />
<echo msg="Creating distribution directory: ${dist.dir}" />
<mkdir dir="${dist.dir}" />
<echo msg="Creating report directory: ${report.dir}" />
<mkdir dir="${report.dir}" />
<mkdir dir="${build.dir}/logs"/>
<mkdir dir="${report.dir}/tests"/>
</target>
<!--
Builds ORM package, preparing it for distribution.
-->
<target name="build-orm" depends="prepare">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<copy todir="${build.dir}/doctrine-orm">
<target name="copy-files" depends="prepare">
<copy todir="${build.dir}/${project.name}-${version}">
<fileset refid="shared-artifacts"/>
</copy>
<copy todir="${build.dir}/doctrine-orm">
<copy todir="${build.dir}/${project.name}-${version}">
<fileset refid="common-sources"/>
<fileset refid="dbal-sources"/>
<fileset refid="orm-sources"/>
</copy>
<copy todir="${build.dir}/doctrine-orm/Doctrine">
<copy todir="${build.dir}/${project.name}-${version}/Doctrine">
<fileset refid="symfony-sources"/>
</copy>
<copy todir="${build.dir}/doctrine-orm/bin">
<copy todir="${build.dir}/${project.name}-${version}/bin">
<fileset refid="bin-scripts"/>
</copy>
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php > ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php" passthru="true" />
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Yaml/.git" includeemptydirs="true"/>
<delete dir="${build.dir}/doctrine-orm/Doctrine/Symfony/Component/Console/.git" includeemptydirs="true"/>
</target>
<target name="build" depends="test, build-orm"/>
<!--
Runs the full test suite.
-->
<target name="test" depends="prepare">
<if><equals arg1="${test.phpunit_generate_coverage}" arg2="1" />
<then>
<property name="test.phpunit_coverage_file" value="${build.dir}/logs/clover.xml" />
</then>
<else>
<property name="test.phpunit_coverage_file" value="false" />
</else>
</if>
<nativephpunit
testfile="./tests/Doctrine/Tests/AllTests.php" junitlogfile="${build.dir}/logs/testsuites.xml"
testdirectory="./tests" coverageclover="${test.phpunit_coverage_file}" configuration="${test.phpunit_configuration_file}"
/>
<phpunitreport infile="${build.dir}/logs/testsuites.xml" format="frames" todir="${report.dir}/tests" />
<nativephpunit testfile="./tests/Doctrine/Tests/ORM/Performance/AllTests.php" testdirectory="./tests" haltonfailure="false" haltonerror="false" />
<tstamp/>
<copy file="${build.dir}/logs/testsuites.xml" tofile="${log.archive.dir}/latest/log.xml" overwrite="true"/>
<if><equals arg1="${test.pmd_reports}" arg2="1" />
<then>
<exec command="${test.pdepend_exec} --jdepend-xml=${build.dir}/logs/jdepend.xml ./lib/Doctrine" />
<exec command="${test.phpmd_exec} ./lib/Doctrine xml codesize --reportfile ${build.dir}/logs/phpmd.xml" />
<copy file="${build.dir}/logs/jdepend.xml" tofile="${log.archive.dir}/latest/jdepend.xml" overwrite="true"/>
<copy file="${build.dir}/logs/phpmd.xml" tofile="${log.archive.dir}/latest/phpmd.xml" overwrite="true"/>
</then>
</if>
</target>
<!--
Builds distributable PEAR packages.
-->
<target name="build-packages" depends="build-orm">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<target name="define-pear-package" depends="copy-files">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/${project.name}-${version}">
<name>DoctrineORM</name>
<summary>Doctrine Object Relational Mapper</summary>
<channel>pear.doctrine-project.org</channel>
@@ -155,123 +86,29 @@
<lead user="romanb" name="Roman Borschel" email="roman@code-factory.org" />
<lead user="beberlei" name="Benjamin Eberlei" email="kontakt@beberlei.de" />
<license>LGPL</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
<version release="${pear.version}" api="${pear.version}" />
<stability release="${pear.stability}" api="${pear.stability}" />
<notes>-</notes>
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
<package name="DoctrineSymfonyConsole" channel="pear.doctrine-project.org" minimum_version="2.0.0" />
<package name="DoctrineSymfonyYaml" channel="pear.doctrine-project.org" minimum_version="2.0.0" />
<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>
<dirroles key="bin">script</dirroles>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Symfony/Component/Yaml/</ignore>
<ignore>Symfony/Component/Yaml/</ignore>
<ignore>Symfony/Component/Console/</ignore>
<release>
<install as="doctrine" name="bin/doctrine" />
<install as="doctrine.php" name="bin/doctrine.php" />
<install as="doctrine.bat" name="bin/doctrine.bat" />
</release>
<replacement path="bin/doctrine.bat" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.bat" type="pear-config" from="@bin_dir@" to="bin_dir" />
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineORM-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<tar destfile="dist/DoctrineORM-${version}-full.tar.gz" compression="gzip" basedir="${build.dir}">
<fileset dir="${build.dir}">
<include name="**/**" />
<exclude name="logs/" />
<exclude name="doctrine-orm/package.xml" />
</fileset>
</tar>
</target>
<target name="git-tag">
<exec command="grep '${version}-DEV' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<exec command="sed 's/${version}-DEV/${version}/' ${project.basedir}/lib/Doctrine/ORM/Version.php > ${project.basedir}/lib/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${project.basedir}/lib/Doctrine/ORM/Version2.php ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git add ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git commit -m 'Release ${version}'" />
<exec command="git tag -m 'Tag ${version}' -a ${version}" passthru="true" />
</target>
<target name="pirum-release">
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineORM-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
<target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
</target>
<target name="update-dev-version">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<propertyprompt propertyName="next_version" defaultValue="${version}" promptText="Enter next version string (without -DEV)" />
<exec command="sed 's/${version}/${next_version}-DEV/' ${project.basedir}/lib/Doctrine/ORM/Version.php > ${project.basedir}/lib/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${project.basedir}/lib/Doctrine/ORM/Version2.php ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git add ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
<!--
Builds distributable PEAR packages for the Symfony Dependencies
-->
<target name="release-symfony-dependencies" depends="build-orm">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineSymfonyConsole</name>
<summary>Symfony Console Component</summary>
<channel>pear.doctrine-project.org</channel>
<description>A command line interface tool from the Symfony project. Packaged for shipping with Doctrine projects using ORM version numbers.</description>
<lead user="fabpot" name="Fabien Potencier" email="fabien.potencier@symfony-project.com" />
<license>NewBSD License</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
<notes>-</notes>
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
</dependencies>
<ignore>bin/</ignore>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Doctrine/ORM/</ignore>
<ignore>Symfony/Component/Yaml/</ignore>
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineSymfonyConsole-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineSymfonyYaml</name>
<summary>Symfony Yaml Component</summary>
<channel>pear.doctrine-project.org</channel>
<description>A YAML Parser from the Symfony project. Packaged for shipping with Doctrine projects using ORM version numbers.</description>
<lead user="fabpot" name="Fabien Potencier" email="fabien.potencier@symfony-project.com" />
<license>NewBSD License</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
<notes>-</notes>
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
</dependencies>
<ignore>bin/</ignore>
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<ignore>Doctrine/ORM/</ignore>
<ignore>Symfony/Component/Console/</ignore>
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineSymfonyYaml-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineSymfonyConsole-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineSymfonyYaml-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
</project>

23
composer.json Normal file
View File

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

View File

@@ -92,7 +92,7 @@
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />
@@ -150,7 +150,7 @@
<xs:restriction base="xs:token">
<xs:enumeration value="CASCADE"/>
<xs:enumeration value="RESTRICT"/>
<xs:enumeration value="SET_NULL"/>
<xs:enumeration value="SET NULL"/>
</xs:restriction>
</xs:simpleType>
@@ -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>

View File

@@ -20,7 +20,9 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\QueryException;
Doctrine\DBAL\Cache\QueryCacheProfile,
Doctrine\ORM\Query\QueryException,
Doctrine\ORM\Internal\Hydration\CacheHydrator;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -28,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>
@@ -76,7 +77,7 @@ abstract class AbstractQuery
protected $_resultSetMapping;
/**
* @var Doctrine\ORM\EntityManager The entity manager used by this query object.
* @var \Doctrine\ORM\EntityManager The entity manager used by this query object.
*/
protected $_em;
@@ -91,23 +92,9 @@ abstract class AbstractQuery
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* The locally set cache driver used for caching result sets of this query.
*
* @var CacheDriver
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
*/
protected $_resultCacheDriver;
/**
* Boolean flag for whether or not to cache the results of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* @var string The id to store the result cache entry under.
*/
protected $_resultCacheId;
protected $_queryCacheProfile;
/**
* @var boolean Boolean value that indicates whether or not expire the result cache.
@@ -115,14 +102,14 @@ abstract class AbstractQuery
protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
*/
protected $_resultCacheTTL;
protected $_hydrationCacheProfile;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
* @param Doctrine\ORM\EntityManager $entityManager
* @param \Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $em)
{
@@ -132,7 +119,7 @@ abstract class AbstractQuery
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return Doctrine\ORM\EntityManager
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
@@ -163,6 +150,16 @@ abstract class AbstractQuery
return $this->_params;
}
/**
* Get all defined parameter types.
*
* @return array The defined query parameter types.
*/
public function getParameterTypes()
{
return $this->_paramTypes;
}
/**
* Gets a query parameter.
*
@@ -171,7 +168,26 @@ abstract class AbstractQuery
*/
public function getParameter($key)
{
return isset($this->_params[$key]) ? $this->_params[$key] : null;
if (isset($this->_params[$key])) {
return $this->_params[$key];
}
return null;
}
/**
* Gets a query parameter type.
*
* @param mixed $key The key (index or name) of the bound parameter.
* @return mixed The parameter type of the bound parameter.
*/
public function getParameterType($key)
{
if (isset($this->_paramTypes[$key])) {
return $this->_paramTypes[$key];
}
return null;
}
/**
@@ -191,36 +207,83 @@ abstract class AbstractQuery
* @param string $type The parameter type. If specified, the given value will be run through
* the type conversion of this type. This is usually not needed for
* strings and numeric types.
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setParameter($key, $value, $type = null)
{
$key = trim($key, ':');
$value = $this->processParameterValue($value);
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
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.
*
* @param array $params
* @param array $types
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setParameters(array $params, array $types = array())
{
foreach ($params as $key => $value) {
if (isset($types[$key])) {
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
$this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
}
return $this;
}
@@ -228,44 +291,109 @@ abstract class AbstractQuery
* Sets the ResultSetMapping that should be used for hydration.
*
* @param ResultSetMapping $rsm
* @return Doctrine\ORM\AbstractQuery
* @return \Doctrine\ORM\AbstractQuery
*/
public function setResultSetMapping(Query\ResultSetMapping $rsm)
{
$this->_resultSetMapping = $rsm;
return $this;
}
/**
* Defines a cache driver to be used for caching result sets.
* Set a cache profile for hydration caching.
*
* @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery
* 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.
*
* @param \Doctrine\Common\Cache\Cache $driver Cache driver
* @return \Doctrine\ORM\AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
$this->_resultCacheDriver = $resultCacheDriver;
if ($resultCacheDriver) {
$this->_useResultCache = true;
}
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
: new QueryCacheProfile(0, null, $resultCacheDriver);
return $this;
}
/**
* Returns the cache driver used for caching result sets.
*
* @return Doctrine\Common\Cache\Cache Cache driver
* @deprecated
* @return \Doctrine\Common\Cache\Cache Cache driver
*/
public function getResultCacheDriver()
{
if ($this->_resultCacheDriver) {
return $this->_resultCacheDriver;
} else {
return $this->_em->getConfiguration()->getResultCacheImpl();
if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_queryCacheProfile->getResultCacheDriver();
}
return $this->_em->getConfiguration()->getResultCacheImpl();
}
/**
@@ -273,57 +401,62 @@ abstract class AbstractQuery
* how long and which ID to use for the cache entry.
*
* @param boolean $bool
* @param integer $timeToLive
* @param integer $lifetime
* @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
{
$this->_useResultCache = $bool;
if ($timeToLive) {
$this->setResultCacheLifetime($timeToLive);
}
if ($resultCacheId) {
$this->_resultCacheId = $resultCacheId;
if ($bool) {
$this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId);
return $this;
}
$this->_queryCacheProfile = null;
return $this;
}
/**
* Defines how long the result cache will be active before expire.
*
* @param integer $timeToLive How long the cache entry is valid.
* @return Doctrine\ORM\AbstractQuery This query instance.
* @param integer $lifetime How long the cache entry is valid.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheLifetime($timeToLive)
public function setResultCacheLifetime($lifetime)
{
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
$lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setLifetime($lifetime)
: new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl());
$this->_resultCacheTTL = $timeToLive;
return $this;
}
/**
* Retrieves the lifetime of resultset cache.
*
* @deprecated
* @return integer
*/
public function getResultCacheLifetime()
{
return $this->_resultCacheTTL;
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
}
/**
* Defines if the result cache is active or not.
*
* @param boolean $expire Whether or not to force resultset cache expiration.
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function expireResultCache($expire = true)
{
$this->_expireResultCache = $expire;
return $this;
}
@@ -337,6 +470,14 @@ abstract class AbstractQuery
return $this->_expireResultCache;
}
/**
* @return QueryCacheProfile
*/
public function getQueryCacheProfile()
{
return $this->_queryCacheProfile;
}
/**
* Change the default fetch mode of an association for this query.
*
@@ -354,6 +495,7 @@ abstract class AbstractQuery
}
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
return $this;
}
@@ -362,11 +504,12 @@ abstract class AbstractQuery
*
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Query::HYDRATE_* constants.
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setHydrationMode($hydrationMode)
{
$this->_hydrationMode = $hydrationMode;
return $this;
}
@@ -431,14 +574,15 @@ abstract class AbstractQuery
return null;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
if ( ! is_array($result)) {
return $result;
}
return $result;
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
/**
@@ -462,14 +606,15 @@ abstract class AbstractQuery
throw new NoResultException;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
if ( ! is_array($result)) {
return $result;
}
return $result;
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
/**
@@ -490,11 +635,12 @@ abstract class AbstractQuery
*
* @param string $name The name of the hint.
* @param mixed $value The value of the hint.
* @return Doctrine\ORM\AbstractQuery
* @return \Doctrine\ORM\AbstractQuery
*/
public function setHint($name, $value)
{
$this->_hints[$name] = $value;
return $this;
}
@@ -511,7 +657,7 @@ abstract class AbstractQuery
/**
* Return the key value map of query hints that are currently set.
*
*
* @return array
*/
public function getHints()
@@ -525,7 +671,7 @@ abstract class AbstractQuery
*
* @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use.
* @return IterableResult
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = null)
{
@@ -561,51 +707,44 @@ abstract class AbstractQuery
$this->setParameters($params);
}
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
$setCacheEntry = function() {};
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
if ($this->_hydrationCacheProfile !== null) {
list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$queryCacheProfile = $this->getHydrationCacheProfile();
$cache = $queryCacheProfile->getResultCacheDriver();
$result = $cache->fetch($cacheKey);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached[$key];
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(
$stmt, $this->_resultSetMapping, $this->_hints
);
}
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
/**
* Set the result cache id to use to store the result set cache entry.
* If this is not explicitely set by the developer then a hash is automatically
* generated for you.
*
* @param string $id
* @return Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheId($id)
{
$this->_resultCacheId = $id;
return $this;
$setCacheEntry($data);
return $data;
}
/**
@@ -615,38 +754,55 @@ abstract class AbstractQuery
*
* @return array ($key, $hash)
*/
protected function getResultCacheId()
protected function getHydrationCacheId()
{
if ($this->_resultCacheId) {
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$params[$key] = $idValues;
} else {
$params[$key] = $value;
}
}
$params = $this->getParameters();
$sql = $this->getSql();
ksort($this->_hints);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
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);
}
/**
* Set the result cache id to use to store the result set cache entry.
* If this is not explicitely set by the developer then a hash is automatically
* generated for you.
*
* @param string $id
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheId($id)
{
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setCacheKey($id)
: new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl());
return $this;
}
/**
* Get the result cache id to use to store the result set cache entry if set.
*
* @deprecated
* @return string
*/
public function getResultCacheId()
{
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
}
/**
* Executes the query and returns a the resulting Statement object.
*
* @return Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
* @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
*/
abstract protected function _doExecute();

View File

@@ -85,7 +85,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the namespace where proxy classes reside.
*
*
* @return string
*/
public function getProxyNamespace()
@@ -96,7 +96,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Sets the namespace where proxy classes reside.
*
*
* @param string $ns
*/
public function setProxyNamespace($ns)
@@ -118,22 +118,23 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Add a new default annotation driver with a correctly configured annotation reader.
*
*
* @param array $paths
* @return Mapping\Driver\AnnotationDriver
*/
public function newDefaultAnnotationDriver($paths = array())
{
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
$reader = new AnnotationReader();
$reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
$reader->addNamespace('Doctrine\ORM\Mapping');
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
} else if (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-DEV', '>=')) {
// Register the ORM Annotations in the AnnotationRegistry
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
$reader = new AnnotationReader();
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$reader->setIgnoreNotImportedAnnotations(true);
@@ -162,7 +163,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Resolves a registered namespace alias to the full namespace.
*
* @param string $entityNamespaceAlias
* @param string $entityNamespaceAlias
* @return string
* @throws MappingException
*/
@@ -185,10 +186,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
$this->_attributes['entityNamespaces'] = $entityNamespaces;
}
/**
* Retrieves the list of registered entity namespace aliases.
*
*
* @return array
*/
public function getEntityNamespaces()
@@ -208,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataDriverImpl'] : null;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for the query cache (SQL cache).
*
@@ -250,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.
*
@@ -360,7 +362,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom string DQL function.
*
*
* @param string $name
* @return string
*/
@@ -403,7 +405,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom numeric DQL function.
*
*
* @param string $name
* @return string
*/
@@ -446,7 +448,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the implementation class name of a registered custom date/time DQL function.
*
*
* @param string $name
* @return string
*/
@@ -497,7 +499,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Set a class metadata factory.
*
*
* @param string $cmf
*/
public function setClassMetadataFactoryName($cmfName)
@@ -515,4 +517,57 @@ class Configuration extends \Doctrine\DBAL\Configuration
}
return $this->_attributes['classMetadataFactoryName'];
}
}
/**
* Add a filter to the list of possible filters.
*
* @param string $name The name of the filter.
* @param string $className The class name of the filter.
*/
public function addFilter($name, $className)
{
$this->_attributes['filters'][$name] = $className;
}
/**
* Gets the class name for a given filter name.
*
* @param string $name The name of the filter.
*
* @return string The class name of the filter, or null of it is not
* defined.
*/
public function getFilterClassName($name)
{
return isset($this->_attributes['filters'][$name]) ?
$this->_attributes['filters'][$name] : null;
}
/**
* Set default repository class.
*
* @since 2.2
* @param string $className
* @throws ORMException If not is a \Doctrine\ORM\EntityRepository
*/
public function setDefaultRepositoryClassName($className)
{
if ($className != "Doctrine\ORM\EntityRepository" &&
!is_subclass_of($className, 'Doctrine\ORM\EntityRepository')){
throw ORMException::invalidEntityRepository($className);
}
$this->_attributes['defaultRepositoryClassName'] = $className;
}
/**
* Get default repository class.
*
* @since 2.2
* @return string
*/
public function getDefaultRepositoryClassName()
{
return isset($this->_attributes['defaultRepositoryClassName']) ?
$this->_attributes['defaultRepositoryClassName'] : 'Doctrine\ORM\EntityRepository';
}
}

View File

@@ -27,7 +27,8 @@ use Closure, Exception,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataFactory,
Doctrine\ORM\Query\ResultSetMapping,
Doctrine\ORM\Proxy\ProxyFactory;
Doctrine\ORM\Proxy\ProxyFactory,
Doctrine\ORM\Query\FilterCollection;
/**
* The EntityManager is the central access point to ORM functionality.
@@ -43,21 +44,21 @@ class EntityManager implements ObjectManager
/**
* The used Configuration.
*
* @var Doctrine\ORM\Configuration
* @var \Doctrine\ORM\Configuration
*/
private $config;
/**
* The database connection used by the EntityManager.
*
* @var Doctrine\DBAL\Connection
* @var \Doctrine\DBAL\Connection
*/
private $conn;
/**
* The metadata factory, used to retrieve the ORM metadata of entity classes.
*
* @var Doctrine\ORM\Mapping\ClassMetadataFactory
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
private $metadataFactory;
@@ -71,14 +72,14 @@ class EntityManager implements ObjectManager
/**
* The UnitOfWork used to coordinate object-level transactions.
*
* @var Doctrine\ORM\UnitOfWork
* @var \Doctrine\ORM\UnitOfWork
*/
private $unitOfWork;
/**
* The event manager that is the central point of the event system.
*
* @var Doctrine\Common\EventManager
* @var \Doctrine\Common\EventManager
*/
private $eventManager;
@@ -92,14 +93,14 @@ class EntityManager implements ObjectManager
/**
* The proxy factory used to create dynamic proxies.
*
* @var Doctrine\ORM\Proxy\ProxyFactory
* @var \Doctrine\ORM\Proxy\ProxyFactory
*/
private $proxyFactory;
/**
* The expression builder instance used to generate query expressions.
*
* @var Doctrine\ORM\Query\Expr
* @var \Doctrine\ORM\Query\Expr
*/
private $expressionBuilder;
@@ -110,13 +111,20 @@ class EntityManager implements ObjectManager
*/
private $closed = false;
/**
* Collection of query filters.
*
* @var Doctrine\ORM\Query\FilterCollection
*/
private $filterCollection;
/**
* Creates a new EntityManager that operates on the given database connection
* and uses the given Configuration and EventManager implementations.
*
* @param Doctrine\DBAL\Connection $conn
* @param Doctrine\ORM\Configuration $config
* @param Doctrine\Common\EventManager $eventManager
* @param \Doctrine\DBAL\Connection $conn
* @param \Doctrine\ORM\Configuration $config
* @param \Doctrine\Common\EventManager $eventManager
*/
protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
{
@@ -128,18 +136,20 @@ class EntityManager implements ObjectManager
$this->metadataFactory = new $metadataFactoryClassName;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses());
$this->proxyFactory = new ProxyFactory(
$this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses()
);
}
/**
* Gets the database connection object used by the EntityManager.
*
* @return Doctrine\DBAL\Connection
* @return \Doctrine\DBAL\Connection
*/
public function getConnection()
{
@@ -149,7 +159,7 @@ class EntityManager implements ObjectManager
/**
* Gets the metadata factory used to gather the metadata of classes.
*
* @return Doctrine\ORM\Mapping\ClassMetadataFactory
* @return \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
public function getMetadataFactory()
{
@@ -168,13 +178,14 @@ class EntityManager implements ObjectManager
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
* </code>
*
* @return Doctrine\ORM\Query\Expr
* @return \Doctrine\ORM\Query\Expr
*/
public function getExpressionBuilder()
{
if ($this->expressionBuilder === null) {
$this->expressionBuilder = new Query\Expr;
}
return $this->expressionBuilder;
}
@@ -199,22 +210,23 @@ class EntityManager implements ObjectManager
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
*
* @param Closure $func The function to execute transactionally.
* @return mixed Returns the non-empty value returned from the closure or true instead
*/
public function transactional(Closure $func)
{
$this->conn->beginTransaction();
try {
$return = $func($this);
$this->flush();
$this->conn->commit();
return $return ?: true;
} catch (Exception $e) {
$this->close();
$this->conn->rollback();
throw $e;
}
}
@@ -244,12 +256,12 @@ class EntityManager implements ObjectManager
*
* The class name must be the fully-qualified class name without a leading backslash
* (as it is returned by get_class($obj)) or an aliased class name.
*
*
* Examples:
* MyProject\Domain\User
* sales:PriceRequest
*
* @return Doctrine\ORM\Mapping\ClassMetadata
* @return \Doctrine\ORM\Mapping\ClassMetadata
* @internal Performance-sensitive method.
*/
public function getClassMetadata($className)
@@ -261,14 +273,16 @@ class EntityManager implements ObjectManager
* Creates a new Query object.
*
* @param string The DQL string.
* @return Doctrine\ORM\Query
* @return \Doctrine\ORM\Query
*/
public function createQuery($dql = "")
{
$query = new Query($this);
if ( ! empty($dql)) {
$query->setDql($dql);
}
return $query;
}
@@ -276,7 +290,7 @@ class EntityManager implements ObjectManager
* Creates a Query from a named query.
*
* @param string $name
* @return Doctrine\ORM\Query
* @return \Doctrine\ORM\Query
*/
public function createNamedQuery($name)
{
@@ -295,6 +309,7 @@ class EntityManager implements ObjectManager
$query = new NativeQuery($this);
$query->setSql($sql);
$query->setResultSetMapping($rsm);
return $query;
}
@@ -302,11 +317,12 @@ class EntityManager implements ObjectManager
* Creates a NativeQuery from a named native query.
*
* @param string $name
* @return Doctrine\ORM\NativeQuery
* @return \Doctrine\ORM\NativeQuery
*/
public function createNamedNativeQuery($name)
{
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
return $this->createNativeQuery($sql, $rsm);
}
@@ -325,13 +341,18 @@ class EntityManager implements ObjectManager
* This effectively synchronizes the in-memory state of managed objects with the
* database.
*
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
* If an entity is explicitly passed to this method only this entity and
* the cascade-persist semantics + scheduled inserts/removals are synchronized.
*
* @param object $entity
* @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that
* makes use of optimistic locking fails.
*/
public function flush()
public function flush($entity = null)
{
$this->errorIfClosed();
$this->unitOfWork->commit();
$this->unitOfWork->commit($entity);
}
/**
@@ -355,27 +376,39 @@ class EntityManager implements ObjectManager
* without actually loading it, if the entity is not yet loaded.
*
* @param string $entityName The name of the entity type.
* @param mixed $identifier The entity identifier.
* @param mixed $id The entity identifier.
* @return object The entity reference.
*/
public function getReference($entityName, $identifier)
public function getReference($entityName, $id)
{
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
if ( ! is_array($id)) {
$id = array($class->identifier[0] => $id);
}
$sortedId = array();
foreach ($class->identifier as $identifier) {
if (!isset($id[$identifier])) {
throw ORMException::missingIdentifierField($class->name, $identifier);
}
$sortedId[$identifier] = $id[$identifier];
}
// Check identity map first, if its already in there just return it.
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
if ($entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ($class->subClasses) {
$entity = $this->find($entityName, $identifier);
} else {
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
return $this->find($entityName, $sortedId);
}
if ( ! is_array($sortedId)) {
$sortedId = array($class->identifier[0] => $sortedId);
}
$entity = $this->proxyFactory->getProxy($class->name, $sortedId);
$this->unitOfWork->registerManaged($entity, $sortedId, array());
return $entity;
}
@@ -406,6 +439,7 @@ class EntityManager implements ObjectManager
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
@@ -413,6 +447,7 @@ class EntityManager implements ObjectManager
$entity = $class->newInstance();
$class->setIdentifierValues($entity, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
$this->unitOfWork->markReadOnly($entity);
return $entity;
}
@@ -421,16 +456,11 @@ class EntityManager implements ObjectManager
* Clears the EntityManager. All entities that are currently managed
* by this EntityManager become detached.
*
* @param string $entityName
* @param string $entityName if given, only entities of this type will get detached
*/
public function clear($entityName = null)
{
if ($entityName === null) {
$this->unitOfWork->clear();
} else {
//TODO
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
}
$this->unitOfWork->clear($entityName);
}
/**
@@ -449,7 +479,7 @@ class EntityManager implements ObjectManager
*
* The entity will be entered into the database at or before transaction
* commit or as a result of the flush operation.
*
*
* NOTE: The persist operation always considers entities that are not yet known to
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
*
@@ -460,7 +490,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->persist($entity);
}
@@ -477,7 +509,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->remove($entity);
}
@@ -492,7 +526,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->refresh($entity);
}
@@ -510,6 +546,7 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->unitOfWork->detach($entity);
}
@@ -526,7 +563,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
return $this->unitOfWork->merge($entity);
}
@@ -566,19 +605,20 @@ class EntityManager implements ObjectManager
public function getRepository($entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
$customRepositoryClassName = $metadata->customRepositoryClassName;
$repositoryClassName = $metadata->customRepositoryClassName;
if ($customRepositoryClassName !== null) {
$repository = new $customRepositoryClassName($this, $metadata);
} else {
$repository = new EntityRepository($this, $metadata);
if ($repositoryClassName === null) {
$repositoryClassName = $this->config->getDefaultRepositoryClassName();
}
$repository = new $repositoryClassName($this, $metadata);
$this->repositories[$entityName] = $repository;
return $repository;
@@ -592,15 +632,15 @@ class EntityManager implements ObjectManager
*/
public function contains($entity)
{
return $this->unitOfWork->isScheduledForInsert($entity) ||
$this->unitOfWork->isInIdentityMap($entity) &&
! $this->unitOfWork->isScheduledForDelete($entity);
return $this->unitOfWork->isScheduledForInsert($entity)
|| $this->unitOfWork->isInIdentityMap($entity)
&& ! $this->unitOfWork->isScheduledForDelete($entity);
}
/**
* Gets the EventManager used by the EntityManager.
*
* @return Doctrine\Common\EventManager
* @return \Doctrine\Common\EventManager
*/
public function getEventManager()
{
@@ -610,7 +650,7 @@ class EntityManager implements ObjectManager
/**
* Gets the Configuration used by the EntityManager.
*
* @return Doctrine\ORM\Configuration
* @return \Doctrine\ORM\Configuration
*/
public function getConfiguration()
{
@@ -631,7 +671,7 @@ class EntityManager implements ObjectManager
/**
* Check if the Entity manager is open or closed.
*
*
* @return bool
*/
public function isOpen()
@@ -642,7 +682,7 @@ class EntityManager implements ObjectManager
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*
* @return Doctrine\ORM\UnitOfWork
* @return \Doctrine\ORM\UnitOfWork
*/
public function getUnitOfWork()
{
@@ -656,7 +696,7 @@ class EntityManager implements ObjectManager
* selectively iterate over the result.
*
* @param int $hydrationMode
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
* @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
*/
public function getHydrator($hydrationMode)
{
@@ -671,35 +711,33 @@ class EntityManager implements ObjectManager
* Create a new instance for the given hydration mode.
*
* @param int $hydrationMode
* @return Doctrine\ORM\Internal\Hydration\AbstractHydrator
* @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator
*/
public function newHydrator($hydrationMode)
{
switch ($hydrationMode) {
case Query::HYDRATE_OBJECT:
$hydrator = new Internal\Hydration\ObjectHydrator($this);
break;
return new Internal\Hydration\ObjectHydrator($this);
case Query::HYDRATE_ARRAY:
$hydrator = new Internal\Hydration\ArrayHydrator($this);
break;
return new Internal\Hydration\ArrayHydrator($this);
case Query::HYDRATE_SCALAR:
$hydrator = new Internal\Hydration\ScalarHydrator($this);
break;
return new Internal\Hydration\ScalarHydrator($this);
case Query::HYDRATE_SINGLE_SCALAR:
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
break;
return new Internal\Hydration\SingleScalarHydrator($this);
case Query::HYDRATE_SIMPLEOBJECT:
$hydrator = new Internal\Hydration\SimpleObjectHydrator($this);
break;
return new Internal\Hydration\SimpleObjectHydrator($this);
default:
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
$hydrator = new $class($this);
break;
return new $class($this);
}
throw ORMException::invalidHydrationMode($hydrationMode);
}
return $hydrator;
throw ORMException::invalidHydrationMode($hydrationMode);
}
/**
@@ -712,6 +750,18 @@ class EntityManager implements ObjectManager
return $this->proxyFactory;
}
/**
* Helper method to initialize a lazy loading proxy or persistent collection.
*
* This method is a no-op for other objects
*
* @param object $obj
*/
public function initializeObject($obj)
{
$this->unitOfWork->initializeObject($obj);
}
/**
* Factory method to create EntityManager instances.
*
@@ -723,20 +773,62 @@ class EntityManager implements ObjectManager
*/
public static function create($conn, Configuration $config, EventManager $eventManager = null)
{
if (!$config->getMetadataDriverImpl()) {
if ( ! $config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
} else if ($conn instanceof Connection) {
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
} else {
throw new \InvalidArgumentException("Invalid argument: " . $conn);
switch (true) {
case (is_array($conn)):
$conn = \Doctrine\DBAL\DriverManager::getConnection(
$conn, $config, ($eventManager ?: new EventManager())
);
break;
case ($conn instanceof Connection):
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
break;
default:
throw new \InvalidArgumentException("Invalid argument: " . $conn);
}
return new EntityManager($conn, $config, $conn->getEventManager());
}
/**
* Gets the enabled filters.
*
* @return FilterCollection The active filter collection.
*/
public function getFilters()
{
if (null === $this->filterCollection) {
$this->filterCollection = new FilterCollection($this);
}
return $this->filterCollection;
}
/**
* Checks whether the state of the filter collection is clean.
*
* @return boolean True, if the filter collection is clean.
*/
public function isFiltersStateClean()
{
return null === $this->filterCollection
|| $this->filterCollection->isClean();
}
/**
* Checks whether the Entity Manager has filters.
*
* @return True, if the EM has a filter collection.
*/
public function hasFilters()
{
return null !== $this->filterCollection;
}
}

View File

@@ -48,7 +48,7 @@ class EntityRepository implements ObjectRepository
protected $_em;
/**
* @var Doctrine\ORM\Mapping\ClassMetadata
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
protected $_class;
@@ -107,42 +107,51 @@ class EntityRepository implements ObjectRepository
*/
public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
{
if ( ! is_array($id)) {
$id = array($this->_class->identifier[0] => $id);
}
$sortedId = array();
foreach ($this->_class->identifier as $identifier) {
if (!isset($id[$identifier])) {
throw ORMException::missingIdentifierField($this->_class->name, $identifier);
}
$sortedId[$identifier] = $id[$identifier];
}
// Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
if (!($entity instanceof $this->_class->name)) {
if ($entity = $this->_em->getUnitOfWork()->tryGetById($sortedId, $this->_class->rootEntityName)) {
if ( ! ($entity instanceof $this->_class->name)) {
return null;
}
if ($lockMode != LockMode::NONE) {
if ($lockMode !== LockMode::NONE) {
$this->_em->lock($entity, $lockMode, $lockVersion);
}
return $entity; // Hit!
}
if ( ! is_array($id) || count($id) <= 1) {
// @todo FIXME: Not correct. Relies on specific order.
$value = is_array($id) ? array_values($id) : array($id);
$id = array_combine($this->_class->identifier, $value);
}
switch ($lockMode) {
case LockMode::NONE:
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId);
if ($lockMode == LockMode::NONE) {
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
} else if ($lockMode == LockMode::OPTIMISTIC) {
if (!$this->_class->isVersioned) {
throw OptimisticLockException::notVersioned($this->_entityName);
}
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
case LockMode::OPTIMISTIC:
if ( ! $this->_class->isVersioned) {
throw OptimisticLockException::notVersioned($this->_entityName);
}
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId);
return $entity;
} else {
if (!$this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
return $entity;
default:
if ( ! $this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId, null, null, array(), $lockMode);
}
}
@@ -178,7 +187,7 @@ class EntityRepository implements ObjectRepository
*/
public function findOneBy(array $criteria)
{
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria, null, null, array(), 0, 1);
}
/**
@@ -191,31 +200,35 @@ class EntityRepository implements ObjectRepository
*/
public function __call($method, $arguments)
{
if (substr($method, 0, 6) == 'findBy') {
$by = substr($method, 6, strlen($method));
$method = 'findBy';
} else if (substr($method, 0, 9) == 'findOneBy') {
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
} else {
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
switch (true) {
case (substr($method, 0, 6) == 'findBy'):
$by = substr($method, 6, strlen($method));
$method = 'findBy';
break;
case (substr($method, 0, 9) == 'findOneBy'):
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
break;
default:
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
}
if ( !isset($arguments[0])) {
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
throw ORMException::findByRequiresParameter($method.$by);
if (empty($arguments)) {
throw ORMException::findByRequiresParameter($method . $by);
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
/**
@@ -226,6 +239,14 @@ class EntityRepository implements ObjectRepository
return $this->_entityName;
}
/**
* @return string
*/
public function getClassName()
{
return $this->getEntityName();
}
/**
* @return EntityManager
*/
@@ -241,4 +262,4 @@ class EntityRepository implements ObjectRepository
{
return $this->_class;
}
}
}

View File

@@ -19,42 +19,59 @@
namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\EntityManager;
/**
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
* of entities.
*
* @since 2.0
* @link www.doctrine-project.org
* @since 2.0
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class LifecycleEventArgs extends \Doctrine\Common\EventArgs
class LifecycleEventArgs extends EventArgs
{
/**
* @var EntityManager
* @var \Doctrine\ORM\EntityManager
*/
private $_em;
private $em;
/**
* @var object
*/
private $_entity;
public function __construct($entity, $em)
private $entity;
/**
* Constructor
*
* @param object $entity
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct($entity, EntityManager $em)
{
$this->_entity = $entity;
$this->_em = $em;
}
public function getEntity()
{
return $this->_entity;
$this->entity = $entity;
$this->em = $em;
}
/**
* @return EntityManager
* Retireve associated Entity.
*
* @return object
*/
public function getEntity()
{
return $this->entity;
}
/**
* Retrieve associated EntityManager.
*
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->_em;
return $this->em;
}
}

View File

@@ -1,9 +1,25 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\EntityManager;
@@ -11,32 +27,36 @@ use Doctrine\ORM\EntityManager;
* Class that holds event arguments for a loadMetadata event.
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
* @since 2.0
*/
class LoadClassMetadataEventArgs extends EventArgs
{
/**
* @var ClassMetadata
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
private $classMetadata;
/**
* @var EntityManager
* @var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* @param ClassMetadataInfo $classMetadata
* @param EntityManager $em
* Constructor.
*
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
{
$this->classMetadata = $classMetadata;
$this->em = $em;
$this->em = $em;
}
/**
* @return ClassMetadataInfo
* Retrieve associated ClassMetadata.
*
* @return \Doctrine\ORM\Mapping\ClassMetadataInfo
*/
public function getClassMetadata()
{
@@ -44,7 +64,9 @@ class LoadClassMetadataEventArgs extends EventArgs
}
/**
* @return EntityManager
* Retrieve associated EntityManager.
*
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{

View File

@@ -15,7 +15,7 @@
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
*/
namespace Doctrine\ORM\Event;
@@ -23,9 +23,8 @@ namespace Doctrine\ORM\Event;
* Provides event arguments for the onClear event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
@@ -37,18 +36,49 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
private $em;
/**
* @param \Doctrine\ORM\EntityManager $em
* @var string
*/
public function __construct($em)
private $entityClass;
/**
* Constructor.
*
* @param \Doctrine\ORM\EntityManager $em
* @param string $entityClass Optional entity class
*/
public function __construct($em, $entityClass = null)
{
$this->em = $em;
$this->em = $em;
$this->entityClass = $entityClass;
}
/**
* Retrieve associated EntityManager.
*
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
/**
* Name of the entity class that is cleared, or empty if all are cleared.
*
* @return string
*/
public function getEntityClass()
{
return $this->entityClass;
}
/**
* Check if event clears all entities.
*
* @return bool
*/
public function clearsAllEntities()
{
return ($this->entityClass === null);
}
}

View File

@@ -21,55 +21,63 @@
namespace Doctrine\ORM\Event;
use Doctrine\ORM\EntityManager;
/**
* Provides event arguments for the preFlush event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class OnFlushEventArgs extends \Doctrine\Common\EventArgs
{
/**
* @var EntityManager
* @var Doctirne\ORM\EntityManager
*/
private $_em;
//private $_entitiesToPersist = array();
//private $_entitiesToRemove = array();
public function __construct($em)
private $em;
//private $entitiesToPersist = array();
//private $entitiesToRemove = array();
/**
* Constructor.
*
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->em = $em;
}
/**
* @return EntityManager
* Retrieve associated EntityManager.
*
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->_em;
return $this->em;
}
/*
public function addEntityToPersist($entity)
{
}
public function addEntityToRemove($entity)
{
}
public function addEntityToUpdate($entity)
{
}
public function getEntitiesToPersist()
{
return $this->_entitiesToPersist;

View File

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

View File

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

View File

@@ -1,4 +1,23 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Event;
@@ -8,55 +27,63 @@ use Doctrine\Common\EventArgs,
/**
* Class that holds event arguments for a preInsert/preUpdate event.
*
* @author Guilherme Blanco <guilehrmeblanco@hotmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
* @since 2.0
*/
class PreUpdateEventArgs extends LifecycleEventArgs
{
/**
* @var array
*/
private $_entityChangeSet;
private $entityChangeSet;
/**
* Constructor.
*
* @param object $entity
* @param EntityManager $em
* @param \Doctrine\ORM\EntityManager $em
* @param array $changeSet
*/
public function __construct($entity, $em, array &$changeSet)
public function __construct($entity, EntityManager $em, array &$changeSet)
{
parent::__construct($entity, $em);
$this->_entityChangeSet = &$changeSet;
}
public function getEntityChangeSet()
{
return $this->_entityChangeSet;
$this->entityChangeSet = &$changeSet;
}
/**
* Field has a changeset?
* Retrieve entity changeset.
*
* @return bool
* @return array
*/
public function getEntityChangeSet()
{
return $this->entityChangeSet;
}
/**
* Check if field has a changeset.
*
* @return boolean
*/
public function hasChangedField($field)
{
return isset($this->_entityChangeSet[$field]);
return isset($this->entityChangeSet[$field]);
}
/**
* Get the old value of the changeset of the changed field.
*
*
* @param string $field
* @return mixed
*/
public function getOldValue($field)
{
$this->_assertValidField($field);
$this->assertValidField($field);
return $this->_entityChangeSet[$field][0];
return $this->entityChangeSet[$field][0];
}
/**
@@ -67,31 +94,37 @@ class PreUpdateEventArgs extends LifecycleEventArgs
*/
public function getNewValue($field)
{
$this->_assertValidField($field);
$this->assertValidField($field);
return $this->_entityChangeSet[$field][1];
return $this->entityChangeSet[$field][1];
}
/**
* Set the new value of this field.
*
*
* @param string $field
* @param mixed $value
*/
public function setNewValue($field, $value)
{
$this->_assertValidField($field);
$this->assertValidField($field);
$this->_entityChangeSet[$field][1] = $value;
$this->entityChangeSet[$field][1] = $value;
}
private function _assertValidField($field)
/**
* Assert the field exists in changeset.
*
* @param string $field
*/
private function assertValidField($field)
{
if (!isset($this->_entityChangeSet[$field])) {
throw new \InvalidArgumentException(
"Field '".$field."' is not a valid field of the entity ".
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs."
);
if ( ! isset($this->entityChangeSet[$field])) {
throw new \InvalidArgumentException(sprintf(
'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.',
$field,
get_class($this->getEntity())
));
}
}
}

View File

@@ -35,55 +35,55 @@ final class Events
/**
* The preRemove event occurs for a given entity before the respective
* EntityManager remove operation for that entity is executed.
*
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const preRemove = 'preRemove';
/**
* The postRemove event occurs for an entity after the entity has
* The postRemove event occurs for an entity after the entity has
* been deleted. It will be invoked after the database delete operations.
*
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const postRemove = 'postRemove';
/**
* The prePersist event occurs for a given entity before the respective
* EntityManager persist operation for that entity is executed.
*
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const prePersist = 'prePersist';
/**
* The postPersist event occurs for an entity after the entity has
* The postPersist event occurs for an entity after the entity has
* been made persistent. It will be invoked after the database insert operations.
* Generated primary key values are available in the postPersist event.
*
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const postPersist = 'postPersist';
/**
* The preUpdate event occurs before the database update operations to
* entity data.
*
* The preUpdate event occurs before the database update operations to
* entity data.
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const preUpdate = 'preUpdate';
/**
* The postUpdate event occurs after the database update operations to
* entity data.
*
* The postUpdate event occurs after the database update operations to
* entity data.
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const postUpdate = 'postUpdate';
@@ -91,35 +91,53 @@ final class Events
* The postLoad event occurs for an entity after the entity has been loaded
* into the current EntityManager from the database or after the refresh operation
* has been applied to it.
*
*
* Note that the postLoad event occurs for an entity before any associations have been
* initialized. Therefore it is not safe to access associations in a postLoad callback
* or event handler.
*
*
* This is an entity lifecycle event.
*
*
* @var string
*/
const postLoad = 'postLoad';
/**
* The loadClassMetadata event occurs after the mapping metadata for a class
* has been loaded from a mapping source (annotations/xml/yaml).
*
*
* @var string
*/
const loadClassMetadata = 'loadClassMetadata';
/**
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
* but before any changes to managed entites have been calculated. This event is
* always raised right after EntityManager#flush() call.
*/
const preFlush = 'preFlush';
/**
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
* after any changes to managed entities have been determined but before any
* actual database operations are executed. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the onFlush event is not raised.
*
*
* @var string
*/
const onFlush = 'onFlush';
/**
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
* after all actual database operations are executed successfully. The event is only raised if there is
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
* the postFlush event is not raised. The event won't be raised if an error occurs during the
* flush operation.
*
* @var string
*/
const postFlush = 'postFlush';
/**
* The onClear event occurs when the EntityManager#clear() operation is invoked,
* after all references to entities have been removed from the unit of work.

View File

@@ -26,7 +26,7 @@ abstract class AbstractIdGenerator
/**
* Generates an identifier for an entity.
*
* @param Doctrine\ORM\Entity $entity
* @param \Doctrine\ORM\Entity $entity
* @return mixed
*/
abstract public function generate(EntityManager $em, $entity);
@@ -35,7 +35,7 @@ abstract class AbstractIdGenerator
* Gets whether this generator is a post-insert generator which means that
* {@link generate()} must be called after the entity has been inserted
* into the database.
*
*
* By default, this method returns FALSE. Generators that have this requirement
* must override this method and return TRUE.
*

View File

@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Id;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMException;
/**
@@ -42,46 +43,29 @@ class AssignedGenerator extends AbstractIdGenerator
*/
public function generate(EntityManager $em, $entity)
{
$class = $em->getClassMetadata(get_class($entity));
$class = $em->getClassMetadata(get_class($entity));
$idFields = $class->getIdentifierFieldNames();
$identifier = array();
if ($class->isIdentifierComposite) {
$idFields = $class->getIdentifierFieldNames();
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedId($entity);
}
}
} else {
$idField = $class->identifier[0];
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedId($entity);
if ( ! isset($value)) {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
}
if (isset($class->associationMappings[$idField])) {
if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$value = current($em->getUnitOfWork()->getEntityIdentifier($value));
}
$identifier[$idField] = $value;
}
return $identifier;
}
}
}

View File

@@ -46,7 +46,7 @@ class IdentityGenerator extends AbstractIdGenerator
*/
public function generate(EntityManager $em, $entity)
{
return $em->getConnection()->lastInsertId($this->_seqName);
return (int)$em->getConnection()->lastInsertId($this->_seqName);
}
/**

View File

@@ -37,7 +37,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
/**
* Initializes a new sequence generator.
*
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
* @param \Doctrine\ORM\EntityManager $em The EntityManager to use.
* @param string $sequenceName The name of the sequence.
* @param integer $allocationSize The allocation size of the sequence.
*/
@@ -46,7 +46,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
$this->_sequenceName = $sequenceName;
$this->_allocationSize = $allocationSize;
}
/**
* Generates an ID for the given entity.
*
@@ -59,10 +59,12 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
// Allocate new values
$conn = $em->getConnection();
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
$this->_nextValue = $conn->fetchColumn($sql);
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
$this->_nextValue = (int)$conn->fetchColumn($sql);
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
}
return $this->_nextValue++;
}
@@ -90,13 +92,14 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
{
return serialize(array(
'allocationSize' => $this->_allocationSize,
'sequenceName' => $this->_sequenceName
'sequenceName' => $this->_sequenceName
));
}
public function unserialize($serialized)
{
$array = unserialize($serialized);
$this->_sequenceName = $array['sequenceName'];
$this->_allocationSize = $array['allocationSize'];
}

View File

@@ -50,11 +50,12 @@ class TableGenerator extends AbstractIdGenerator
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
// Allocate new values
$conn = $em->getConnection();
if ($conn->getTransactionNestingLevel() == 0) {
if ($conn->getTransactionNestingLevel() === 0) {
// use select for update
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
$currentLevel = $conn->fetchColumn($sql);
if ($currentLevel != null) {
$this->_nextValue = $currentLevel;
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
@@ -62,7 +63,7 @@ class TableGenerator extends AbstractIdGenerator
$updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql(
$this->_tableName, $this->_sequenceName, $this->_allocationSize
);
if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) {
// no affected rows, concurrency issue, throw exception
}
@@ -74,6 +75,7 @@ class TableGenerator extends AbstractIdGenerator
// or do we want to work with table locks exclusively?
}
}
return $this->_nextValue++;
}
}

View File

@@ -23,20 +23,21 @@ namespace Doctrine\ORM\Internal;
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
* correct order in which changes to entities need to be persisted.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class CommitOrderCalculator
{
const NOT_VISITED = 1;
const IN_PROGRESS = 2;
const VISITED = 3;
private $_nodeStates = array();
private $_classes = array(); // The nodes to sort
private $_relatedClasses = array();
private $_sorted = array();
/**
* Clears the current graph.
*
@@ -47,10 +48,10 @@ class CommitOrderCalculator
$this->_classes =
$this->_relatedClasses = array();
}
/**
* Gets a valid commit order for all current nodes.
*
*
* Uses a depth-first search (DFS) to traverse the graph.
* The desired topological sorting is the reverse postorder of these searches.
*
@@ -60,17 +61,16 @@ class CommitOrderCalculator
{
// Check whether we need to do anything. 0 or 1 node is easy.
$nodeCount = count($this->_classes);
if ($nodeCount == 0) {
return array();
} else if ($nodeCount == 1) {
return array_values($this->_classes);
if ($nodeCount <= 1) {
return ($nodeCount == 1) ? array_values($this->_classes) : array();
}
// Init
foreach ($this->_classes as $node) {
$this->_nodeStates[$node->name] = self::NOT_VISITED;
}
// Go
foreach ($this->_classes as $node) {
if ($this->_nodeStates[$node->name] == self::NOT_VISITED) {
@@ -100,17 +100,17 @@ class CommitOrderCalculator
$this->_nodeStates[$node->name] = self::VISITED;
$this->_sorted[] = $node;
}
public function addDependency($fromClass, $toClass)
{
$this->_relatedClasses[$fromClass->name][] = $toClass;
}
public function hasClass($className)
{
return isset($this->_classes[$className]);
}
public function addClass($class)
{
$this->_classes[$class->name] = $class;

View File

@@ -22,15 +22,17 @@ namespace Doctrine\ORM\Internal\Hydration;
use PDO,
Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager;
Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata;
/**
* Base class for all hydrators. A hydrator is a class that provides some form
* of transformation of an SQL result set into another structure.
*
* @since 2.0
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*/
abstract class AbstractHydrator
{
@@ -58,13 +60,13 @@ abstract class AbstractHydrator
/**
* Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
*
* @param Doctrine\ORM\EntityManager $em The EntityManager to use.
* @param \Doctrine\ORM\EntityManager $em The EntityManager to use.
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->_em = $em;
$this->_platform = $em->getConnection()->getDatabasePlatform();
$this->_uow = $em->getUnitOfWork();
$this->_uow = $em->getUnitOfWork();
}
/**
@@ -72,14 +74,17 @@ abstract class AbstractHydrator
*
* @param object $stmt
* @param object $resultSetMapping
*
* @return IterableResult
*/
public function iterate($stmt, $resultSetMapping, array $hints = array())
{
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$this->_prepare();
$this->prepare();
return new IterableResult($this);
}
@@ -92,12 +97,16 @@ abstract class AbstractHydrator
*/
public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
{
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$this->_prepare();
$result = $this->_hydrateAll();
$this->_cleanup();
$this->prepare();
$result = $this->hydrateAllData();
$this->cleanup();
return $result;
}
@@ -110,12 +119,17 @@ abstract class AbstractHydrator
public function hydrateRow()
{
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row) {
$this->_cleanup();
$this->cleanup();
return false;
}
$result = array();
$this->_hydrateRow($row, $this->_cache, $result);
$this->hydrateRowData($row, $this->_cache, $result);
return $result;
}
@@ -123,16 +137,17 @@ abstract class AbstractHydrator
* Excutes one-time preparation tasks, once each time hydration is started
* through {@link hydrateAll} or {@link iterate()}.
*/
protected function _prepare()
protected function prepare()
{}
/**
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
* through {@link hydrateAll} or {@link iterate()}.
*/
protected function _cleanup()
protected function cleanup()
{
$this->_rsm = null;
$this->_stmt->closeCursor();
$this->_stmt = null;
}
@@ -146,61 +161,81 @@ abstract class AbstractHydrator
* @param array $cache The cache to use.
* @param mixed $result The result to fill.
*/
protected function _hydrateRow(array $data, array &$cache, array &$result)
protected function hydrateRowData(array $data, array &$cache, array &$result)
{
throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
}
/**
* Hydrates all rows from the current statement instance at once.
*/
abstract protected function _hydrateAll();
abstract protected function hydrateAllData();
/**
* Processes a row of the result set.
*
* Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
* Puts the elements of a result row into a new array, grouped by the class
* Puts the elements of a result row into a new array, grouped by the dql alias
* they belong to. The column names in the result set are mapped to their
* field names during this procedure as well as any necessary conversions on
* the values applied.
* the values applied. Scalar values are kept in a specfic key 'scalars'.
*
* @param array $data SQL Result Row
* @param array &$cache Cache for column to field result information
* @param array &$id Dql-Alias => ID-Hash
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
*
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias.
*/
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if (isset($this->_rsm->scalarMappings[$key])) {
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
} else if (isset($this->_rsm->fieldMappings[$key])) {
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue;
} else {
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$fieldName = $this->_rsm->metaMappings[$key];
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
switch (true) {
// NOTE: Most of the times it's a field mapping, so keep it first!!!
case (isset($this->_rsm->fieldMappings[$key])):
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
break;
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;
case (isset($this->_rsm->metaMappings[$key])):
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$fieldName = $this->_rsm->metaMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]);
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
break;
default:
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue 2;
}
}
if (isset($cache[$key]['isScalar'])) {
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;
}
@@ -211,13 +246,17 @@ abstract class AbstractHydrator
}
if (isset($cache[$key]['isMetaColumn'])) {
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value !== null) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
if ($cache[$key]['isIdentifier']) {
$nonemptyComponents[$dqlAlias] = true;
}
}
continue;
}
// in an inheritance hierachy the same field could be defined several times.
// in an inheritance hierarchy the same field could be defined several times.
// We overwrite this value so long we dont have a non-null value, that value we keep.
// Per definition it cannot be that a field is defined several times and has several values.
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
@@ -236,6 +275,7 @@ abstract class AbstractHydrator
/**
* Processes a row of the result set.
*
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
* simply converts column names to field names and properly converts the
* values according to their types. The resulting row has the same number
@@ -243,48 +283,95 @@ abstract class AbstractHydrator
*
* @param array $data
* @param array $cache
*
* @return array The processed row.
*/
protected function _gatherScalarRowData(&$data, &$cache)
protected function gatherScalarRowData(&$data, &$cache)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if (isset($this->_rsm->scalarMappings[$key])) {
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
} else if (isset($this->_rsm->fieldMappings[$key])) {
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue;
} else {
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
switch (true) {
// NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!!
case (isset($this->_rsm->scalarMappings[$key])):
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
break;
case (isset($this->_rsm->fieldMappings[$key])):
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
break;
case (isset($this->_rsm->metaMappings[$key])):
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
break;
default:
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue 2;
}
}
$fieldName = $cache[$key]['fieldName'];
if (isset($cache[$key]['isScalar'])) {
$rowData[$fieldName] = $value;
} else if (isset($cache[$key]['isMetaColumn'])) {
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
} else {
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
->convertToPHPValue($value, $this->_platform);
switch (true) {
case (isset($cache[$key]['isScalar'])):
$rowData[$fieldName] = $value;
break;
case (isset($cache[$key]['isMetaColumn'])):
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
break;
default:
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
}
}
return $rowData;
}
/**
* Register entity as managed in UnitOfWork.
*
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
* @param object $entity
* @param array $data
*
* @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow
*/
protected function registerManaged(ClassMetadata $class, $entity, array $data)
{
if ($class->isIdentifierComposite) {
$id = array();
foreach ($class->identifier as $fieldName) {
if (isset($class->associationMappings[$fieldName])) {
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
} else {
$id[$fieldName] = $data[$fieldName];
}
}
} else {
if (isset($class->associationMappings[$class->identifier[0]])) {
$id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
} else {
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
}
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
}

View File

@@ -25,8 +25,9 @@ use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
* The ArrayHydrator produces a nested array "graph" that is often (not always)
* interchangeable with the corresponding object graph for read-only access.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 1.0
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*/
class ArrayHydrator extends AbstractHydrator
{
@@ -38,45 +39,55 @@ class ArrayHydrator extends AbstractHydrator
private $_idTemplate = array();
private $_resultCounter = 0;
/** @override */
protected function _prepare()
/**
* {@inheritdoc}
*/
protected function prepare()
{
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
$this->_identifierMap = array();
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
$this->_idTemplate[$dqlAlias] = '';
}
}
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
$cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result);
$this->hydrateRowData($data, $cache, $result);
}
return $result;
}
/** @override */
protected function _hydrateRow(array $data, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $row, array &$cache, array &$result)
{
// 1) Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars'];
unset($rowData['scalars']);
if (empty($rowData)) {
++$this->_resultCounter;
}
@@ -90,7 +101,12 @@ class ArrayHydrator extends AbstractHydrator
// It's a joined result
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
$path = $parent . '.' . $dqlAlias;
$path = $parent . '.' . $dqlAlias;
// missing parent data, skipping as RIGHT JOIN hydration is not supported.
if ( ! isset($nonemptyComponents[$parent]) ) {
continue;
}
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
@@ -104,39 +120,41 @@ class ArrayHydrator extends AbstractHydrator
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
continue;
}
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
$relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
// Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
}
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
if ( ! $indexExists || ! $indexIsValid) {
$element = $data;
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$baseElement[$relationAlias][$element[$field]] = $element;
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
} else {
$baseElement[$relationAlias][] = $element;
}
end($baseElement[$relationAlias]);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
key($baseElement[$relationAlias]);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]);
}
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
}
} else {
$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = null;
} else if ( ! isset($baseElement[$relationAlias])) {
@@ -152,32 +170,42 @@ class ArrayHydrator extends AbstractHydrator
} else {
// It's a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array($entityKey => null);
} else {
$result[] = null;
}
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
continue;
}
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias];
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
if ($this->_rsm->isMixed) {
$result[] = array($element[$field] => $element);
++$this->_resultCounter;
} else {
$result[$element[$field]] = $element;
}
} else {
if ($this->_rsm->isMixed) {
$result[] = array($element);
++$this->_resultCounter;
} else {
$result[] = $element;
}
if ($this->_rsm->isMixed) {
$element = array($entityKey => $element);
}
end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
$result[$resultKey] = $element;
} else {
$resultKey = $this->_resultCounter;
$result[] = $element;
++$this->_resultCounter;
}
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$resultKey = $index;
/*if ($this->_rsm->isMixed) {
$result[] =& $result[$index];
++$this->_resultCounter;
@@ -189,8 +217,17 @@ class ArrayHydrator extends AbstractHydrator
// Append scalar values to mixed result sets
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
// this only ever happens when no object is fetched (scalar result only)
if (isset($this->_rsm->indexByMap['scalars'])) {
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
} else {
$resultKey = $this->_resultCounter - 1;
}
}
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
$result[$resultKey][$name] = $value;
}
}
}
@@ -208,28 +245,45 @@ class ArrayHydrator extends AbstractHydrator
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index];
return;
} else {
if ($coll) {
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
}
}
}
if ( ! $coll) {
return;
}
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
return;
}
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
return;
}
private function _getClassMetadata($className)
/**
* Retrieve ClassMetadata associated to entity class name.
*
* @param string $className
*
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
private function getClassMetadata($className)
{
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
return $this->_ce[$className];
}
}
}

View File

@@ -8,10 +8,19 @@ class HydrationException extends \Doctrine\ORM\ORMException
{
return new self("The result returned by the query was not unique.");
}
public static function parentObjectOfRelationNotFound($alias, $parentAlias)
{
return new self("The parent object of entity result with alias '$alias' was not found."
. " The parent alias is '$parentAlias'.");
}
public static function emptyDiscriminatorValue($dqlAlias)
{
return new self("The DQL alias '" . $dqlAlias . "' contains an entity ".
"of an inheritance hierachy with an empty discriminator value. This means " .
"that the database contains inconsistent data with an empty " .
"discriminator value in a table row."
);
}
}

View File

@@ -29,7 +29,7 @@ namespace Doctrine\ORM\Internal\Hydration;
class IterableResult implements \Iterator
{
/**
* @var Doctrine\ORM\Internal\Hydration\AbstractHydrator
* @var \Doctrine\ORM\Internal\Hydration\AbstractHydrator
*/
private $_hydrator;
@@ -49,7 +49,7 @@ class IterableResult implements \Iterator
private $_current = null;
/**
* @param Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
* @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
*/
public function __construct($hydrator)
{

View File

@@ -23,14 +23,19 @@ use PDO,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Query,
Doctrine\ORM\Event\LifecycleEventArgs,
Doctrine\ORM\Events,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection;
Doctrine\Common\Collections\Collection,
Doctrine\ORM\Proxy\Proxy;
/**
* The ObjectHydrator constructs an object graph out of an SQL result set.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*
* @internal Highly performance-sensitive code.
*/
class ObjectHydrator extends AbstractHydrator
@@ -53,59 +58,63 @@ class ObjectHydrator extends AbstractHydrator
/** @override */
protected function _prepare()
protected function prepare()
{
$this->_identifierMap =
$this->_resultPointers =
$this->_idTemplate = array();
$this->_resultCounter = 0;
if (!isset($this->_hints['deferEagerLoad'])) {
if ( ! isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
$class = $this->_em->getClassMetadata($className);
$this->_idTemplate[$dqlAlias] = '';
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $class;
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
// Remember which associations are "fetch joined", so that we know where to inject
// collection stubs or proxies and where not.
if (isset($this->_rsm->relationMap[$dqlAlias])) {
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
continue;
}
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
}
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true;
if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
continue;
}
// Mark any non-collection opposite sides as fetched, too.
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
continue;
}
// handle fetch-joined owning side bi-directional one-to-one associations
if ($assoc['inversedBy']) {
$class = $this->_ce[$className];
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
continue;
}
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
if ($sourceClass->subClasses) {
foreach ($sourceClass->subClasses as $sourceSubclassName) {
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
}
}
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
// Mark any non-collection opposite sides as fetched, too.
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
} else {
if ($assoc['inversedBy']) {
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
if ($class->subClasses) {
foreach ($class->subClasses as $targetSubclassName) {
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
}
}
}
}
}
}
$this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
}
}
}
@@ -113,16 +122,17 @@ class ObjectHydrator extends AbstractHydrator
/**
* {@inheritdoc}
*/
protected function _cleanup()
protected function cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup();
parent::cleanup();
$this->_identifierMap =
$this->_initializedCollections =
$this->_existingCollections =
$this->_resultPointers = array();
if ($eagerLoad) {
$this->_em->getUnitOfWork()->triggerEagerLoads();
}
@@ -131,13 +141,13 @@ class ObjectHydrator extends AbstractHydrator
/**
* {@inheritdoc}
*/
protected function _hydrateAll()
protected function hydrateAllData()
{
$result = array();
$cache = array();
$cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result);
$this->hydrateRowData($row, $cache, $result);
}
// Take snapshots from all newly initialized collections
@@ -152,35 +162,40 @@ class ObjectHydrator extends AbstractHydrator
* Initializes a related collection.
*
* @param object $entity The entity to which the collection belongs.
* @param ClassMetadata $class
* @param string $name The name of the field on the entity that holds the collection.
* @param string $parentDqlAlias Alias of the parent fetch joining this collection.
*/
private function _initRelatedCollection($entity, $class, $fieldName)
private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
{
$oid = spl_object_hash($entity);
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
$value = $class->reflFields[$fieldName]->getValue($entity);
$value = $class->reflFields[$fieldName]->getValue($entity);
if ($value === null) {
$value = new ArrayCollection;
}
if ( ! $value instanceof PersistentCollection) {
$value = new PersistentCollection(
$this->_em,
$this->_ce[$relation['targetEntity']],
$value
$this->_em, $this->_ce[$relation['targetEntity']], $value
);
$value->setOwner($entity, $relation);
$class->reflFields[$fieldName]->setValue($entity, $value);
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
! $value->isInitialized()) {
} else if (
isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
! $value->isInitialized()
) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
$value->setInitialized(true);
$value->unwrap()->clear();
$this->_initializedCollections[$oid . $fieldName] = $value;
} else {
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
@@ -192,7 +207,7 @@ class ObjectHydrator extends AbstractHydrator
/**
* Gets an entity instance.
*
*
* @param $data The instance data.
* @param $dqlAlias The DQL alias of the entity's class.
* @return object The entity.
@@ -200,23 +215,46 @@ class ObjectHydrator extends AbstractHydrator
private function _getEntity(array $data, $dqlAlias)
{
$className = $this->_rsm->aliasMap[$dqlAlias];
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
$discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
if ($data[$discrColumn] === "") {
throw HydrationException::emptyDiscriminatorValue($dqlAlias);
}
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
$this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$this->_hints['fetchAlias'] = $dqlAlias;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
{
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_ce[$className];
/* @var $class ClassMetadata */
if ($class->isIdentifierComposite) {
$idHash = '';
foreach ($class->identifier as $fieldName) {
$idHash .= $data[$fieldName] . ' ';
if (isset($class->associationMappings[$fieldName])) {
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
} else {
$idHash .= $data[$fieldName] . ' ';
}
}
return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
} else if (isset($class->associationMappings[$class->identifier[0]])) {
return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
} else {
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
}
@@ -226,7 +264,7 @@ class ObjectHydrator extends AbstractHydrator
* Gets a ClassMetadata instance from the local cache.
* If the instance is not yet in the local cache, it is loaded into the
* local cache.
*
*
* @param string $className The name of the class.
* @return ClassMetadata
*/
@@ -235,42 +273,45 @@ class ObjectHydrator extends AbstractHydrator
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
return $this->_ce[$className];
}
/**
* Hydrates a single row in an SQL result set.
*
*
* @internal
* First, the data of the row is split into chunks where each chunk contains data
* that belongs to a particular component/class. Afterwards, all these chunks
* are processed, one after the other. For each chunk of class data only one of the
* following code paths is executed:
*
*
* Path A: The data chunk belongs to a joined/associated object and the association
* is collection-valued.
* Path B: The data chunk belongs to a joined/associated object and the association
* is single-valued.
* Path C: The data chunk belongs to a root result element/object that appears in the topmost
* level of the hydrated result. A typical example are the objects of the type
* specified by the FROM clause in a DQL query.
*
* specified by the FROM clause in a DQL query.
*
* @param array $data The data of the row to process.
* @param array $cache The cache to use.
* @param array $result The result array to fill.
*/
protected function _hydrateRow(array $data, array &$cache, array &$result)
protected function hydrateRowData(array $row, array &$cache, array &$result)
{
// Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
// Split the row data into chunks of class data.
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars'];
unset($rowData['scalars']);
if (empty($rowData)) {
++$this->_resultCounter;
}
@@ -288,10 +329,16 @@ class ObjectHydrator extends AbstractHydrator
// seen for this parent-child relationship
$path = $parentAlias . '.' . $dqlAlias;
// We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
if (!isset($nonemptyComponents[$parentAlias])) {
// TODO: Add special case code where we hydrate the right join objects into identity map at least
continue;
}
// Get a reference to the parent object to which the joined element belongs.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
$first = reset($this->_resultPointers);
$parentObject = $this->_resultPointers[$parentAlias][key($first)];
$parentObject = $first[key($first)];
} else if (isset($this->_resultPointers[$parentAlias])) {
$parentObject = $this->_resultPointers[$parentAlias];
} else {
@@ -307,13 +354,14 @@ 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;
if (isset($this->_initializedCollections[$collKey])) {
$reflFieldValue = $this->_initializedCollections[$collKey];
} else if ( ! isset($this->_existingCollections[$collKey])) {
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
}
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
@@ -332,8 +380,7 @@ class ObjectHydrator extends AbstractHydrator
$element = $this->_getEntity($data, $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
$indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
$reflFieldValue->hydrateSet($indexValue, $element);
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
} else {
@@ -348,21 +395,24 @@ class ObjectHydrator extends AbstractHydrator
// Update result pointer
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
}
} else if ( ! $reflField->getValue($parentObject)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$coll->setOwner($parentObject, $relation);
$reflField->setValue($parentObject, $coll);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
} 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);
if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {
if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) {
// we only need to take action if this value is null,
// we refresh the entity or its an unitialized proxy.
if (isset($nonemptyComponents[$dqlAlias])) {
$element = $this->_getEntity($data, $dqlAlias);
$reflField->setValue($parentObject, $element);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation['targetEntity']];
if ($relation['isOwningSide']) {
//TODO: Just check hints['fetched'] here?
// If there is an inverse mapping on the target class its bidirectional
@@ -383,6 +433,8 @@ class ObjectHydrator extends AbstractHydrator
}
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
} else {
$this->_uow->setOriginalEntityProperty($oid, $relationField, null);
}
// else leave $reflFieldValue null for single-valued associations
} else {
@@ -393,38 +445,48 @@ class ObjectHydrator extends AbstractHydrator
} else {
// PATH C: Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array($entityKey => null);
} else {
$result[] = null;
}
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
continue;
}
// check for existing result from the iterations before
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if ($this->_rsm->isMixed) {
$element = array($entityKey => $element);
}
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
if ($this->_rsm->isMixed) {
$element = array($key => $element);
$result[] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
++$this->_resultCounter;
} else {
$result[$key] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
}
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateSet($key, $element);
$this->_hints['collection']->hydrateSet($resultKey, $element);
}
$result[$resultKey] = $element;
} else {
if ($this->_rsm->isMixed) {
$element = array(0 => $element);
}
$result[] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateAdd($element);
}
$result[] = $element;
}
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
@@ -432,6 +494,7 @@ class ObjectHydrator extends AbstractHydrator
// Update result pointer
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$this->_resultPointers[$dqlAlias] = $result[$index];
$resultKey = $index;
/*if ($this->_rsm->isMixed) {
$result[] = $result[$index];
++$this->_resultCounter;
@@ -442,8 +505,16 @@ class ObjectHydrator extends AbstractHydrator
// Append scalar values to mixed result sets
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
if (isset($this->_rsm->indexByMap['scalars'])) {
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
} else {
$resultKey = $this->_resultCounter - 1;
}
}
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
$result[$resultKey][$name] = $value;
}
}
}

View File

@@ -26,25 +26,32 @@ use Doctrine\DBAL\Connection;
* The created result is almost the same as a regular SQL result set, except
* that column names are mapped to field names and data type conversions take place.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class ScalarHydrator extends AbstractHydrator
{
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
$cache = array();
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
$result[] = $this->_gatherScalarRowData($data, $cache);
$this->hydrateRowData($data, $cache, $result);
}
return $result;
}
/** @override */
protected function _hydrateRow(array $data, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $data, array &$cache, array &$result)
{
$result[] = $this->_gatherScalarRowData($data, $cache);
$result[] = $this->gatherScalarRowData($data, $cache);
}
}

View File

@@ -17,31 +17,37 @@
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Internal\Hydration;
use \PDO;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use \PDO,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Event\LifecycleEventArgs,
Doctrine\ORM\Events,
Doctrine\ORM\Query;
class SimpleObjectHydrator extends AbstractHydrator
{
const REFRESH_ENTITY = 'doctrine_refresh_entity';
/**
* @var ClassMetadata
*/
private $class;
/**
* @var array
*/
private $declaringClasses = array();
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result);
$this->hydrateRowData($row, $cache, $result);
}
$this->_em->getUnitOfWork()->triggerEagerLoads();
@@ -49,93 +55,129 @@ class SimpleObjectHydrator extends AbstractHydrator
return $result;
}
protected function _prepare()
/**
* {@inheritdoc}
*/
protected function prepare()
{
if (count($this->_rsm->aliasMap) == 1) {
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
}
} else {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
if (count($this->_rsm->aliasMap) !== 1) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.");
}
if ($this->_rsm->scalarMappings) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
}
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
// We only need to add declaring classes if we have inheritance.
if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
return;
}
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
}
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $sqlResult, array &$cache, array &$result)
{
$data = array();
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($sqlResult as $column => $value) {
$entityName = $this->class->name;
$data = array();
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
$cache[$column]['field'] = true;
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
// We need to find the correct entity class name if we have inheritance in resultset
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
if ($sqlResult[$discrColumnName] === '') {
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
}
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
}
foreach ($sqlResult as $column => $value) {
// Hydrate column information if not yet present
if ( ! isset($cache[$column])) {
if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
continue;
}
if (isset($cache[$column]['field'])) {
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
$cache[$column] = $info;
}
// Convert field to a valid PHP value
if (isset($cache[$column]['field'])) {
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
$value = $type->convertToPHPValue($value, $this->_platform);
}
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) {
$data[$cache[$column]['name']] = $value;
}
$entityName = $this->class->name;
} else {
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$field = $this->_rsm->fieldMappings[$column];
$class = $this->declaringClasses[$column];
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$cache[$column]['name'] = $field;
$cache[$column]['class'] = $class;
}
} else if (isset($this->_rsm->relationMap[$column])) {
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
$cache[$column]['name'] = $field;
}
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
}
if (isset($cache[$column]['class'])) {
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
// the second and part is to prevent overwrites in case of multiple
// inheritance classes using the same property name (See AbstractHydrator)
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
$data[$cache[$column]['name']] = $value;
}
}
}
if (isset($this->_hints[self::REFRESH_ENTITY])) {
$this->_hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
$id[$fieldName] = $data[$fieldName];
}
} else {
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
}
$this->_em->getUnitOfWork()->registerManaged($this->_hints[self::REFRESH_ENTITY], $id, $data);
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
$uow = $this->_em->getUnitOfWork();
$entity = $uow->createEntity($entityName, $data, $this->_hints);
$result[] = $entity;
}
}
/**
* Retrieve column information form ResultSetMapping.
*
* @param string $entityName
* @param string $column
*
* @return array
*/
protected function hydrateColumnInfo($entityName, $column)
{
switch (true) {
case (isset($this->_rsm->fieldMappings[$column])):
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;
// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}
return array(
'class' => $class,
'name' => $this->_rsm->fieldMappings[$column],
'field' => true,
);
case (isset($this->_rsm->relationMap[$column])):
$class = isset($this->_rsm->relationMap[$column])
? $this->_rsm->relationMap[$column]
: $this->class;
// If class is not self referencing, ignore
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
return null;
}
// TODO: Decide what to do with associations. It seems original code is incomplete.
// One solution is to load the association, but it might require extra efforts.
return array('name' => $column);
default:
return array(
'name' => $this->_rsm->metaMappings[$column]
);
}
}
}

View File

@@ -19,31 +19,38 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connection,
Doctrine\ORM\NoResultException,
Doctrine\ORM\NonUniqueResultException;
/**
* Hydrator that hydrates a single scalar value from the result set.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class SingleScalarHydrator extends AbstractHydrator
{
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$cache = array();
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
$num = count($result);
$data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
$numRows = count($data);
if ($num == 0) {
throw new \Doctrine\ORM\NoResultException;
} else if ($num > 1 || count($result[key($result)]) > 1) {
throw new \Doctrine\ORM\NonUniqueResultException;
if ($numRows === 0) {
throw new NoResultException();
}
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
if ($numRows > 1 || count($data[key($data)]) > 1) {
throw new NonUniqueResultException();
}
$cache = array();
$result = $this->gatherScalarRowData($data[key($data)], $cache);
return array_shift($result);
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
interface Annotation
{
}

View File

@@ -0,0 +1,167 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Builder;
use Doctrine\ORM\Mapping\ClassMetadata;
class AssociationBuilder
{
/**
* @var ClassMetadataBuilder
*/
protected $builder;
/**
* @var array
*/
protected $mapping;
/**
* @var array
*/
protected $joinColumns;
/**
*
* @var int
*/
protected $type;
/**
* @param ClassMetadataBuilder $builder
* @param array $mapping
*/
public function __construct(ClassMetadataBuilder $builder, array $mapping, $type)
{
$this->builder = $builder;
$this->mapping = $mapping;
$this->type = $type;
}
public function mappedBy($fieldName)
{
$this->mapping['mappedBy'] = $fieldName;
return $this;
}
public function inversedBy($fieldName)
{
$this->mapping['inversedBy'] = $fieldName;
return $this;
}
public function cascadeAll()
{
$this->mapping['cascade'] = array("ALL");
return $this;
}
public function cascadePersist()
{
$this->mapping['cascade'][] = "persist";
return $this;
}
public function cascadeRemove()
{
$this->mapping['cascade'][] = "remove";
return $this;
}
public function cascadeMerge()
{
$this->mapping['cascade'][] = "merge";
return $this;
}
public function cascadeDetach()
{
$this->mapping['cascade'][] = "detach";
return $this;
}
public function cascadeRefresh()
{
$this->mapping['cascade'][] = "refresh";
return $this;
}
public function fetchExtraLazy()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY;
return $this;
}
public function fetchEager()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_EAGER;
return $this;
}
public function fetchLazy()
{
$this->mapping['fetch'] = ClassMetadata::FETCH_LAZY;
return $this;
}
/**
* Add Join Columns
*
* @param string $columnName
* @param string $referencedColumnName
* @param bool $nullable
* @param bool $unique
* @param string $onDelete
* @param string $columnDef
*/
public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{
$this->joinColumns[] = array(
'name' => $columnName,
'referencedColumnName' => $referencedColumnName,
'nullable' => $nullable,
'unique' => $unique,
'onDelete' => $onDelete,
'columnDefinition' => $columnDef,
);
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
if ($this->joinColumns) {
$mapping['joinColumns'] = $this->joinColumns;
}
$cm = $this->builder->getClassMetadata();
if ($this->type == ClassMetadata::MANY_TO_ONE) {
$cm->mapManyToOne($mapping);
} else if ($this->type == ClassMetadata::ONE_TO_ONE) {
$cm->mapOneToOne($mapping);
} else {
throw new \InvalidArgumentException("Type should be a ToOne Assocation here");
}
return $this->builder;
}
}

View File

@@ -0,0 +1,470 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Builder;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Builder Object for ClassMetadata
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.2
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class ClassMetadataBuilder
{
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataInfo
*/
private $cm;
/**
* @param \Doctrine\ORM\Mapping\ClassMetadataInfo $cm
*/
public function __construct(ClassMetadataInfo $cm)
{
$this->cm = $cm;
}
/**
* @return ClassMetadata
*/
public function getClassMetadata()
{
return $this->cm;
}
/**
* Mark the class as mapped superclass.
*
* @return ClassMetadataBuilder
*/
public function setMappedSuperClass()
{
$this->cm->isMappedSuperclass = true;
return $this;
}
/**
* Set custom Repository class name
*
* @param string $repositoryClassName
* @return ClassMetadataBuilder
*/
public function setCustomRepositoryClass($repositoryClassName)
{
$this->cm->setCustomRepositoryClass($repositoryClassName);
return $this;
}
/**
* Mark class read only
*
* @return ClassMetadataBuilder
*/
public function setReadOnly()
{
$this->cm->markReadOnly();
return $this;
}
/**
* Set the table name
*
* @param string $name
* @return ClassMetadataBuilder
*/
public function setTable($name)
{
$this->cm->setPrimaryTable(array('name' => $name));
return $this;
}
/**
* Add Index
*
* @param array $columns
* @param string $name
* @return ClassMetadataBuilder
*/
public function addIndex(array $columns, $name)
{
if (!isset($this->cm->table['indexes'])) {
$this->cm->table['indexes'] = array();
}
$this->cm->table['indexes'][$name] = array('columns' => $columns);
return $this;
}
/**
* Add Unique Constraint
*
* @param array $columns
* @param string $name
* @return ClassMetadataBuilder
*/
public function addUniqueConstraint(array $columns, $name)
{
if ( ! isset($this->cm->table['uniqueConstraints'])) {
$this->cm->table['uniqueConstraints'] = array();
}
$this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns);
return $this;
}
/**
* Add named query
*
* @param string $name
* @param string $dqlQuery
* @return ClassMetadataBuilder
*/
public function addNamedQuery($name, $dqlQuery)
{
$this->cm->addNamedQuery(array(
'name' => $name,
'query' => $dqlQuery,
));
return $this;
}
/**
* Set class as root of a joined table inheritance hierachy.
*
* @return ClassMetadataBuilder
*/
public function setJoinedTableInheritance()
{
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);
return $this;
}
/**
* Set class as root of a single table inheritance hierachy.
*
* @return ClassMetadataBuilder
*/
public function setSingleTableInheritance()
{
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
return $this;
}
/**
* Set the discriminator column details.
*
* @param string $name
* @param string $type
*/
public function setDiscriminatorColumn($name, $type = 'string', $length = 255)
{
$this->cm->setDiscriminatorColumn(array(
'name' => $name,
'type' => $type,
'length' => $length,
));
return $this;
}
/**
* Add a subclass to this inheritance hierachy.
*
* @param string $name
* @param string $class
* @return ClassMetadataBuilder
*/
public function addDiscriminatorMapClass($name, $class)
{
$this->cm->addDiscriminatorMapClass($name, $class);
return $this;
}
/**
* Set deferred explicit change tracking policy.
*
* @return ClassMetadataBuilder
*/
public function setChangeTrackingPolicyDeferredExplicit()
{
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);
return $this;
}
/**
* Set notify change tracking policy.
*
* @return ClassMetadataBuilder
*/
public function setChangeTrackingPolicyNotify()
{
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY);
return $this;
}
/**
* Add lifecycle event
*
* @param string $methodName
* @param string $event
* @return ClassMetadataBuilder
*/
public function addLifecycleEvent($methodName, $event)
{
$this->cm->addLifecycleCallback($methodName, $event);
return $this;
}
/**
* Add Field
*
* @param string $name
* @param string $type
* @param array $mapping
*/
public function addField($name, $type, array $mapping = array())
{
$mapping['fieldName'] = $name;
$mapping['type'] = $type;
$this->cm->mapField($mapping);
return $this;
}
/**
* Create a field builder.
*
* @param string $name
* @param string $type
* @return FieldBuilder
*/
public function createField($name, $type)
{
return new FieldBuilder(
$this,
array(
'fieldName' => $name,
'type' => $type
)
);
}
/**
* Add a simple many to one association, optionally with the inversed by field.
*
* @param string $name
* @param string $targetEntity
* @param string|null $inversedBy
* @return ClassMetadataBuilder
*/
public function addManyToOne($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createManyToOne($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Create a ManyToOne Assocation Builder.
*
* Note: This method does not add the association, you have to call build() on the AssociationBuilder.
*
* @param string $name
* @param string $targetEntity
* @return AssociationBuilder
*/
public function createManyToOne($name, $targetEntity)
{
return new AssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::MANY_TO_ONE
);
}
/**
* Create OneToOne Assocation Builder
*
* @param string $name
* @param string $targetEntity
* @return AssociationBuilder
*/
public function createOneToOne($name, $targetEntity)
{
return new AssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::ONE_TO_ONE
);
}
/**
* Add simple inverse one-to-one assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addInverseOneToOne($name, $targetEntity, $mappedBy)
{
$builder = $this->createOneToOne($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
/**
* Add simple owning one-to-one assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $inversedBy
* @return ClassMetadataBuilder
*/
public function addOwningOneToOne($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createOneToOne($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Create ManyToMany Assocation Builder
*
* @param string $name
* @param string $targetEntity
* @return ManyToManyAssociationBuilder
*/
public function createManyToMany($name, $targetEntity)
{
return new ManyToManyAssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::MANY_TO_MANY
);
}
/**
* Add a simple owning many to many assocation.
*
* @param string $name
* @param string $targetEntity
* @param string|null $inversedBy
* @return ClassMetadataBuilder
*/
public function addOwningManyToMany($name, $targetEntity, $inversedBy = null)
{
$builder = $this->createManyToMany($name, $targetEntity);
if ($inversedBy) {
$builder->inversedBy($inversedBy);
}
return $builder->build();
}
/**
* Add a simple inverse many to many assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addInverseManyToMany($name, $targetEntity, $mappedBy)
{
$builder = $this->createManyToMany($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
/**
* Create a one to many assocation builder
*
* @param string $name
* @param string $targetEntity
* @return OneToManyAssociationBuilder
*/
public function createOneToMany($name, $targetEntity)
{
return new OneToManyAssociationBuilder(
$this,
array(
'fieldName' => $name,
'targetEntity' => $targetEntity
),
ClassMetadata::ONE_TO_MANY
);
}
/**
* Add simple OneToMany assocation.
*
* @param string $name
* @param string $targetEntity
* @param string $mappedBy
* @return ClassMetadataBuilder
*/
public function addOneToMany($name, $targetEntity, $mappedBy)
{
$builder = $this->createOneToMany($name, $targetEntity);
$builder->mappedBy($mappedBy);
return $builder->build();
}
}

View File

@@ -0,0 +1,223 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Builder;
/**
* Field Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.2
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class FieldBuilder
{
/**
* @var ClassMetadataBuilder
*/
private $builder;
/**
* @var array
*/
private $mapping;
/**
* @var bool
*/
private $version;
/**
* @var string
*/
private $generatedValue;
/**
* @var array
*/
private $sequenceDef;
/**
*
* @param ClassMetadataBuilder $builder
* @param array $mapping
*/
public function __construct(ClassMetadataBuilder $builder, array $mapping)
{
$this->builder = $builder;
$this->mapping = $mapping;
}
/**
* Set length.
*
* @param int $length
* @return FieldBuilder
*/
public function length($length)
{
$this->mapping['length'] = $length;
return $this;
}
/**
* Set nullable
*
* @param bool
* @return FieldBuilder
*/
public function nullable($flag = true)
{
$this->mapping['nullable'] = (bool)$flag;
return $this;
}
/**
* Set Unique
*
* @param bool
* @return FieldBuilder
*/
public function unique($flag = true)
{
$this->mapping['unique'] = (bool)$flag;
return $this;
}
/**
* Set column name
*
* @param string $name
* @return FieldBuilder
*/
public function columnName($name)
{
$this->mapping['columnName'] = $name;
return $this;
}
/**
* Set Precision
*
* @param int $p
* @return FieldBuilder
*/
public function precision($p)
{
$this->mapping['precision'] = $p;
return $this;
}
/**
* Set scale.
*
* @param int $s
* @return FieldBuilder
*/
public function scale($s)
{
$this->mapping['scale'] = $s;
return $this;
}
/**
* Set field as primary key.
*
* @return FieldBuilder
*/
public function isPrimaryKey()
{
$this->mapping['id'] = true;
return $this;
}
/**
* @param int $strategy
* @return FieldBuilder
*/
public function generatedValue($strategy = 'AUTO')
{
$this->generatedValue = $strategy;
return $this;
}
/**
* Set field versioned
*
* @return FieldBuilder
*/
public function isVersionField()
{
$this->version = true;
return $this;
}
/**
* Set Sequence Generator
*
* @param string $sequenceName
* @param int $allocationSize
* @param int $initialValue
* @return FieldBuilder
*/
public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1)
{
$this->sequenceDef = array(
'sequenceName' => $sequenceName,
'allocationSize' => $allocationSize,
'initialValue' => $initialValue,
);
return $this;
}
/**
* Set column definition.
*
* @param string $def
* @return FieldBuilder
*/
public function columnDefinition($def)
{
$this->mapping['columnDefinition'] = $def;
return $this;
}
/**
* Finalize this field and attach it to the ClassMetadata.
*
* Without this call a FieldBuilder has no effect on the ClassMetadata.
*
* @return ClassMetadataBuilder
*/
public function build()
{
$cm = $this->builder->getClassMetadata();
if ($this->generatedValue) {
$cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue));
}
if ($this->version) {
$cm->setVersionMapping($this->mapping);
}
$cm->mapField($this->mapping);
if ($this->sequenceDef) {
$cm->setSequenceGeneratorDefinition($this->sequenceDef);
}
return $this->builder;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Builder;
/**
* ManyToMany Association Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
{
private $joinTableName;
private $inverseJoinColumns = array();
public function setJoinTable($name)
{
$this->joinTableName = $name;
return $this;
}
/**
* Add Inverse Join Columns
*
* @param string $columnName
* @param string $referencedColumnName
* @param bool $nullable
* @param bool $unique
* @param string $onDelete
* @param string $columnDef
*/
public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
{
$this->inverseJoinColumns[] = array(
'name' => $columnName,
'referencedColumnName' => $referencedColumnName,
'nullable' => $nullable,
'unique' => $unique,
'onDelete' => $onDelete,
'columnDefinition' => $columnDef,
);
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
$mapping['joinTable'] = array();
if ($this->joinColumns) {
$mapping['joinTable']['joinColumns'] = $this->joinColumns;
}
if ($this->inverseJoinColumns) {
$mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns;
}
if ($this->joinTableName) {
$mapping['joinTable']['name'] = $this->joinTableName;
}
$cm = $this->builder->getClassMetadata();
$cm->mapManyToMany($mapping);
return $this->builder;
}
}

View File

@@ -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\ORM\Mapping\Builder;
/**
* OneToMany Association Builder
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class OneToManyAssociationBuilder extends AssociationBuilder
{
/**
* @param array $fieldNames
* @return OneToManyAssociationBuilder
*/
public function setOrderBy(array $fieldNames)
{
$this->mapping['orderBy'] = $fieldNames;
return $this;
}
public function setIndexBy($fieldName)
{
$this->mapping['indexBy'] = $fieldName;
return $this;
}
/**
* @return ClassMetadataBuilder
*/
public function build()
{
$mapping = $this->mapping;
if ($this->joinColumns) {
$mapping['joinColumns'] = $this->joinColumns;
}
$cm = $this->builder->getClassMetadata();
$cm->mapOneToMany($mapping);
return $this->builder;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class ChangeTrackingPolicy implements Annotation
{
/** @var string */
public $value;
}

View File

@@ -24,7 +24,7 @@ use ReflectionClass, ReflectionProperty;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
* of an entity and it's associations.
*
*
* Once populated, ClassMetadata instances are usually cached in a serialized form.
*
* <b>IMPORTANT NOTE:</b>
@@ -41,306 +41,4 @@ use ReflectionClass, ReflectionProperty;
*/
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;
} else {
$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) {
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
foreach ($this->associationMappings as $field => $mapping) {
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $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;
}
}

View File

@@ -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;
/**
@@ -43,14 +45,14 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* @var EntityManager
*/
private $em;
/**
* @var AbstractPlatform
*/
private $targetPlatform;
/**
* @var Driver\Driver
* @var \Doctrine\ORM\Mapping\Driver\Driver
*/
private $driver;
@@ -73,7 +75,12 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* @var bool
*/
private $initialized = false;
/**
* @var ReflectionService
*/
private $reflectionService;
/**
* @param EntityManager $$em
*/
@@ -85,7 +92,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Sets the cache driver used by the factory to cache ClassMetadata instances.
*
* @param Doctrine\Common\Cache\Cache $cacheDriver
* @param \Doctrine\Common\Cache\Cache $cacheDriver
*/
public function setCacheDriver($cacheDriver)
{
@@ -95,22 +102,22 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Gets the cache driver used by the factory to cache ClassMetadata instances.
*
* @return Doctrine\Common\Cache\Cache
* @return \Doctrine\Common\Cache\Cache
*/
public function getCacheDriver()
{
return $this->cacheDriver;
}
public function getLoadedMetadata()
{
return $this->loadedMetadata;
}
/**
* Forces the factory to load the metadata of all classes known to the underlying
* mapping driver.
*
*
* @return array The ClassMetadata instances of all mapped classes.
*/
public function getAllMetadata()
@@ -143,7 +150,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* Gets the class metadata descriptor for a class.
*
* @param string $className The name of the class.
* @return Doctrine\ORM\Mapping\ClassMetadata
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
public function getMetadataFor($className)
{
@@ -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) {
@@ -188,7 +196,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Checks whether the factory has the metadata for a class loaded already.
*
*
* @param string $className
* @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
*/
@@ -199,7 +207,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Sets the metadata descriptor for a specific class.
*
*
* NOTE: This is only useful in very special cases, like when generating proxy classes.
*
* @param string $className
@@ -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);
@@ -274,6 +283,9 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->setDiscriminatorMap($parent->discriminatorMap);
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
if ($parent->isMappedSuperclass) {
$class->setCustomRepositoryClass($parent->customRepositoryClassName);
}
}
// Invoke driver
@@ -306,36 +318,24 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->setPrimaryTable($parent->table);
}
if ($parent && $parent->containsForeignIdentifier) {
$class->containsForeignIdentifier = true;
}
if ($parent && !empty ($parent->namedQueries)) {
$this->addInheritedNamedQueries($class, $parent);
}
$class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->wakeupReflection($class, $this->getReflectionService());
// Verify & complete identifier mapping
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
throw MappingException::identifierRequired($className);
}
$this->validateRuntimeMetadata($class, $parent);
// verify inheritance
if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (!$parent) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
$this->loadedMetadata[$className] = $class;
$parent = $class;
@@ -351,11 +351,47 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
return $loaded;
}
/**
* Validate runtime metadata is correctly defined.
*
* @param ClassMetadata $class
* @param ClassMetadata $parent
*/
protected function validateRuntimeMetadata($class, $parent)
{
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) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) {
// enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur.
throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName);
}
} else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
// second condition is necessary for mapped superclasses in the middle of an inheritance hierachy
throw MappingException::noInheritanceOnMappedSuperClass($class->name);
}
}
/**
* Creates a new ClassMetadata instance for the given class name.
*
* @param string $className
* @return Doctrine\ORM\Mapping\ClassMetadata
* @return \Doctrine\ORM\Mapping\ClassMetadata
*/
protected function newClassMetadataInstance($className)
{
@@ -365,8 +401,8 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Adds inherited fields to the subclass mapping.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
{
@@ -387,8 +423,8 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
/**
* Adds inherited association mappings to the subclass mapping.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{
@@ -411,11 +447,30 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
}
}
/**
* Adds inherited named queries to the subclass mapping.
*
* @since 2.2
* @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->namedQueries as $name => $query) {
if (!isset ($subClass->namedQueries[$name])) {
$subClass->addNamedQuery(array(
'name' => $query['name'],
'query' => $query['query']
));
}
}
}
/**
* Completes the ID generator mapping. If "auto" is specified we choose the generator
* most appropriate for the targeted database platform.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function completeIdGeneratorMapping(ClassMetadataInfo $class)
{
@@ -437,7 +492,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
// Not pretty but necessary and the simplest solution that currently works.
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
$class->getTableName() . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
null;
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
break;
@@ -467,4 +522,72 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
throw new ORMException("Unknown generator type: " . $class->generatorType);
}
}
/**
* Check if this class is mapped by this EntityManager + ClassMetadata configuration
*
* @param $class
* @return bool
*/
public function isTransient($class)
{
if ( ! $this->initialized) {
$this->initialize();
}
// Check for namespace alias
if (strpos($class, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $class);
$class = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
}
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);
}
}

View File

@@ -19,8 +19,9 @@
namespace Doctrine\ORM\Mapping;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
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
@@ -119,7 +120,7 @@ class ClassMetadataInfo implements ClassMetadata
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
@@ -136,10 +137,6 @@ class ClassMetadataInfo implements ClassMetadata
* Identifies a many-to-one association.
*/
const MANY_TO_ONE = 2;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Identifies a one-to-many association.
*/
@@ -148,6 +145,10 @@ class ClassMetadataInfo implements ClassMetadata
* Identifies a many-to-many association.
*/
const MANY_TO_MANY = 8;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Combined bitmask for to-many (collection-valued) associations.
*/
@@ -206,7 +207,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
*
* @var array
*/
public $namedQueries = array();
@@ -318,7 +319,7 @@ class ClassMetadataInfo implements ClassMetadata
public $discriminatorMap = array();
/**
* READ-ONLY: The definition of the descriminator column used in JOINED and SINGLE_TABLE
* READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
* inheritance mappings.
*
* @var array
@@ -361,7 +362,7 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>mappedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the owning side.
* This key must be specified on the inverse side of a bidirectional association.
*
*
* - <b>inversedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the inverse side.
* This key must be specified on the owning side of a bidirectional association.
@@ -388,7 +389,7 @@ class ClassMetadataInfo implements ClassMetadata
* Specification of a field on target-entity that is used to index the collection by.
* This field HAS to be either the primary key or a unique column. Otherwise the collection
* does not contain all the entities that are actually related.
*
*
* A join table definition has the following structure:
* <pre>
* array(
@@ -430,7 +431,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The definition of the sequence generator of this class. Only used for the
* SEQUENCE generation strategy.
*
*
* The definition has the following structure:
* <code>
* array(
@@ -494,6 +495,20 @@ class ClassMetadataInfo implements ClassMetadata
*/
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 implements ClassMetadata
$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 implements ClassMetadata
*/
public function getReflectionClass()
{
if ( ! $this->reflClass) {
$this->reflClass = new ReflectionClass($this->name);
}
return $this->reflClass;
}
@@ -685,7 +999,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($this->namedQueries[$queryName])) {
throw MappingException::queryNotFound($this->name, $queryName);
}
return $this->namedQueries[$queryName];
return $this->namedQueries[$queryName]['dql'];
}
/**
@@ -746,6 +1060,14 @@ class ClassMetadataInfo implements ClassMetadata
$this->isIdentifierComposite = true;
}
}
if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
if (isset($mapping['id']) && $mapping['id'] === true) {
throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
}
$mapping['requireSQLConversion'] = true;
}
}
/**
@@ -774,9 +1096,20 @@ class ClassMetadataInfo implements ClassMetadata
// If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity.
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
&& strlen($this->namespace) > 0) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
if (isset($mapping['targetEntity'])) {
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
isset($mapping['orphanRemoval']) &&
$mapping['orphanRemoval'] == true) {
throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
}
// Complete id mapping
@@ -809,7 +1142,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
// Mandatory and optional attributes for either side
if ( ! $mapping['mappedBy']) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
@@ -825,7 +1158,7 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
}
// Fetch mode. Default fetch mode to LAZY, if not set.
if ( ! isset($mapping['fetch'])) {
$mapping['fetch'] = self::FETCH_LAZY;
@@ -833,22 +1166,18 @@ class ClassMetadataInfo implements ClassMetadata
// Cascades
$cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
if (in_array('all', $cascades)) {
$cascades = array(
'remove',
'persist',
'refresh',
'merge',
'detach'
);
$cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
}
$mapping['cascade'] = $cascades;
$mapping['isCascadeRemove'] = in_array('remove', $cascades);
$mapping['isCascadePersist'] = in_array('persist', $cascades);
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
return $mapping;
}
@@ -862,11 +1191,11 @@ class ClassMetadataInfo implements ClassMetadata
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$mapping['isOwningSide'] = true;
}
if ($mapping['isOwningSide']) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
@@ -878,9 +1207,11 @@ class ClassMetadataInfo implements ClassMetadata
$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'];
}
@@ -908,9 +1239,8 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
@@ -934,17 +1264,16 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($mapping['mappedBy'])) {
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
}
//TODO: if orphanRemoval, cascade=remove is implicit!
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
(bool) $mapping['orphanRemoval'] : false;
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) {
throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
}
}
return $mapping;
}
@@ -962,7 +1291,7 @@ class ClassMetadataInfo implements ClassMetadata
} else {
$targetShortName = strtolower($mapping['targetEntity']);
}
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']['name'])) {
$mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
@@ -1009,6 +1338,8 @@ class ClassMetadataInfo implements ClassMetadata
}
}
$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']));
@@ -1113,23 +1444,23 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getIdentifierColumnNames()
{
if ($this->isIdentifierComposite) {
$columnNames = array();
foreach ($this->identifier as $idField) {
if (isset($this->associationMappings[$idField])) {
// no composite pk as fk entity assumption:
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
} else {
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
}
$columnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
continue;
}
return $columnNames;
} else if(isset($this->fieldMappings[$this->identifier[0]])) {
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
} else {
// no composite pk as fk entity assumption:
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']);
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
$columnNames = array_merge($columnNames, $assocColumnNames);
}
return $columnNames;
}
/**
@@ -1236,7 +1567,7 @@ class ClassMetadataInfo implements ClassMetadata
* Gets the type of a field.
*
* @param string $fieldName
* @return Doctrine\DBAL\Types\Type
* @return \Doctrine\DBAL\Types\Type
*/
public function getTypeOfField($fieldName)
{
@@ -1247,7 +1578,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Gets the type of a column.
*
* @return Doctrine\DBAL\Types\Type
* @return \Doctrine\DBAL\Types\Type
*/
public function getTypeOfColumn($columnName)
{
@@ -1272,7 +1603,7 @@ class ClassMetadataInfo implements ClassMetadata
public function getTemporaryIdTableName()
{
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema
return str_replace('.', '_', $this->table['name'] . '_id_tmp');
return str_replace('.', '_', $this->getTableName() . '_id_tmp');
}
/**
@@ -1365,15 +1696,17 @@ class ClassMetadataInfo implements ClassMetadata
{
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'];
}
}
if (isset($table['indexes'])) {
$this->table['indexes'] = $table['indexes'];
}
if (isset($table['uniqueConstraints'])) {
$this->table['uniqueConstraints'] = $table['uniqueConstraints'];
}
@@ -1448,8 +1781,15 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($this->namedQueries[$queryMapping['name']])) {
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
}
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
$this->namedQueries[$queryMapping['name']] = $query;
$name = $queryMapping['name'];
$query = $queryMapping['query'];
$dql = str_replace('__CLASS__', $this->name, $query);
$this->namedQueries[$name] = array(
'name' => $name,
'query' => $query,
'dql' => $dql
);
}
/**
@@ -1504,14 +1844,16 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Stores the association mapping.
*
* @param AssociationMapping $assocMapping
* @param array $assocMapping
*/
protected function _storeAssociationMapping(array $assocMapping)
{
$sourceFieldName = $assocMapping['fieldName'];
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
}
$this->associationMappings[$sourceFieldName] = $assocMapping;
}
@@ -1522,6 +1864,10 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function setCustomRepositoryClass($repositoryClassName)
{
if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
&& strlen($this->namespace) > 0) {
$repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
}
$this->customRepositoryClassName = $repositoryClassName;
}
@@ -1564,9 +1910,6 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Adds a lifecycle callback for entities of this class.
*
* Note: If the same callback is registered more than once, the old one
* will be overridden.
*
* @param string $callback
* @param string $event
*/
@@ -1625,20 +1968,33 @@ class ClassMetadataInfo implements ClassMetadata
public function setDiscriminatorMap(array $map)
{
foreach ($map as $value => $className) {
if (strpos($className, '\\') === false && strlen($this->namespace)) {
$className = $this->namespace . '\\' . $className;
$this->addDiscriminatorMapClass($value, $className);
}
}
/**
* Add one entry of the discriminator map with a new class and corresponding name.
*
* @param string $name
* @param string $className
*/
public function addDiscriminatorMapClass($name, $className)
{
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$name] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $name;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$value] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name)) {
$this->subClasses[] = $className;
}
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
$this->subClasses[] = $className;
}
}
}
@@ -1708,7 +2064,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Return the single association join column (if any).
*
*
* @param string $fieldName
* @return string
*/
@@ -1750,7 +2106,7 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($this->associationMappings AS $assocName => $mapping) {
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
return $assocName;
}
}
@@ -1800,7 +2156,7 @@ class ClassMetadataInfo implements ClassMetadata
$this->versionField = $mapping['fieldName'];
if ( ! isset($mapping['default'])) {
if ($mapping['type'] == 'integer') {
if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
$mapping['default'] = 1;
} else if ($mapping['type'] == 'datetime') {
$mapping['default'] = 'CURRENT_TIMESTAMP';
@@ -1840,48 +2196,49 @@ class ClassMetadataInfo implements ClassMetadata
{
$this->isReadOnly = true;
}
/**
* A numerically indexed list of field names of this persistent class.
*
*
* This array includes identifier fields if present on this class.
*
*
* @return array
*/
public function getFieldNames()
{
return array_keys($this->fieldMappings);
}
/**
* A numerically indexed list of association names of this persistent class.
*
*
* This array includes identifier associations if present on this class.
*
*
* @return array
*/
public function getAssociationNames()
{
return array_keys($this->associationMappings);
}
/**
* Returns the target class name of the given association.
*
*
* @param string $assocName
* @return string
*/
public function getAssociationTargetClass($assocName)
{
if (!isset($this->associationMappings[$assocName])) {
if ( ! isset($this->associationMappings[$assocName])) {
throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
}
return $this->associationMappings[$assocName]['targetEntity'];
}
/**
* Get fully-qualified class name of this persistent class.
*
*
* @return string
*/
public function getName()
@@ -1889,33 +2246,67 @@ class ClassMetadataInfo implements ClassMetadata
return $this->name;
}
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* Gets the (possibly quoted) column name of a mapped field for safe use
* in an SQL statement.
*
*
* @param string $field
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedColumnName($field, $platform)
{
return isset($this->fieldMappings[$field]['quoted']) ?
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
$this->fieldMappings[$field]['columnName'];
return isset($this->fieldMappings[$field]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
: $this->fieldMappings[$field]['columnName'];
}
/**
* Gets the (possibly quoted) primary table name of this class for safe use
* in an SQL statement.
*
*
* @param AbstractPlatform $platform
* @return string
*/
public function getQuotedTableName($platform)
{
return isset($this->table['quoted']) ?
$platform->quoteIdentifier($this->table['name']) :
$this->table['name'];
return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
}
/**
@@ -1926,8 +2317,24 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getQuotedJoinTableName(array $assoc, $platform)
{
return isset($assoc['joinTable']['quoted'])
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
}
/**
* @param string $fieldName
* @return bool
*/
public function isAssociationInverseSide($fieldName)
{
return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
}
/**
* @param string $fieldName
* @return string
*/
public function getAssociationMappedByTargetField($fieldName)
{
return $this->associationMappings[$fieldName]['mappedBy'];
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Column implements Annotation
{
/** @var string */
public $name;
/** @var mixed */
public $type = 'string';
/** @var integer */
public $length;
/** @var integer */
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
/** @var integer */
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
/** @var boolean */
public $unique = false;
/** @var boolean */
public $nullable = false;
/** @var array */
public $options = array();
/** @var string */
public $columnDefinition;
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class DiscriminatorColumn implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $type;
/** @var integer */
public $length;
/** @var mixed */
public $fieldName; // field name used in non-object hydration (array/scalar)
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class DiscriminatorMap implements Annotation
{
/** @var array<string> */
public $value;
}

View File

@@ -25,7 +25,7 @@ use Doctrine\ORM\Mapping\MappingException;
/**
* Base driver for file-based metadata drivers.
*
*
* A file driver operates in a mode where it loads the mapping files of individual
* classes on demand. This requires the user to adhere to the convention of 1 mapping
* file per class and the file names of the mapping files must correspond to the full
@@ -56,16 +56,16 @@ abstract class AbstractFileDriver implements Driver
*/
protected $_fileExtension;
/**
* Initializes a new FileDriver that looks in the given path(s) for mapping
* documents and operates in the specified operating mode.
*
* @param string|array $paths One or multiple paths where mapping documents can be found.
*/
public function __construct($paths)
{
/**
* Initializes a new FileDriver that looks in the given path(s) for mapping
* documents and operates in the specified operating mode.
*
* @param string|array $paths One or multiple paths where mapping documents can be found.
*/
public function __construct($paths)
{
$this->addPaths((array) $paths);
}
}
/**
* Append lookup paths to metadata driver.
@@ -117,7 +117,10 @@ abstract class AbstractFileDriver implements Driver
public function getElement($className)
{
$result = $this->_loadMappingFile($this->_findMappingFile($className));
if(!isset($result[$className])){
throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->_fileExtension);
}
return $result[$className];
}
@@ -145,7 +148,7 @@ abstract class AbstractFileDriver implements Driver
/**
* Gets the names of all mapped classes known to this driver.
*
*
* @return array The names of all mapped classes known to this driver.
*/
public function getAllClassNames()
@@ -157,23 +160,23 @@ abstract class AbstractFileDriver implements Driver
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) {
continue;
}
// NOTE: All files found here means classes are not transient!
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
return $classes;
}
@@ -188,7 +191,7 @@ abstract class AbstractFileDriver implements Driver
protected function _findMappingFile($className)
{
$fileName = str_replace('\\', '.', $className) . $this->_fileExtension;
// Check whether file exists
foreach ((array) $this->_paths as $path) {
if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) {
@@ -202,7 +205,7 @@ abstract class AbstractFileDriver implements Driver
/**
* Loads a mapping file with the given name and returns a map
* from class/entity names to their corresponding elements.
*
*
* @param string $file The mapping file to load.
* @return array
*/

View File

@@ -97,6 +97,16 @@ class AnnotationDriver implements Driver
return $this->_paths;
}
/**
* Retrieve the current annotation reader
*
* @return AnnotationReader
*/
public function getReader()
{
return $this->_reader;
}
/**
* Get the file extension used to look for mapping files under
*
@@ -124,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;
}
@@ -137,12 +151,15 @@ class AnnotationDriver implements Driver
// Evaluate Entity annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
if ($entityAnnot->repositoryClass !== null) {
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
}
if ($entityAnnot->readOnly) {
$metadata->markReadOnly();
}
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
@@ -158,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;
}
}
}
@@ -179,7 +204,14 @@ class AnnotationDriver implements Driver
if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
$namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
if (!is_array($namedQueriesAnnot->value)) {
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
}
foreach ($namedQueriesAnnot->value as $namedQuery) {
if (!($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) {
throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
}
$metadata->addNamedQuery(array(
'name' => $namedQuery->name,
'query' => $namedQuery->query
@@ -243,7 +275,6 @@ class AnnotationDriver implements Driver
'unique' => $joinColumnAnnot->unique,
'nullable' => $joinColumnAnnot->nullable,
'onDelete' => $joinColumnAnnot->onDelete,
'onUpdate' => $joinColumnAnnot->onUpdate,
'columnDefinition' => $joinColumnAnnot->columnDefinition,
);
} else if ($joinColumnsAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) {
@@ -254,7 +285,6 @@ class AnnotationDriver implements Driver
'unique' => $joinColumn->unique,
'nullable' => $joinColumn->nullable,
'onDelete' => $joinColumn->onDelete,
'onUpdate' => $joinColumn->onUpdate,
'columnDefinition' => $joinColumn->columnDefinition,
);
}
@@ -320,7 +350,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
$mapping['cascade'] = $oneToOneAnnot->cascade;
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
$metadata->mapOneToOne($mapping);
} else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
@@ -328,7 +358,7 @@ class AnnotationDriver implements Driver
$mapping['cascade'] = $oneToManyAnnot->cascade;
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@@ -344,7 +374,7 @@ class AnnotationDriver implements Driver
$mapping['cascade'] = $manyToOneAnnot->cascade;
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
$metadata->mapManyToOne($mapping);
} else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
$joinTable = array();
@@ -362,7 +392,6 @@ class AnnotationDriver implements Driver
'unique' => $joinColumn->unique,
'nullable' => $joinColumn->nullable,
'onDelete' => $joinColumn->onDelete,
'onUpdate' => $joinColumn->onUpdate,
'columnDefinition' => $joinColumn->columnDefinition,
);
}
@@ -374,7 +403,6 @@ class AnnotationDriver implements Driver
'unique' => $joinColumn->unique,
'nullable' => $joinColumn->nullable,
'onDelete' => $joinColumn->onDelete,
'onUpdate' => $joinColumn->onUpdate,
'columnDefinition' => $joinColumn->columnDefinition,
);
}
@@ -386,7 +414,8 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
$mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@@ -403,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;
}
@@ -437,6 +465,10 @@ class AnnotationDriver implements Driver
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush);
}
}
}
}
@@ -455,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;
@@ -499,15 +530,15 @@ class AnnotationDriver implements Driver
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
),
'/^.+\\' . $this->_fileExtension . '$/i',
'/^.+' . str_replace('.', '\.', $this->_fileExtension) . '$/i',
\RecursiveRegexIterator::GET_MATCH
);
foreach ($iterator as $file) {
$sourceFile = realpath($file[0]);
require_once $sourceFile;
$includedFiles[] = $sourceFile;
}
}
@@ -527,6 +558,22 @@ class AnnotationDriver implements Driver
return $classes;
}
/**
* Attempts to resolve the fetch mode.
*
* @param string $className The class name
* @param string $fetchMode The fetch mode
* @return integer The fetch mode as defined in ClassMetadata
* @throws MappingException If the fetch mode is not valid
*/
private function getFetchMode($className, $fetchMode)
{
if(!defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
throw MappingException::invalidFetchMode($className, $fetchMode);
}
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
}
/**
* Factory method for the Annotation Driver
*

View File

@@ -76,7 +76,7 @@ class DatabaseDriver implements Driver
/**
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
*
* @param AnnotationReader $reader The AnnotationReader to use.
*/
public function __construct(AbstractSchemaManager $schemaManager)
@@ -111,7 +111,7 @@ class DatabaseDriver implements Driver
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
@@ -129,7 +129,14 @@ class DatabaseDriver implements Driver
foreach ($foreignKeys AS $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
@@ -145,7 +152,7 @@ class DatabaseDriver implements Driver
}
}
}
/**
* {@inheritdoc}
*/
@@ -169,7 +176,7 @@ class DatabaseDriver implements Driver
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
} else {
@@ -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);
}
}
}

View File

@@ -17,190 +17,38 @@
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
use Doctrine\Common\Annotations\Annotation;
/* Annotations */
/** @Annotation */
final class Entity extends Annotation {
public $repositoryClass;
public $readOnly = false;
}
/** @Annotation */
final class MappedSuperclass extends Annotation {}
/** @Annotation */
final class InheritanceType extends Annotation {}
/** @Annotation */
final class DiscriminatorColumn extends Annotation {
public $name;
public $fieldName; // field name used in non-object hydration (array/scalar)
public $type;
public $length;
}
/** @Annotation */
final class DiscriminatorMap extends Annotation {}
/** @Annotation */
final class Id extends Annotation {}
/** @Annotation */
final class GeneratedValue extends Annotation {
public $strategy = 'AUTO';
}
/** @Annotation */
final class Version extends Annotation {}
/** @Annotation */
final class JoinColumn extends Annotation {
public $name;
public $fieldName; // field name used in non-object hydration (array/scalar)
public $referencedColumnName = 'id';
public $unique = false;
public $nullable = true;
public $onDelete;
public $onUpdate;
public $columnDefinition;
}
/** @Annotation */
final class JoinColumns extends Annotation {}
/** @Annotation */
final class Column extends Annotation {
public $type = 'string';
public $length;
// The precision for a decimal (exact numeric) column (Applies only for decimal column)
public $precision = 0;
// The scale for a decimal (exact numeric) column (Applies only for decimal column)
public $scale = 0;
public $unique = false;
public $nullable = false;
public $name;
public $options = array();
public $columnDefinition;
}
/** @Annotation */
final class OneToOne extends Annotation {
public $targetEntity;
public $mappedBy;
public $inversedBy;
public $cascade;
public $fetch = 'LAZY';
public $orphanRemoval = false;
}
/** @Annotation */
final class OneToMany extends Annotation {
public $mappedBy;
public $targetEntity;
public $cascade;
public $fetch = 'LAZY';
public $orphanRemoval = false;
public $indexBy;
}
/** @Annotation */
final class ManyToOne extends Annotation {
public $targetEntity;
public $cascade;
public $fetch = 'LAZY';
public $inversedBy;
}
/** @Annotation */
final class ManyToMany extends Annotation {
public $targetEntity;
public $mappedBy;
public $inversedBy;
public $cascade;
public $fetch = 'LAZY';
public $indexBy;
}
/** @Annotation */
final class ElementCollection extends Annotation {
public $tableName;
}
/** @Annotation */
final class Table extends Annotation {
public $name;
public $schema;
public $indexes;
public $uniqueConstraints;
}
/** @Annotation */
final class UniqueConstraint extends Annotation {
public $name;
public $columns;
}
/** @Annotation */
final class Index extends Annotation {
public $name;
public $columns;
}
/** @Annotation */
final class JoinTable extends Annotation {
public $name;
public $schema;
public $joinColumns = array();
public $inverseJoinColumns = array();
}
/** @Annotation */
final class SequenceGenerator extends Annotation {
public $sequenceName;
public $allocationSize = 1;
public $initialValue = 1;
}
/** @Annotation */
final class ChangeTrackingPolicy extends Annotation {}
/** @Annotation */
final class OrderBy extends Annotation {}
/** @Annotation */
final class NamedQueries extends Annotation {}
/** @Annotation */
final class NamedQuery extends Annotation {
public $name;
public $query;
}
/* Annotations for lifecycle callbacks */
/** @Annotation */
final class HasLifecycleCallbacks extends Annotation {}
/** @Annotation */
final class PrePersist extends Annotation {}
/** @Annotation */
final class PostPersist extends Annotation {}
/** @Annotation */
final class PreUpdate extends Annotation {}
/** @Annotation */
final class PostUpdate extends Annotation {}
/** @Annotation */
final class PreRemove extends Annotation {}
/** @Annotation */
final class PostRemove extends Annotation {}
/** @Annotation */
final class PostLoad extends Annotation {}
require_once __DIR__.'/../Annotation.php';
require_once __DIR__.'/../Entity.php';
require_once __DIR__.'/../MappedSuperclass.php';
require_once __DIR__.'/../InheritanceType.php';
require_once __DIR__.'/../DiscriminatorColumn.php';
require_once __DIR__.'/../DiscriminatorMap.php';
require_once __DIR__.'/../Id.php';
require_once __DIR__.'/../GeneratedValue.php';
require_once __DIR__.'/../Version.php';
require_once __DIR__.'/../JoinColumn.php';
require_once __DIR__.'/../JoinColumns.php';
require_once __DIR__.'/../Column.php';
require_once __DIR__.'/../OneToOne.php';
require_once __DIR__.'/../OneToMany.php';
require_once __DIR__.'/../ManyToOne.php';
require_once __DIR__.'/../ManyToMany.php';
require_once __DIR__.'/../ElementCollection.php';
require_once __DIR__.'/../Table.php';
require_once __DIR__.'/../UniqueConstraint.php';
require_once __DIR__.'/../Index.php';
require_once __DIR__.'/../JoinTable.php';
require_once __DIR__.'/../SequenceGenerator.php';
require_once __DIR__.'/../ChangeTrackingPolicy.php';
require_once __DIR__.'/../OrderBy.php';
require_once __DIR__.'/../NamedQueries.php';
require_once __DIR__.'/../NamedQuery.php';
require_once __DIR__.'/../HasLifecycleCallbacks.php';
require_once __DIR__.'/../PrePersist.php';
require_once __DIR__.'/../PostPersist.php';
require_once __DIR__.'/../PreUpdate.php';
require_once __DIR__.'/../PostUpdate.php';
require_once __DIR__.'/../PreRemove.php';
require_once __DIR__.'/../PostRemove.php';
require_once __DIR__.'/../PostLoad.php';
require_once __DIR__.'/../PreFlush.php';

View File

@@ -34,18 +34,18 @@ interface Driver
{
/**
* Loads the metadata for the specified class into the provided container.
*
*
* @param string $className
* @param ClassMetadataInfo $metadata
*/
function loadMetadataForClass($className, ClassMetadataInfo $metadata);
/**
* Gets the names of all mapped classes known to this driver.
*
*
* @return array The names of all mapped classes known to this driver.
*/
function getAllClassNames();
function getAllClassNames();
/**
* Whether the class with the specified name should have its metadata loaded.

View File

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

View File

@@ -0,0 +1,176 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
/**
* XmlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license MIT
*/
class SimplifiedXmlDriver extends XmlDriver
{
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.xml';
public function __construct($prefixes)
{
$this->addNamespacePrefixes($prefixes);
}
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function addNamespacePrefixes($prefixes)
{
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
$this->addPaths(array_flip($prefixes));
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
{
if (null === $this->_classCache) {
$this->initialize();
}
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
return array_merge($classes, array_keys($this->_classCache));
}
public function getElement($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
if (!isset($this->_classCache[$className])) {
$this->_classCache[$className] = parent::getElement($className);
}
return $this->_classCache[$className];
}
protected function initialize()
{
$this->_classCache = array();
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (is_file($filename)) {
return $filename;
}
throw MappingException::mappingFileNotFound($className, $filename);
}
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\MappingException;
/**
* YamlDriver that additionally looks for mapping information in a global file.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @license MIT
*/
class SimplifiedYamlDriver extends YamlDriver
{
protected $_prefixes = array();
protected $_globalBasename;
protected $_classCache;
protected $_fileExtension = '.orm.yml';
public function __construct($prefixes)
{
$this->addNamespacePrefixes($prefixes);
}
public function setGlobalBasename($file)
{
$this->_globalBasename = $file;
}
public function getGlobalBasename()
{
return $this->_globalBasename;
}
public function addNamespacePrefixes($prefixes)
{
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
$this->addPaths(array_flip($prefixes));
}
public function addNamespacePrefix($prefix, $path)
{
$this->_prefixes[$path] = $prefix;
}
public function getNamespacePrefixes()
{
return $this->_prefixes;
}
public function isTransient($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
// The mapping is defined in the global mapping file
if (isset($this->_classCache[$className])) {
return false;
}
try {
$this->_findMappingFile($className);
return false;
} catch (MappingException $e) {
return true;
}
}
public function getAllClassNames()
{
if (null === $this->_classCache) {
$this->initialize();
}
$classes = array();
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if (!is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
$fileName = $file->getBasename($this->_fileExtension);
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
continue;
}
// NOTE: All files found here means classes are not transient!
if (isset($this->_prefixes[$path])) {
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
} else {
$classes[] = str_replace('.', '\\', $fileName);
}
}
}
}
return array_merge($classes, array_keys($this->_classCache));
}
public function getElement($className)
{
if (null === $this->_classCache) {
$this->initialize();
}
if (!isset($this->_classCache[$className])) {
$this->_classCache[$className] = parent::getElement($className);
}
return $this->_classCache[$className];
}
protected function initialize()
{
$this->_classCache = array();
if (null !== $this->_globalBasename) {
foreach ($this->_paths as $path) {
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
}
}
}
}
protected function _findMappingFile($className)
{
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
foreach ($this->_paths as $path) {
if (!isset($this->_prefixes[$path])) {
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
}
continue;
}
$prefix = $this->_prefixes[$path];
if (0 !== strpos($className, $prefix.'\\')) {
continue;
}
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
if (is_file($filename)) {
return $filename;
}
throw MappingException::mappingFileNotFound($className, $filename);
}
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
}
}

View File

@@ -38,21 +38,21 @@ class StaticPHPDriver implements Driver
{
/**
* Paths of entity directories.
*
*
* @var array
*/
private $_paths = array();
/**
* Map of all class names.
*
*
* @var array
*/
private $_classNames;
/**
* The file extension of mapping documents.
*
*
* @var string
*/
private $_fileExtension = '.php';

View File

@@ -52,13 +52,16 @@ class XmlDriver extends AbstractFileDriver
$xmlRoot = $this->getElement($className);
if ($xmlRoot->getName() == 'entity') {
$metadata->setCustomRepositoryClass(
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
);
if (isset($xmlRoot['repository-class'])) {
$metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']);
}
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
$metadata->markReadOnly();
}
} else if ($xmlRoot->getName() == 'mapped-superclass') {
$metadata->setCustomRepositoryClass(
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
@@ -86,7 +89,7 @@ class XmlDriver extends AbstractFileDriver
if (isset($xmlRoot['schema'])) {
$metadata->table['schema'] = (string)$xmlRoot['schema'];
}*/
if (isset($xmlRoot['inheritance-type'])) {
$inheritanceType = (string)$xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
@@ -163,9 +166,12 @@ class XmlDriver extends AbstractFileDriver
foreach ($xmlRoot->field as $fieldMapping) {
$mapping = array(
'fieldName' => (string)$fieldMapping['name'],
'type' => (string)$fieldMapping['type']
);
if (isset($fieldMapping['type'])) {
$mapping['type'] = (string)$fieldMapping['type'];
}
if (isset($fieldMapping['column'])) {
$mapping['columnName'] = (string)$fieldMapping['column'];
}
@@ -210,20 +216,27 @@ class XmlDriver extends AbstractFileDriver
$associationIds = array();
foreach ($xmlRoot->id as $idElement) {
if ((bool)$idElement['association-key'] == true) {
$associationIds[(string)$idElement['fieldName']] = true;
$associationIds[(string)$idElement['name']] = true;
continue;
}
$mapping = array(
'id' => true,
'fieldName' => (string)$idElement['name'],
'type' => (string)$idElement['type']
'fieldName' => (string)$idElement['name']
);
if (isset($idElement['type'])) {
$mapping['type'] = (string)$idElement['type'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = (string)$idElement['column'];
}
if (isset($idElement['column-definition'])) {
$mapping['columnDefinition'] = (string)$idElement['column-definition'];
}
$metadata->mapField($mapping);
if (isset($idElement->generator)) {
@@ -285,8 +298,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
}
if (isset($oneToOneElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement->{'orphan-removal'};
if (isset($oneToOneElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToOneElement['orphan-removal'];
}
$metadata->mapOneToOne($mapping);
@@ -310,8 +323,8 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
}
if (isset($oneToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement->{'orphan-removal'};
if (isset($oneToManyElement['orphan-removal'])) {
$mapping['orphanRemoval'] = (bool)$oneToManyElement['orphan-removal'];
}
if (isset($oneToManyElement->{'order-by'})) {
@@ -322,8 +335,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($oneToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$oneToManyElement->{'index-by'};
if (isset($oneToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$oneToManyElement['index-by'];
} else if (isset($oneToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapOneToMany($mapping);
@@ -366,10 +381,6 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
}
if (isset($manyToOneElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$manyToOneElement->{'orphan-removal'};
}
$metadata->mapManyToOne($mapping);
}
}
@@ -386,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'})) {
@@ -417,10 +432,6 @@ class XmlDriver extends AbstractFileDriver
$mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
}
if (isset($manyToManyElement->{'orphan-removal'})) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement->{'orphan-removal'};
}
if (isset($manyToManyElement->{'order-by'})) {
$orderBy = array();
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} AS $orderByField) {
@@ -429,8 +440,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
if (isset($manyToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$manyToManyElement['index-by'];
} else if (isset($manyToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapManyToMany($mapping);
@@ -471,10 +484,6 @@ class XmlDriver extends AbstractFileDriver
$joinColumn['onDelete'] = (string)$joinColumnElement['on-delete'];
}
if (isset($joinColumnElement['on-update'])) {
$joinColumn['onUpdate'] = (string)$joinColumnElement['on-update'];
}
if (isset($joinColumnElement['column-definition'])) {
$joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition'];
}

View File

@@ -46,13 +46,16 @@ class YamlDriver extends AbstractFileDriver
$element = $this->getElement($className);
if ($element['type'] == 'entity') {
$metadata->setCustomRepositoryClass(
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
);
if (isset($element['repositoryClass'])) {
$metadata->setCustomRepositoryClass($element['repositoryClass']);
}
if (isset($element['readOnly']) && $element['readOnly'] == true) {
$metadata->markReadOnly();
}
} else if ($element['type'] == 'mappedSuperclass') {
$metadata->setCustomRepositoryClass(
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
);
$metadata->isMappedSuperclass = true;
} else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
@@ -162,16 +165,15 @@ class YamlDriver extends AbstractFileDriver
continue;
}
if (!isset($idElement['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
}
$mapping = array(
'id' => true,
'fieldName' => $name,
'type' => $idElement['type']
'fieldName' => $name
);
if (isset($idElement['type'])) {
$mapping['type'] = $idElement['type'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = $idElement['column'];
}
@@ -180,6 +182,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['length'] = $idElement['length'];
}
if (isset($idElement['columnDefinition'])) {
$mapping['columnDefinition'] = $idElement['columnDefinition'];
}
$metadata->mapField($mapping);
if (isset($idElement['generator'])) {
@@ -198,19 +204,21 @@ class YamlDriver extends AbstractFileDriver
// Evaluate fields
if (isset($element['fields'])) {
foreach ($element['fields'] as $name => $fieldMapping) {
if (!isset($fieldMapping['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
$mapping = array(
'fieldName' => $name
);
if (isset($fieldMapping['type'])) {
$e = explode('(', $fieldMapping['type']);
$fieldMapping['type'] = $e[0];
$mapping['type'] = $fieldMapping['type'];
if (isset($e[1])) {
$fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1);
}
}
$e = explode('(', $fieldMapping['type']);
$fieldMapping['type'] = $e[0];
if (isset($e[1])) {
$fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1);
}
$mapping = array(
'fieldName' => $name,
'type' => $fieldMapping['type']
);
if (isset($fieldMapping['id'])) {
$mapping['id'] = true;
if (isset($fieldMapping['generator']['strategy'])) {
@@ -375,10 +383,6 @@ class YamlDriver extends AbstractFileDriver
$mapping['cascade'] = $manyToOneElement['cascade'];
}
if (isset($manyToOneElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToOneElement['orphanRemoval'];
}
$metadata->mapManyToOne($mapping);
}
}
@@ -398,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(
@@ -430,12 +431,12 @@ class YamlDriver extends AbstractFileDriver
$mapping['joinTable'] = $joinTable;
}
if (isset($manyToManyElement['cascade'])) {
$mapping['cascade'] = $manyToManyElement['cascade'];
if (isset($manyToManyElement['inversedBy'])) {
$mapping['inversedBy'] = $manyToManyElement['inversedBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
if (isset($manyToManyElement['cascade'])) {
$mapping['cascade'] = $manyToManyElement['cascade'];
}
if (isset($manyToManyElement['orderBy'])) {
@@ -446,6 +447,10 @@ class YamlDriver extends AbstractFileDriver
$mapping['indexBy'] = $manyToManyElement['indexBy'];
}
if (isset($manyToManyElement['orphanRemoval'])) {
$mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval'];
}
$metadata->mapManyToMany($mapping);
}
}
@@ -490,10 +495,6 @@ class YamlDriver extends AbstractFileDriver
$joinColumn['onDelete'] = $joinColumnElement['onDelete'];
}
if (isset($joinColumnElement['onUpdate'])) {
$joinColumn['onUpdate'] = $joinColumnElement['onUpdate'];
}
if (isset($joinColumnElement['columnDefinition'])) {
$joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition'];
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ALL")
* @todo check available targets
*/
final class ElementCollection implements Annotation
{
/** @var string */
public $tableName;
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class Entity implements Annotation
{
/** @var string */
public $repositoryClass;
/** @var boolean */
public $readOnly = false;
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class GeneratedValue implements Annotation
{
/** @var string */
public $strategy = 'AUTO';
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class HasLifecycleCallbacks implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Id implements Annotation
{
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class Index implements Annotation
{
/** @var string */
public $name;
/** @var array<string> */
public $columns;
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class InheritanceType implements Annotation
{
/** @var string */
public $value;
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class JoinColumn implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $referencedColumnName = 'id';
/** @var boolean */
public $unique = false;
/** @var boolean */
public $nullable = true;
/** @var mixed */
public $onDelete;
/** @var string */
public $columnDefinition;
/** @var string */
public $fieldName; // field name used in non-object hydration (array/scalar)
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class JoinColumns implements Annotation
{
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $value;
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class JoinTable implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $schema;
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $joinColumns = array();
/** @var array<\Doctrine\ORM\Mapping\JoinColumn> */
public $inverseJoinColumns = array();
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class ManyToMany implements Annotation
{
/** @var string */
public $targetEntity;
/** @var string */
public $mappedBy;
/** @var string */
public $inversedBy;
/** @var array<string> */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class ManyToOne implements Annotation
{
/** @var string */
public $targetEntity;
/** @var array<string> */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var string */
public $inversedBy;
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class MappedSuperclass implements Annotation
{
/** @var string */
public $repositoryClass;
}

View File

@@ -68,6 +68,11 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("No mapping file found named '$fileName' for class '$entityName'.");
}
public static function invalidMappingFile($entityName, $fileName)
{
return new self("Invalid mapping file '$fileName' for class '$entityName'.");
}
public static function mappingNotFound($className, $fieldName)
{
return new self("No mapping found for field '$fieldName' on class '$className'.");
@@ -184,7 +189,7 @@ class MappingException extends \Doctrine\ORM\ORMException
if ( ! empty($path)) {
$path = '[' . $path . ']';
}
return new self(
'File mapping drivers must have a valid directory path, ' .
'however the given path ' . $path . ' seems to be incorrect!'
@@ -226,6 +231,11 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
}
public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type)
{
return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers.");
}
/**
* @param string $className
* @param string $columnName
@@ -270,6 +280,12 @@ class MappingException extends \Doctrine\ORM\ORMException
"part of the identifier in '$className#$field'.");
}
public static function illegalOrphanRemoval($className, $field)
{
return new self("Orphan removal is only allowed on one-to-one and one-to-many ".
"associations, but " . $className."#" .$field . " is not.");
}
public static function illegalInverseIdentifierAssocation($className, $field)
{
return new self("An inverse association is not allowed to be identifier in '$className#$field'.");
@@ -279,18 +295,38 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'.");
}
public static function noInheritanceOnMappedSuperClass($className)
{
return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'.");
}
public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName)
{
return new self(
"Entity '" . $className . "' has to be part of the descriminator map of '" . $rootClassName . "' " .
"Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " .
"to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " .
"to avoid this exception from occuring."
);
}
}
public static function lifecycleCallbackMethodNotFound($className, $methodName)
{
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
}
public static function invalidFetchMode($className, $annotation)
{
return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'");
}
public static function compositeKeyAssignedIdGeneratorRequired($className)
{
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."'.");
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class NamedQueries implements Annotation
{
/** @var array<\Doctrine\ORM\Mapping\NamedQuery> */
public $value;
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class NamedQuery implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $query;
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OneToMany implements Annotation
{
/** @var string */
public $mappedBy;
/** @var string */
public $targetEntity;
/** @var array<string> */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
/** @var string */
public $indexBy;
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OneToOne implements Annotation
{
/** @var string */
public $targetEntity;
/** @var string */
public $mappedBy;
/** @var string */
public $inversedBy;
/** @var array<string> */
public $cascade;
/** @var string */
public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false;
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class OrderBy implements Annotation
{
/** @var array<string> */
public $value;
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostLoad implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostPersist implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostRemove implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PostUpdate implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreFlush implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PrePersist implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreRemove implements Annotation
{
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("METHOD")
*/
final class PreUpdate implements Annotation
{
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class SequenceGenerator implements Annotation
{
/** @var string */
public $sequenceName;
/** @var integer */
public $allocationSize = 1;
/** @var integer */
public $initialValue = 1;
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("CLASS")
*/
final class Table implements Annotation
{
/** @var string */
public $name;
/** @var string */
public $schema;
/** @var array<\Doctrine\ORM\Mapping\Index> */
public $indexes;
/** @var array<\Doctrine\ORM\Mapping\UniqueConstraint> */
public $uniqueConstraints;
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("ANNOTATION")
*/
final class UniqueConstraint implements Annotation
{
/** @var string */
public $name;
/** @var array<string> */
public $columns;
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Version implements Annotation
{
}

View File

@@ -1,4 +1,4 @@
<?php
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -38,6 +38,7 @@ final class NativeQuery extends AbstractQuery
public function setSQL($sql)
{
$this->_sql = $sql;
return $this;
}
@@ -57,17 +58,19 @@ final class NativeQuery extends AbstractQuery
*/
protected function _doExecute()
{
$stmt = $this->_em->getConnection()->prepare($this->_sql);
$params = $this->_params;
foreach ($params as $key => $value) {
if (isset($this->_paramTypes[$key])) {
$stmt->bindValue($key, $value, $this->_paramTypes[$key]);
} else {
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$types = $this->_paramTypes;
return $stmt;
if ($params && is_int(key($params))) {
ksort($params);
ksort($types);
$params = array_values($params);
$types = array_values($types);
}
return $this->_em->getConnection()->executeQuery(
$this->_sql, $params, $types, $this->_queryCacheProfile
);
}
}

View File

@@ -21,7 +21,7 @@ namespace Doctrine\ORM;
/**
* Exception thrown when an ORM query unexpectedly does not return any results.
*
*
* @author robo
* @since 2.0
*/

View File

@@ -21,7 +21,7 @@ namespace Doctrine\ORM;
/**
* Exception thrown when an ORM query unexpectedly returns more than one result.
*
*
* @author robo
* @since 2.0
*/

View File

@@ -34,32 +34,40 @@ class ORMException extends Exception
return new self("It's a requirement to specify a Metadata Driver and pass it ".
"to Doctrine\ORM\Configuration::setMetadataDriverImpl().");
}
public static function entityMissingForeignAssignedId($entity, $relatedEntity)
{
return new self(
"Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " .
"however this entity has no ientity itself. You have to call EntityManager#persist() on the related entity " .
"and make sure it an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " .
"however this entity has no identity itself. You have to call EntityManager#persist() on the related entity " .
"and make sure that an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " .
"of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call " .
"EntityManager#flush() between both persist operations."
);
}
public static function entityMissingAssignedId($entity)
public static function entityMissingAssignedIdForField($entity, $field)
{
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " .
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " .
"The identifier generation strategy for this entity requires the ID field to be populated before ".
"EntityManager#persist() is called. If you want automatically generated identifiers instead " .
"EntityManager#persist() is called. If you want automatically generated identifiers instead " .
"you need to adjust the metadata mapping accordingly."
);
}
public static function unrecognizedField($field)
{
return new self("Unrecognized field: $field");
}
/**
* @param string $className
* @param string $field
*/
public static function invalidOrientation($className, $field)
{
return new self("Invalid order by orientation specified for " . $className . "#" . $field);
}
public static function invalidFlushMode($mode)
{
return new self("'$mode' is an invalid flush mode.");
@@ -130,4 +138,15 @@ class ORMException extends Exception
"Unknown Entity namespace alias '$entityNamespaceAlias'."
);
}
public static function invalidEntityRepository($className)
{
return new self("Invalid repository class '".$className."'. ".
"it must be a Doctrine\ORM\EntityRepository.");
}
public static function missingIdentifierField($className, $fieldName)
{
return new self("The identifier $fieldName is missing for a query of " . $className);
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -21,6 +21,7 @@ namespace Doctrine\ORM;
use Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\Common\Collections\Collection,
Doctrine\Common\Collections\ArrayCollection,
Closure;
/**
@@ -66,7 +67,7 @@ final class PersistentCollection implements Collection
/**
* The EntityManager that manages the persistence of the collection.
*
* @var Doctrine\ORM\EntityManager
* @var \Doctrine\ORM\EntityManager
*/
private $em;
@@ -93,29 +94,29 @@ final class PersistentCollection implements Collection
/**
* Whether the collection has already been initialized.
*
*
* @var boolean
*/
private $initialized = true;
/**
* The wrapped Collection instance.
*
*
* @var Collection
*/
private $coll;
/**
* Creates a new persistent collection.
*
*
* @param EntityManager $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param array The collection elements.
*/
public function __construct(EntityManager $em, $class, $coll)
{
$this->coll = $coll;
$this->em = $em;
$this->coll = $coll;
$this->em = $em;
$this->typeClass = $class;
}
@@ -129,8 +130,8 @@ final class PersistentCollection implements Collection
*/
public function setOwner($entity, array $assoc)
{
$this->owner = $entity;
$this->association = $assoc;
$this->owner = $entity;
$this->association = $assoc;
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
}
@@ -144,7 +145,7 @@ final class PersistentCollection implements Collection
{
return $this->owner;
}
public function getTypeClass()
{
return $this->typeClass;
@@ -154,25 +155,27 @@ final class PersistentCollection implements Collection
* INTERNAL:
* Adds an element to a collection during hydration. This will automatically
* complete bidirectional associations in the case of a one-to-many association.
*
*
* @param mixed $element The element to add.
*/
public function hydrateAdd($element)
{
$this->coll->add($element);
// If _backRefFieldName is set and its a one-to-many association,
// we need to set the back reference.
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) {
// Set back reference to owner
$this->typeClass->reflFields[$this->backRefFieldName]
->setValue($element, $this->owner);
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
$element, $this->owner
);
$this->em->getUnitOfWork()->setOriginalEntityProperty(
spl_object_hash($element),
$this->backRefFieldName,
$this->owner);
spl_object_hash($element), $this->backRefFieldName, $this->owner
);
}
}
/**
* INTERNAL:
* Sets a keyed element in the collection during hydration.
@@ -183,12 +186,14 @@ final class PersistentCollection implements Collection
public function hydrateSet($key, $element)
{
$this->coll->set($key, $element);
// If _backRefFieldName is set, then the association is bidirectional
// and we need to set the back reference.
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) {
// Set back reference to owner
$this->typeClass->reflFields[$this->backRefFieldName]
->setValue($element, $this->owner);
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
$element, $this->owner
);
}
}
@@ -198,23 +203,31 @@ final class PersistentCollection implements Collection
*/
public function initialize()
{
if ( ! $this->initialized && $this->association) {
if ($this->isDirty) {
// Has NEW objects added through add(). Remember them.
$newObjects = $this->coll->toArray();
}
$this->coll->clear();
$this->em->getUnitOfWork()->loadCollection($this);
$this->takeSnapshot();
// Reattach NEW objects added through add(), if any.
if (isset($newObjects)) {
foreach ($newObjects as $obj) {
$this->coll->add($obj);
}
$this->isDirty = true;
}
$this->initialized = true;
if ($this->initialized || ! $this->association) {
return;
}
// Has NEW objects added through add(). Remember them.
$newObjects = array();
if ($this->isDirty) {
$newObjects = $this->coll->toArray();
}
$this->coll->clear();
$this->em->getUnitOfWork()->loadCollection($this);
$this->takeSnapshot();
// Reattach NEW objects added through add(), if any.
if ($newObjects) {
foreach ($newObjects as $obj) {
$this->coll->add($obj);
}
$this->isDirty = true;
}
$this->initialized = true;
}
/**
@@ -224,7 +237,7 @@ final class PersistentCollection implements Collection
public function takeSnapshot()
{
$this->snapshot = $this->coll->toArray();
$this->isDirty = false;
$this->isDirty = false;
}
/**
@@ -246,8 +259,11 @@ final class PersistentCollection implements Collection
*/
public function getDeleteDiff()
{
return array_udiff_assoc($this->snapshot, $this->coll->toArray(),
function($a, $b) {return $a === $b ? 0 : 1;});
return array_udiff_assoc(
$this->snapshot,
$this->coll->toArray(),
function($a, $b) { return $a === $b ? 0 : 1; }
);
}
/**
@@ -258,31 +274,40 @@ final class PersistentCollection implements Collection
*/
public function getInsertDiff()
{
return array_udiff_assoc($this->coll->toArray(), $this->snapshot,
function($a, $b) {return $a === $b ? 0 : 1;});
return array_udiff_assoc(
$this->coll->toArray(),
$this->snapshot,
function($a, $b) { return $a === $b ? 0 : 1; }
);
}
/**
* INTERNAL: Gets the association mapping of the collection.
*
* @return Doctrine\ORM\Mapping\AssociationMapping
* @return \Doctrine\ORM\Mapping\AssociationMapping
*/
public function getMapping()
{
return $this->association;
}
/**
* Marks this collection as changed/dirty.
*/
private function changed()
{
if ( ! $this->isDirty) {
$this->isDirty = true;
if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY &&
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
if ($this->isDirty) {
return;
}
$this->isDirty = true;
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);
}
}
@@ -306,17 +331,17 @@ final class PersistentCollection implements Collection
{
$this->isDirty = $dirty;
}
/**
* Sets the initialized flag of the collection, forcing it into that state.
*
*
* @param boolean $bool
*/
public function setInitialized($bool)
{
$this->initialized = $bool;
}
/**
* Checks whether this collection has been initialized.
*
@@ -331,6 +356,7 @@ final class PersistentCollection implements Collection
public function first()
{
$this->initialize();
return $this->coll->first();
}
@@ -338,6 +364,7 @@ final class PersistentCollection implements Collection
public function last()
{
$this->initialize();
return $this->coll->last();
}
@@ -351,13 +378,19 @@ final class PersistentCollection implements Collection
// not used we can issue a straight SQL delete/update on the
// association (table). Without initializing the collection.
$this->initialize();
$removed = $this->coll->remove($key);
if ($removed) {
$this->changed();
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
if ( ! $removed) {
return $removed;
}
$this->changed();
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
return $removed;
@@ -368,25 +401,36 @@ final class PersistentCollection implements Collection
*/
public function removeElement($element)
{
// TODO: Assuming the identity of entities in a collection is always based
// on their primary key (there is no equals/hashCode in PHP),
// if the collection is not initialized, we could issue a straight
// SQL DELETE/UPDATE on the association (table) without initializing
// the collection.
/*if ( ! $this->initialized) {
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
->deleteRows($this, $element);
}*/
$this->initialize();
$removed = $this->coll->removeElement($element);
if ($removed) {
$this->changed();
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
if ($this->coll->contains($element)) {
return $this->coll->removeElement($element);
}
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
if ($persister->removeElement($this, $element)) {
return $element;
}
return null;
}
$this->initialize();
$removed = $this->coll->removeElement($element);
if ( ! $removed) {
return $removed;
}
$this->changed();
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
return $removed;
}
@@ -396,6 +440,7 @@ final class PersistentCollection implements Collection
public function containsKey($key)
{
$this->initialize();
return $this->coll->containsKey($key);
}
@@ -404,14 +449,14 @@ final class PersistentCollection implements Collection
*/
public function contains($element)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->coll->contains($element) ||
$this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->contains($this, $element);
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
return $this->coll->contains($element) || $persister->contains($this, $element);
}
$this->initialize();
return $this->coll->contains($element);
}
@@ -421,6 +466,7 @@ final class PersistentCollection implements Collection
public function exists(Closure $p)
{
$this->initialize();
return $this->coll->exists($p);
}
@@ -430,6 +476,7 @@ final class PersistentCollection implements Collection
public function indexOf($element)
{
$this->initialize();
return $this->coll->indexOf($element);
}
@@ -439,6 +486,7 @@ final class PersistentCollection implements Collection
public function get($key)
{
$this->initialize();
return $this->coll->get($key);
}
@@ -448,6 +496,7 @@ final class PersistentCollection implements Collection
public function getKeys()
{
$this->initialize();
return $this->coll->getKeys();
}
@@ -457,6 +506,7 @@ final class PersistentCollection implements Collection
public function getValues()
{
$this->initialize();
return $this->coll->getValues();
}
@@ -465,13 +515,14 @@ final class PersistentCollection implements Collection
*/
public function count()
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->count($this) + $this->coll->count();
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0);
}
$this->initialize();
return $this->coll->count();
}
@@ -481,7 +532,9 @@ final class PersistentCollection implements Collection
public function set($key, $value)
{
$this->initialize();
$this->coll->set($key, $value);
$this->changed();
}
@@ -491,7 +544,9 @@ final class PersistentCollection implements Collection
public function add($value)
{
$this->coll->add($value);
$this->changed();
return true;
}
@@ -501,15 +556,17 @@ final class PersistentCollection implements Collection
public function isEmpty()
{
$this->initialize();
return $this->coll->isEmpty();
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$this->initialize();
return $this->coll->getIterator();
}
@@ -519,6 +576,7 @@ final class PersistentCollection implements Collection
public function map(Closure $func)
{
$this->initialize();
return $this->coll->map($func);
}
@@ -528,15 +586,17 @@ final class PersistentCollection implements Collection
public function filter(Closure $p)
{
$this->initialize();
return $this->coll->filter($p);
}
/**
* {@inheritdoc}
*/
public function forAll(Closure $p)
{
$this->initialize();
return $this->coll->forAll($p);
}
@@ -546,15 +606,17 @@ final class PersistentCollection implements Collection
public function partition(Closure $p)
{
$this->initialize();
return $this->coll->partition($p);
}
/**
* {@inheritdoc}
*/
public function toArray()
{
$this->initialize();
return $this->coll->toArray();
}
@@ -566,20 +628,32 @@ final class PersistentCollection implements Collection
if ($this->initialized && $this->isEmpty()) {
return;
}
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
$uow = $this->em->getUnitOfWork();
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();
foreach ($this->coll as $element) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
$uow->scheduleOrphanRemoval($element);
}
}
$this->coll->clear();
$this->initialized = true; // direct call, {@link initialize()} is too expensive
if ($this->association['isOwningSide']) {
$this->changed();
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
$uow->scheduleCollectionDeletion($this);
$this->takeSnapshot();
}
}
/**
* Called by PHP when this collection is serialized. Ensures that only the
* elements are properly serialized.
@@ -591,7 +665,7 @@ final class PersistentCollection implements Collection
{
return array('coll', 'initialized');
}
/* ArrayAccess implementation */
/**
@@ -619,6 +693,7 @@ final class PersistentCollection implements Collection
if ( ! isset($offset)) {
return $this->add($value);
}
return $this->set($offset, $value);
}
@@ -629,12 +704,12 @@ final class PersistentCollection implements Collection
{
return $this->remove($offset);
}
public function key()
{
return $this->coll->key();
}
/**
* Gets the element of the collection at the current iterator position.
*/
@@ -642,7 +717,7 @@ final class PersistentCollection implements Collection
{
return $this->coll->current();
}
/**
* Moves the internal iterator position to the next element.
*/
@@ -650,9 +725,11 @@ final class PersistentCollection implements Collection
{
return $this->coll->next();
}
/**
* Retrieves the wrapped Collection instance.
*
* @return \Doctrine\Common\Collections\Collection
*/
public function unwrap()
{
@@ -668,17 +745,42 @@ final class PersistentCollection implements Collection
*
* @param int $offset
* @param int $length
*
* @return array
*/
public function slice($offset, $length = null)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->slice($this, $offset, $length);
if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
return $persister->slice($this, $offset, $length);
}
$this->initialize();
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();
}
}

View File

@@ -36,19 +36,19 @@ abstract class AbstractCollectionPersister
protected $_em;
/**
* @var Doctrine\DBAL\Connection
* @var \Doctrine\DBAL\Connection
*/
protected $_conn;
/**
* @var Doctrine\ORM\UnitOfWork
* @var \Doctrine\ORM\UnitOfWork
*/
protected $_uow;
/**
* Initializes a new instance of a class derived from AbstractCollectionPersister.
*
* @param Doctrine\ORM\EntityManager $em
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
@@ -65,9 +65,11 @@ abstract class AbstractCollectionPersister
public function delete(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$sql = $this->_getDeleteSQL($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
}
@@ -96,30 +98,34 @@ abstract class AbstractCollectionPersister
public function update(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$this->deleteRows($coll);
//$this->updateRows($coll);
$this->insertRows($coll);
}
public function deleteRows(PersistentCollection $coll)
{
{
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
}
//public function updateRows(PersistentCollection $coll)
//{}
public function insertRows(PersistentCollection $coll)
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
}
@@ -145,6 +151,16 @@ abstract class AbstractCollectionPersister
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
}
public function removeElement(PersistentCollection $coll, $element)
{
throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister.");
}
public function removeKey(PersistentCollection $coll, $key)
{
throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister.");
}
public function get(PersistentCollection $coll, $index)
{
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
@@ -152,7 +168,7 @@ abstract class AbstractCollectionPersister
/**
* Gets the SQL statement used for deleting a row from the collection.
*
*
* @param PersistentCollection $coll
*/
abstract protected function _getDeleteRowSQL(PersistentCollection $coll);
@@ -188,4 +204,4 @@ abstract class AbstractCollectionPersister
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
}

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