Compare commits

...

229 Commits
2.9.2 ... 2.1.7

Author SHA1 Message Date
Benjamin Eberlei
03d2b0f30c Release 2.1.7 2012-05-28 00:01:53 +02:00
Benjamin Eberlei
5544895cf9 Fix a bunch of tests for Oracle 2012-05-27 23:59:32 +02:00
Benjamin Eberlei
6af72006e4 Bump DBAL to 2.1.7 2012-05-27 21:19:53 +02:00
Benjamin Eberlei
fd76138a56 [DDC-1770] Fix multiple SingleTableInheritance in FROM clause in 2.1.x, was fixed in 2.2 already. 2012-05-27 19:57:59 +02:00
Karsten Dambekalns
1364f23d8c [DDC-1835] Fix clone side effects in PersistentCollection 2012-05-27 10:18:28 +02:00
Benjamin Eberlei
681d3ef45a Merge branch 'DDC-1799' into 2.1.x 2012-05-27 10:00:59 +02:00
Benjamin Eberlei
53c08b59b5 [DDC-1799] Fix bug in YamlExporter using OneToOne instead of ManyToOne 2012-05-27 10:00:19 +02:00
Benjamin Eberlei
07ee8c5249 Merge remote-tracking branch 'origin/2.1.x' into 2.1.x 2012-04-16 18:51:34 +02:00
Tiago Ribeiro
0c69f4ea9c Fixes autoloading of generated Annotations 2012-04-16 18:51:18 +02:00
Benjamin Eberlei
57acceaff2 Merge pull request #326 from Netpositive/2.1.x
2.1.x setDiscriminatorMap fix
2012-04-07 00:47:23 -07:00
Somfai Mátyás
7d3fee59bc Fixing a bug when calling setDiscriminatorMap from multiple sources (ie: from Events::loadClassMetadata and annotation). 2012-04-04 14:44:28 +02:00
Benjamin Eberlei
cab10eb96d Merge pull request #280 from rivaros/DC-1648
Fix for DDC-1648
2012-03-14 13:35:25 -07:00
Benjamin Eberlei
3f0a148dc2 Merge branch 'DDC-1683' into 2.1.x 2012-03-14 20:51:35 +01:00
Benjamin Eberlei
134ea63849 [DDC-1683] Fix bug with booleans not handled by Expr#literal() in query builder. 2012-03-14 20:50:51 +01:00
Benjamin Eberlei
39d0b14bdb Merge branch 'DDC-1695' into 2.1.x 2012-03-11 23:34:15 +01:00
Benjamin Eberlei
22cebf9880 [DDC-1695] Fix bug in SQL Walker array hydration with escaped fields. 2012-03-11 23:33:54 +01:00
rivaros
015cbc46be Convention fix 2012-03-05 10:29:06 +02:00
Benjamin Eberlei
734629af08 Merge branch 'DDC-1678' into 2.1.x 2012-03-03 22:18:55 +01:00
Benjamin Eberlei
e47b5a2b28 DDC-1678 - Adjust test to run on 2.1.x 2012-03-03 22:18:45 +01:00
Francisco Facioni
5152c48ce3 UnitTest for ManyToMany update notification 2012-03-03 22:17:24 +01:00
Francisco Facioni
92028473f0 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:17:24 +01:00
Benjamin Eberlei
3e7c9a1ff4 Merge branch 'DDC-1659' into 2.1.x 2012-02-20 09:40:30 +01:00
Benjamin Eberlei
e6b994eecc [DDC-1659] Remove test for functionality in the higher versions. 2012-02-20 09:40:16 +01:00
Benjamin Eberlei
e094c93035 [DDC-1659] Remove read only marker when clearing entities. 2012-02-20 09:39:17 +01:00
Benjamin Eberlei
c98cc258e8 Merge branch 'DDC-1643' into 2.1.x 2012-02-18 00:48:13 +01:00
Benjamin Eberlei
53bb7e36e3 [DDC-1643] Fix bugs when cloning PersistentCollection and re-using it. 2012-02-18 00:47:33 +01:00
Rivaros
490bdd23f7 convention fixes #2 2012-02-13 16:39:46 +02:00
Rivaros
0805f69101 Convention fixes 2012-02-13 15:05:28 +02:00
Rivaros
9503cf6ff6 Primary Keys as Foreign Keys - reverse engineering 2012-02-13 11:35:55 +02:00
Benjamin Eberlei
affa6feb8a DDC-1594 - Fix merge error 2012-02-10 20:46:27 +01:00
Benjamin Eberlei
061ea2b337 Bump dev version to 2.1.7 2012-01-29 17:53:01 +01:00
Benjamin Eberlei
e1647229cd Release 2.1.6 2012-01-29 17:53:01 +01:00
Benjamin Eberlei
9111d794d5 Bump build commons 2012-01-29 17:52:52 +01:00
Benjamin Eberlei
34ec1e3248 Bump dev version to 2.1.6 2012-01-29 17:51:45 +01:00
Benjamin Eberlei
3c52fa5ea6 Release 2.1.6 2012-01-29 17:51:45 +01:00
Benjamin Eberlei
b3ed478fb4 Merge remote branch 'origin/2.1.x' into 2.1.x 2012-01-29 16:49:39 +00:00
Benjamin Eberlei
c5973f8019 Bump dependencies 2012-01-29 16:48:50 +00:00
Benjamin Eberlei
6512902eae Maximum version in PEAR packages. 2012-01-29 17:39:28 +01:00
Benjamin Eberlei
798a8bca92 Merge branch 'DDC-1526' into 2.1.x 2012-01-28 12:26:02 +01:00
Benjamin Eberlei
7bca37e827 [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:25:01 +01:00
armetiz
818a5d5904 Update lib/Doctrine/ORM/Tools/SchemaTool.php 2012-01-25 22:40:59 +01:00
Benjamin Eberlei
20ac6f383c Merge branch 'DDC-1619' into 2.1.x 2012-01-25 10:29:13 +01:00
Benjamin Eberlei
60d97e068c [DDC-1619] Add QueryBuilder#distinct 2012-01-25 10:28:24 +01:00
Benjamin Eberlei
94162d1c03 Merge pull request #271 from sonata-project/2.1.x
DDC-1618 - Query::Iterate() with fetch join exception but no associaiton selected
2012-01-24 14:59:31 -08:00
Thomas Rabaix
1c042b5b8d Add SqlWalker::HINT_DISTINCT constant 2012-01-24 23:34:20 +01:00
Thomas Rabaix
b770a6687a Fix DDC-1618 - add more check before throwing an iterateWithFetchJoinNotAllowed exception 2012-01-24 23:16:51 +01:00
Benjamin Eberlei
0abf2484ce Merge branch 'DDC-1603' into 2.1.x 2012-01-21 15:37:48 +01:00
armetiz
7d8aa2213d Unique key name isn't correctly set - DDC-1603 2012-01-21 15:37:20 +01:00
Benjamin Eberlei
12a639d4a2 Merge branch 'DDC-1594' into 2.1.x 2012-01-15 17:46:41 +01:00
Benjamin Eberlei
432a035baa DDC-1594 - Fix problem with merge and an existing managed proxy instance. 2012-01-15 17:46:19 +01:00
Benjamin Eberlei
97df2f8749 Fix notice when using regenerate if exists and file is not new. 2012-01-12 11:21:16 +01:00
Benjamin Eberlei
61aec8fdcd DDC-1589 - Remove mbstring dependency from Composite 2012-01-09 14:34:31 +01:00
Adrien BRAULT
292fbd9756 Fix some PHPDoc @return type. 2011-12-28 08:50:33 +01:00
Benjamin Eberlei
c2112c0584 Fix PEAR Path 2011-12-20 21:56:37 +01:00
Benjamin Eberlei
05c7e0055a Bump dev version to 2.1.6 2011-12-19 19:27:40 +01:00
Benjamin Eberlei
fb7b5c6fb1 Bump dev version to 2.1.5 2011-12-19 19:27:12 +01:00
Benjamin Eberlei
da0e3439ab Release 2.1.5 2011-12-19 19:27:12 +01:00
Benjamin Eberlei
a1df81e18c Merge build-commons back into 2.1.x 2011-12-19 19:19:43 +01:00
Guilherme Blanco
1dea57013c Cherry-pick fix for DDC-1548 2011-12-19 18:49:14 +01:00
Benjamin Eberlei
91b1a25bb2 Add DDC-1545 Test 2011-12-19 18:13:11 +01:00
Benjamin Eberlei
473ba2f04c 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 18:10:33 +01:00
Benjamin Eberlei
e80eae4c98 DDC-1514 - Fix complex self-referencing + proxy hydration problem. 2011-12-15 23:00:34 +01:00
Benjamin Eberlei
68184a3803 DDC-1519 - Merge fix 2011-12-15 20:59:11 +01:00
Deni
3412ef935a Fixed typo in the XmlDriver 2011-12-15 20:16:40 +01:00
Benjamin Eberlei
4215a24969 Merge branch 'DDC-1520' into 2.1.x 2011-12-13 21:34:13 +01:00
Benjamin Eberlei
02d8802570 Bugfix in UnitOfWorklib/Doctrine/ORM/UnitOfWork.phptriggerEagerLoads() 2011-12-13 21:32:10 +01:00
Benjamin Eberlei
0e107880cf DDC-1527 - Fix bug with complex type handling in repositories 2011-12-12 16:20:46 +01:00
Guilherme Blanco
3b259dcb42 Merge pull request #215 from andrewmackrodt/2.1.x
2.1.x
2011-12-11 13:00:41 -08:00
Andrew Mackrodt
d87e90d392 Added absolute namespace paths to phpdoc annotations - issue DDC-1025. 2011-12-11 20:52:29 +00:00
Benjamin Eberlei
c1ad170bed DDC-1515 - Now the real bugfix 2011-12-11 20:22:11 +01:00
Benjamin Eberlei
565962ec8b Revert "DDC-1515 - Bugfix in hydration of dependant entities without additional fields."
This reverts commit d9b39f6fce.
2011-12-11 16:34:34 +01:00
Benjamin Eberlei
eb9812feb5 Merge branch 'DDC-1515' into 2.1.x 2011-12-11 16:02:13 +01:00
Benjamin Eberlei
d9b39f6fce DDC-1515 - Bugfix in hydration of dependant entities without additional fields. 2011-12-11 16:01:36 +01:00
Benjamin Eberlei
b1db2ad0d8 Merge branch 'DDC-1512' into 2.1.x 2011-11-28 11:18:45 +01:00
Benjamin Eberlei
92b3d0a383 DDC-1512 - Adjustment for 2.1.x branch 2011-11-28 11:18:30 +01:00
Benjamin Eberlei
fc8997a935 DDC-1512 - Make ClassMetadataFactory::isTransient() entity namespace aware. 2011-11-28 11:17:30 +01:00
Benjamin Eberlei
88d937cc89 Bump Dev Version to 2.1.5-DEV 2011-11-23 19:44:51 +00:00
Benjamin Eberlei
bff55577cd Release 2.1.4 2011-11-23 19:44:26 +00:00
Benjamin Eberlei
7ff30e273a Merge branch 'DDC-1509' into 2.1.x 2011-11-23 20:38:58 +01:00
Benjamin Eberlei
9c81989349 DDC-1509 - Fix regression in doMerge() introduced with the DDC-1383 bugfix 2011-11-23 20:37:37 +01:00
Benjamin Eberlei
e0bb84e1da Bump Dev Version to 2.1.4-DEV 2011-11-21 15:08:18 +00:00
Benjamin Eberlei
692f423047 Release 2.1.3 2011-11-21 15:08:06 +00:00
Benjamin Eberlei
0814e8abc7 Bump dependencies to Common 2.1.3 and DBAL 2.1.5 2011-11-21 16:05:25 +01:00
Benjamin Eberlei
d4a1ea8995 Merge branch 'DDC-1500' into 2.1.x 2011-11-21 15:06:15 +01:00
Benjamin Eberlei
c2febabdd0 DDC-1500 - Fix potential security problem in EntityRepository ORDER BY orientations 2011-11-21 15:05:43 +01:00
Benjamin Eberlei
633e342d82 Make 2.1.x forwards compatible with Common 2.2-DEV 2011-11-19 13:06:01 +01:00
Guilherme Blanco
2e7318dcc2 Fixed DDC-1492. 2011-11-19 08:17:02 +01:00
Benjamin Eberlei
6b3edbbbf9 Merge branch 'DBAL-171' into 2.1.x 2011-11-18 17:45:14 +01:00
Benjamin Eberlei
fdcb46e780 DBAL-171 - Fix bug where params where resorted but types where not in DQL Query 2011-11-18 17:44:42 +01:00
Benjamin Eberlei
6da0314deb Merge branch 'DDC-1496' into 2.1.x 2011-11-18 15:45:04 +01:00
Benjamin Eberlei
b61d109fe2 DDC-1496 - Fix bug with OneToMany collections having orphanRemoval=true and Collection#clear() being called. 2011-11-18 15:44:36 +01:00
Benjamin Eberlei
960c94f661 Fix side-effect test failures in functional tests on PostgreSQL 2011-11-18 14:11:08 +01:00
Benjamin Eberlei
515b0205c6 Merge branch 'DDC-1452' into 2.1.x 2011-11-18 14:04:15 +01:00
Benjamin Eberlei
d76c5b11f6 onUpdate doesnt exist anymore 2011-11-18 14:04:05 +01:00
Benjamin Eberlei
88043577a4 DDC-1452 - Backport fix 2011-11-18 14:02:21 +01:00
Benjamin Eberlei
d0b9f7cb2b Generalize config files 2011-11-13 22:42:46 +01:00
Benjamin Eberlei
10a3f5e10e Each branch needs a .travis.yml 2011-11-13 22:39:27 +01:00
Alexander
eeddaecd26 Fixed failing tests in PHPUnit 3.6.2 (expecting \Exception was deprecated) 2011-11-13 22:27:15 +01:00
Benjamin Eberlei
50f7f3a48e Fix assertType() to assertInstanceOf() 2011-11-13 22:26:51 +01:00
Benjamin Eberlei
e94a293446 Remove merge artifact (funny this went into the docblock, not affecting the tests at all) 2011-11-13 17:28:36 +01:00
Benjamin Eberlei
08e5e47780 Merge branch 'thiagofesta-master' into 2.1.x 2011-11-13 17:19:58 +01:00
Benjamin Eberlei
e3a5ffbb55 DDC-1477 - Adjust patch to really fix bug in Proxy generation 2011-11-13 17:19:29 +01:00
Benjamin Eberlei
bf06c3418b Merge branch 'DDC-1458' into 2.1.x 2011-11-11 08:19:48 +01:00
Alexander
6a8172902a Cleanup 2011-11-11 08:16:44 +01:00
Benjamin Eberlei
d9328ea5e7 Autoload to composer.json 2011-11-01 15:06:42 +01:00
Benjamin Eberlei
8cd8c993c8 Merge branch 'DBAL-1420' into 2.1.x 2011-10-31 23:13:59 +01:00
Benjamin Eberlei
f45d8f488b DBAL-1420 - Use safe mode for schema validation. Dropping stuff isnt necessary here. 2011-10-31 23:13:51 +01:00
Benjamin Eberlei
6257daf097 Fix xml fix again 2011-10-31 22:14:46 +01:00
Benjamin Eberlei
d46910f6bb Merge branch 'XML-IndexBy-Fix' into 2.1.x 2011-10-31 22:11:51 +01:00
Guilherme Blanco
5c4e3d9bd0 Fixed bug in XMLDriver where relation indexes are treathed as elements but documented as attributes. 2011-10-31 22:11:21 +01:00
Benjamin Eberlei
7d0d06cd9a Merge branch 'DDC-1346' into 2.1.x 2011-10-31 21:56:04 +01:00
Guilherme Blanco
e4f49231bf Added support for ResultVariable referencing in ArithmeticPrimary. Fixes DDC-1346. 2011-10-31 21:55:27 +01:00
Benjamin Eberlei
ca600f466e Merge branch 'DDC-1316' into 2.1.x 2011-10-31 21:48:23 +01:00
Guilherme Blanco
01d6b73f78 Small changes for code readability. Added type binding in JoinedSubclassPersister, which was missing. Fixes DDC-1316. 2011-10-31 21:47:45 +01:00
Benjamin Eberlei
c36b1f1d00 Merge branch 'DDC-1462' into 2.1.x 2011-10-31 21:35:38 +01:00
Benjamin Eberlei
588f7fc765 DDC-1462 - Fix bug in slice when calling on a dirty collection that is marked extra lazy 2011-10-31 21:34:53 +01:00
Benjamin Eberlei
1becb8dfd5 Merge branch 'DDC-1399' into 2.1.x 2011-10-31 21:17:56 +01:00
Benjamin Eberlei
394447d92d DDC-1399 - Fix extra lazy collections when inner collection contains values but persistent collection is marked not dirty because of flush() 2011-10-31 21:17:24 +01:00
Benjamin Eberlei
f834c61a2b Merge branch 'DDC-1435' into 2.1.x 2011-10-29 12:08:00 +02:00
Guilherme Blanco
67cf5b7160 Implemented support to entities with association marked as @Id support in many situations. Fixed DDC-1435. 2011-10-29 12:07:32 +02:00
Benjamin Eberlei
e3a04c3458 Merge branch 'DQLFixes' into 2.1.x 2011-10-29 11:56:55 +02:00
Benjamin Eberlei
cb7e27460e DDC-1079 - SimpleSelectExpression related fixes and refactorings in SqlWalker 2011-10-29 11:55:16 +02:00
Benjamin Eberlei
9dc5c5ddce Merge branch 'DDC-1410' into 2.1.x 2011-10-28 23:47:25 +02:00
Benjamin Eberlei
bbd01ea457 DDC-1410 - Remove code-inlining that caused problems 2011-10-28 23:45:57 +02:00
Benjamin Eberlei
360c578c5d DDC-1454 - Fix exists() with Joined table inheritance 2011-10-28 00:59:25 +02:00
Benjamin Eberlei
ca289bf1ea Merge branch 'DDC-1358' into 2.1.x 2011-10-16 11:17:53 +02:00
Benjamin Eberlei
17341cf051 DDC-1358 - Fix bug where multiple NULL root entity combined with scalar results will break the object and array hydrator.
This case likeli only occurs when doing native queries. A guard clause that prevents hydration from breaking
when RIGHT JOIN queries with null root entities appear has been added aswell.
2011-10-16 11:17:25 +02:00
Benjamin Eberlei
5638b8c5c0 Add tests for DDC-1389 to 2.1.x 2011-10-16 09:37:22 +02:00
Benjamin Eberlei
ccaf00cfab Merge remote-tracking branch 'origin/2.1.x' into 2.1.x 2011-10-15 19:58:54 +02:00
Benjamin Eberlei
b57f4ecb52 Merge pull request #123 from lenar/2.1.x
Fix couple of bugs with composite keys
2011-10-15 10:58:11 -07:00
Benjamin Eberlei
7ae96dd9c7 Merge branch 'DDC-1383' into 2.1.x 2011-10-15 19:16:04 +02:00
Benjamin Eberlei
0b3b4ecef6 DDC-1383 - Proxy Generation in merge was flawed with inheritance 2011-10-15 19:15:43 +02:00
Benjamin Eberlei
8e3fc30477 Merge branch 'DDC-1411' into 2.1.x 2011-10-15 17:53:47 +02:00
Benjamin Eberlei
db2c51d860 DDC-1411 - Fixed onDelete handling in EntityGenerator 2011-10-15 17:53:25 +02:00
Benjamin Eberlei
b6ac1b4962 Merge branch 'DDC-1414' into 2.1.x 2011-10-15 17:47:48 +02:00
Benjamin Eberlei
7734e3e2ec DDC-1414 - Missing push to $newNodes 2011-10-15 17:47:41 +02:00
Benjamin Eberlei
45494fd6a0 Merge branch 'DDC-1421' into 2.1.x 2011-10-15 17:01:54 +02:00
Benjamin Eberlei
efad33a70e DDC-1421 - Fix potential bug and code-smells 2011-10-15 17:01:39 +02:00
Benjamin Eberlei
ba9feb1519 Fix notice 2011-10-15 16:02:32 +02:00
Benjamin Eberlei
4683d76c52 Remove unncessary line 2011-10-15 15:59:17 +02:00
Benjamin Eberlei
4ddd4a68ef Merge branch 'DDC-1386' into 2.1.x 2011-10-15 15:53:53 +02:00
Benjamin Eberlei
d9f9228d95 Add way to keep track of read only objects in the UnitOfWork which are never updated during flush.
Changed the behavior of EntityManager#getPartialReference to be read-only. No changes are ever
done to this entities. Changed UnitOfWork#computeChangeSet to never create a changeset for
fields that are partially omitted from a DQL or NativeQuery.

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

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

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
build.properties
build/
logs/
reports/

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

11
build.properties Normal file
View File

@@ -0,0 +1,11 @@
# Project Name
project.name=DoctrineORM
# Dependency minimum versions
dependencies.common=2.1.0
dependencies.dbal=2.1.0
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

@@ -1,15 +0,0 @@
version=2.0.0BETA2
dependencies.common=2.0.0BETA4
dependencies.dbal=2.0.0BETA4
stability=beta
build.dir=build
dist.dir=dist
report.dir=reports
log.archive.dir=logs
project.pirum_dir=
project.download_dir=
test.phpunit_configuration_file=
test.phpunit_generate_coverage=0
test.pmd_reports=0
test.pdepend_exec=
test.phpmd_exec=

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.1.99" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" maximum_version="2.1.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.1.7",
"description": "Object-Relational-Mapper for PHP",
"keywords": ["orm", "database"],
"homepage": "http://www.doctrine-project.org",
"license": "LGPL",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
"php": ">=5.3.2",
"ext-pdo": "*",
"doctrine/common": "2.1.*",
"doctrine/dbal": "2.1.*"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }
}
}

View File

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

View File

@@ -76,7 +76,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;
@@ -122,7 +122,7 @@ abstract class AbstractQuery
/**
* 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 +132,7 @@ abstract class AbstractQuery
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return Doctrine\ORM\EntityManager
* @return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
@@ -162,6 +162,16 @@ abstract class AbstractQuery
{
return $this->_params;
}
/**
* Get all defined parameter types.
*
* @return array The defined query parameter types.
*/
public function getParameterTypes()
{
return $this->_paramTypes;
}
/**
* Gets a query parameter.
@@ -174,6 +184,17 @@ abstract class AbstractQuery
return isset($this->_params[$key]) ? $this->_params[$key] : null;
}
/**
* Gets a query parameter type.
*
* @param mixed $key The key (index or name) of the bound parameter.
* @return mixed The parameter type of the bound parameter.
*/
public function getParameterType($key)
{
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null;
}
/**
* Gets the SQL query that corresponds to this query object.
* The returned SQL syntax depends on the connection driver that is used
@@ -191,7 +212,7 @@ 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)
{
@@ -210,7 +231,7 @@ abstract class AbstractQuery
*
* @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())
{
@@ -228,7 +249,7 @@ 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)
{
@@ -239,8 +260,8 @@ abstract class AbstractQuery
/**
* Defines a cache driver to be used for caching result sets.
*
* @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery
* @param \Doctrine\Common\Cache\Cache $driver Cache driver
* @return \Doctrine\ORM\AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
@@ -257,7 +278,7 @@ abstract class AbstractQuery
/**
* Returns the cache driver used for caching result sets.
*
* @return Doctrine\Common\Cache\Cache Cache driver
* @return \Doctrine\Common\Cache\Cache Cache driver
*/
public function getResultCacheDriver()
{
@@ -275,7 +296,7 @@ abstract class AbstractQuery
* @param boolean $bool
* @param integer $timeToLive
* @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)
{
@@ -293,7 +314,7 @@ abstract class AbstractQuery
* 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.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheLifetime($timeToLive)
{
@@ -319,7 +340,7 @@ abstract class AbstractQuery
* 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)
{
@@ -362,7 +383,7 @@ 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)
{
@@ -490,7 +511,7 @@ 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)
{
@@ -600,7 +621,7 @@ abstract class AbstractQuery
* generated for you.
*
* @param string $id
* @return Doctrine\ORM\AbstractQuery This query instance.
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheId($id)
{
@@ -646,7 +667,7 @@ abstract class AbstractQuery
/**
* 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

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

View File

@@ -43,21 +43,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 +71,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 +92,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;
@@ -114,9 +114,9 @@ class EntityManager implements ObjectManager
* 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,7 +128,7 @@ class EntityManager implements ObjectManager
$this->metadataFactory = new $metadataFactoryClassName;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
@@ -139,7 +139,7 @@ class EntityManager implements ObjectManager
/**
* Gets the database connection object used by the EntityManager.
*
* @return Doctrine\DBAL\Connection
* @return \Doctrine\DBAL\Connection
*/
public function getConnection()
{
@@ -149,7 +149,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,7 +168,7 @@ 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()
{
@@ -203,18 +203,18 @@ class EntityManager implements ObjectManager
public function transactional(Closure $func)
{
$this->conn->beginTransaction();
try {
$return = $func($this);
$this->flush();
$this->conn->commit();
return $return ?: true;
} catch (Exception $e) {
$this->close();
$this->conn->rollback();
throw $e;
}
}
@@ -244,12 +244,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,7 +261,7 @@ 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 = "")
{
@@ -276,7 +276,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)
{
@@ -302,7 +302,7 @@ 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)
{
@@ -325,7 +325,7 @@ 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
* @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that
* makes use of optimistic locking fails.
*/
public function flush()
@@ -413,6 +413,7 @@ class EntityManager implements ObjectManager
$entity = $class->newInstance();
$class->setIdentifierValues($entity, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
$this->unitOfWork->markReadOnly($entity);
return $entity;
}
@@ -449,7 +450,7 @@ class EntityManager implements ObjectManager
*
* The entity will be entered into the database at or before transaction
* commit or as a result of the flush operation.
*
*
* NOTE: The persist operation always considers entities that are not yet known to
* this EntityManager as NEW. Do not pass detached entities to the persist operation.
*
@@ -600,7 +601,7 @@ class EntityManager implements ObjectManager
/**
* Gets the EventManager used by the EntityManager.
*
* @return Doctrine\Common\EventManager
* @return \Doctrine\Common\EventManager
*/
public function getEventManager()
{
@@ -610,7 +611,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 +632,7 @@ class EntityManager implements ObjectManager
/**
* Check if the Entity manager is open or closed.
*
*
* @return bool
*/
public function isOpen()
@@ -642,7 +643,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 +657,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,7 +672,7 @@ 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)
{
@@ -712,6 +713,14 @@ class EntityManager implements ObjectManager
return $this->proxyFactory;
}
/**
* {@inheritDoc}
*/
public function initializeObject($entity)
{
$this->unitOfWork->initializeObject($entity);
}
/**
* Factory method to create EntityManager instances.
*

View File

@@ -48,7 +48,7 @@ class EntityRepository implements ObjectRepository
protected $_em;
/**
* @var Doctrine\ORM\Mapping\ClassMetadata
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
protected $_class;
@@ -112,7 +112,7 @@ class EntityRepository implements ObjectRepository
if (!($entity instanceof $this->_class->name)) {
return null;
}
if ($lockMode != LockMode::NONE) {
$this->_em->lock($entity, $lockMode, $lockVersion);
}
@@ -141,7 +141,7 @@ class EntityRepository implements ObjectRepository
if (!$this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
}
}
@@ -241,4 +241,4 @@ class EntityRepository implements ObjectRepository
{
return $this->_class;
}
}
}

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

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.
*/

View File

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

View File

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

View File

@@ -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

@@ -24,7 +24,8 @@ use PDO,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Query,
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.
@@ -62,7 +63,7 @@ class ObjectHydrator extends AbstractHydrator
if (!isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
@@ -82,26 +83,18 @@ class ObjectHydrator extends AbstractHydrator
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
if ($sourceClass->subClasses) {
foreach ($sourceClass->subClasses as $sourceSubclassName) {
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
}
}
$this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true;
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
// Mark any non-collection opposite sides as fetched, too.
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
$this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
} else {
if ($assoc['inversedBy']) {
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
if ($class->subClasses) {
foreach ($class->subClasses as $targetSubclassName) {
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
}
}
$this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
}
}
}
@@ -116,13 +109,13 @@ class ObjectHydrator extends AbstractHydrator
protected function _cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup();
$this->_identifierMap =
$this->_initializedCollections =
$this->_existingCollections =
$this->_resultPointers = array();
if ($eagerLoad) {
$this->_em->getUnitOfWork()->triggerEagerLoads();
}
@@ -154,7 +147,7 @@ class ObjectHydrator extends AbstractHydrator
* @param object $entity The entity to which the collection belongs.
* @param string $name The name of the field on the entity that holds the collection.
*/
private function _initRelatedCollection($entity, $class, $fieldName)
private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
{
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
@@ -175,7 +168,7 @@ class ObjectHydrator extends AbstractHydrator
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
! $value->isInitialized()) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
@@ -192,7 +185,7 @@ class ObjectHydrator extends AbstractHydrator
/**
* Gets an entity instance.
*
*
* @param $data The instance data.
* @param $dqlAlias The DQL alias of the entity's class.
* @return object The entity.
@@ -205,18 +198,33 @@ class ObjectHydrator extends AbstractHydrator
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
$class = $this->_ce[$className];
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
$this->_hints['fetchAlias'] = $dqlAlias;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
{
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_ce[$className];
/* @var $class ClassMetadata */
if ($class->isIdentifierComposite) {
$idHash = '';
foreach ($class->identifier as $fieldName) {
$idHash .= $data[$fieldName] . ' ';
if (isset($class->associationMappings[$fieldName])) {
$idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
} else {
$idHash .= $data[$fieldName] . ' ';
}
}
return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
} else if (isset($class->associationMappings[$class->identifier[0]])) {
return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
} else {
return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
}
@@ -226,7 +234,7 @@ class ObjectHydrator extends AbstractHydrator
* Gets a ClassMetadata instance from the local cache.
* If the instance is not yet in the local cache, it is loaded into the
* local cache.
*
*
* @param string $className The name of the class.
* @return ClassMetadata
*/
@@ -240,21 +248,21 @@ class ObjectHydrator extends AbstractHydrator
/**
* Hydrates a single row in an SQL result set.
*
*
* @internal
* First, the data of the row is split into chunks where each chunk contains data
* that belongs to a particular component/class. Afterwards, all these chunks
* are processed, one after the other. For each chunk of class data only one of the
* following code paths is executed:
*
*
* Path A: The data chunk belongs to a joined/associated object and the association
* is collection-valued.
* Path B: The data chunk belongs to a joined/associated object and the association
* is single-valued.
* Path C: The data chunk belongs to a root result element/object that appears in the topmost
* level of the hydrated result. A typical example are the objects of the type
* specified by the FROM clause in a DQL query.
*
* specified by the FROM clause in a DQL query.
*
* @param array $data The data of the row to process.
* @param array $cache The cache to use.
* @param array $result The result array to fill.
@@ -288,10 +296,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 +321,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]]);
@@ -348,16 +363,18 @@ 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);
@@ -383,6 +400,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 {
@@ -394,6 +413,18 @@ class ObjectHydrator extends AbstractHydrator
// PATH C: Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array(0 => null);
} else {
$result[] = null;
}
++$this->_resultCounter;
continue;
}
// check for existing result from the iterations before
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {

View File

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

View File

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

View File

@@ -119,7 +119,7 @@ class ClassMetadataInfo implements ClassMetadata
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
* association is fetched.
*/
const FETCH_EAGER = 3;
/**
@@ -206,7 +206,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The named queries allowed to be called directly from Repository.
*
*
* @var array
*/
public $namedQueries = array();
@@ -361,7 +361,7 @@ class ClassMetadataInfo implements ClassMetadata
* - <b>mappedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the owning side.
* This key must be specified on the inverse side of a bidirectional association.
*
*
* - <b>inversedBy</b> (string, required for bidirectional associations)
* The name of the field that completes the bidirectional association on the inverse side.
* This key must be specified on the owning side of a bidirectional association.
@@ -388,7 +388,7 @@ class ClassMetadataInfo implements ClassMetadata
* Specification of a field on target-entity that is used to index the collection by.
* This field HAS to be either the primary key or a unique column. Otherwise the collection
* does not contain all the entities that are actually related.
*
*
* A join table definition has the following structure:
* <pre>
* array(
@@ -430,7 +430,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* READ-ONLY: The definition of the sequence generator of this class. Only used for the
* SEQUENCE generation strategy.
*
*
* The definition has the following structure:
* <code>
* array(
@@ -774,9 +774,13 @@ class ClassMetadataInfo implements ClassMetadata
// If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity.
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
&& strlen($this->namespace) > 0) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
if (isset($mapping['targetEntity'])) {
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
}
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
// Complete id mapping
@@ -809,7 +813,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($mapping['targetEntity'])) {
throw MappingException::missingTargetEntity($mapping['fieldName']);
}
// Mandatory and optional attributes for either side
if ( ! $mapping['mappedBy']) {
if (isset($mapping['joinTable']) && $mapping['joinTable']) {
@@ -825,7 +829,7 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
}
// Fetch mode. Default fetch mode to LAZY, if not set.
if ( ! isset($mapping['fetch'])) {
$mapping['fetch'] = self::FETCH_LAZY;
@@ -848,7 +852,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
return $mapping;
}
@@ -862,11 +866,11 @@ class ClassMetadataInfo implements ClassMetadata
protected function _validateAndCompleteOneToOneMapping(array $mapping)
{
$mapping = $this->_validateAndCompleteAssociationMapping($mapping);
if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
$mapping['isOwningSide'] = true;
}
if ($mapping['isOwningSide']) {
if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
// Apply default join column
@@ -880,7 +884,9 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
if ($mapping['type'] === self::ONE_TO_ONE) {
if (count($mapping['joinColumns']) == 1) {
$joinColumn['unique'] = true;
if (! isset($mapping['id']) || ! $mapping['id']) {
$joinColumn['unique'] = true;
}
} else {
$uniqueContraintColumns[] = $joinColumn['name'];
}
@@ -908,9 +914,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 +939,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 +966,7 @@ class ClassMetadataInfo implements ClassMetadata
} else {
$targetShortName = strtolower($mapping['targetEntity']);
}
// owning side MUST have a join table
if ( ! isset($mapping['joinTable']['name'])) {
$mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
@@ -1113,23 +1117,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 +1240,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 +1251,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)
{
@@ -1625,18 +1629,20 @@ class ClassMetadataInfo implements ClassMetadata
public function setDiscriminatorMap(array $map)
{
foreach ($map as $value => $className) {
if (strpos($className, '\\') === false && strlen($this->namespace)) {
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
$className = $this->namespace . '\\' . $className;
}
$className = ltrim($className, '\\');
$this->discriminatorMap[$value] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name)) {
if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
$this->subClasses[] = $className;
}
}
@@ -1708,7 +1714,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Return the single association join column (if any).
*
*
* @param string $fieldName
* @return string
*/
@@ -1750,7 +1756,7 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($this->associationMappings AS $assocName => $mapping) {
if ($this->isAssociationWithSingleJoinColumn($assocName) &&
$this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
return $assocName;
}
}
@@ -1840,34 +1846,34 @@ class ClassMetadataInfo implements ClassMetadata
{
$this->isReadOnly = true;
}
/**
* A numerically indexed list of field names of this persistent class.
*
*
* This array includes identifier fields if present on this class.
*
*
* @return array
*/
public function getFieldNames()
{
return array_keys($this->fieldMappings);
}
/**
* A numerically indexed list of association names of this persistent class.
*
*
* This array includes identifier associations if present on this class.
*
*
* @return array
*/
public function getAssociationNames()
{
return array_keys($this->associationMappings);
}
/**
* Returns the target class name of the given association.
*
*
* @param string $assocName
* @return string
*/
@@ -1878,10 +1884,10 @@ class ClassMetadataInfo implements ClassMetadata
}
return $this->associationMappings[$assocName]['targetEntity'];
}
/**
* Get fully-qualified class name of this persistent class.
*
*
* @return string
*/
public function getName()
@@ -1889,10 +1895,46 @@ class ClassMetadataInfo implements ClassMetadata
return $this->name;
}
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* Gets the (possibly quoted) column name of a mapped field for safe use
* in an SQL statement.
*
*
* @param string $field
* @param AbstractPlatform $platform
* @return string
@@ -1903,11 +1945,11 @@ class ClassMetadataInfo implements ClassMetadata
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
$this->fieldMappings[$field]['columnName'];
}
/**
* Gets the (possibly quoted) primary table name of this class for safe use
* in an SQL statement.
*
*
* @param AbstractPlatform $platform
* @return string
*/
@@ -1930,4 +1972,14 @@ class ClassMetadataInfo implements ClassMetadata
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
}
public function isAssociationInverseSide($fieldName)
{
return isset($this->associationMappings[$fieldName]) && !$this->associationMappings[$fieldName]['isOwningSide'];
}
public function getAssociationMappedByTargetField($fieldName)
{
return $this->associationMappings[$fieldName]['mappedBy'];
}
}

View File

@@ -185,12 +185,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 +292,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

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

View File

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

View File

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

View File

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

View File

@@ -66,7 +66,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,21 +93,21 @@ final class PersistentCollection implements Collection
/**
* Whether the collection has already been initialized.
*
*
* @var boolean
*/
private $initialized = true;
/**
* The wrapped Collection instance.
*
*
* @var Collection
*/
private $coll;
/**
* Creates a new persistent collection.
*
*
* @param EntityManager $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param array The collection elements.
@@ -144,7 +144,7 @@ final class PersistentCollection implements Collection
{
return $this->owner;
}
public function getTypeClass()
{
return $this->typeClass;
@@ -154,7 +154,7 @@ final class PersistentCollection implements Collection
* INTERNAL:
* Adds an element to a collection during hydration. This will automatically
* complete bidirectional associations in the case of a one-to-many association.
*
*
* @param mixed $element The element to add.
*/
public function hydrateAdd($element)
@@ -172,7 +172,7 @@ final class PersistentCollection implements Collection
$this->owner);
}
}
/**
* INTERNAL:
* Sets a keyed element in the collection during hydration.
@@ -265,13 +265,13 @@ final class PersistentCollection implements Collection
/**
* 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.
*/
@@ -280,7 +280,7 @@ final class PersistentCollection implements Collection
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->owner && $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
}
}
@@ -306,17 +306,17 @@ final class PersistentCollection implements Collection
{
$this->isDirty = $dirty;
}
/**
* Sets the initialized flag of the collection, forcing it into that state.
*
*
* @param boolean $bool
*/
public function setInitialized($bool)
{
$this->initialized = $bool;
}
/**
* Checks whether this collection has been initialized.
*
@@ -377,7 +377,7 @@ final class PersistentCollection implements Collection
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
->deleteRows($this, $element);
}*/
$this->initialize();
$removed = $this->coll->removeElement($element);
if ($removed) {
@@ -410,7 +410,7 @@ final class PersistentCollection implements Collection
->getCollectionPersister($this->association)
->contains($this, $element);
}
$this->initialize();
return $this->coll->contains($element);
}
@@ -468,7 +468,7 @@ final class PersistentCollection implements Collection
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->count($this) + $this->coll->count();
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
}
$this->initialize();
@@ -503,7 +503,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->isEmpty();
}
/**
* {@inheritdoc}
*/
@@ -530,7 +530,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->filter($p);
}
/**
* {@inheritdoc}
*/
@@ -548,7 +548,7 @@ final class PersistentCollection implements Collection
$this->initialize();
return $this->coll->partition($p);
}
/**
* {@inheritdoc}
*/
@@ -567,6 +567,9 @@ final class PersistentCollection implements Collection
return;
}
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
// hence for event listeners we need the objects in memory.
$this->initialize();
foreach ($this->coll as $element) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
}
@@ -579,7 +582,7 @@ final class PersistentCollection implements Collection
$this->takeSnapshot();
}
}
/**
* Called by PHP when this collection is serialized. Ensures that only the
* elements are properly serialized.
@@ -591,7 +594,7 @@ final class PersistentCollection implements Collection
{
return array('coll', 'initialized');
}
/* ArrayAccess implementation */
/**
@@ -629,12 +632,12 @@ final class PersistentCollection implements Collection
{
return $this->remove($offset);
}
public function key()
{
return $this->coll->key();
}
/**
* Gets the element of the collection at the current iterator position.
*/
@@ -642,7 +645,7 @@ final class PersistentCollection implements Collection
{
return $this->coll->current();
}
/**
* Moves the internal iterator position to the next element.
*/
@@ -650,7 +653,7 @@ final class PersistentCollection implements Collection
{
return $this->coll->next();
}
/**
* Retrieves the wrapped Collection instance.
*/
@@ -672,7 +675,10 @@ final class PersistentCollection implements Collection
*/
public function slice($offset, $length = null)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
if ( ! $this->initialized &&
! $this->isDirty &&
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->slice($this, $offset, $length);
@@ -681,4 +687,28 @@ final class PersistentCollection implements Collection
$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()
{
if (is_object($this->coll)) {
$this->coll = clone $this->coll;
}
$this->initialize();
$this->owner = null;
$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,9 +98,11 @@ abstract class AbstractCollectionPersister
public function update(PersistentCollection $coll)
{
$mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side
}
$this->deleteRows($coll);
//$this->updateRows($coll);
$this->insertRows($coll);
@@ -108,6 +112,7 @@ abstract class AbstractCollectionPersister
{
$deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
}
@@ -120,6 +125,7 @@ abstract class AbstractCollectionPersister
{
$insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
}
@@ -188,4 +194,4 @@ abstract class AbstractCollectionPersister
* @param mixed $element
*/
abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -49,9 +49,9 @@ abstract class AbstractSqlExecutor
/**
* Executes all sql statements.
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param \Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @return Doctrine\DBAL\Driver\Statement
* @return \Doctrine\DBAL\Driver\Statement
*/
abstract public function execute(Connection $conn, array $params, array $types);
}

View File

@@ -98,13 +98,13 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
}
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
}
/**
* Executes all SQL statements.
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param \Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/

View File

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

View File

@@ -73,7 +73,7 @@ class Expr
* Creates an ASCending order expression.
*
* @param $sort
* @return OrderBy
* @return Expr\OrderBy
*/
public function asc($expr)
{
@@ -84,7 +84,7 @@ class Expr
* Creates a DESCending order expression.
*
* @param $sort
* @return OrderBy
* @return Expr\OrderBy
*/
public function desc($expr)
{
@@ -560,6 +560,8 @@ class Expr
{
if (is_numeric($literal) && !is_string($literal)) {
return (string) $literal;
} else if (is_bool($literal)) {
return $literal ? "true" : "false";
} else {
return "'" . str_replace("'", "''", $literal) . "'";
}

View File

@@ -39,30 +39,30 @@ class Composite extends Base
if ($this->count() === 1) {
return (string) $this->_parts[0];
}
$components = array();
foreach ($this->_parts as $part) {
$components[] = $this->processQueryPart($part);
}
return implode($this->_separator, $components);
}
private function processQueryPart($part)
{
$queryPart = (string) $part;
if (is_object($part) && $part instanceof self && $part->count() > 1) {
return $this->_preSeparator . $queryPart . $this->_postSeparator;
}
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
if (mb_stripos($queryPart, ' OR ') !== false || mb_stripos($queryPart, ' AND ') !== false) {
if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) {
return $this->_preSeparator . $queryPart . $this->_postSeparator;
}
return $queryPart;
}
}
}

View File

@@ -37,8 +37,8 @@ class ParameterTypeInferer
{
/**
* Infer type of a given value, returning a compatible constant:
* - Type (Doctrine\DBAL\Types\Type::*)
* - Connection (Doctrine\DBAL\Connection::PARAM_*)
* - Type (\Doctrine\DBAL\Types\Type::*)
* - Connection (\Doctrine\DBAL\Connection::PARAM_*)
*
* @param mixed $value Parameter value
*

View File

@@ -75,14 +75,14 @@ class Parser
/**
* The lexer.
*
* @var Doctrine\ORM\Query\Lexer
* @var \Doctrine\ORM\Query\Lexer
*/
private $_lexer;
/**
* The parser result.
*
* @var Doctrine\ORM\Query\ParserResult
* @var \Doctrine\ORM\Query\ParserResult
*/
private $_parserResult;
@@ -170,7 +170,7 @@ class Parser
/**
* Gets the lexer used by the parser.
*
* @return Doctrine\ORM\Query\Lexer
* @return \Doctrine\ORM\Query\Lexer
*/
public function getLexer()
{
@@ -180,7 +180,7 @@ class Parser
/**
* Gets the ParserResult that is being filled with information during parsing.
*
* @return Doctrine\ORM\Query\ParserResult
* @return \Doctrine\ORM\Query\ParserResult
*/
public function getParserResult()
{
@@ -1466,7 +1466,7 @@ class Parser
/**
* RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
*
* @return Doctrine\ORM\Query\AST\RangeVariableDeclaration
* @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
*/
public function RangeVariableDeclaration()
{
@@ -1537,7 +1537,7 @@ class Parser
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression
* ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression]
*
* @return Doctrine\ORM\Query\AST\Join
* @return \Doctrine\ORM\Query\AST\Join
*/
public function Join()
{
@@ -1607,7 +1607,7 @@ class Parser
/**
* IndexBy ::= "INDEX" "BY" StateFieldPathExpression
*
* @return Doctrine\ORM\Query\AST\IndexBy
* @return \Doctrine\ORM\Query\AST\IndexBy
*/
public function IndexBy()
{
@@ -1698,7 +1698,7 @@ class Parser
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @return Doctrine\ORM\Query\AST\CoalesceExpression
* @return \Doctrine\ORM\Query\AST\CoalesceExpression
*/
public function CoalesceExpression()
{
@@ -1722,7 +1722,7 @@ class Parser
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @return Doctrine\ORM\Query\AST\ExistsExpression
* @return \Doctrine\ORM\Query\AST\ExistsExpression
*/
public function NullIfExpression()
{
@@ -1743,7 +1743,7 @@ class Parser
* IdentificationVariable | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
*
* @return Doctrine\ORM\Query\AST\SelectExpression
* @return \Doctrine\ORM\Query\AST\SelectExpression
*/
public function SelectExpression()
{
@@ -1967,7 +1967,7 @@ class Parser
/**
* ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
*
* @return Doctrine\ORM\Query\AST\ConditionalPrimary
* @return \Doctrine\ORM\Query\AST\ConditionalPrimary
*/
public function ConditionalPrimary()
{
@@ -2290,7 +2290,7 @@ class Parser
/**
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
*/
public function ArithmeticPrimary()
{
@@ -2314,7 +2314,11 @@ class Parser
if ($peek['value'] == '.') {
return $this->SingleValuedPathExpression();
}
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
return $this->ResultVariable();
}
return $this->StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER:

View File

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

View File

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

View File

@@ -35,6 +35,11 @@ use Doctrine\DBAL\LockMode,
*/
class SqlWalker implements TreeWalker
{
/**
* @var string
*/
const HINT_DISTINCT = 'doctrine.distinct';
/**
* @var ResultSetMapping
*/
@@ -57,7 +62,7 @@ class SqlWalker implements TreeWalker
private $_em;
/**
* @var Doctrine\DBAL\Connection
* @var \Doctrine\DBAL\Connection
*/
private $_conn;
@@ -157,32 +162,24 @@ class SqlWalker implements TreeWalker
*/
public function getExecutor($AST)
{
$isDeleteStatement = $AST instanceof AST\DeleteStatement;
$isUpdateStatement = $AST instanceof AST\UpdateStatement;
switch (true) {
case ($AST instanceof AST\DeleteStatement):
$primaryClass = $this->_em->getClassMetadata($AST->deleteClause->abstractSchemaName);
if ($isDeleteStatement) {
$primaryClass = $this->_em->getClassMetadata(
$AST->deleteClause->abstractSchemaName
);
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableDeleteExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
if ($primaryClass->isInheritanceTypeJoined()) {
return new Exec\MultiTableDeleteExecutor($AST, $this);
} else {
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
} else if ($isUpdateStatement) {
$primaryClass = $this->_em->getClassMetadata(
$AST->updateClause->abstractSchemaName
);
case ($AST instanceof AST\UpdateStatement):
$primaryClass = $this->_em->getClassMetadata($AST->updateClause->abstractSchemaName);
if ($primaryClass->isInheritanceTypeJoined()) {
return new Exec\MultiTableUpdateExecutor($AST, $this);
} else {
return new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
}
return ($primaryClass->isInheritanceTypeJoined())
? new Exec\MultiTableUpdateExecutor($AST, $this)
: new Exec\SingleTableDeleteUpdateExecutor($AST, $this);
default:
return new Exec\SingleSelectExecutor($AST, $this);
}
return new Exec\SingleSelectExecutor($AST, $this);
}
/**
@@ -255,13 +252,10 @@ class SqlWalker implements TreeWalker
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName
. ' = '
. $tableAlias . '.' . $columnName;
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
}
@@ -273,13 +267,11 @@ class SqlWalker implements TreeWalker
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform)
. ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName
. ' = '
. $tableAlias . '.' . $columnName;
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
}
}
@@ -341,8 +333,7 @@ class SqlWalker implements TreeWalker
$encapsulate = true;
}
$sql .= ($sql != '' ? ' AND ' : '')
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '')
$sql .= (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '')
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
}
}
@@ -510,7 +501,7 @@ class SqlWalker implements TreeWalker
throw QueryException::associationPathInverseSideNotSupported();
}
break;
default:
throw QueryException::invalidPathExpression($pathExpr);
}
@@ -526,9 +517,12 @@ class SqlWalker implements TreeWalker
*/
public function walkSelectClause($selectClause)
{
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode(
', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)
);
$sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '');
$sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions));
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) {
$this->_query->setHint(self::HINT_DISTINCT, true);
}
$addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) &&
$this->_query->getHydrationMode() == Query::HYDRATE_OBJECT
@@ -554,7 +548,8 @@ class SqlWalker implements TreeWalker
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
$sql .= ", $tblAlias." . $discrColumn['name'] . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
@@ -571,10 +566,12 @@ class SqlWalker implements TreeWalker
} else {
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
}
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
@@ -589,7 +586,9 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
@@ -599,6 +598,8 @@ class SqlWalker implements TreeWalker
}
}
$sql .= implode(', ', $sqlSelectExpressions);
return $sql;
}
@@ -617,7 +618,7 @@ class SqlWalker implements TreeWalker
$rangeDecl = $identificationVariableDecl->rangeVariableDeclaration;
$dqlAlias = $rangeDecl->aliasIdentificationVariable;
$this->_rootAliases[] = $dqlAlias;
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
@@ -743,8 +744,7 @@ class SqlWalker implements TreeWalker
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true) {
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) {
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
}
@@ -873,7 +873,7 @@ class SqlWalker implements TreeWalker
return $sql;
}
/**
* Walks down a CoalesceExpression AST node and generates the corresponding SQL.
*
@@ -883,32 +883,32 @@ class SqlWalker implements TreeWalker
public function walkCoalesceExpression($coalesceExpression)
{
$sql = 'COALESCE(';
$scalarExpressions = array();
foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
$scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
}
$sql .= implode(', ', $scalarExpressions) . ')';
return $sql;
}
public function walkCaseExpression($expression)
{
switch (true) {
case ($expression instanceof AST\CoalesceExpression):
return $this->walkCoalesceExpression($expression);
case ($expression instanceof AST\NullIfExpression):
return $this->walkNullIfExpression($expression);
default:
return '';
}
}
/**
* Walks down a NullIfExpression AST node and generates the corresponding SQL.
*
@@ -917,14 +917,14 @@ class SqlWalker implements TreeWalker
*/
public function walkNullIfExpression($nullIfExpression)
{
$firstExpression = is_string($nullIfExpression->firstExpression)
$firstExpression = is_string($nullIfExpression->firstExpression)
? $this->_conn->quote($nullIfExpression->firstExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
$secondExpression = is_string($nullIfExpression->secondExpression)
$secondExpression = is_string($nullIfExpression->secondExpression)
? $this->_conn->quote($nullIfExpression->secondExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
}
@@ -961,7 +961,7 @@ class SqlWalker implements TreeWalker
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
@@ -1024,13 +1024,13 @@ class SqlWalker implements TreeWalker
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
if ($expr instanceof AST\Literal) {
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
} else {
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
}
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
@@ -1047,9 +1047,9 @@ class SqlWalker implements TreeWalker
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
@@ -1227,67 +1227,77 @@ class SqlWalker implements TreeWalker
*/
public function walkSimpleSelectExpression($simpleSelectExpression)
{
$sql = '';
$expr = $simpleSelectExpression->expression;
$sql = ' ';
if ($expr instanceof AST\PathExpression) {
$sql .= $this->walkPathExpression($expr);
} else if ($expr instanceof AST\AggregateExpression) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
switch (true) {
case ($expr instanceof AST\PathExpression):
$sql .= $this->walkPathExpression($expr);
break;
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
} else if ($expr instanceof AST\Subselect) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
case ($expr instanceof AST\AggregateExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
break;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else if (
$expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor ||
$expr instanceof AST\ArithmeticPrimary
) {
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
$alias = $this->_scalarResultCounter++;
} else {
$alias = $simpleSelectExpression->fieldIdentificationVariable;
}
case ($expr instanceof AST\Subselect):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
} else {
// IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$first = true;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
foreach ($class->identifier as $identifier) {
if ($first) $first = false; else $sql .= ', ';
$sql .= $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform);
}
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
break;
case ($expr instanceof AST\Functions\FunctionNode):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\ArithmeticPrimary):
case ($expr instanceof AST\Literal):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
break;
default: // IdentificationVariable
$class = $this->_queryComponents[$expr]['metadata'];
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$sqlParts = array();
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $tableAlias . '.' . $columnName;
}
$sql .= implode(', ', $sqlParts);
break;
}
return ' ' . $sql;
return $sql;
}
/**
@@ -1310,25 +1320,24 @@ class SqlWalker implements TreeWalker
*/
public function walkGroupByClause($groupByClause)
{
$sql = '';
$sqlParts = array();
foreach ($groupByClause->groupByItems AS $groupByItem) {
if (is_string($groupByItem)) {
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
if ($sql != '') {
$sql .= ', ';
}
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
$sql .= $this->walkGroupByItem($groupByItem);
}
} else {
if ($sql != '') {
$sql .= ', ';
}
$sql .= $this->walkGroupByItem($groupByItem);
if ( ! is_string($groupByItem)) {
$sqlParts[] = $this->walkGroupByItem($groupByItem);
continue;
}
foreach ($this->_queryComponents[$groupByItem]['metadata']->identifier AS $idField) {
$groupByItem = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $idField);
$groupByItem->type = AST\PathExpression::TYPE_STATE_FIELD;
$sqlParts[] = $this->walkGroupByItem($groupByItem);
}
}
return ' GROUP BY ' . $sql;
return ' GROUP BY ' . implode(', ', $sqlParts);
}
/**
@@ -1350,12 +1359,11 @@ class SqlWalker implements TreeWalker
*/
public function walkDeleteClause(AST\DeleteClause $deleteClause)
{
$sql = 'DELETE FROM ';
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $deleteClause->aliasIdentificationVariable);
$class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable);
$this->_rootAliases[] = $deleteClause->aliasIdentificationVariable;
return $sql;
@@ -1369,17 +1377,14 @@ class SqlWalker implements TreeWalker
*/
public function walkUpdateClause($updateClause)
{
$sql = 'UPDATE ';
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($class->getTableName(), $class->getTableName(), $updateClause->aliasIdentificationVariable);
$class = $this->_em->getClassMetadata($updateClause->abstractSchemaName);
$tableName = $class->getTableName();
$sql = 'UPDATE ' . $class->getQuotedTableName($this->_platform);
$this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable);
$this->_rootAliases[] = $updateClause->aliasIdentificationVariable;
$sql .= ' SET ' . implode(
', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)
);
$sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems));
return $sql;
}
@@ -1395,16 +1400,21 @@ class SqlWalker implements TreeWalker
$useTableAliasesBefore = $this->_useSqlTableAliases;
$this->_useSqlTableAliases = false;
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$sql = $this->walkPathExpression($updateItem->pathExpression) . ' = ';
$newValue = $updateItem->newValue;
if ($newValue === null) {
$sql .= 'NULL';
} else if ($newValue instanceof AST\Node) {
$sql .= $newValue->dispatch($this);
} else {
$sql .= $this->_conn->quote($newValue);
switch (true) {
case ($newValue instanceof AST\Node):
$sql .= $newValue->dispatch($this);
break;
case ($newValue === null):
$sql .= 'NULL';
break;
default:
$sql .= $this->_conn->quote($newValue);
break;
}
$this->_useSqlTableAliases = $useTableAliasesBefore;
@@ -1423,7 +1433,15 @@ class SqlWalker implements TreeWalker
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases);
$condSql = $this->walkConditionalExpression($whereClause->conditionalExpression);
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
if ($condSql) {
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
}
if ($discrSql) {
return ' WHERE ' . $discrSql;
}
return '';
}
/**
@@ -1436,11 +1454,11 @@ class SqlWalker implements TreeWalker
{
// Phase 2 AST optimization: Skip processment of ConditionalExpression
// if only one ConditionalTerm is defined
return ( ! ($condExpr instanceof AST\ConditionalExpression))
? $this->walkConditionalTerm($condExpr)
: implode(
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
);
if ( ! ($condExpr instanceof AST\ConditionalExpression)) {
return $this->walkConditionalTerm($condExpr);
}
return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms));
}
/**
@@ -1453,11 +1471,11 @@ class SqlWalker implements TreeWalker
{
// Phase 2 AST optimization: Skip processment of ConditionalTerm
// if only one ConditionalFactor is defined
return ( ! ($condTerm instanceof AST\ConditionalTerm))
? $this->walkConditionalFactor($condTerm)
: implode(
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
);
if ( ! ($condTerm instanceof AST\ConditionalTerm)) {
return $this->walkConditionalFactor($condTerm);
}
return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors));
}
/**
@@ -1485,7 +1503,9 @@ class SqlWalker implements TreeWalker
{
if ($primary->isSimpleConditionalExpression()) {
return $primary->simpleConditionalExpression->dispatch($this);
} else if ($primary->isConditionalExpression()) {
}
if ($primary->isConditionalExpression()) {
$condExpr = $primary->conditionalExpression;
return '(' . $this->walkConditionalExpression($condExpr) . ')';
@@ -1519,12 +1539,12 @@ class SqlWalker implements TreeWalker
$sql .= 'EXISTS (SELECT 1 FROM ';
$entityExpr = $collMemberExpr->entityExpression;
$collPathExpr = $collMemberExpr->collectionValuedPathExpression;
$fieldName = $collPathExpr->field;
$dqlAlias = $collPathExpr->identificationVariable;
$class = $this->_queryComponents[$dqlAlias]['metadata'];
if ($entityExpr instanceof AST\InputParameter) {
$dqlParamKey = $entityExpr->name;
$entity = $this->_query->getParameter($dqlParamKey);
@@ -1532,42 +1552,41 @@ class SqlWalker implements TreeWalker
//TODO
throw new \BadMethodCallException("Not implemented");
}
$assoc = $class->associationMappings[$fieldName];
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$sql .= $targetClass->getQuotedTableName($this->_platform)
. ' ' . $targetTableAlias . ' WHERE ';
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
$first = true;
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform)
. ' = '
$sql .= $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform)
. ' = '
. $targetTableAlias . '.' . $sourceColumn;
}
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
} else { // many-to-many
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
$joinTable = $owningAssoc['joinTable'];
@@ -1575,13 +1594,13 @@ class SqlWalker implements TreeWalker
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
// join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform)
. ' ' . $joinTableAlias . ' INNER JOIN '
. $targetClass->getQuotedTableName($this->_platform)
. ' ' . $targetTableAlias . ' ON ';
// join conditions
$joinColumns = $assoc['isOwningSide']
? $joinTable['inverseJoinColumns']
@@ -1607,27 +1626,26 @@ class SqlWalker implements TreeWalker
foreach ($joinColumns as $joinColumn) {
if ($first) $first = false; else $sql .= ' AND ';
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
. $sourceTableAlias . '.' . $class->getQuotedColumnName(
$class->fieldNames[$joinColumn['referencedColumnName']],
$this->_platform);
}
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
}
return $sql . ')';
}
/**
* Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL.
*
@@ -1677,11 +1695,9 @@ class SqlWalker implements TreeWalker
$sql = $this->walkPathExpression($inExpr->pathExpression)
. ($inExpr->not ? ' NOT' : '') . ' IN (';
if ($inExpr->subselect) {
$sql .= $this->walkSubselect($inExpr->subselect);
} else {
$sql .= implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
}
$sql .= ($inExpr->subselect)
? $this->walkSubselect($inExpr->subselect)
: implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals));
$sql .= ')';
@@ -1716,11 +1732,11 @@ class SqlWalker implements TreeWalker
// We need to modify the parameter value to be its correspondent mapped value
$dqlParamKey = $instanceOfExpr->value->name;
$paramValue = $this->_query->getParameter($dqlParamKey);
if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) {
throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue));
}
$entityClassName = $paramValue->name;
} else {
// Get name from ClassMetadata to resolve aliases.
@@ -1734,7 +1750,7 @@ class SqlWalker implements TreeWalker
if (!isset($discrMap[$entityClassName])) {
throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName);
}
$sql .= $this->_conn->quote($discrMap[$entityClassName]);
}
@@ -1749,9 +1765,9 @@ class SqlWalker implements TreeWalker
*/
public function walkInParameter($inParam)
{
return $inParam instanceof AST\InputParameter ?
$this->walkInputParameter($inParam) :
$this->walkLiteral($inParam);
return $inParam instanceof AST\InputParameter
? $this->walkInputParameter($inParam)
: $this->walkLiteral($inParam);
}
/**
@@ -1840,23 +1856,19 @@ class SqlWalker implements TreeWalker
*/
public function walkComparisonExpression($compExpr)
{
$sql = '';
$leftExpr = $compExpr->leftExpression;
$leftExpr = $compExpr->leftExpression;
$rightExpr = $compExpr->rightExpression;
$sql = '';
if ($leftExpr instanceof AST\Node) {
$sql .= $leftExpr->dispatch($this);
} else {
$sql .= is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr);
}
$sql .= ($leftExpr instanceof AST\Node)
? $leftExpr->dispatch($this)
: (is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr));
$sql .= ' ' . $compExpr->operator . ' ';
if ($rightExpr instanceof AST\Node) {
$sql .= $rightExpr->dispatch($this);
} else {
$sql .= is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr);
}
$sql .= ($rightExpr instanceof AST\Node)
? $rightExpr->dispatch($this)
: (is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr));
return $sql;
}
@@ -1895,11 +1907,11 @@ class SqlWalker implements TreeWalker
*/
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
{
return ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression))
? $this->walkArithmeticTerm($simpleArithmeticExpr)
: implode(
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
);
if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) {
return $this->walkArithmeticTerm($simpleArithmeticExpr);
}
return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms));
}
/**
@@ -1911,16 +1923,22 @@ class SqlWalker implements TreeWalker
public function walkArithmeticTerm($term)
{
if (is_string($term)) {
if (isset($this->_queryComponents[$term])) {
$columnName = $this->_queryComponents[$term]['token']['value'];
return $this->_scalarResultAliasMap[$columnName];
}
return $term;
}
// Phase 2 AST optimization: Skip processment of ArithmeticTerm
// if only one ArithmeticFactor is defined
return ( ! ($term instanceof AST\ArithmeticTerm))
? $this->walkArithmeticFactor($term)
: implode(
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
);
if ( ! ($term instanceof AST\ArithmeticTerm)) {
return $this->walkArithmeticFactor($term);
}
return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors));
}
/**
@@ -1934,13 +1952,16 @@ class SqlWalker implements TreeWalker
if (is_string($factor)) {
return $factor;
}
// Phase 2 AST optimization: Skip processment of ArithmeticFactor
// if only one ArithmeticPrimary is defined
return ( ! ($factor instanceof AST\ArithmeticFactor))
? $this->walkArithmeticPrimary($factor)
: ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''))
. $this->walkArithmeticPrimary($factor->arithmeticPrimary);
if ( ! ($factor instanceof AST\ArithmeticFactor)) {
return $this->walkArithmeticPrimary($factor);
}
$sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : '');
return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary);
}
/**
@@ -1953,7 +1974,9 @@ class SqlWalker implements TreeWalker
{
if ($primary instanceof AST\SimpleArithmeticExpression) {
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
} else if ($primary instanceof AST\Node) {
}
if ($primary instanceof AST\Node) {
return $primary->dispatch($this);
}

View File

@@ -55,7 +55,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
/**
* Retrieve Query Instance reponsible for the current walkers execution.
*
* @return Doctrine\ORM\Query
* @return \Doctrine\ORM\Query
*/
protected function _getQuery()
{
@@ -65,7 +65,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
/**
* Retrieve ParserResult
*
* @return Doctrine\ORM\Query\ParserResult
* @return \Doctrine\ORM\Query\ParserResult
*/
protected function _getParserResult()
{

View File

@@ -50,6 +50,7 @@ class QueryBuilder
* @var array The array of DQL parts collected.
*/
private $_dqlParts = array(
'distinct' => false,
'select' => array(),
'from' => array(),
'join' => array(),
@@ -119,7 +120,7 @@ class QueryBuilder
* For more complex expression construction, consider storing the expression
* builder object in a local variable.
*
* @return Expr
* @return Query\Expr
*/
public function expr()
{
@@ -342,8 +343,8 @@ class QueryBuilder
* ->from('User', 'u')
* ->where('u.id = :user_id1 OR u.id = :user_id2')
* ->setParameters(array(
* ':user_id1' => 1,
* ':user_id2' => 2
* 'user_id1' => 1,
* 'user_id2' => 2
* ));
* </code>
*
@@ -502,6 +503,25 @@ class QueryBuilder
return $this->add('select', new Expr\Select($selects), false);
}
/**
* Add a DISTINCT flag to this query.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->distinct()
* ->from('User', 'u');
* </code>
*
* @param bool
* @return QueryBuilder
*/
public function distinct($flag = true)
{
$this->_dqlParts['distinct'] = (bool) $flag;
return $this;
}
/**
* Adds an item that is to be returned in the query result.
*
@@ -973,8 +993,10 @@ class QueryBuilder
private function _getDQLForSelect()
{
$dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$dql = 'SELECT'
. ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '')
. $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', '));
$fromParts = $this->getDQLPart('from');
$joinParts = $this->getDQLPart('join');
$fromClauses = array();
@@ -1082,4 +1104,4 @@ class QueryBuilder
}
}
}
}
}

View File

@@ -50,7 +50,7 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
/* @var $entityManager Doctrine\ORM\EntityManager */
/* @var $entityManager \Doctrine\ORM\EntityManager */
$entityManager = $this->getHelper('em')->getEntityManager();
$entityClassNames = $entityManager->getConfiguration()

View File

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

View File

@@ -189,6 +189,8 @@ public function <methodName>()
if ( ! $this->_isNew) {
$this->_parseTokensInEntityFile(file_get_contents($path));
} else {
$this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
}
if ($this->_backupExisting && file_exists($path)) {
@@ -438,7 +440,7 @@ public function <methodName>()
if ($inClass) {
$inClass = false;
$lastSeenClass = $lastSeenNamespace . '\\' . $token[1];
$lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
$this->_staticReflection[$lastSeenClass]['properties'] = array();
$this->_staticReflection[$lastSeenClass]['methods'] = array();
}
@@ -451,7 +453,7 @@ public function <methodName>()
} else if ($token[0] == T_FUNCTION) {
if ($tokens[$i+2][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
} else if ($tokens[$i+2][0] == T_AMPERSAND && $tokens[$i+3][0] == T_STRING) {
} else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
$this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
}
} else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
@@ -462,6 +464,14 @@ public function <methodName>()
private function _hasProperty($property, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate property if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasProperty($property)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($property, $this->_staticReflection[$metadata->name]['properties'])
@@ -470,6 +480,14 @@ public function <methodName>()
private function _hasMethod($method, ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
// don't generate method if its already on the base class.
$reflClass = new \ReflectionClass($this->_getClassToExtend());
if ($reflClass->hasMethod($method)) {
return true;
}
}
return (
isset($this->_staticReflection[$metadata->name]) &&
in_array($method, $this->_staticReflection[$metadata->name]['methods'])
@@ -686,11 +704,18 @@ public function <methodName>()
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
{
$methodName = $type . Inflector::classify($fieldName);
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
$addMethod = end($addMethod);
$methodName = $type . $addMethod;
} else {
$methodName = $type . Inflector::classify($fieldName);
}
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$var = sprintf('_%sMethodTemplate', $type);
$template = self::$$var;
@@ -723,9 +748,10 @@ public function <methodName>()
if ($this->_hasMethod($methodName, $metadata)) {
return;
}
$this->_staticReflection[$metadata->name]['methods'][] = $methodName;
$replacements = array(
'<name>' => $this->_annotationsPrefix . $name,
'<name>' => $this->_annotationsPrefix . ucfirst($name),
'<methodName>' => $methodName,
);
@@ -759,7 +785,7 @@ public function <methodName>()
}
if (isset($joinColumn['onDelete'])) {
$joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false');
$joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
}
if (isset($joinColumn['onUpdate'])) {
@@ -781,6 +807,14 @@ public function <methodName>()
if ($this->_generateAnnotations) {
$lines[] = $this->_spaces . ' *';
if (isset($associationMapping['id']) && $associationMapping['id']) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id';
if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
}
}
$type = null;
switch ($associationMapping['type']) {
@@ -1037,4 +1071,4 @@ public function <methodName>()
throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
}
}
}
}

View File

@@ -27,7 +27,7 @@ use Doctrine\ORM\Tools\Export\ExportException;
/**
* Abstract base class which is to be used for the Exporter drivers
* which can be found in Doctrine\ORM\Tools\Export\Driver
* which can be found in \Doctrine\ORM\Tools\Export\Driver
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org

View File

@@ -166,7 +166,13 @@ class YamlExporter extends AbstractExporter
);
$associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray);
$array['oneToOne'][$name] = $associationMappingArray;
if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) {
$array['oneToOne'][$name] = $associationMappingArray;
} else {
$array['manyToOne'][$name] = $associationMappingArray;
}
} else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) {
$oneToManyMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],

View File

@@ -56,7 +56,7 @@ class SchemaTool
* Initializes a new SchemaTool instance that uses the connection of the
* provided EntityManager.
*
* @param Doctrine\ORM\EntityManager $em
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
@@ -174,7 +174,7 @@ class SchemaTool
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table);
} else {
// Add an ID FK column to child tables
/* @var Doctrine\ORM\Mapping\ClassMetadata $class */
/* @var \Doctrine\ORM\Mapping\ClassMetadata $class */
$idMapping = $class->fieldMappings[$class->identifier[0]];
$this->_gatherColumn($class, $idMapping, $table);
$columnName = $class->getQuotedColumnName($class->identifier[0], $this->_platform);
@@ -217,13 +217,13 @@ class SchemaTool
if (isset($class->table['indexes'])) {
foreach ($class->table['indexes'] AS $indexName => $indexData) {
$table->addIndex($indexData['columns'], $indexName);
$table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] AS $indexName => $indexData) {
$table->addUniqueIndex($indexData['columns'], $indexName);
$table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName);
}
}

View File

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

View File

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

View File

@@ -116,7 +116,7 @@ class UnitOfWork implements PropertyChangedListener
* Map of entities that are scheduled for dirty checking at commit time.
* This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT.
* Keys are object ids (spl_object_hash).
*
*
* @var array
* @todo rename: scheduledForSynchronization
*/
@@ -135,10 +135,10 @@ class UnitOfWork implements PropertyChangedListener
* @var array
*/
private $entityUpdates = array();
/**
* Any pending extra updates that have been scheduled by persisters.
*
*
* @var array
*/
private $extraUpdates = array();
@@ -176,7 +176,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* The EntityManager that "owns" this UnitOfWork instance.
*
* @var Doctrine\ORM\EntityManager
* @var \Doctrine\ORM\EntityManager
*/
private $em;
@@ -184,7 +184,7 @@ class UnitOfWork implements PropertyChangedListener
* The calculator used to calculate the order in which changes to
* entities need to be written to the database.
*
* @var Doctrine\ORM\Internal\CommitOrderCalculator
* @var \Doctrine\ORM\Internal\CommitOrderCalculator
*/
private $commitOrderCalculator;
@@ -201,26 +201,31 @@ class UnitOfWork implements PropertyChangedListener
* @var array
*/
private $collectionPersisters = array();
/**
* The EventManager used for dispatching events.
*
*
* @var EventManager
*/
private $evm;
/**
* Orphaned entities that are scheduled for removal.
*
*
* @var array
*/
private $orphanRemovals = array();
//private $_readOnlyObjects = array();
/**
* Read-Only objects are never evaluated
*
* @var array
*/
private $readOnlyObjects = array();
/**
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
*
*
* @var array
*/
private $eagerLoadingEntities = array();
@@ -228,7 +233,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Initializes a new UnitOfWork instance, bound to the given EntityManager.
*
* @param Doctrine\ORM\EntityManager $em
* @param \Doctrine\ORM\EntityManager $em
*/
public function __construct(EntityManager $em)
{
@@ -240,15 +245,15 @@ class UnitOfWork implements PropertyChangedListener
* Commits the UnitOfWork, executing all operations that have been postponed
* up to this point. The state of all managed entities will be synchronized with
* the database.
*
*
* The operations are executed in the following order:
*
*
* 1) All entity insertions
* 2) All entity updates
* 3) All collection deletions
* 4) All collection updates
* 5) All entity deletions
*
*
*/
public function commit()
{
@@ -269,12 +274,12 @@ class UnitOfWork implements PropertyChangedListener
$this->remove($orphan);
}
}
// Raise onFlush
if ($this->evm->hasListeners(Events::onFlush)) {
$this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em));
}
// Now we need a commit order to maintain referential integrity
$commitOrder = $this->getCommitOrder();
@@ -341,7 +346,7 @@ class UnitOfWork implements PropertyChangedListener
$this->scheduledForDirtyCheck =
$this->orphanRemovals = array();
}
/**
* Executes any extra updates that have been scheduled.
*/
@@ -401,8 +406,13 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $class->isInheritanceTypeNone()) {
$class = $this->em->getClassMetadata(get_class($entity));
}
$oid = spl_object_hash($entity);
if (isset($this->readOnlyObjects[$oid])) {
return;
}
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
$value = $refProp->getValue($entity);
@@ -415,16 +425,16 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $value instanceof Collection) {
$value = new ArrayCollection($value);
}
$assoc = $class->associationMappings[$name];
// Inject PersistentCollection
$coll = new PersistentCollection(
$this->em,
$this->em->getClassMetadata($assoc['targetEntity']),
$value
);
$coll->setOwner($entity, $assoc);
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($entity, $coll);
@@ -458,7 +468,15 @@ class UnitOfWork implements PropertyChangedListener
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (isset($originalData[$propName])) {
$orgValue = $originalData[$propName];
} else if (array_key_exists($propName, $originalData)) {
$orgValue = null;
} else {
// skip field, its a partially omitted one!
continue;
}
if (isset($class->associationMappings[$propName])) {
$assoc = $class->associationMappings[$propName];
if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) {
@@ -474,6 +492,23 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionDeletions[] = $orgValue;
$changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
}
// Persistent collection was exchanged with the "originally"
// created one. This can only mean it was cloned and replaced
// on another entity.
if ($actualValue instanceof PersistentCollection) {
$owner = $actualValue->getOwner();
if ($owner === null) { // cloned
$actualValue->setOwner($entity, $assoc);
} else if ($owner !== $entity) { // no clone, we have to fix
if (!$actualValue->isInitialized()) {
$actualValue->initialize(); // we have to do this otherwise the cols share state
}
$newValue = clone $actualValue;
$newValue->setOwner($entity, $assoc);
$class->reflFields[$propName]->setValue($entity, $newValue);
}
}
}
} else if ($isChangeTrackingNotify) {
continue;
@@ -493,6 +528,15 @@ class UnitOfWork implements PropertyChangedListener
$val = $class->reflFields[$field]->getValue($entity);
if ($val !== null) {
$this->computeAssociationChanges($assoc, $val);
if (!isset($this->entityChangeSets[$oid]) &&
$assoc['isOwningSide'] &&
$assoc['type'] == ClassMetadata::MANY_TO_MANY &&
$val instanceof PersistentCollection &&
$val->isDirty()) {
$this->entityChangeSets[$oid] = array();
$this->originalEntityData[$oid] = $actualData;
$this->entityUpdates[$oid] = $entity;
}
}
}
}
@@ -528,7 +572,7 @@ class UnitOfWork implements PropertyChangedListener
foreach ($entitiesToProcess as $entity) {
// Ignore uninitialized proxy objects
if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
continue;
}
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
@@ -620,16 +664,16 @@ class UnitOfWork implements PropertyChangedListener
$this->scheduleForInsert($entity);
}
/**
* INTERNAL:
* Computes the changeset of an individual entity, independently of the
* computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
*
*
* The passed entity must be a managed entity. If the entity already has a change set
* because this method is invoked during a commit cycle then the change sets are added.
* whereby changes detected in this method prevail.
*
*
* @ignore
* @param ClassMetadata $class The class descriptor of the entity.
* @param object $entity The entity for which to (re)calculate the change set.
@@ -638,11 +682,11 @@ class UnitOfWork implements PropertyChangedListener
public function recomputeSingleEntityChangeSet($class, $entity)
{
$oid = spl_object_hash($entity);
if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
throw new InvalidArgumentException('Entity must be managed.');
}
/* TODO: Just return if changetracking policy is NOTIFY?
if ($class->isChangeTrackingNotify()) {
return;
@@ -682,19 +726,19 @@ class UnitOfWork implements PropertyChangedListener
/**
* Executes all entity insertions for entities of the specified type.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function executeInserts($class)
{
$className = $class->name;
$persister = $this->getEntityPersister($className);
$hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]);
$hasListeners = $this->evm->hasListeners(Events::postPersist);
if ($hasLifecycleCallbacks || $hasListeners) {
$entities = array();
}
foreach ($this->entityInsertions as $oid => $entity) {
if (get_class($entity) === $className) {
$persister->addInsert($entity);
@@ -719,7 +763,7 @@ class UnitOfWork implements PropertyChangedListener
$this->addToIdentityMap($entity);
}
}
if ($hasLifecycleCallbacks || $hasListeners) {
foreach ($entities as $entity) {
if ($hasLifecycleCallbacks) {
@@ -735,7 +779,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Executes all entity updates for entities of the specified type.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function executeUpdates($class)
{
@@ -746,15 +790,15 @@ class UnitOfWork implements PropertyChangedListener
$hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate);
$hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]);
$hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate);
foreach ($this->entityUpdates as $oid => $entity) {
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
if ($hasPreUpdateLifecycleCallbacks) {
$class->invokeLifecycleCallbacks(Events::preUpdate, $entity);
$this->recomputeSingleEntityChangeSet($class, $entity);
}
if ($hasPreUpdateListeners) {
$this->evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs(
$entity, $this->em, $this->entityChangeSets[$oid])
@@ -765,7 +809,7 @@ class UnitOfWork implements PropertyChangedListener
$persister->update($entity);
}
unset($this->entityUpdates[$oid]);
if ($hasPostUpdateLifecycleCallbacks) {
$class->invokeLifecycleCallbacks(Events::postUpdate, $entity);
}
@@ -779,16 +823,16 @@ class UnitOfWork implements PropertyChangedListener
/**
* Executes all entity deletions for entities of the specified type.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
*/
private function executeDeletions($class)
{
$className = $class->name;
$persister = $this->getEntityPersister($className);
$hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]);
$hasListeners = $this->evm->hasListeners(Events::postRemove);
foreach ($this->entityDeletions as $oid => $entity) {
if (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className) {
$persister->delete($entity);
@@ -829,14 +873,16 @@ class UnitOfWork implements PropertyChangedListener
$this->entityDeletions
);
}
$calc = $this->getCommitOrderCalculator();
// See if there are any new classes in the changeset, that are not in the
// commit order graph yet (dont have a node).
// TODO: Can we know the know the possible $newNodes based on something more efficient? IdentityMap?
$newNodes = array();
foreach ($entityChangeSet as $oid => $entity) {
$className = get_class($entity);
$className = get_class($entity);
if ( ! $calc->hasClass($className)) {
$class = $this->em->getClassMetadata($className);
$calc->addClass($class);
@@ -845,12 +891,13 @@ class UnitOfWork implements PropertyChangedListener
}
// Calculate dependencies for new nodes
foreach ($newNodes as $class) {
while ($class = array_pop($newNodes)) {
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
if ( ! $calc->hasClass($targetClass->name)) {
$calc->addClass($targetClass);
$newNodes[] = $targetClass;
}
$calc->addDependency($targetClass, $class);
// If the target class has mapped subclasses,
@@ -860,6 +907,7 @@ class UnitOfWork implements PropertyChangedListener
$targetSubClass = $this->em->getClassMetadata($subClassName);
if ( ! $calc->hasClass($subClassName)) {
$calc->addClass($targetSubClass);
$newNodes[] = $targetSubClass;
}
$calc->addDependency($targetSubClass, $class);
}
@@ -928,14 +976,14 @@ class UnitOfWork implements PropertyChangedListener
$this->entityUpdates[$oid] = $entity;
}
}
/**
* INTERNAL:
* Schedules an extra update that will be executed immediately after the
* regular entity updates within the currently running commit cycle.
*
*
* Extra updates for entities are stored as (entity, changeset) tuples.
*
*
* @ignore
* @param object $entity The entity for which to schedule an extra update.
* @param array $changeset The changeset of the entity (what to update).
@@ -973,13 +1021,13 @@ class UnitOfWork implements PropertyChangedListener
/**
* INTERNAL:
* Schedules an entity for deletion.
*
*
* @param object $entity
*/
public function scheduleForDelete($entity)
{
$oid = spl_object_hash($entity);
if (isset($this->entityInsertions[$oid])) {
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
@@ -1017,7 +1065,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Checks whether an entity is scheduled for insertion, update or deletion.
*
*
* @param $entity
* @return boolean
*/
@@ -1102,6 +1150,22 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
} else if (!$class->idGenerator->isPostInsertGenerator()) {
// if we have a pre insert generator we can't be sure that having an id
// really means that the entity exists. We have to verify this through
// the last resort: a db lookup
// Last try before db lookup: check the identity map.
if ($this->tryGetById($id, $class->rootEntityName)) {
return self::STATE_DETACHED;
} else {
// db lookup
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
return self::STATE_DETACHED;
} else {
return self::STATE_NEW;
}
}
} else {
return self::STATE_DETACHED;
}
@@ -1132,6 +1196,8 @@ class UnitOfWork implements PropertyChangedListener
$className = $classMetadata->rootEntityName;
if (isset($this->identityMap[$className][$idHash])) {
unset($this->identityMap[$className][$idHash]);
unset($this->readOnlyObjects[$oid]);
//$this->entityStates[$oid] = self::STATE_DETACHED;
return true;
}
@@ -1186,7 +1252,7 @@ class UnitOfWork implements PropertyChangedListener
if ($idHash === '') {
return false;
}
return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]);
}
@@ -1217,7 +1283,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Persists an entity as part of the current unit of work.
*
*
* This method is internally called during persist() cascades as it tracks
* the already visited entities to prevent infinite recursions.
*
@@ -1295,8 +1361,8 @@ class UnitOfWork implements PropertyChangedListener
}
$visited[$oid] = $entity; // mark visited
// Cascade first, because scheduleForDelete() removes the entity from the identity map, which
// Cascade first, because scheduleForDelete() removes the entity from the identity map, which
// can cause problems when a lazy proxy has to be initialized for the cascade operation.
$this->cascadeRemove($entity, $visited);
@@ -1368,6 +1434,10 @@ class UnitOfWork implements PropertyChangedListener
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
$managedCopy = $entity;
} else {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__load();
}
// Try to look the entity up in the identity map.
$id = $class->getIdentifierValues($entity);
@@ -1376,7 +1446,20 @@ class UnitOfWork implements PropertyChangedListener
$managedCopy = $class->newInstance();
$this->persistNew($class, $managedCopy);
} else {
$managedCopy = $this->tryGetById($id, $class->rootEntityName);
$flatId = $id;
if ($class->containsForeignIdentifier) {
// convert foreign identifiers into scalar foreign key
// values to avoid object to string conversion failures.
foreach ($id as $idField => $idValue) {
if (isset($class->associationMappings[$idField])) {
$targetClassMetadata = $this->em->getClassMetadata($class->associationMappings[$idField]['targetEntity']);
$associatedId = $this->getEntityIdentifier($idValue);
$flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]];
}
}
}
$managedCopy = $this->tryGetById($flatId, $class->rootEntityName);
if ($managedCopy) {
// We have the entity in-memory already, just make sure its not removed.
if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) {
@@ -1385,7 +1468,7 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
// We need to fetch the managed copy in order to merge.
$managedCopy = $this->em->find($class->name, $id);
$managedCopy = $this->em->find($class->name, $flatId);
}
if ($managedCopy === null) {
@@ -1398,6 +1481,10 @@ class UnitOfWork implements PropertyChangedListener
} else {
throw new EntityNotFoundException;
}
} else {
if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized__) {
$managedCopy->__load();
}
}
}
@@ -1406,7 +1493,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion);
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
}
}
@@ -1426,15 +1513,18 @@ class UnitOfWork implements PropertyChangedListener
// do not merge fields marked lazy that have not been fetched.
continue;
} else if ( ! $assoc2['isCascadeMerge']) {
if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) {
$prop->setValue($managedCopy, $other);
} else {
if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) {
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
$id = $targetClass->getIdentifierValues($other);
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id);
$prop->setValue($managedCopy, $proxy);
$this->registerManaged($proxy, $id, array());
$relatedId = $targetClass->getIdentifierValues($other);
if ($targetClass->subClasses) {
$other = $this->em->find($targetClass->name, $relatedId);
} else {
$other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
$this->registerManaged($other, $relatedId, array());
}
}
$prop->setValue($managedCopy, $other);
}
} else {
$mergeCol = $prop->getValue($entity);
@@ -1456,7 +1546,8 @@ class UnitOfWork implements PropertyChangedListener
}
if ($assoc2['isCascadeMerge']) {
$managedCol->initialize();
if (!$managedCol->isEmpty()) {
// clear and set dirty a managed collection if its not also the same collection to merge from.
if (!$managedCol->isEmpty() && $managedCol != $mergeCol) {
$managedCol->unwrap()->clear();
$managedCol->setDirty(true);
if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
@@ -1496,7 +1587,7 @@ class UnitOfWork implements PropertyChangedListener
return $managedCopy;
}
/**
* Detaches an entity from the persistence management. It's persistence will
* no longer be managed by Doctrine.
@@ -1508,10 +1599,10 @@ class UnitOfWork implements PropertyChangedListener
$visited = array();
$this->doDetach($entity, $visited);
}
/**
* Executes a detach operation on the given entity.
*
*
* @param object $entity
* @param array $visited
*/
@@ -1523,7 +1614,7 @@ class UnitOfWork implements PropertyChangedListener
}
$visited[$oid] = $entity; // mark visited
switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
case self::STATE_MANAGED:
if ($this->isInIdentityMap($entity)) {
@@ -1537,14 +1628,14 @@ class UnitOfWork implements PropertyChangedListener
case self::STATE_DETACHED:
return;
}
$this->cascadeDetach($entity, $visited);
}
/**
* Refreshes the state of the given entity from the database, overwriting
* any local, unpersisted changes.
*
*
* @param object $entity The entity to refresh.
* @throws InvalidArgumentException If the entity is not MANAGED.
*/
@@ -1553,10 +1644,10 @@ class UnitOfWork implements PropertyChangedListener
$visited = array();
$this->doRefresh($entity, $visited);
}
/**
* Executes a refresh operation on an entity.
*
*
* @param object $entity The entity to refresh.
* @param array $visited The already visited entities during cascades.
* @throws InvalidArgumentException If the entity is not MANAGED.
@@ -1579,10 +1670,10 @@ class UnitOfWork implements PropertyChangedListener
} else {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
$this->cascadeRefresh($entity, $visited);
}
/**
* Cascades a refresh operation to associated entities.
*
@@ -1610,7 +1701,7 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
/**
* Cascades a detach operation to associated entities.
*
@@ -1655,6 +1746,10 @@ class UnitOfWork implements PropertyChangedListener
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
continue;
}
if ($relatedEntities instanceof PersistentCollection) {
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
@@ -1682,7 +1777,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $assoc['isCascadePersist']) {
continue;
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
if ($relatedEntities instanceof PersistentCollection) {
@@ -1711,11 +1806,11 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $assoc['isCascadeRemove']) {
continue;
}
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
$entity->__load();
}
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
// If its a PersistentCollection initialization is intended! No unwrap!
@@ -1737,10 +1832,10 @@ class UnitOfWork implements PropertyChangedListener
*/
public function lock($entity, $lockMode, $lockVersion = null)
{
if ($this->getEntityState($entity) != self::STATE_MANAGED) {
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
$entityName = get_class($entity);
$class = $this->em->getClassMetadata($entityName);
@@ -1760,7 +1855,7 @@ class UnitOfWork implements PropertyChangedListener
if (!$this->em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
$oid = spl_object_hash($entity);
$this->getEntityPersister($class->name)->lock(
@@ -1773,7 +1868,7 @@ class UnitOfWork implements PropertyChangedListener
/**
* Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
*
* @return Doctrine\ORM\Internal\CommitOrderCalculator
* @return \Doctrine\ORM\Internal\CommitOrderCalculator
*/
public function getCommitOrderCalculator()
{
@@ -1800,6 +1895,7 @@ class UnitOfWork implements PropertyChangedListener
$this->collectionDeletions =
$this->collectionUpdates =
$this->extraUpdates =
$this->readOnlyObjects =
$this->orphanRemovals = array();
if ($this->commitOrderCalculator !== null) {
$this->commitOrderCalculator->clear();
@@ -1809,13 +1905,13 @@ class UnitOfWork implements PropertyChangedListener
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em));
}
}
/**
* INTERNAL:
* Schedules an orphaned entity for removal. The remove() operation will be
* invoked on that entity at the beginning of the next commit of this
* UnitOfWork.
*
*
* @ignore
* @param object $entity
*/
@@ -1823,7 +1919,7 @@ class UnitOfWork implements PropertyChangedListener
{
$this->orphanRemovals[spl_object_hash($entity)] = $entity;
}
/**
* INTERNAL:
* Schedules a complete collection for removal when this UnitOfWork commits.
@@ -1852,7 +1948,7 @@ class UnitOfWork implements PropertyChangedListener
* @param array $hints Any hints to account for during reconstitution/lookup of the entity.
* @return object The managed entity instance.
* @internal Highly performance-sensitive method.
*
*
* @todo Rename: getOrCreateEntity
*/
public function createEntity($className, array $data, &$hints = array())
@@ -1878,8 +1974,8 @@ class UnitOfWork implements PropertyChangedListener
}
$id = array($class->identifier[0] => $idHash);
}
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
$entity = $this->identityMap[$class->rootEntityName][$idHash];
$oid = spl_object_hash($entity);
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
@@ -1890,6 +1986,11 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
$overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
// If only a specific entity is set to refresh, check that it's the one
if(isset($hints[Query::HINT_REFRESH_ENTITY])) {
$overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
}
}
if ($overrideLocalValues) {
@@ -1917,12 +2018,12 @@ class UnitOfWork implements PropertyChangedListener
// Loading the entity right here, if its in the eager loading map get rid of it there.
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
// Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
foreach ($class->associationMappings as $field => $assoc) {
// Check if the association is not among the fetch-joined associations already.
if (isset($hints['fetched'][$className][$field])) {
if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
continue;
}
@@ -1958,7 +2059,7 @@ class UnitOfWork implements PropertyChangedListener
$relatedIdHash = implode(' ', $associatedId);
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
// if this is an uninitialized proxy, we are deferring eager loads,
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
// then we cann append this entity for eager loading!
@@ -1967,7 +2068,7 @@ class UnitOfWork implements PropertyChangedListener
!$targetClass->isIdentifierComposite &&
$newValue instanceof Proxy &&
$newValue->__isInitialized__ === false) {
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
}
} else {
@@ -1976,7 +2077,7 @@ class UnitOfWork implements PropertyChangedListener
// a way to solve this with deferred eager loading, which means putting
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
$newValue = $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
->loadOneToOneEntity($assoc, $entity, $associatedId);
} else {
// Deferred eager load only works for single identifier classes
@@ -2003,7 +2104,7 @@ class UnitOfWork implements PropertyChangedListener
}
$this->originalEntityData[$oid][$field] = $newValue;
$class->reflFields[$field]->setValue($entity, $newValue);
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
@@ -2012,7 +2113,7 @@ class UnitOfWork implements PropertyChangedListener
} else {
// Inverse side of x-to-one can never be lazy
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null));
->loadOneToOneEntity($assoc, $entity));
}
} else {
// Inject collection
@@ -2033,7 +2134,7 @@ class UnitOfWork implements PropertyChangedListener
}
}
}
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
@@ -2060,7 +2161,11 @@ class UnitOfWork implements PropertyChangedListener
foreach ($eagerLoadingEntities AS $entityName => $ids) {
$class = $this->em->getClassMetadata($entityName);
$this->getEntityPersister($entityName)->loadAll(array_combine($class->identifier, array(array_values($ids))));
if ($ids) {
$this->getEntityPersister($entityName)->loadAll(
array_combine($class->identifier, array(array_values($ids)))
);
}
}
}
@@ -2110,7 +2215,7 @@ class UnitOfWork implements PropertyChangedListener
}
return array();
}
/**
* @ignore
*/
@@ -2206,7 +2311,7 @@ class UnitOfWork implements PropertyChangedListener
* Gets the EntityPersister for an Entity.
*
* @param string $entityName The name of the Entity.
* @return Doctrine\ORM\Persisters\AbstractEntityPersister
* @return \Doctrine\ORM\Persisters\AbstractEntityPersister
*/
public function getEntityPersister($entityName)
{
@@ -2304,27 +2409,27 @@ class UnitOfWork implements PropertyChangedListener
/**
* Gets the currently scheduled entity insertions in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityInsertions()
{
return $this->entityInsertions;
}
/**
* Gets the currently scheduled entity updates in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityUpdates()
{
return $this->entityUpdates;
}
/**
* Gets the currently scheduled entity deletions in this UnitOfWork.
*
*
* @return array
*/
public function getScheduledEntityDeletions()
@@ -2351,10 +2456,10 @@ class UnitOfWork implements PropertyChangedListener
{
return $this->collectionUpdates;
}
/**
* Helper method to initialize a lazy loading proxy or persistent collection.
*
*
* @param object
* @return void
*/
@@ -2366,15 +2471,48 @@ class UnitOfWork implements PropertyChangedListener
$obj->initialize();
}
}
/**
* Helper method to show an object as string.
*
*
* @param object $obj
* @return string
* @return string
*/
private static function objToStr($obj)
{
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
}
/**
* Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
*
* This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
* on this object that might be necessary to perform a correct udpate.
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function markReadOnly($object)
{
if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
throw new InvalidArgumentException("Managed entity required");
}
$this->readOnlyObjects[spl_object_hash($object)] = true;
}
/**
* Is this entity read only?
*
* @throws \InvalidArgumentException
* @param $object
* @return void
*/
public function isReadOnly($object)
{
if ( ! is_object($object) ) {
throw new InvalidArgumentException("Managed entity required");
}
return isset($this->readOnlyObjects[spl_object_hash($object)]);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,77 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\ORM\Events;
require_once __DIR__ . '/../../TestInit.php';
/**
* ManyToManyEventTest
*
* @author Francisco Facioni <fran6co@gmail.com>
*/
class ManyToManyEventTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @var PostUpdateListener
*/
private $listener;
protected function setUp()
{
$this->useModelSet('cms');
parent::setUp();
$this->listener = new PostUpdateListener();
$evm = $this->_em->getEventManager();
$evm->addEventListener(Events::postUpdate, $this->listener);
}
public function testListenerShouldBeNotifiedOnlyWhenUpdating()
{
$user = $this->createNewValidUser();
$this->_em->persist($user);
$this->_em->flush();
$this->assertFalse($this->listener->wasNotified);
$group = new CmsGroup();
$group->name = "admins";
$user->addGroup($group);
$this->_em->persist($user);
$this->_em->flush();
$this->assertTrue($this->listener->wasNotified);
}
/**
* @return CmsUser
*/
private function createNewValidUser()
{
$user = new CmsUser();
$user->username = 'fran6co';
$user->name = 'Francisco Facioni';
$user->status = 'foo';
$group = new CmsGroup();
$group->name = "users";
$user->addGroup($group);
return $user;
}
}
class PostUpdateListener
{
/**
* @var bool
*/
public $wasNotified = false;
/**
* @param $args
*/
public function postUpdate($args)
{
$this->wasNotified = true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,97 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Persistence\PersistentObject;
/**
*/
class PersistentCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\PersistentCollectionHolder'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\PersistentCollectionContent'),
));
} catch (\Exception $e) {
}
}
public function testPersist()
{
$collectionHolder = new PersistentCollectionHolder();
$content = new PersistentCollectionContent('first element');
$collectionHolder->addElement($content);
$this->_em->persist($collectionHolder);
$this->_em->flush();
$this->_em->clear();
$collectionHolder = $this->_em->find(__NAMESPACE__ . '\PersistentCollectionHolder', $collectionHolder->id);
$collectionHolder->getCollection();
$content = new PersistentCollectionContent('second element');
$collectionHolder->addElement($content);
$this->assertEquals(2, $collectionHolder->getCollection()->count());
}
}
/**
* @Entity
*/
class PersistentCollectionHolder
{
/**
* @Id @Column(type="integer") @GeneratedValue
* @var int
*/
public $id;
/**
* @var \Doctrine\Common\Collections\Collection
* @ManyToMany(targetEntity="PersistentCollectionContent", cascade={"all"})
* @JoinTable(name="pcholder_content_mm")
*/
public $collection;
public function __construct()
{
$this->collection = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @param PersistentCollectionContent $element
*/
public function addElement(PersistentCollectionContent $element)
{
$this->collection->add($element);
}
/**
* @return \Doctrine\Common\Collections\Collection
*/
public function getCollection()
{
return clone $this->collection;
}
}
/**
* @Entity
*/
class PersistentCollectionContent
{
/**
* @Id @Column(type="integer") @GeneratedValue
* @var int
*/
public $id;
}

View File

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

View File

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

View File

@@ -15,9 +15,12 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\ReadOnlyEntity'),
));
} catch(\Exception $e) {
}
}
public function testReadOnlyEntityNeverChangeTracked()
@@ -36,6 +39,21 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->number);
}
/**
* @group DDC-1659
*/
public function testClearReadOnly()
{
$readOnly = new ReadOnlyEntity("Test1", 1234);
$this->_em->persist($readOnly);
$this->_em->flush();
$this->_em->getUnitOfWork()->markReadOnly($readOnly);
$this->_em->clear();
$this->assertFalse($this->_em->getUnitOfWork()->isReadOnly($readOnly));
}
}
/**
@@ -50,7 +68,7 @@ class ReadOnlyEntity
public $id;
/** @column(type="string") */
public $name;
/** @Column(type="integer") */
/** @Column(type="integer", name="number_col") */
public $number;
public function __construct($name, $number)
@@ -58,4 +76,4 @@ class ReadOnlyEntity
$this->name = $name;
$this->number = $number;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -367,4 +367,13 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $contract->getSalesPerson());
}
/**
* @group DDC-1770
*/
public function testTwoSingleTableInheritanceInFromClause()
{
$dql = 'SELECT f1,f2 FROM Doctrine\Tests\Models\Company\CompanyFixContract f1, Doctrine\Tests\Models\Company\CompanyFixContract f2';
$result = $this->_em->createQuery($dql)->getResult();
}
}

View File

@@ -44,11 +44,10 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
->setParameter('author', $user)
->getResult();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author AND a.text = :text";
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author";
$farticle = $this->_em->createQuery($dql)
->setParameter('author', $user)
->setParameter('topic', 'This is John Galt speaking!')
->setParameter('text', 'Yadda Yadda!')
->getSingleResult();
$this->assertSame($article, $farticle);
@@ -70,14 +69,13 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($article);
$this->_em->flush();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3 AND a.text = ?4";
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3";
$farticle = $this->_em->createQuery($dql)
->setParameter(1, 'This is John Galt speaking!')
->setParameter(2, $user)
->setParameter(3, $user)
->setParameter(4, 'Yadda Yadda!')
->getSingleResult();
$this->assertSame($article, $farticle);
}
}
}

View File

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

View File

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

View File

@@ -209,8 +209,15 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->article1->addTranslation('en', 'Bar');
$this->article1->addTranslation('en', 'Baz');
$this->setExpectedException('Exception');
$this->_em->flush();
$exceptionThrown = false;
try {
// exception depending on the underyling Database Driver
$this->_em->flush();
} catch(\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
}
/**
@@ -410,4 +417,20 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
return $this->_em->find(get_class($editor), $editor->id);
}
/**
* @group DDC-1519
*/
public function testMergeForeignKeyIdentifierEntity()
{
$idCriteria = array('source' => $this->article1->id(), 'target' => $this->article2->id());
$refRep = $this->_em->find("Doctrine\Tests\Models\DDC117\DDC117Reference", $idCriteria);
$this->_em->detach($refRep);
$refRep = $this->_em->merge($refRep);
$this->assertEquals($this->article1->id(), $refRep->source()->id());
$this->assertEquals($this->article2->id(), $refRep->target()->id());
}
}

View File

@@ -106,7 +106,7 @@ class DDC1209_3
{
/**
* @Id
* @Column(type="datetime")
* @Column(type="datetime", name="ddc1203_date")
*/
private $date;
@@ -122,4 +122,4 @@ class DateTime2 extends \DateTime
{
return $this->format('Y');
}
}
}

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