Compare commits

...

267 Commits

Author SHA1 Message Date
Jonathan H. Wage
984535cadc Preparing v2.5.12 release 2017-10-23 13:21:04 -05:00
Jonathan H. Wage
35a579efdc Merge pull request #6733 from fabpot/patch-2
Allow Symfony 4
2017-10-23 12:50:36 -05:00
Fabien Potencier
a2b4e5b293 Allow Symfony 4 2017-09-27 15:33:54 -07:00
Marco Pivetta
24d95796bb Bumping development version to v2.5.12-DEV 2017-09-18 08:52:10 +02:00
Marco Pivetta
249b737094 Preparing v2.5.11 release 2017-09-18 08:50:20 +02:00
Marco Pivetta
36dc28d43e Merge pull request #6635 from davidbarratt/instantiator
Allow installation of `doctrine/instantiator:^1.0.1`
2017-08-19 18:47:32 +02:00
David Barratt
0ec9e53c8d Fix doctrine/instantiator constraint
The current constraint prevents doctrine from being installed on PHP 7.1
2017-08-19 11:51:46 -04:00
Marco Pivetta
aa80f6c0b5 Removed PHPUnit 5.6+ API usage 2017-08-18 21:24:47 +02:00
Marco Pivetta
fd6d4890c4 Bumping development release to 2.5.11-DEV 2017-08-18 21:23:23 +02:00
Marco Pivetta
c78afd5172 Preparing v2.5.10 release 2017-08-18 21:17:35 +02:00
Marco Pivetta
fb7b78c004 Merge branch 'fix/#6633-#3788-avoid-xml-external-entity-loading-errors-in-xml-mapping-driver-2.5' into 2.5
Backport #6633
Backport #3788
2017-08-18 21:12:12 +02:00
Marco Pivetta
0de69e5a80 #6633 #3788 documenting why simplexml_load_file() was not used 2017-08-18 21:10:45 +02:00
Aljosha Papsch
d7e1f883d8 XmlDriver: Avoid PHP bug #62577 by avoiding simplexml_load_file.
Doctrine is affected by PHP bug #62577. simplexml_load_file is not
able to load files if libxml_disable_entity_loader(true) has been
called. simplexml_load_file fails with the message:

I/O warning : failed to load external entity "/my/mappings/my_entity.dcm.xml"
in /path-to/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php on line 711

This error occurs even if there are no external entities in the XML file.

Waiting for the PHP bug to be resolved is infeasible, because it is
unresolved since years. Therefore Doctrine needs to circumvent the bug
by replacing simplexml_load_file with simplexml_load_string while getting
the file contents itself. simplexml_load_string is not affected by the
PHP bug.
2017-08-18 21:10:11 +02:00
Marco Pivetta
82e0d7e21a Removing ::class meta-constant for tests that need to run against my grandparents' PHP 2017-08-16 20:06:14 +02:00
Marco Pivetta
26880983fc Removing ::class meta-constant for tests that need to run against my grandparents' PHP 2017-08-16 19:24:39 +02:00
Marco Pivetta
d9899732ca Bumping development release to v2.5.10DEV 2017-08-16 15:30:59 +02:00
Marco Pivetta
4f96fc62ce Preparing v2.5.9 release 2017-08-16 15:30:32 +02:00
Marco Pivetta
6184343fd5 Merge branch 'fix/#6626-skip-proxy-generation-for-embeddable-classes-2.5' into 2.5
Backport #6626
Backport #6625
2017-08-16 15:21:29 +02:00
Marco Pivetta
ee22be27a5 #6625 #6626 removing PHP 5.3 incompatibilities (required for backport) 2017-08-16 15:21:14 +02:00
Marco Pivetta
8f77210955 #6626 #6625 minor CS fixes (removed useless assignments) 2017-08-16 15:20:10 +02:00
Issei.M
f736acc8f5 Replace double quote with single quote 2017-08-16 15:20:01 +02:00
Issei.M
f1534610e1 Fix CS / Add annotation 2017-08-16 15:18:15 +02:00
Issei.M
6622bbbbf3 Skip embeddable classes proxy generation 2017-08-16 15:18:04 +02:00
Marco Pivetta
1554af0c07 Merge branch 'fix/#6623-#1515-ensure-abstracthydrator-hydrateall-cleans-up-on-unit-of-work-clear-2.5' into 2.5
Close #6623
2017-08-16 14:50:13 +02:00
Nikolas Tsiongas
814aa9f322 fix AbstractHydrator addEventListener on hydrateAll() 2017-08-16 14:49:22 +02:00
Marco Pivetta
f311dd1dd1 Bumping development version to v2.5.9-DEV 2017-08-13 20:47:26 +02:00
Marco Pivetta
e3aa3f2d1d Preparing v2.5.8 release 2017-08-13 20:46:56 +02:00
Marco Pivetta
c83f479633 Merge pull request #6621 from Powerhamster/patch-1
fixed undefined variable
2017-08-13 20:27:18 +02:00
Thomas Rothe
741f1db198 fixed undefined variable
changed $conditions to $condition so $isConditionalJoin is working
2017-08-12 11:33:11 +02:00
Marco Pivetta
22546a3811 Correcting connection existence in tearDown operations 2017-08-11 23:28:20 +02:00
Marco Pivetta
efa058bd8f If no connection was enstablished, skip the tearDown operations 2017-08-11 22:56:02 +02:00
Marco Pivetta
767577cec6 Removing ::class pseudo-constant usage 2017-08-11 22:49:46 +02:00
Marco Pivetta
c0f593e422 Removing ::class syntax to make dinosaurs run against this codebase too 2017-08-11 22:22:50 +02:00
Marco Pivetta
d89d238594 Merge branch 'fix/#6464-#6475-correct-SQL-generated-with-JTI-and-WITH-condition-2.5' into 2.5
Backport #6464
Backport #6475
2017-08-11 21:43:05 +02:00
Marco Pivetta
2337b7aedd #6464 #6475 cleaning up test - removed invalid fetch join, CS 2017-08-11 21:41:05 +02:00
Stefan Siegl
9e6f061bfb #6464 code review updates 2017-08-11 21:40:53 +02:00
Stefan Siegl
bf1188127e generate nested join sql for CTIs, closes #6464 2017-08-11 21:40:16 +02:00
Stefan Siegl
c73ec2aa76 #6464 add test 2017-08-11 21:39:45 +02:00
Marco Pivetta
095611c4b6 Merge branch 'fix/#6614-clean-modified-collection-causing-double-dirty-object-persistence-2.5' into 2.5
Backport #6613
Backport #6614
Backport #6616
2017-08-11 21:25:27 +02:00
Marco Pivetta
96c6f4cf1d #6613 #6614 #6616 removed unused import 2017-08-11 21:25:10 +02:00
Marco Pivetta
5cacb6e14f #6613 #6614 #6616 minor performance optimisations around the new restoreNewObjectsInDirtyCollection implementation 2017-08-11 21:23:58 +02:00
Marco Pivetta
ab63628960 #6613 #6614 #6616 removing DDC6613 test, which was fully ported to unit tests 2017-08-11 21:23:55 +02:00
Marco Pivetta
15731c7bde #6613 #6614 #6616 ensuring that the collection is marked as non-dirty if all new items are contained in the initialized ones 2017-08-11 21:23:51 +02:00
Andreas Braun
abb429a0c9 Add failing test for dirty flag 2017-08-11 21:23:47 +02:00
Marco Pivetta
61cb03bf30 #6613 #6614 #6616 removing repeated PersistentCollectionTest chunks of code 2017-08-11 21:23:40 +02:00
Marco Pivetta
d6bcb5b1f8 #6613 #6614 #6616 initializing a dirty collection that has new items that are also coming from initialization data de-duplicates new and persisted items 2017-08-11 21:22:59 +02:00
Marco Pivetta
bdae362777 #6613 #6614 #6616 moved integration test basics to a unit test that verifies basic dirty collection initialization semantics 2017-08-11 21:22:54 +02:00
Marco Pivetta
59c5574554 #6613 #6614 correcting broken test that isn't using objects against a PersistentCollection 2017-08-11 21:22:43 +02:00
Marco Pivetta
9545bf9d8c #6613 #6614 correcting broken test that isn't using objects against a PersistentCollection 2017-08-11 21:22:40 +02:00
Marco Pivetta
5521d1f325 #6613 #6614 ensuring that only newly added items that weren't loaded are restored in the dirty state of the collection 2017-08-11 21:22:34 +02:00
Marco Pivetta
49694dc335 #6613 #6614 after initialization, the collection should be dirty anyway 2017-08-11 21:21:43 +02:00
Marco Pivetta
3155d970d3 #6613 #6614 adding assertions about collection initialization and dirty status 2017-08-11 21:21:40 +02:00
Marco Pivetta
09189fc021 #6613 #6614 removing IDE-generated header 2017-08-11 21:21:36 +02:00
Marco Pivetta
5a0d3e5fb8 #6613 #6614 removing phone/user specifics, using ORM naming for associations 2017-08-11 21:21:27 +02:00
Marco Pivetta
b9ba4e3207 #6613 #6614 correcting column mapping (was integer, should be string), segregating phone creation away 2017-08-11 21:21:24 +02:00
Marco Pivetta
d7919678e5 #6613 #6614 remove superfluous mappings 2017-08-11 21:21:21 +02:00
Marco Pivetta
8b185eb822 #6613 #6614 rewrote test logic to be less magic-constant-dependent 2017-08-11 21:21:17 +02:00
Marco Pivetta
693a0546d3 #6613 #6614 CS - applying @group annotation to the test 2017-08-11 21:21:14 +02:00
Marco Pivetta
5c5c8fc487 #6613 #6614 removing dedicated DDC6613 model directory 2017-08-11 21:21:10 +02:00
Marco Pivetta
85dc707cc8 #6613 #6614 smashing entity definitions into the test 2017-08-11 21:21:03 +02:00
Marco Pivetta
64a1251b61 #6613 #6614 better test specification - removing useless assertions 2017-08-11 21:20:42 +02:00
Marco Pivetta
65ed6a2c2f #6613 #6614 simplifying entity definition - using auto-assigned string identifiers to reduce moving parts 2017-08-11 21:20:34 +02:00
Marco Pivetta
d27a9fce7a Merge pull request #6580 from Tobion/patch-1
Allow DBAL 2.6 and common 2.8 to be installed
2017-07-25 05:02:26 +02:00
Tobias Schultze
11659f5cfe Allow common 2.8 to be installed 2017-07-24 16:38:01 +02:00
Tobias Schultze
68dad26482 Allow DBAL 2.6 to be installed
DBAL 2.6 is released but currently dependencies can't be resolved as ORM 2.5 does not allow DBAL 2.6 and ORM 2.6 is not relased yet.
2017-07-24 16:27:53 +02:00
Marco Pivetta
b3ceef0fb6 Merge branch 'fix/#6550-correct-return-value-of-extra-lazy-removeElement-calls' into 2.5
Backport #6550 to 2.5
2017-07-22 09:27:30 +02:00
Andreas Braun
095b365146 Add test for removing element not in collection 2017-07-22 09:27:13 +02:00
Andreas Braun
7c1ebd99bc Fix return of removeElement on collections
Fixes #5745
2017-07-22 09:27:01 +02:00
Marco Pivetta
c06f19db8d Merge branch 'fix/#1515-clean-hydrator-listeners-on-hydration-end-2.5' into 2.5
Close #1515

This is a backport of the original PR - the same patch should land in `master` too, after a cleanup
2017-06-24 03:23:19 +02:00
Emiel Nijpels
0be9be4e24 DDC-3146 remove event listener from event listener in abstract hydrator in cleanup function 2017-06-24 03:23:01 +02:00
Luís Cobucci
b2bf5ee92e Leave PHP 7.1 and nightly to master 2017-06-22 07:57:54 +02:00
Luís Cobucci
698bd813a2 Remove HHVM from build 2017-06-22 07:46:45 +02:00
Jáchym Toušek
b2ac8fdfd7 Fix CountOutputWalker for queries with GROUP BY 2017-06-22 07:15:35 +02:00
Marco Pivetta
9c2b54b748 Adding classes required by the SchemaToolTest that exist in 'master', but not in '2.5' 2017-06-21 07:27:41 +02:00
Marco Pivetta
910784213f Corrected duplicate import statements due to cherry picking 2017-06-21 06:49:45 +02:00
Marco Pivetta
1d1de7de80 Merge branch 'fix/#5798-undefined-schema-tool-index-2.5' into 2.5
Close #5798
2017-06-21 06:32:00 +02:00
Sergey Fedotov
741da6eed7 Fix undefined index for discriminator column in SchemaTool 2017-06-21 06:31:23 +02:00
Marco Pivetta
ad5397b581 Merge branch 'fix/#5715-fix-metadata-filtering-in-cli-tools-2.5' into 2.5
Close #5715
2017-06-21 06:15:42 +02:00
Guilliam Xavier
57bb46ca9d Add regex tests for MetadataFilter (PR #507) 2017-06-21 06:15:25 +02:00
Guilliam Xavier
0416d5e036 Add more basic tests for MetadataFilter 2017-06-21 06:15:14 +02:00
Guilliam Xavier
a14432117a Fix MetadataFilter not testing filters after first 2017-06-21 06:15:01 +02:00
Guilliam Xavier
824f62d3bb Add failing test for #5715 (unit test for MetadataFilter) 2017-06-21 06:14:48 +02:00
Marco Pivetta
c0f0fe060f Merge branch 'fix/#1541-minor-docblock-correction-in-resultset-mapping-builder' into 2.5
Backport #1541 to 2.5
2017-05-20 16:45:59 +02:00
aleeeftw
caffbe04a2 Minor docblock correction
The documentation for the method ‘addJoinedEntityFromClassMetadata’ is
wrong. As we can see currently says you need to pass an object and that
is wrong. The $relation variable is passed to ‘addJoinedEntityResult’
which is using it as a ‘string’.
2017-05-20 16:45:25 +02:00
Marco Pivetta
d2c805b071 Correcting PHP 5.4 compliance by removing ::class usage (moving to real constants) 2017-05-02 09:33:48 +02:00
Marco Pivetta
04fc7a9a1c Merge branch 'fix/#6367-#6362-inheritance-alias-hydration' into 2.5
Close #6367
Close #6362
2017-05-02 09:26:54 +02:00
Timothy Clissold
149b8f4e09 Fix inheritance join alias 2017-05-02 09:26:44 +02:00
Marco Pivetta
48e8c02cb8 Merge pull request #6381 from ElisDN/ElisDN-phpdoc
Fixed PHPDoc
2017-04-03 09:04:13 +02:00
Елисеев Дмитрий
e218866a69 Fixed PHPDoc 2017-04-02 20:29:46 +03:00
Marco Pivetta
e6c434196c Merge pull request #6178 from doctrine/fix/#6174-#5570-merging-new-entities-should-also-trigger-prepersist-lifecycle-callbacks-2.5
Backport #6177 - fix #6174 #5570: merging new entities should also trigger prepersist lifecycle callbacks with the merged data
2016-12-18 16:42:34 +01:00
Marco Pivetta
d52dbe62ac #6174 #5570 switching ::class to string constants for PHP 5.4 compat (still supported in ORM 2.5.x) 2016-12-18 16:24:42 +01:00
Marco Pivetta
b0ede40f47 #6174 #5570 removed modifications applied to the CompanyContractListener, since UnitOfWorkTest now completely encapsulates the scenarios being covered 2016-12-18 16:13:11 +01:00
Marco Pivetta
3645a9c44d #6174 #5570 removed unused imports 2016-12-18 16:13:04 +01:00
Marco Pivetta
39ce6f96a0 #6174 #5570 renamed entity for better fitting the use-cases it's in 2016-12-18 16:12:57 +01:00
Marco Pivetta
e43f5304ef #6174 #5570 removed unused test class 2016-12-18 16:12:46 +01:00
Marco Pivetta
67724eb7ae #6174 #5570 adding group annotations to newly introduced test 2016-12-18 16:12:36 +01:00
Marco Pivetta
8d4bc0638d #6174 #5570 prePersist listeners should never be called when entities are merged, but are already in the UoW 2016-12-18 16:12:29 +01:00
Marco Pivetta
81186105b6 #6174 #5570 started moving tests around prePersist event subscriber triggering on UnitOfWork into the UnitOfWorkTest 2016-12-18 16:12:03 +01:00
Marco Pivetta
beef8acdf5 #6174 #5570 CS fixes around the EntityListenersOnMergeTest 2016-12-18 16:10:17 +01:00
Marco Pivetta
26fc8d60e6 #6174 #5570 adding group annotation to newly introduced tests 2016-12-18 16:10:08 +01:00
Marco Pivetta
12e8ab216a #6174 #5570 CS - spacing/variable naming 2016-12-18 16:09:59 +01:00
Marco Pivetta
dac1a16964 #6174 #5570 removed unused/dead code 2016-12-18 16:09:52 +01:00
Marco Pivetta
d9821d3fda #6174 #5570 CS - spacing 2016-12-18 16:09:45 +01:00
Marco Pivetta
576a4d7e31 #6174 #5570 CS - spacing 2016-12-18 16:09:38 +01:00
Marco Pivetta
eaee924180 #6174 #5570 flattened nested conditionals 2016-12-18 16:09:32 +01:00
Marco Pivetta
cf941ce54f #6174 #5570 documenting thrown exception types 2016-12-18 16:09:25 +01:00
Marco Pivetta
cfb7461f51 #6174 #5570 CS - alignment 2016-12-18 16:09:18 +01:00
bilouwan
569c08ce55 Rename test 2016-12-18 16:09:11 +01:00
bilouwan
295523cdca Cherry pick unit test from PR #5570 (Fix PrePersist EventListener when using merge instead of persist) 2016-12-18 16:09:00 +01:00
bilouwan
25efabdb74 doMerge will mergeEntityStateIntoManagedCopy BEFORE persistNew to let lifecyle events changes be persisted 2016-12-18 16:08:15 +01:00
bilouwan
1d96178097 Create failing test to reveal the issue 2016-12-18 16:08:05 +01:00
Marco Pivetta
20cb50451d Merge pull request #6159 from nicolas-cajelli/backport-fix-relation-cache-#1551-to-2.5
#5821 Backport #1551 - Fixed support for inverse side second level cache
2016-12-12 08:34:27 +01:00
Guilherme Blanco
0ff512ba8f Fixed support for inverse side second level cache 2016-12-05 14:36:33 -03:00
Marco Pivetta
5e9014fd99 Merge pull request #6156 from Slamdunk/patch-2
Allow doctrine/common 2.7
2016-12-04 06:53:07 +01:00
Filippo Tessarotto
a90cd9dfe8 Allow doctrine/common 2.7 2016-12-02 09:00:50 +01:00
Marco Pivetta
4d699789a2 Merge branch 'fix/#6110-collection-clear-should-also-clear-keys-2.5' into 2.5
Close #6110
2016-11-26 06:06:11 +01:00
Marco Pivetta
1486c8f8e2 split test into multiple sub-scenarios involving PersistentCollection key checking #6110 2016-11-26 06:05:31 +01:00
Steevan BARBOYON
3dadfa49d5 Clear $this->collection even when empty, to reset indexes 2016-11-26 06:04:35 +01:00
Marco Pivetta
9b36947a48 Merge branch 'fix/#6028-l2c-inheritance-query-cache-use-parent-entity-name-2.5' into 2.5
Backport #6028 to 2.5.x
2016-11-23 18:06:28 +01:00
Marco Pivetta
2122297fdb #6028 removed specific ::class usage, since 2.5.x still supports PHP 5.4.x 2016-11-23 18:06:14 +01:00
Marco Pivetta
af99cba28c #6028 removed specific ::class usage, since 2.5.x still supports PHP 5.4.x 2016-11-23 18:02:15 +01:00
Luís Cobucci
9bcee455ca Make child entity share the timestamp region with parent class 2016-11-23 17:58:05 +01:00
Marco Pivetta
73e4be7c7b Merge branch 'fix/#5768-#5755-clone-proxy-private-properties-in-multi-level-inheritances-2.5' into 2.5
Close #5768
Close #5755
2016-09-10 20:51:13 +02:00
Ed Hartwell Goose
d7026c46ec Fixes #5755, uses '->getReflectionProperties()' instead of '->getReflectionClass()->getProperties()' to ensure all fields are copied, and adds test to confirm behaviour 2016-09-10 20:48:12 +02:00
Marco Pivetta
b7bfbb6adb Merge branch 'fix/#5689-avoid-object-hash-conflicts-due-to-merge-operations-2.5' into 2.5
Close #5689
2016-09-10 20:22:23 +02:00
Marco Pivetta
c9161fcd6f #5689 removed unused reflection access 2016-09-10 20:19:29 +02:00
Marco Pivetta
147f8fffff #5689 removed OidReuseTest, which was moved to UnitOfWork tests 2016-09-10 20:18:10 +02:00
Marco Pivetta
e73428a051 #5689 moved OidReuseTest contents into the UnitOfWork tests 2016-09-10 20:17:59 +02:00
Mathieu De Zutter
a3d93afc4f Additional assertion to check that unreferenced objects are not in UOW. 2016-09-10 20:16:38 +02:00
Mathieu De Zutter
b0e4e3eda4 Remove old code in comments. 2016-09-10 20:16:33 +02:00
Mathieu De Zutter
95dcf51ad5 Avoid conflicts due to spl_object_hash().
When merging an entity with a to-many association, it will store the
original entity data using the object hash of the to-be-merged entity
instead of the managed entity. Since this to-be-merged entity is not
managed by Doctrine, it can disappear from the memory. A new object
can reuse the same memory location and thus have the same object hash.
When one tries to persist this object as new, Doctrine will refuse it
because it thinks that the entity is managed+dirty.

This patch is a very naive fix: it just disables storing the original
entity data in case of to-many associations. It may not be the ideal
or even a good solution at all, but it solves the problem of object
hash reuse.

The test case relies on the immediate reusing of memory locations by
PHP. The variable $user has twice the same object hash, though referring
a different object. Tested on PHP 5.6.17

Without the fix, the test fails on the last line with:
A managed+dirty entity Doctrine\Tests\Models\CMS\CmsUser@[...] can not
be scheduled for insertion.
2016-09-10 20:16:28 +02:00
Marco Pivetta
5365a418e9 Removed non-existing CLASSNAME reference 2016-09-09 23:25:54 +02:00
Marco Pivetta
47ce079b64 Removing ::class usage (PHP 5.5 is not yet required on ORM 2.5) 2016-09-09 22:52:54 +02:00
Marco Pivetta
8e95672f49 Removing ::class usage (PHP 5.5 is not yet required on ORM 2.5) 2016-09-09 22:51:42 +02:00
Marco Pivetta
e16de704a1 Merge branch 'fix/#6001-second-level-cache-query-cache-timestamp-from-region-2.5' into 2.5
Close #6001
2016-09-08 14:02:07 +02:00
Marco Pivetta
3e57c46afd #6001 adding orderBy, limit and offset variables (defined in master, not in 2.5) 2016-09-08 14:01:56 +02:00
Marco Pivetta
5e702ad524 #6001 documenting minor BC break in QueryCacheEntry#time type - specific version used 2016-09-08 13:58:30 +02:00
Luís Cobucci
9e9864c684 The timestamp verification is now done by the validator
So it's useless to keep it here too.
2016-09-08 13:58:24 +02:00
Luís Cobucci
88567ea395 Evict query cache when entities are updated 2016-09-08 13:56:46 +02:00
Luís Cobucci
bf18aac62d Add timestamp key to QueryCacheKey 2016-09-08 13:54:47 +02:00
Luís Cobucci
4d16e30a16 Use microtime to have more precision on cache time 2016-09-08 13:54:03 +02:00
Marco Pivetta
d592c14a6c Merge branch 'fix/#6004-#5989-fix-hydration-in-a-joined-inheritance-with-simple-array-or-json-array-2.5' into 2.5
Close #6004
Close #5989
2016-09-08 13:38:57 +02:00
Carl Vuorinen
da41161d73 Add unit test for SimpleObjectHydrator 2016-09-08 13:38:34 +02:00
Carl Vuorinen
33e23cdddb PR fixes (public properties & correct letter case in annotations) 2016-09-08 13:38:31 +02:00
Carl Vuorinen
c47c23a101 Use yoda condition in the null check 2016-09-08 13:38:27 +02:00
Carl Vuorinen
7352b97b14 Fix hydration in a joined inheritance with simple array or json array
SimpleArrayType and JsonArrayType convert NULL value to an empty array, which fails the null check that is used to prevent overwrite
Fixes issue #5989
2016-09-08 13:38:23 +02:00
Carl Vuorinen
dce0aeaa15 Create a failing test for issue #5989
Field with type=simple_array in a joined inheritance gets overridden by empty array in the hydrator
2016-09-08 13:38:18 +02:00
Marco Pivetta
a2c23fb9cb Merge branch 'fix/#5975-fix_hydrating_fetch_join_with_composite_pk-2.5' into 2.5
Close #5975
Close #5762
Close #5776
2016-09-07 23:22:01 +02:00
Marco Pivetta
aa6dc9695d #5975 minor test cleanups 2016-09-07 23:20:47 +02:00
Marco Pivetta
c4a2a348f4 #5975 short array syntax 2016-09-07 23:20:43 +02:00
Alexander Kurilo
6ab27993fc Use ::class const instead of FQCN string (#5762) 2016-09-07 23:20:27 +02:00
Alexander Kurilo
5eedccc22a Remove irrelevant accessors (#5762) 2016-09-07 23:20:22 +02:00
John Keller
592122fbcb add functional test and bug fix for issue #5762 2016-09-07 23:20:17 +02:00
Marco Pivetta
aa5820309e Merge branch 'fix/#5867-allow-embeddable-usage-in-inheritance-2.5' into 2.5
Close #5867
Close #4097
Close #4277
2016-06-19 12:48:28 +02:00
Marco Pivetta
b183818fa8 #5867 s/::class/::CLASSNAME for PHP 5.4 compat 2016-06-19 12:48:15 +02:00
Marco Pivetta
2af84c6025 #5867 @group annotations, describing scenario 2016-06-19 12:47:05 +02:00
Marco Pivetta
f181cf6c6b #5867 simplifying test case by inlining all required models into the test case 2016-06-19 12:45:54 +02:00
Luís Cobucci
62431ae477 Allow the usage of embedded objects on parent classes.
The `ClassMetadataInfo` was always using the "current class" to
fetch the reflection of a property even when a field is declared
on the parent class (which causes `ReflectionProperty` to throw
an exception).
2016-06-19 12:45:47 +02:00
Marco Pivetta
0d93461e66 Merge branch 'fix/#5858-yaml-exporter-should-only-introspect-join-column-on-owning-association-side-2.5' into 2.5
Close #5858
2016-06-08 13:34:23 +02:00
Thomas Ploch
a5eb0f2e82 Exporters should only inspect joinColumns for owning side in bi-directional OneToOne
rebased commits:

- Added test case for bi-directional OneToOne in YamlExporter
- Only inspect joinColumns for owning side in bi-directional OneToOne in YamlExporter
- Adding bi-directional test case without joinColumn to XmlExporter test
- Same testcase also applied to PhpExporter
- Fixing bi-directional issue in PhpExporter when inspecting joinColumns index
- Implemented @Ocramius suggestions
2016-06-08 13:34:10 +02:00
Marco Pivetta
9a393ccba7 Removed reliance on ::class meta-constant (only available in PHP 5.5+) 2016-06-06 01:48:32 +02:00
Marco Pivetta
224ac9725e Removed annotation reader constructor argument (incorrect argument used) 2016-06-06 01:34:20 +02:00
Marco Pivetta
0af9ee0140 Merge branch 'fix/#5850-clearing-specific-entity-name-should-clear-also-its-entity-insertions-2.5' into 2.5
Close #5850
Close #5849
2016-06-06 00:34:54 +02:00
Marco Pivetta
fecadf059c #5849 #5850 renamed clearEntityInsertions to clearEntityInsertionsForEntityName, for clarity 2016-06-06 00:32:01 +02:00
Marco Pivetta
800215040a #5849 #5850 refactored clearIdentityMapForEntityName to remove useless looping 2016-06-06 00:31:56 +02:00
Marco Pivetta
ec4dd4ab44 #5849 #5850 renamed clearIdentityMap to clearIdentityMapForEntityName, for clarity 2016-06-06 00:31:51 +02:00
Marco Pivetta
7378035f68 #5849 #5850 correcting test scenario: identity map could not be built with auto-generated identities+persist 2016-06-06 00:31:23 +02:00
Marco Pivetta
7fbcbfa271 #5849 #5850 adding group annotations to the newly introduced test case 2016-06-06 00:30:46 +02:00
Rico Humme
4a38c96ec5 Correct naming convention of function. Was confusing otherwise 2016-06-06 00:30:42 +02:00
Rico Humme
110d771883 Split of functionality in separate functions 2016-06-06 00:30:37 +02:00
Rico Humme
996c5048ab Test Case for Clear entityInsertions for specific entityName 2016-06-06 00:30:33 +02:00
Rico Humme
cd746beae2 Clear entityInsertions for specific entityName 2016-06-06 00:30:28 +02:00
Marco Pivetta
dfbc6bbea3 Merge branch 'fix/#5599-having-regression-fix-2.5' into 2.5
Backport #5599 into 2.5
2016-01-16 10:29:29 -06:00
Bill Schaller
c4209b4654 Fix issue were identifier operands in /,* arithmetic terms were not checked to see if they're query components 2016-01-16 10:28:52 -06:00
Alessandro Lai
6279c80e05 Regression test: HAVING clause does not translate variable name when used with * and / math operators 2016-01-16 10:28:43 -06:00
Marco Pivetta
d05aa6a5e0 Bumping to development version 2.5.5-DEV 2016-01-05 22:36:06 +01:00
Marco Pivetta
bc4ddbfb01 Release 2.5.4 2016-01-05 22:34:58 +01:00
Marco Pivetta
14499f5021 Removing coveralls installation/reporting from 2.5: not required 2016-01-05 22:25:57 +01:00
Marco Pivetta
aae43cbb77 Merge branch 'hotfix/#1568-identifier-type-cached-incorrectly-backport-2-5' into 2.5
Backport #1568 to 2.5
2016-01-05 22:14:40 +01:00
Jan Langer
db6cb8dedc Second level cache stores identifier with correct type even if findById is called with wrong identifier type 2016-01-05 22:13:03 +01:00
Guido Contreras Woda
5092da074a Test that reflects the issue described in http://www.doctrine-project.org/jira/browse/DDC-3967 2016-01-05 22:12:55 +01:00
Marco Pivetta
1c6524db55 Bumping to development version 2.5.4-DEV 2015-12-25 16:50:31 +01:00
Marco Pivetta
d9fc5388f1 2.5.3 release 2015-12-25 16:50:05 +01:00
Marco Pivetta
d2e51eacff Reverting incorrect DBAL 2.6 bump 2015-12-25 16:49:48 +01:00
Marco Pivetta
5a6ae4686f Allowing doctrine/common 2.6 2015-12-25 15:58:40 +01:00
Marco Pivetta
8b8a1cbe81 Merge branch 'hotfix/common-2.6-upgrade-compat-2.5' into 2.5 2015-12-25 15:26:15 +01:00
Marco Pivetta
27a5284899 doctrine/common 2.6.0 compat
Less strict assertion - no need to check the exact file name
2015-12-25 15:24:37 +01:00
Marco Pivetta
0086d17afe Common 2.6 compatibility
Internal structure of the ArrayCache has changed, therefore we should fix the tests depending on it instead
2015-12-25 15:24:32 +01:00
Marco Pivetta
ab62167c8a Merge branch 'hotfix/#4884-support-proxy-php7-hints-generation-2.5' into 2.5
Close #4884
2015-12-25 14:47:12 +01:00
Marco Pivetta
6d43195669 #4884 - allow installation of doctrine/common 2.6.x, which allows generating type-hints on proxies 2015-12-25 14:46:59 +01:00
Marco Pivetta
7065ff0ac9 Merge branch 'hotfix/#1572-target-entity-resolver-dql-with-interfaces-support' into 2.5
Close #1572
2015-12-11 21:35:18 +01:00
Marco Pivetta
aa61328e90 #1572 - test coverage - interfaces should also resolve to target entities when in DQL 2015-12-11 21:30:19 +01:00
Marco Pivetta
62719f2a97 Merge branch 'hotfix/#1573-merge-associated-versioned-entity-2.5.x' into 2.5
Close #1573
2015-12-11 20:19:10 +01:00
Marco Pivetta
66770c5bfe #1573 - correcting test asset namespace, removing unused properties and bi-directional association 2015-12-11 20:18:53 +01:00
Marco Pivetta
42691c21b4 Removing empty newline 2015-12-11 20:18:48 +01:00
Marco Pivetta
596e895763 #1573 - correcting docblock arguments/description 2015-12-11 20:18:42 +01:00
Marco Pivetta
d5c82094df #1573 removing unused API 2015-12-11 20:18:37 +01:00
bilouwan
4148220f9c Refactor testing Proxy not initilized 2015-12-11 20:18:31 +01:00
bilouwan
e173c930ec Fix superflous whitespaces & empty lines 2015-12-11 20:18:25 +01:00
bilouwan
7071984559 Fix compatibility with php5.4 2015-12-11 20:18:19 +01:00
bilouwan
216c466233 Unit test & fix for merge versionned entity 2015-12-11 20:18:12 +01:00
Marco Pivetta
65f5777e60 Merge branch 'hotfix/php7-xdebug-incompatibility-fixes-2.5.x' into 2.5
Close #5547
2015-12-11 19:35:42 +01:00
Marco Pivetta
6e3ce26429 Correcting minor test case incompatibility with XDebug 2.4.x
In PHP 5.x + XDebug < 2.4, the output would be "string:..."
In PHP 7.x + XDebug >= 2.4, the output would be "the/file/name.php:11:string:..."

This is an improvement in XDebug that is quite annoying for our purposes, but is actually welcome to most users anyway.

This commit simply fixes that incompatibility
2015-12-11 19:35:28 +01:00
oprokidnev
752d4f9eac Target entity resolver for DQL
Since we have target entity resolver in doctrine this class check is not enought.
To gain interface resolution it is better to add interface check in addition to class_check here.
2015-11-27 16:00:56 +05:00
Marco Pivetta
2983081a60 Bumping current dev version to 2.5.3-DEV 2015-11-23 13:44:56 +01:00
Marco Pivetta
464b5fdbfb Release 2.5.2 2015-11-23 13:44:25 +01:00
Guilherme Blanco
04254a8e34 Merge pull request #1512 from neoglez/2.5
Backport of "LimitSubqueryOutputWalker: fix aliasing of property in OrderBy from MappedSuperclass"
2015-11-15 22:08:59 -05:00
Guilherme Blanco
1d213e6733 Merge pull request #1543 from nicolas-grekas/dep-30
Allow symfony 3.0 components on 2.5
2015-11-09 14:54:44 -05:00
Klein Thomas
bc82e94afc Move to 2.5 section 2015-11-09 03:56:16 +00:00
Klein Thomas
b9af1c8fa5 Update Upgrade.md after minor bc break in 2.5.1
The introduction of the second parameter in EntityRepository#createQueryBuilder generates a runtime notice if you have a sub-class of EntityRepository that has a second parameter in the createQueryBuilder method
2015-11-09 03:56:07 +00:00
Pantel
d606efd4eb [DDC-3711] add Test that check if the association key are composite 2015-11-09 03:46:06 +00:00
Pantel
581e1638a2 [DDC-3711] add Tests that check if the association key are composite 2015-11-09 03:45:56 +00:00
Pantel
17ae8d1b2d [DDC-3711] Correct Error on manyToMany with composite primary key 2015-11-09 03:45:46 +00:00
Guilherme Blanco
3a058f8522 Merge branch 'hotfix/#1375-prevent-duplicate-unique-index' into 2.5
Backported #1375 to 2.5
2015-11-07 16:47:26 +00:00
Michał Bundyra
567220ef71 prevent duplicate unique index 2015-11-07 16:46:22 +00:00
Marco Pivetta
39098ce415 Merge branch 'hotfix/#1507-fixed-wrong-property-name-in-resultset-mapping-builder' into 2.5
Backport merge #1507 into 2.5.x
Close #1507
2015-11-07 10:35:30 -05:00
François-Xavier de Guillebon
1eb9c8a7f6 Added test 2015-11-07 10:35:08 -05:00
François-Xavier de Guillebon
f2f53ba9dc Fixed wrong variable used as array key 2015-11-07 10:35:02 -05:00
François-Xavier de Guillebon
ebbc443ec3 Fixed wrong property name 2015-11-07 10:34:55 -05:00
Nicolas Grekas
16802d2614 Allow symfony 3.0 components
Tests should tell if any deprecated interfaces of Symfony are used. If not, then the bundle is defacto compatible with 3.0
2015-11-05 11:43:20 +01:00
neoglez
ef73249bc7 Entity to test a mapped superclass
Backport of https://github.com/doctrine/doctrine2/pull/1377/files to branch 2.5
2015-09-21 09:32:54 +02:00
neoglez
ed637e51b9 Backport of "fix aliasing of property in OrderBy from MappedSuperclass"
Backport of "LimitSubqueryOutputWalker: fix aliasing of property in OrderBy from MappedSuperclass" ( e501137d1a )
See my comment on a3ece3b419
2015-09-21 08:58:30 +02:00
neoglez
a3ece3b419 UnitTest backport of "Failing test case for broken paginator case"
UnitTest backport of "Failing test case for broken paginator case" ( 192da14842 ).
The branch 2.x is very important because it's related to ZF2 doctrine module (see https://github.com/doctrine/DoctrineORMModule/blob/master/composer.json ) and specially this issue affects the use case of extending the ZF2 user entity defined in ZfcUser ( https://github.com/ZF-Commons/ZfcUser ).
This test is meant to show the need of the backport of e501137d1a
2015-09-21 08:52:46 +02:00
Marco Pivetta
2d1bc78749 Merge branch 'hotfix/#1510-DDC-3908-fix-cache-related-tests-in-2.5' into 2.5
Close #1510
2015-09-19 10:45:24 +02:00
Matthias Pigulla
eaf8b1c7ca Fix tests related to caches, as per doctrine/cache 1.5.0 changes
Backports #1510
Fixes DDC-3908

dd47003641 removes the 'DoctrineNamespaceCacheKey[]' entry from the cache. Thus, all tests counting cache entries were off by one.
2015-09-19 10:44:47 +02:00
Benjamin Eberlei
8070b50150 Bump version to 2.5.2 2015-08-31 14:59:39 +02:00
Benjamin Eberlei
e6a83bedbe Release 2.5.1 2015-08-31 14:59:39 +02:00
Benjamin Eberlei
b6e5464b98 Fix version 2015-08-31 14:59:36 +02:00
Benjamin Eberlei
6366d190d7 [DCOM-293] Fix security misconfiguration vulnerability allowing local remote arbitrary code execution. 2015-08-31 14:58:12 +02:00
Bill Schaller
89eed31e79 Merge pull request #1463 from ehimen/paginate-order-by-subselect
Fixed issue when paginator orders by a subselect expression
Conflicts:
	tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php
2015-08-04 14:31:08 -04:00
Marco Pivetta
4ca00f7a9d Merge branch 'hotfix/#1387-DDC-3699-do-not-merge-managed-uninitialized-entities-2.5' into 2.5
Close #1387
2015-07-15 21:52:41 +01:00
Marco Pivetta
6bc405455e DDC-3699 - #1387 - leveraging the OrmFunctionalTestCase API 2015-07-15 21:52:25 +01:00
Marco Pivetta
173729e560 DDC-3699 - #1387 - catching specific exceptions 2015-07-15 21:52:20 +01:00
Marco Pivetta
86abbb0e78 DDC-3699 - #1387 - simpifying tests, clarifying on test method names 2015-07-15 21:52:16 +01:00
Lenard Palko
69ef75ff2d Added test cases for both one-to-one and one-to-many cases. 2015-07-15 21:52:08 +01:00
Lenard Palko
c68edec0c2 Fix skipping properties if they are listed after a not loaded relation. 2015-07-15 21:51:55 +01:00
Marco Pivetta
f9bbd953a7 Merge branch 'hotfix/#1381-wakeup-reflection-with-embeddable-and-staticreflection-serialization-fix-2.5' into 2.5
Close #1381
2015-07-15 20:50:28 +01:00
Nico Vogelaar
9097014c3d Fixes ClassMetadata wakeupReflection with embeddable and StaticReflectionService 2015-07-15 20:50:05 +01:00
Marco Pivetta
12d178777a Merge branch 'hotfix/#1380-non-cache-persister-bug-2.5' into 2.5
Close #1380
2015-07-15 20:45:38 +01:00
Marco Pivetta
ed1c4de2b6 DDC-3683 - #1380 - reverting BC break, annotating correct types, cs fixes 2015-07-15 20:45:02 +01:00
Darien Hager
fff56c7f3f Remove runtime assertion 2015-07-15 20:44:55 +01:00
Darien Hager
97e90ddefc Clarify state-changes, replace array_key_exists() with isset() for speed 2015-07-15 20:44:50 +01:00
Darien Hager
d5adda954d Whitespace formatting tweaks 2015-07-15 20:44:39 +01:00
Darien Hager
c507b52f20 Remove now-superfluous EntityManager check 2015-07-15 20:44:33 +01:00
Darien Hager
08be905fc3 Refactor LoadClassMetadataEventArgs to ensure it contains an EntityManager 2015-07-15 20:44:24 +01:00
Darien Hager
d29cc3660f Change the test listener than layers on second-level-caching so that it is more conservative, only turning on caching-associations when it knows the target entity is cache-able. 2015-07-15 20:44:10 +01:00
Darien Hager
768c291cd1 Stumbled across a bug where signatures didn't match, but also the current persister-type didn't support getCacheRegion(). Unsure of exact mechanism, but clearly the constructor doesn't take the second argument anyway, may be old code. 2015-07-15 20:43:51 +01:00
Bill Schaller
10ed690d99 Backport Merge pull request #1430 from michael-lavaveshkul/master
"INSTANCE OF" example doesn't match description.
2015-06-18 10:41:56 -04:00
Benjamin Eberlei
e4e59d8064 Merge branch 'DDC-3756' into 2.5 2015-06-16 21:44:14 +02:00
Restless-ET
0e208f7538 [2.5][Bug] Fix ConvertDoctrine1Schema->getMetadata
This bug was introduced at #1205 while resolving #1200.
2015-06-16 21:43:54 +02:00
Guilherme Blanco
3e6c6af845 Merge pull request #1382 from holtkamp/patch-second-level-cache-association-hydration
Patch second level cache association hydration
2015-04-14 11:57:54 -04:00
Marco Pivetta
584345397b Merge branch 'hotfix/#1374-fix-ddc-767-test-php7-pg94-2.5' into 2.5 2015-04-06 04:23:17 +01:00
Matteo Beccati
786791b8fe Fix DDC767Test failing on php7 + pg94
The failure happens when running the full suite or even just:

phpunit tests/Doctrine/Tests/ORM/Functional/Ticket
2015-04-06 04:23:03 +01:00
Marco Pivetta
724dfa0de3 Merge branch 'hotfix/#1361-persistent-collection-collection-hint-fix' into 2.5 2015-04-05 01:30:32 +01:00
Marco Pivetta
39592ba59c Correcting ObjectHydrator logic: if an array is a default value for a collection-valued property, it should be cast to a Collection 2015-04-05 01:29:59 +01:00
Marco Pivetta
58a6013d15 Correcting static introspection issue in cache specific tests (null was being passed to a PersistentCollection) 2015-04-05 01:29:52 +01:00
Marco Pivetta
4580429616 Removing irrelevant tests (as per discussion with @guilhermeblanco and @stof 2015-04-05 01:29:42 +01:00
Marco Pivetta
6e563a313e a PersistentCollection should only allow another collection as a wrapped collection 2015-04-05 01:29:35 +01:00
Marco Pivetta
e8c9cb2f23 Reverting BC break: PersistentConnection#__construct() now accepts null|array|Collection again 2015-04-05 01:29:28 +01:00
Marco Pivetta
4792b4f974 FQCN reference (class was not imported correctly) 2015-04-05 01:29:19 +01:00
Marco Pivetta
c2f6b09ee0 PersistentCollection should still accept null and array as constructor argument, as it did before 2015-04-05 01:29:08 +01:00
Marco Pivetta
cb3179865b Minor docblock correction (discovered during testing) 2015-04-05 01:29:00 +01:00
Marco Pivetta
a1602bd91f Hydration of fetch-joined results fails when an entity has a default value of array for the collection property 2015-04-05 01:28:53 +01:00
Marco Pivetta
34696126e0 Merge branch 'hotfix/#1365-query-dql-function-test-determinism-2.5' into 2.5 2015-04-05 00:10:25 +01:00
Bill Schaller
aa4b2e59ce fix rare query test failures due to nondeterminism without order by clause 2015-04-05 00:10:11 +01:00
Marco Pivetta
2cd86deeb4 Merge branch 'hotfix/#1360-docs-fix-misleading-embeddable-documentation-prefix' into 2.5 2015-04-02 23:25:11 +01:00
Marco Pivetta
52288abf48 Merge branch 'docs/#1359-correcting-mapping-in-working-with-objects-reference' into 2.5 2015-04-02 21:56:03 +01:00
121 changed files with 3269 additions and 319 deletions

View File

@@ -5,8 +5,6 @@ php:
- 5.5
- 5.6
- 7.0
- hhvm
- hhvm-nightly
env:
- DB=mysql
@@ -15,7 +13,7 @@ env:
before_script:
- if [[ $TRAVIS_PHP_VERSION = '5.6' && $DB = 'sqlite' ]]; then PHPUNIT_FLAGS="--coverage-clover ./build/logs/clover.xml"; else PHPUNIT_FLAGS=""; fi
- if [[ $TRAVIS_PHP_VERSION != '5.6' && $TRAVIS_PHP_VERSION != 'hhvm' && $TRAVIS_PHP_VERSION != 'hhvm-nightly' && $TRAVIS_PHP_VERSION != '7.0' ]]; then phpenv config-rm xdebug.ini; fi
- if [[ $PHPUNIT_FLAGS == "" ]]; then phpenv config-rm xdebug.ini; fi
- composer self-update
- composer install --prefer-source --dev
@@ -23,19 +21,11 @@ script:
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml $PHPUNIT_FLAGS
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
after_script:
- php vendor/bin/coveralls -v
matrix:
exclude:
- php: hhvm
env: DB=pgsql # driver currently unsupported by HHVM
- php: hhvm
env: DB=mysqli # driver currently unsupported by HHVM
- php: hhvm-nightly
env: DB=pgsql # driver currently unsupported by HHVM
- php: hhvm-nightly
env: DB=mysqli # driver currently unsupported by HHVM
fast_finish: true
allow_failures:
- php: 7.0
- php: hhvm-nightly # hhvm-nightly currently chokes on composer installation
cache:
directories:
- $HOME/.composer/cache

View File

@@ -1,5 +1,11 @@
# Upgrade to 2.5
## Minor BC BREAK: query cache key time is now a float
As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
instead of an integer in order to have more precision and also to be consistent
with the `TimestampCacheEntry#time`.
## Minor BC BREAK: discriminator map must now include all non-transient classes
It is now required that you declare the root of an inheritance in the
@@ -138,6 +144,10 @@ From now on, the resultset will look like this:
...
)
## Minor BC BREAK: added second parameter $indexBy in EntityRepository#createQueryBuilder method signature
Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()
# Upgrade to 2.4
## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()

View File

@@ -16,16 +16,15 @@
"php": ">=5.4",
"ext-pdo": "*",
"doctrine/collections": "~1.2",
"doctrine/dbal": ">=2.5-dev,<2.6-dev",
"doctrine/instantiator": "~1.0.1",
"doctrine/common": ">=2.5-dev,<2.6-dev",
"doctrine/dbal": ">=2.5-dev,<2.7-dev",
"doctrine/instantiator": "^1.0.1",
"doctrine/common": ">=2.5-dev,<2.9-dev",
"doctrine/cache": "~1.4",
"symfony/console": "~2.5"
"symfony/console": "~2.5|~3.0|~4.0"
},
"require-dev": {
"symfony/yaml": "~2.1",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "dev-master"
"symfony/yaml": "~2.3|~3.0|~4.0",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"

View File

@@ -601,5 +601,5 @@ Querying for the staffs without getting any technicians can be achieved by this
.. code-block:: php
<?php
$query = $em->createQuery("SELECT staff FROM MyProject\Model\Staff staff WHERE staff INSTANCE OF MyProject\Model\Staff");
$query = $em->createQuery("SELECT staff FROM MyProject\Model\Staff staff WHERE staff NOT INSTANCE OF MyProject\Model\Technician");
$staffs = $query->getResult();

View File

@@ -28,7 +28,7 @@ use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\ResultSetMapping;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@@ -993,32 +993,54 @@ abstract class AbstractQuery
private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
{
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL);
$queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
$result = $queryCache->get($querykey, $rsm, $this->_hints);
$queryKey = new QueryCacheKey(
$this->getHash(),
$this->lifetime,
$this->cacheMode ?: Cache::MODE_NORMAL,
$this->getTimestampKey()
);
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
}
return $result;
}
$result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
$cached = $queryCache->put($querykey, $rsm, $result, $this->_hints);
$cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints);
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
if ($cached) {
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
}
}
return $result;
}
/**
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
*/
private function getTimestampKey()
{
$entityName = reset($this->_resultSetMapping->aliasMap);
if (empty($entityName)) {
return null;
}
$metadata = $this->_em->getClassMetadata($entityName);
return new Cache\TimestampCacheKey($metadata->rootEntityName);
}
/**
* Get the result cache id to use to store the result set cache entry.
* Will return the configured id if it exists otherwise a hash will be

View File

@@ -110,7 +110,9 @@ class CacheConfiguration
public function getQueryValidator()
{
if ($this->queryValidator === null) {
$this->queryValidator = new TimestampQueryCacheValidator();
$this->queryValidator = new TimestampQueryCacheValidator(
$this->cacheFactory->getTimestampRegion()
);
}
return $this->queryValidator;

View File

@@ -180,13 +180,7 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping)
{
/* @var $targetPersister \Doctrine\ORM\Cache\Persister\CachedPersister */
$targetPersister = $em->getUnitOfWork()->getEntityPersister($mapping['targetEntity']);
return new DefaultCollectionHydrator(
$em,
$targetPersister->getCacheRegion()
);
return new DefaultCollectionHydrator($em);
}
/**

View File

@@ -73,7 +73,7 @@ class DefaultEntityHydrator implements EntityHydrator
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity)
{
$data = $this->uow->getOriginalEntityData($entity);
$data = array_merge($data, $key->identifier); // why update has no identifier values ?
$data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ?
foreach ($metadata->associationMappings as $name => $assoc) {

View File

@@ -23,7 +23,7 @@ namespace Doctrine\ORM\Cache;
/**
* Defines a region that supports multi-get reading.
*
* With one method call we can get multipe items.
* With one method call we can get multiple items.
*
* @since 2.5
* @author Asmir Mustafic
@@ -31,7 +31,7 @@ namespace Doctrine\ORM\Cache;
interface MultiGetRegion
{
/**
* Get all items from the cache indentifed by $keys.
* Get all items from the cache identified by $keys.
* It returns NULL if some elements can not be found.
*
* @param CollectionCacheEntry $collection The collection of the items to be retrieved.

View File

@@ -32,6 +32,7 @@ use Doctrine\Common\Util\ClassUtils;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
abstract class AbstractCollectionPersister implements CachedCollectionPersister
@@ -164,10 +165,18 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
public function storeCollectionCache(CollectionCacheKey $key, $elements)
{
/* @var $targetPersister CachedEntityPersister */
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
$targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);
// Only preserve ordering if association configured it
if ( ! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
// Elements may be an array or a Collection
$elements = array_values(is_array($elements) ? $elements : $elements->getValues());
}
$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);
foreach ($entry->identifiers as $index => $entityKey) {
if ($targetRegion->contains($entityKey)) {

View File

@@ -131,7 +131,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->timestampRegion = $cacheFactory->getTimestampRegion();
$this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
$this->timestampKey = new TimestampCacheKey($this->class->getTableName());
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
}
/**
@@ -296,17 +296,16 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
* @param array $orderBy
* @param integer $limit
* @param integer $offset
* @param integer $timestamp
*
* @return string
*/
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null)
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
{
list($params) = ($criteria instanceof Criteria)
? $this->persister->expandCriteriaParameters($criteria)
: $this->persister->expandParameters($criteria);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
}
/**
@@ -377,17 +376,16 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
}
//handle only EntityRepository#findOneBy
$timestamp = $this->timestampRegion->get($this->timestampKey);
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, null, null, null);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$result = $queryCache->get($querykey, $rsm);
$result = $queryCache->get($queryKey, $rsm);
if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}
return $result[0];
@@ -397,15 +395,15 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
return null;
}
$cached = $queryCache->put($querykey, $rsm, array($result));
$cached = $queryCache->put($queryKey, $rsm, array($result));
if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}
if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}
@@ -417,32 +415,31 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
*/
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
{
$timestamp = $this->timestampRegion->get($this->timestampKey);
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, null, null, null);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$result = $queryCache->get($querykey, $rsm);
$result = $queryCache->get($queryKey, $rsm);
if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}
return $result;
}
$result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
$cached = $queryCache->put($querykey, $rsm, $result);
$cached = $queryCache->put($queryKey, $rsm, $result);
if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}
if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}
@@ -516,32 +513,34 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
*/
public function loadCriteria(Criteria $criteria)
{
$orderBy = $criteria->getOrderings();
$limit = $criteria->getMaxResults();
$offset = $criteria->getFirstResult();
$query = $this->persister->getSelectSQL($criteria);
$timestamp = $this->timestampRegion->get($this->timestampKey);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$cacheResult = $queryCache->get($querykey, $rsm);
$cacheResult = $queryCache->get($queryKey, $rsm);
if ($cacheResult !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}
return $cacheResult;
}
$result = $this->persister->loadCriteria($criteria);
$cached = $queryCache->put($querykey, $rsm, $result);
$cached = $queryCache->put($queryKey, $rsm, $result);
if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}
if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}

View File

@@ -27,6 +27,7 @@ use Doctrine\ORM\Cache\EntityCacheKey;
* Specific non-strict read/write cached entity persister
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
@@ -78,13 +79,16 @@ class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$deleted = $this->persister->delete($entity);
if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}
$this->queuedCache['delete'][] = $key;
return $deleted;
}
/**

View File

@@ -30,6 +30,7 @@ use Doctrine\ORM\Cache\EntityCacheKey;
* Specific read-write entity persister
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
class ReadWriteCachedEntityPersister extends AbstractEntityPersister
@@ -100,21 +101,24 @@ class ReadWriteCachedEntityPersister extends AbstractEntityPersister
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$deleted = $this->persister->delete($entity);
if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}
if ($lock === null) {
return;
return $deleted;
}
$this->queuedCache['delete'][] = array(
'lock' => $lock,
'key' => $key
);
return $deleted;
}
/**

View File

@@ -38,18 +38,18 @@ class QueryCacheEntry implements CacheEntry
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var integer Time creation of this cache entry
* @var float Time creation of this cache entry
*/
public $time;
/**
* @param array $result
* @param integer $time
* @param float $time
*/
public function __construct($result, $time = null)
{
$this->result = $result;
$this->time = $time ?: time();
$this->time = $time ?: microtime(true);
}
/**

View File

@@ -45,14 +45,27 @@ class QueryCacheKey extends CacheKey
public $cacheMode;
/**
* @param string $hash Result cache id
* @param integer $lifetime Query lifetime
* @param integer $cacheMode Query cache mode
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var TimestampCacheKey|null
*/
public function __construct($hash, $lifetime = 0, $cacheMode = Cache::MODE_NORMAL)
{
$this->hash = $hash;
$this->lifetime = $lifetime;
$this->cacheMode = $cacheMode;
public $timestampKey;
/**
* @param string $hash Result cache id
* @param integer $lifetime Query lifetime
* @param int $cacheMode Query cache mode
* @param TimestampCacheKey|null $timestampKey
*/
public function __construct(
$hash,
$lifetime = 0,
$cacheMode = Cache::MODE_NORMAL,
TimestampCacheKey $timestampKey = null
) {
$this->hash = $hash;
$this->lifetime = $lifetime;
$this->cacheMode = $cacheMode;
$this->timestampKey = $timestampKey;
}
}

View File

@@ -57,8 +57,9 @@ class DefaultMultiGetRegion extends DefaultRegion
public function getMultiple(CollectionCacheEntry $collection)
{
$keysToRetrieve = array();
foreach ($collection->identifiers as $index => $key) {
$keysToRetrieve[$index] = $this->name . '_' . $key->hash;
$keysToRetrieve[$index] = $this->getCacheEntryKey($key);
}
$items = $this->cache->fetchMultiple($keysToRetrieve);
@@ -70,6 +71,7 @@ class DefaultMultiGetRegion extends DefaultRegion
foreach ($keysToRetrieve as $index => $key) {
$returnableItems[$index] = $items[$key];
}
return $returnableItems;
}
}

View File

@@ -36,6 +36,8 @@ use Doctrine\ORM\Cache\Region;
*/
class DefaultRegion implements Region
{
const REGION_KEY_SEPARATOR = '_';
/**
* @var CacheAdapter
*/
@@ -84,7 +86,7 @@ class DefaultRegion implements Region
*/
public function contains(CacheKey $key)
{
return $this->cache->contains($this->name . '_' . $key->hash);
return $this->cache->contains($this->getCacheEntryKey($key));
}
/**
@@ -92,7 +94,7 @@ class DefaultRegion implements Region
*/
public function get(CacheKey $key)
{
return $this->cache->fetch($this->name . '_' . $key->hash) ?: null;
return $this->cache->fetch($this->getCacheEntryKey($key)) ?: null;
}
/**
@@ -100,30 +102,29 @@ class DefaultRegion implements Region
*/
public function getMultiple(CollectionCacheEntry $collection)
{
$keysToRetrieve = array();
$result = array();
foreach ($collection->identifiers as $index => $key) {
$keysToRetrieve[$index] = $this->name . '_' . $key->hash;
}
foreach ($collection->identifiers as $key) {
$entryKey = $this->getCacheEntryKey($key);
$entryValue = $this->cache->fetch($entryKey);
$items = array_filter(
array_map([$this->cache, 'fetch'], $keysToRetrieve),
function ($retrieved) {
return false !== $retrieved;
if ($entryValue === false) {
return null;
}
);
if (count($items) !== count($keysToRetrieve)) {
return null;
$result[] = $entryValue;
}
$returnableItems = array();
return $result;
}
foreach ($keysToRetrieve as $index => $key) {
$returnableItems[$index] = $items[$key];
}
return $returnableItems;
/**
* @param CacheKey $key
* @return string
*/
protected function getCacheEntryKey(CacheKey $key)
{
return $this->name . self::REGION_KEY_SEPARATOR . $key->hash;
}
/**
@@ -131,7 +132,7 @@ class DefaultRegion implements Region
*/
public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null)
{
return $this->cache->save($this->name . '_' . $key->hash, $entry, $this->lifetime);
return $this->cache->save($this->getCacheEntryKey($key), $entry, $this->lifetime);
}
/**
@@ -139,7 +140,7 @@ class DefaultRegion implements Region
*/
public function evict(CacheKey $key)
{
return $this->cache->delete($this->name . '_' . $key->hash);
return $this->cache->delete($this->getCacheEntryKey($key));
}
/**

View File

@@ -61,7 +61,7 @@ class FileLockRegion implements ConcurrentRegion
*/
public function __construct(Region $region, $directory, $lockLifetime)
{
if ( ! is_dir($directory) && ! @mkdir($directory, 0777, true)) {
if ( ! is_dir($directory) && ! @mkdir($directory, 0775, true)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $directory));
}
@@ -242,6 +242,7 @@ class FileLockRegion implements ConcurrentRegion
if ( ! @file_put_contents($filename, $lock->value, LOCK_EX)) {
return null;
}
chmod($filename, 0664);
return $lock;
}

View File

@@ -26,15 +26,49 @@ namespace Doctrine\ORM\Cache;
*/
class TimestampQueryCacheValidator implements QueryCacheValidator
{
/**
* @var TimestampRegion
*/
private $timestampRegion;
/**
* @param TimestampRegion $timestampRegion
*/
public function __construct(TimestampRegion $timestampRegion)
{
$this->timestampRegion = $timestampRegion;
}
/**
* {@inheritdoc}
*/
public function isValid(QueryCacheKey $key, QueryCacheEntry $entry)
{
if ($this->regionUpdated($key, $entry)) {
return false;
}
if ($key->lifetime == 0) {
return true;
}
return ($entry->time + $key->lifetime) > time();
return ($entry->time + $key->lifetime) > microtime(true);
}
/**
* @param QueryCacheKey $key
* @param QueryCacheEntry $entry
*
* @return bool
*/
private function regionUpdated(QueryCacheKey $key, QueryCacheEntry $entry)
{
if ($key->timestampKey === null) {
return false;
}
$timestamp = $this->timestampRegion->get($key->timestampKey);
return $timestamp && $timestamp->time > $entry->time;
}
}

View File

@@ -26,6 +26,11 @@ use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClas
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @since 2.0
*
* Note: method annotations are used instead of method overrides (due to BC policy)
*
* @method __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata, \Doctrine\ORM\EntityManager $objectManager)
* @method \Doctrine\ORM\Mapping\ClassMetadata getClassMetadata()
*/
class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs
{

View File

@@ -142,6 +142,9 @@ abstract class AbstractHydrator
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$evm = $this->_em->getEventManager();
$evm->addEventListener(array(Events::onClear), $this);
$this->prepare();
$result = $this->hydrateAllData();
@@ -210,6 +213,9 @@ abstract class AbstractHydrator
$this->_rsm = null;
$this->_cache = array();
$this->_metadataCache = array();
$evm = $this->_em->getEventManager();
$evm->removeEventListener(array(Events::onClear), $this);
}
/**

View File

@@ -189,8 +189,8 @@ class ObjectHydrator extends AbstractHydrator
$relation = $class->associationMappings[$fieldName];
$value = $class->reflFields[$fieldName]->getValue($entity);
if ($value === null) {
$value = new ArrayCollection;
if ($value === null || is_array($value)) {
$value = new ArrayCollection((array) $value);
}
if ( ! $value instanceof PersistentCollection) {
@@ -332,6 +332,9 @@ class ObjectHydrator extends AbstractHydrator
// Split the row data into chunks of class data.
$rowData = $this->gatherRowData($row, $id, $nonemptyComponents);
// reset result pointers for each data row
$this->resultPointers = [];
// Hydrate the data chunks
foreach ($rowData['data'] as $dqlAlias => $data) {
$entityName = $this->_rsm->aliasMap[$dqlAlias];
@@ -357,8 +360,8 @@ class ObjectHydrator extends AbstractHydrator
// 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 = $first[key($first)];
$objectClass = $this->resultPointers[$parentAlias];
$parentObject = $objectClass[key($objectClass)];
} else if (isset($this->resultPointers[$parentAlias])) {
$parentObject = $this->resultPointers[$parentAlias];
} else {

View File

@@ -122,6 +122,9 @@ class SimpleObjectHydrator extends AbstractHydrator
continue;
}
// Check if value is null before conversion (because some types convert null to something else)
$valueIsNull = null === $value;
// Convert field to a valid PHP value
if (isset($cacheKeyInfo['type'])) {
$type = $cacheKeyInfo['type'];
@@ -131,7 +134,7 @@ class SimpleObjectHydrator extends AbstractHydrator
$fieldName = $cacheKeyInfo['fieldName'];
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
if ( ! isset($data[$fieldName]) || $value !== null) {
if ( ! isset($data[$fieldName]) || ! $valueIsNull) {
$data[$fieldName] = $value;
}
}

View File

@@ -940,12 +940,17 @@ class ClassMetadataInfo implements ClassMetadata
continue;
}
$parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
$this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
$fieldRefl = $reflService->getAccessibleProperty(
isset($embeddedClass['declared']) ? $embeddedClass['declared'] : $this->name,
$property
);
$parentReflFields[$property] = $fieldRefl;
$this->reflFields[$property] = $fieldRefl;
}
foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declaredField'])) {
if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
$parentReflFields[$mapping['declaredField']],
$reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),

View File

@@ -811,7 +811,8 @@ class XmlDriver extends FileDriver
protected function loadMappingFile($file)
{
$result = array();
$xmlElement = simplexml_load_file($file);
// Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
$xmlElement = simplexml_load_string(file_get_contents($file));
if (isset($xmlElement->entity)) {
foreach ($xmlElement->entity as $entityElement) {

View File

@@ -514,9 +514,8 @@ class YamlDriver extends FileDriver
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
if (isset($joinTableElement['inverseJoinColumns'])) {
@@ -524,9 +523,8 @@ class YamlDriver extends FileDriver
if ( ! isset($joinColumnElement['name'])) {
$joinColumnElement['name'] = $joinColumnName;
}
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
}
$mapping['joinTable'] = $joinTable;

View File

@@ -19,7 +19,6 @@
namespace Doctrine\ORM;
use Closure;
use Doctrine\Common\Collections\AbstractLazyCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
@@ -101,13 +100,13 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
/**
* Creates a new persistent collection.
*
* @param EntityManagerInterface $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param Collection $coll The collection elements.
* @param EntityManagerInterface $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param Collection $collection The collection elements.
*/
public function __construct(EntityManagerInterface $em, $class, $coll)
public function __construct(EntityManagerInterface $em, $class, Collection $collection)
{
$this->collection = $coll;
$this->collection = $collection;
$this->em = $em;
$this->typeClass = $class;
$this->initialized = true;
@@ -375,11 +374,7 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
if ($persister->removeElement($this, $element)) {
return $element;
}
return null;
return $persister->removeElement($this, $element);
}
$removed = parent::removeElement($element);
@@ -536,6 +531,8 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
public function clear()
{
if ($this->initialized && $this->isEmpty()) {
$this->collection->clear();
return;
}
@@ -687,21 +684,41 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
protected function doInitialize()
{
// Has NEW objects added through add(). Remember them.
$newObjects = array();
$newlyAddedDirtyObjects = array();
if ($this->isDirty) {
$newObjects = $this->collection->toArray();
$newlyAddedDirtyObjects = $this->collection->toArray();
}
$this->collection->clear();
$this->em->getUnitOfWork()->loadCollection($this);
$this->takeSnapshot();
// Reattach NEW objects added through add(), if any.
if ($newObjects) {
foreach ($newObjects as $obj) {
$this->collection->add($obj);
}
if ($newlyAddedDirtyObjects) {
$this->restoreNewObjectsInDirtyCollection($newlyAddedDirtyObjects);
}
}
/**
* @param object[] $newObjects
*
* @return void
*
* Note: the only reason why this entire looping/complexity is performed via `spl_object_hash`
* is because we want to prevent using `array_udiff()`, which is likely to cause very
* high overhead (complexity of O(n^2)). `array_diff_key()` performs the operation in
* core, which is faster than using a callback for comparisons
*/
private function restoreNewObjectsInDirtyCollection(array $newObjects)
{
$loadedObjects = $this->collection->toArray();
$newObjectsByOid = \array_combine(\array_map('spl_object_hash', $newObjects), $newObjects);
$loadedObjectsByOid = \array_combine(\array_map('spl_object_hash', $loadedObjects), $loadedObjects);
$newObjectsThatWereNotLoaded = \array_diff_key($newObjectsByOid, $loadedObjectsByOid);
if ($newObjectsThatWereNotLoaded) {
// Reattach NEW objects added through add(), if any.
\array_walk($newObjectsThatWereNotLoaded, [$this->collection, 'add']);
$this->isDirty = true;
}

View File

@@ -90,10 +90,6 @@ abstract class AbstractCollectionPersister implements CollectionPersister
// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity)) {
return false;
}
return true;
return ! ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity));
}
}

View File

@@ -91,7 +91,9 @@ class ProxyFactory extends AbstractProxyFactory
protected function skipClass(ClassMetadata $metadata)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract();
return $metadata->isMappedSuperclass
|| $metadata->isEmbeddedClass
|| $metadata->getReflectionClass()->isAbstract();
}
/**
@@ -228,7 +230,7 @@ class ProxyFactory extends AbstractProxyFactory
);
}
foreach ($class->getReflectionClass()->getProperties() as $property) {
foreach ($class->getReflectionProperties() as $property) {
if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
continue;
}

View File

@@ -965,7 +965,7 @@ class Parser
$schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
}
$exists = class_exists($schemaName, true);
$exists = class_exists($schemaName, true) || interface_exists($schemaName, true);
if ( ! $exists) {
$this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token);

View File

@@ -347,7 +347,7 @@ class ResultSetMapping
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
*
* @return ResultSetMapping This ResultSetMapping instance.

View File

@@ -110,7 +110,7 @@ class ResultSetMappingBuilder extends ResultSetMapping
* @param string $class The class name of the joined entity.
* @param string $alias The unique alias to use for the joined entity.
* @param string $parentAlias The alias of the entity result that is the parent of this joined result.
* @param object $relation The association field that connects the parent entity result
* @param string $relation The association field that connects the parent entity result
* with the joined entity result.
* @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName).
* @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM).
@@ -438,8 +438,8 @@ class ResultSetMappingBuilder extends ResultSetMapping
$sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName'];
} else if (isset($this->metaMappings[$columnName])) {
$sql .= $this->metaMappings[$columnName];
} else if (isset($this->discriminatorColumn[$columnName])) {
$sql .= $this->discriminatorColumn[$columnName];
} else if (isset($this->discriminatorColumns[$dqlAlias])) {
$sql .= $this->discriminatorColumns[$dqlAlias];
}
$sql .= " AS " . $columnName;

View File

@@ -870,6 +870,19 @@ class SqlWalker implements TreeWalker
* @return string
*/
public function walkRangeVariableDeclaration($rangeVariableDeclaration)
{
return $this->generateRangeVariableDeclarationSQL($rangeVariableDeclaration, false);
}
/**
* Generate appropriate SQL for RangeVariableDeclaration AST node
*
* @param AST\RangeVariableDeclaration $rangeVariableDeclaration
* @param bool $buildNestedJoins
*
* @return string
*/
private function generateRangeVariableDeclarationSQL($rangeVariableDeclaration, $buildNestedJoins)
{
$class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
$dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
@@ -885,7 +898,11 @@ class SqlWalker implements TreeWalker
);
if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
if ($buildNestedJoins) {
$sql = '(' . $sql . $this->_generateClassTableInheritanceJoins($class, $dqlAlias) . ')';
} else {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
}
}
return $sql;
@@ -1093,6 +1110,10 @@ class SqlWalker implements TreeWalker
$this->orderedColumnsMap[$sql] = $type;
if ($expr instanceof AST\Subselect) {
return '(' . $sql . ') ' . $type;
}
return $sql . ' ' . $type;
}
@@ -1117,16 +1138,17 @@ class SqlWalker implements TreeWalker
: ' INNER JOIN ';
switch (true) {
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
case ($joinDeclaration instanceof AST\RangeVariableDeclaration):
$class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
$dqlAlias = $joinDeclaration->aliasIdentificationVariable;
$tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
$isUnconditionalJoin = empty($condition);
$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER && $isUnconditionalJoin)
? ' AND '
: ' ON ';
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration);
$sql .= $this->generateRangeVariableDeclarationSQL($joinDeclaration, !$isUnconditionalJoin);
$conditions = array($condition);
@@ -1147,7 +1169,7 @@ class SqlWalker implements TreeWalker
$sql .= $condExprConjunction . implode(' AND ', $conditions);
break;
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
case ($joinDeclaration instanceof AST\JoinAssociationDeclaration):
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
break;
}
@@ -2260,7 +2282,9 @@ class SqlWalker implements TreeWalker
public function walkArithmeticFactor($factor)
{
if (is_string($factor)) {
return $factor;
return (isset($this->queryComponents[$factor]))
? $this->walkResultVariable($this->queryComponents[$factor]['token']['value'])
: $factor;
}
// Phase 2 AST optimization: Skip processing of ArithmeticFactor

View File

@@ -137,7 +137,7 @@ EOT
// Process destination directory
if ( ! is_dir($destPath = $input->getArgument('dest-path'))) {
mkdir($destPath, 0777, true);
mkdir($destPath, 0775, true);
}
$destPath = realpath($destPath);

View File

@@ -79,7 +79,7 @@ EOT
}
if ( ! is_dir($destPath)) {
mkdir($destPath, 0777, true);
mkdir($destPath, 0775, true);
}
$destPath = realpath($destPath);

View File

@@ -84,10 +84,6 @@ class MetadataFilter extends \FilterIterator implements \Countable
);
}
if ($pregResult === 0) {
return false;
}
if ($pregResult) {
return true;
}

View File

@@ -80,7 +80,7 @@ class ConvertDoctrine1Schema
$schema = array_merge($schema, (array) Yaml::parse(file_get_contents($file)));
}
} else {
$schema = array_merge($schema, (array) Yaml::parse(file_get_contents($file)));
$schema = array_merge($schema, (array) Yaml::parse(file_get_contents($path)));
}
}

View File

@@ -364,7 +364,7 @@ public function __construct(<params>)
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
mkdir($dir, 0775, true);
}
$this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists);
@@ -389,6 +389,7 @@ public function __construct(<params>)
} elseif ( ! $this->isNew && $this->updateEntityIfExists) {
file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
}
chmod($path, 0664);
}
/**

View File

@@ -147,11 +147,12 @@ class <className> extends <repositoryName>
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
mkdir($dir, 0775, true);
}
if ( ! file_exists($path)) {
file_put_contents($path, $code);
chmod($path, 0664);
}
}

View File

@@ -130,7 +130,7 @@ abstract class AbstractExporter
public function export()
{
if ( ! is_dir($this->_outputDir)) {
mkdir($this->_outputDir, 0777, true);
mkdir($this->_outputDir, 0775, true);
}
foreach ($this->_metadata as $metadata) {
@@ -139,12 +139,13 @@ abstract class AbstractExporter
$path = $this->_generateOutputPath($metadata);
$dir = dirname($path);
if ( ! is_dir($dir)) {
mkdir($dir, 0777, true);
mkdir($dir, 0775, true);
}
if (file_exists($path) && !$this->_overwriteExistingFiles) {
throw ExportException::attemptOverwriteExistingFile($path);
}
file_put_contents($path, $output);
chmod($path, 0664);
}
}
}

View File

@@ -117,7 +117,7 @@ class PhpExporter extends AbstractExporter
$oneToOneMappingArray = array(
'mappedBy' => $associationMapping['mappedBy'],
'inversedBy' => $associationMapping['inversedBy'],
'joinColumns' => $associationMapping['joinColumns'],
'joinColumns' => $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : [],
'orphanRemoval' => $associationMapping['orphanRemoval'],
);

View File

@@ -163,7 +163,7 @@ class YamlExporter extends AbstractExporter
}
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
$joinColumns = $associationMapping['joinColumns'];
$joinColumns = $associationMapping['isOwningSide'] ? $associationMapping['joinColumns'] : [];
$newJoinColumns = array();
foreach ($joinColumns as $joinColumn) {

View File

@@ -85,6 +85,14 @@ class CountOutputWalker extends SqlWalker
$sql = parent::walkSelectStatement($AST);
if ($AST->groupByClause) {
return sprintf(
'SELECT %s AS dctrn_count FROM (%s) dctrn_table',
$this->platform->getCountExpression('*'),
$sql
);
}
// Find out the SQL alias of the identifier column of the root entity
// It may be possible to make this work with multiple root entities but that
// would probably require issuing multiple queries or doing a UNION SELECT

View File

@@ -438,7 +438,10 @@ class LimitSubqueryOutputWalker extends SqlWalker
// Field was declared in a parent class, so we need to get the proper SQL table alias
// for the joined parent table.
$otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']);
$sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias);
if (!$otherClassMetadata->isMappedSuperclass) {
$sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias);
}
}
// Compose search/replace patterns

View File

@@ -21,6 +21,7 @@ namespace Doctrine\ORM\Tools;
use Doctrine\ORM\ORMException;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
@@ -274,6 +275,15 @@ class SchemaTool
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) {
$uniqIndex = new Index($indexName, $indexData['columns'], true, false, [], isset($indexData['options']) ? $indexData['options'] : []);
foreach ($table->getIndexes() as $tableIndexName => $tableIndex) {
if ($tableIndex->isFullfilledBy($uniqIndex)) {
$table->dropIndex($tableIndexName);
break;
}
}
$table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName, isset($indexData['options']) ? $indexData['options'] : array());
}
}
@@ -335,7 +345,7 @@ class SchemaTool
$discrColumn = $class->discriminatorColumn;
if ( ! isset($discrColumn['type']) ||
(strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)
(strtolower($discrColumn['type']) == 'string' && ! isset($discrColumn['length']))
) {
$discrColumn['type'] = 'string';
$discrColumn['length'] = 255;

View File

@@ -733,7 +733,6 @@ class UnitOfWork implements PropertyChangedListener
// Look for changes in associations of the entity
foreach ($class->associationMappings as $field => $assoc) {
if (($val = $class->reflFields[$field]->getValue($entity)) === null) {
continue;
}
@@ -799,7 +798,7 @@ class UnitOfWork implements PropertyChangedListener
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
$oid = spl_object_hash($entity);
if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
$this->computeChangeSet($class, $entity);
}
}
@@ -826,10 +825,7 @@ class UnitOfWork implements PropertyChangedListener
if ($value instanceof PersistentCollection && $value->isDirty()) {
$coid = spl_object_hash($value);
if ($assoc['isOwningSide']) {
$this->collectionUpdates[$coid] = $value;
}
$this->collectionUpdates[$coid] = $value;
$this->visitedCollections[$coid] = $value;
}
@@ -1803,7 +1799,7 @@ class UnitOfWork implements PropertyChangedListener
* @throws OptimisticLockException If the entity uses optimistic locking through a version
* attribute and the version check against the managed copy fails.
* @throws ORMInvalidArgumentException If the entity instance is NEW.
* @throws EntityNotFoundException
* @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided
*/
private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
{
@@ -1835,6 +1831,7 @@ class UnitOfWork implements PropertyChangedListener
if ( ! $id) {
$managedCopy = $this->newInstance($class);
$this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
$this->persistNew($class, $managedCopy);
} else {
$flatId = ($class->containsForeignIdentifier)
@@ -1866,31 +1863,16 @@ class UnitOfWork implements PropertyChangedListener
$managedCopy = $this->newInstance($class);
$class->setIdentifierValues($managedCopy, $id);
$this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
$this->persistNew($class, $managedCopy);
}
}
if ($class->isVersioned) {
$reflField = $class->reflFields[$class->versionField];
$managedCopyVersion = $reflField->getValue($managedCopy);
$entityVersion = $reflField->getValue($entity);
// Throw exception if versions don't match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
} else {
$this->ensureVersionMatch($class, $entity, $managedCopy);
$this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
}
}
$visited[$oid] = $managedCopy; // mark visited
if (!($entity instanceof Proxy && ! $entity->__isInitialized())) {
if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
$managedCopy->__load();
}
$this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
}
if ($class->isChangeTrackingDeferredExplicit()) {
$this->scheduleForDirtyCheck($entity);
}
@@ -1908,6 +1890,45 @@ class UnitOfWork implements PropertyChangedListener
return $managedCopy;
}
/**
* @param ClassMetadata $class
* @param object $entity
* @param object $managedCopy
*
* @return void
*
* @throws OptimisticLockException
*/
private function ensureVersionMatch(ClassMetadata $class, $entity, $managedCopy)
{
if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
return;
}
$reflField = $class->reflFields[$class->versionField];
$managedCopyVersion = $reflField->getValue($managedCopy);
$entityVersion = $reflField->getValue($entity);
// Throw exception if versions don't match.
if ($managedCopyVersion == $entityVersion) {
return;
}
throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
}
/**
* Tests if an entity is loaded - must either be a loaded proxy or not a proxy
*
* @param object $entity
*
* @return bool
*/
private function isLoaded($entity)
{
return !($entity instanceof Proxy) || $entity->__isInitialized();
}
/**
* Sets/adds associated managed copies into the previous entity's association field
*
@@ -2395,17 +2416,8 @@ class UnitOfWork implements PropertyChangedListener
$this->commitOrderCalculator->clear();
}
} else {
$visited = array();
foreach ($this->identityMap as $className => $entities) {
if ($className !== $entityName) {
continue;
}
foreach ($entities as $entity) {
$this->doDetach($entity, $visited, false);
}
}
$this->clearIdentityMapForEntityName($entityName);
$this->clearEntityInsertionsForEntityName($entityName);
}
if ($this->evm->hasListeners(Events::onClear)) {
@@ -3357,6 +3369,14 @@ class UnitOfWork implements PropertyChangedListener
*/
private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
{
if (! $this->isLoaded($entity)) {
return;
}
if (! $this->isLoaded($managedCopy)) {
$managedCopy->__load();
}
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) {
@@ -3378,7 +3398,7 @@ class UnitOfWork implements PropertyChangedListener
} else {
if ($other instanceof Proxy && !$other->__isInitialized()) {
// do not merge fields marked lazy that have not been fetched.
return;
continue;
}
if ( ! $assoc2['isCascadeMerge']) {
@@ -3406,7 +3426,7 @@ class UnitOfWork implements PropertyChangedListener
if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
// do not merge fields marked lazy that have not been fetched.
// keep the lazy persistent collection of the managed copy.
return;
continue;
}
$managedCol = $prop->getValue($managedCopy);
@@ -3419,8 +3439,6 @@ class UnitOfWork implements PropertyChangedListener
);
$managedCol->setOwner($managedCopy, $assoc2);
$prop->setValue($managedCopy, $managedCol);
$this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
}
if ($assoc2['isCascadeMerge']) {
@@ -3459,4 +3477,32 @@ class UnitOfWork implements PropertyChangedListener
{
$this->hydrationCompleteHandler->hydrationComplete();
}
/**
* @param string $entityName
*/
private function clearIdentityMapForEntityName($entityName)
{
if (! isset($this->identityMap[$entityName])) {
return;
}
$visited = [];
foreach ($this->identityMap[$entityName] as $entity) {
$this->doDetach($entity, $visited, false);
}
}
/**
* @param string $entityName
*/
private function clearEntityInsertionsForEntityName($entityName)
{
foreach ($this->entityInsertions as $hash => $entity) {
if (get_class($entity) === $entityName) {
unset($this->entityInsertions[$hash]);
}
}
}
}

View File

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

View File

@@ -3,33 +3,87 @@
namespace Doctrine\Tests\EventListener;
use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
class CacheMetadataListener
{
/**
* Tracks which entities we have already forced caching enabled on. This is
* important to avoid some potential infinite-recursion issues.
*
* Key is the name of the entity, payload is unimportant.
*
* @var array
*/
protected $enabledItems = array();
/**
* @param \Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs $event
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $event)
{
$metadata = $event->getClassMetadata();
$cache = array(
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE
);
$em = $event->getObjectManager();
/** @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
if (strstr($metadata->name, 'Doctrine\Tests\Models\Cache')) {
return;
}
$this->enableCaching($metadata, $em);
}
/**
* @param ClassMetadata $metadata
*
* @return bool
*/
private function isVisited(ClassMetaData $metadata)
{
return isset($this->enabledItems[$metadata->getName()]);
}
/**
* @param ClassMetadata $metadata
*/
private function recordVisit(ClassMetaData $metadata)
{
$this->enabledItems[$metadata->getName()] = true;
}
/**
* @param ClassMetadata $metadata
* @param EntityManager $em
*/
protected function enableCaching(ClassMetadata $metadata, EntityManager $em)
{
if ($this->isVisited($metadata)) {
return; // Already handled in the past
}
$cache = array(
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE
);
if ($metadata->isVersioned) {
return;
}
$metadata->enableCache($cache);
$this->recordVisit($metadata);
// only enable association-caching when the target has already been
// given caching settings
foreach ($metadata->associationMappings as $mapping) {
$metadata->enableAssociationCache($mapping['fieldName'], $cache);
$targetMeta = $em->getClassMetadata($mapping['targetEntity']);
$this->enableCaching($targetMeta, $em);
if ($this->isVisited($targetMeta)) {
$metadata->enableAssociationCache($mapping['fieldName'], $cache);
}
}
}
}

View File

@@ -33,7 +33,7 @@ class State
protected $country;
/**
* @Cache
* @Cache("NONSTRICT_READ_WRITE")
* @OneToMany(targetEntity="City", mappedBy="state")
*/
protected $cities;

View File

@@ -26,7 +26,7 @@ class Traveler
protected $name;
/**
* @Cache()
* @Cache("NONSTRICT_READ_WRITE")
* @OneToMany(targetEntity="Travel", mappedBy="traveler", cascade={"persist", "remove"}, orphanRemoval=true)
*
* @var \Doctrine\Common\Collections\Collection

View File

@@ -6,17 +6,17 @@ class CompanyContractListener
{
public $postPersistCalls;
public $prePersistCalls;
public $postUpdateCalls;
public $preUpdateCalls;
public $postRemoveCalls;
public $preRemoveCalls;
public $preFlushCalls;
public $postLoadCalls;
/**
* @PostPersist
*/
@@ -80,5 +80,4 @@ class CompanyContractListener
{
$this->postLoadCalls[] = func_get_args();
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Doctrine\Tests\Models\DDC3699;
/** @Entity @Table(name="ddc3699_child") */
class DDC3699Child extends DDC3699Parent
{
const CLASSNAME = __CLASS__;
/** @Id @Column(type="integer") */
public $id;
/** @Column(type="string") */
public $childField;
/** @OneToOne(targetEntity="DDC3699RelationOne", inversedBy="child") */
public $oneRelation;
/** @OneToMany(targetEntity="DDC3699RelationMany", mappedBy="child") */
public $relations;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\Tests\Models\DDC3699;
/** @MappedSuperclass */
abstract class DDC3699Parent
{
const CLASSNAME = __CLASS__;
/** @Column(type="string") */
public $parentField;
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Doctrine\Tests\Models\DDC3699;
/**
* @Entity
* @Table(name="ddc3699_relation_many")
*/
class DDC3699RelationMany
{
const CLASSNAME = __CLASS__;
/** @Id @Column(type="integer") */
public $id;
/** @ManyToOne(targetEntity="DDC3699Child", inversedBy="relations") */
public $child;
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Doctrine\Tests\Models\DDC3699;
/**
* @Entity
* @Table(name="ddc3699_relation_one")
*/
class DDC3699RelationOne
{
const CLASSNAME = __CLASS__;
/** @Id @Column(type="integer") */
public $id;
/** @OneToOne(targetEntity="DDC3699Child", mappedBy="oneRelation") */
public $child;
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* @author Marc Pantel <pantel.m@gmail.com>
*/
namespace Doctrine\Tests\Models\DDC3711;
use Doctrine\Common\Collections\ArrayCollection;
class DDC3711EntityA
{
/**
* @var int
*/
private $id1;
/**
* @var int
*/
private $id2;
/**
* @var ArrayCollection
*/
private $entityB;
/**
* @return mixed
*/
public function getId1()
{
return $this->id1;
}
/**
* @param mixed $id1
*/
public function setId1($id1)
{
$this->id1 = $id1;
}
/**
* @return mixed
*/
public function getId2()
{
return $this->id2;
}
/**
* @param mixed $id2
*/
public function setId2($id2)
{
$this->id2 = $id2;
}
/**
* @return ArrayCollection
*/
public function getEntityB()
{
return $this->entityB;
}
/**
* @param ArrayCollection $entityB
*
* @return DDC3711EntityA
*/
public function addEntityB($entityB)
{
$this->entityB[] = $entityB;
return $this;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* @author Marc Pantel <pantel.m@gmail.com>
*/
namespace Doctrine\Tests\Models\DDC3711;
use Doctrine\Common\Collections\ArrayCollection;
class DDC3711EntityB
{
/**
* @var int
*/
private $id1;
/**
* @var int
*/
private $id2;
/**
* @var ArrayCollection
*/
private $entityA;
/**
* @return int
*/
public function getId1()
{
return $this->id1;
}
/**
* @param int $id1
*/
public function setId1($id1)
{
$this->id1 = $id1;
}
/**
* @return int
*/
public function getId2()
{
return $this->id2;
}
/**
* @param int $id2
*/
public function setId2($id2)
{
$this->id2 = $id2;
}
/**
* @return ArrayCollection
*/
public function getEntityA()
{
return $this->entityA;
}
/**
* @param ArrayCollection $entityA
*/
public function addEntityA($entityA)
{
$this->entityA[] = $entityA;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Doctrine\Tests\Models\DDC3899;
/**
* @Entity
* @Table(name="dc3899_contracts")
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({
* "fix" = "DDC3899FixContract",
* "flexible" = "DDC3899FlexContract"
* })
*/
abstract class DDC3899Contract
{
/** @Id @Column(type="integer") */
public $id;
/** @Column(type="boolean") */
public $completed = false;
/** @ManyToOne(targetEntity="DDC3899User", inversedBy="contract") */
public $user;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\Tests\Models\DDC3899;
/**
* @Entity
*/
class DDC3899FixContract extends DDC3899Contract
{
/** @column(type="integer") */
public $fixPrice = 0;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Models\DDC3899;
/**
* @Entity
*/
class DDC3899FlexContract extends DDC3899Contract
{
/** @column(type="integer") */
public $hoursWorked = 0;
/** @column(type="integer") */
public $pricePerHour = 0;
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Doctrine\Tests\Models\DDC3899;
/**
* @Entity
* @Table(name="dc3899_users")
*/
class DDC3899User
{
/** @Id @Column(type="integer") */
public $id;
/** @OneToMany(targetEntity="DDC3899Contract", mappedBy="user") */
public $contracts;
}

View File

@@ -9,6 +9,8 @@ namespace Doctrine\Tests\Models\GeoNames;
*/
class Country
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="string", length=2)

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Models\Hydration;
/** @Entity */
class EntityWithArrayDefaultArrayValueM2M
{
const CLASSNAME = __CLASS__;
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
public $id;
/** @ManyToMany(targetEntity=SimpleEntity::class) */
public $collection = [];
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\Tests\Models\Hydration;
/** @Entity */
class SimpleEntity
{
const CLASSNAME = __CLASS__;
/** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
public $id;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Doctrine\Tests\Models\Issue5989;
/**
* @Entity
* @Table(name="issue5989_employees")
*/
class Issue5989Employee extends Issue5989Person
{
/**
* @Column(type="simple_array", nullable=true)
*
* @var array
*/
public $tags;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Doctrine\Tests\Models\Issue5989;
/**
* @Entity
* @Table(name="issue5989_managers")
*/
class Issue5989Manager extends Issue5989Person
{
/**
* @Column(type="simple_array", nullable=true)
*
* @var array
*/
public $tags;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Doctrine\Tests\Models\Issue5989;
/**
* @Entity
* @Table(name="issue5989_persons")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({
* "person" = "Issue5989Person",
* "manager" = "Issue5989Manager",
* "employee" = "Issue5989Employee"
* })
*/
class Issue5989Person
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
public $id;
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Doctrine\Tests\Models\VersionedManyToOne;
/**
* @Entity
* @Table(name="versioned_many_to_one_article")
*/
class Article
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Column(name="name")
*/
public $name;
/**
* @ManyToOne(targetEntity="Category", cascade={"merge", "persist"})
*/
public $category;
/**
* Version column
*
* @Column(type="integer", name="version")
* @Version
*/
public $version;
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Doctrine\Tests\Models\VersionedManyToOne;
/**
* @Entity
* @Table(name="versioned_many_to_one_category")
*/
class Category
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* Version column
*
* @Column(type="integer", name="version")
* @Version
*/
public $version;
}

View File

@@ -2,8 +2,8 @@
namespace Doctrine\Tests\ORM\Cache;
use Doctrine\Tests\DoctrineTestCase;
use Doctrine\ORM\Cache\CacheConfiguration;
use Doctrine\Tests\DoctrineTestCase;
/**
* @group DDC-2183
@@ -64,6 +64,11 @@ class CacheConfigTest extends DoctrineTestCase
public function testSetGetQueryValidator()
{
$factory = $this->getMock('Doctrine\ORM\Cache\CacheFactory');
$factory->method('getTimestampRegion')->willReturn($this->getMock('Doctrine\ORM\Cache\TimestampRegion'));
$this->config->setCacheFactory($factory);
$validator = $this->getMock('Doctrine\ORM\Cache\QueryCacheValidator');
$this->assertInstanceOf('Doctrine\ORM\Cache\TimestampQueryCacheValidator', $this->config->getQueryValidator());
@@ -72,4 +77,4 @@ class CacheConfigTest extends DoctrineTestCase
$this->assertEquals($validator, $this->config->getQueryValidator());
}
}
}

View File

@@ -119,7 +119,7 @@ class DefaultEntityHydratorTest extends OrmTestCase
$this->assertArrayHasKey('name', $cache->data);
$this->assertArrayHasKey('country', $cache->data);
$this->assertEquals(array(
'id' => 11,
'id' => 12,
'name' => 'Bar',
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
), $cache->data);
@@ -147,9 +147,39 @@ class DefaultEntityHydratorTest extends OrmTestCase
$this->assertArrayHasKey('name', $cache->data);
$this->assertArrayHasKey('country', $cache->data);
$this->assertEquals(array(
'id' => 11,
'id' => 12,
'name' => 'Bar',
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
), $cache->data);
}
}
public function testCacheEntryWithWrongIdentifierType()
{
$proxy = $this->em->getReference(Country::CLASSNAME, 11);
$entity = new State('Bat', $proxy);
$uow = $this->em->getUnitOfWork();
$entityData = array('id'=> 12, 'name'=>'Bar', 'country' => $proxy);
$metadata = $this->em->getClassMetadata(State::CLASSNAME);
$key = new EntityCacheKey($metadata->name, array('id'=>'12'));
$entity->setId(12);
$uow->registerManaged($entity, array('id'=>12), $entityData);
$cache = $this->structure->buildCacheEntry($metadata, $key, $entity);
$this->assertInstanceOf('Doctrine\ORM\Cache\CacheEntry', $cache);
$this->assertInstanceOf('Doctrine\ORM\Cache\EntityCacheEntry', $cache);
$this->assertArrayHasKey('id', $cache->data);
$this->assertArrayHasKey('name', $cache->data);
$this->assertArrayHasKey('country', $cache->data);
$this->assertSame($entity->getId(), $cache->data['id']);
$this->assertEquals(array(
'id' => 12,
'name' => 'Bar',
'country' => new AssociationCacheEntry(Country::CLASSNAME, array('id' => 11)),
), $cache->data);
}
}

View File

@@ -386,7 +386,7 @@ class DefaultQueryCacheTest extends OrmTestCase
array('id'=>2, 'name' => 'Bar')
);
$entry->time = time() - 100;
$entry->time = microtime(true) - 100;
$this->region->addReturn('get', $entry);
$this->region->addReturn('get', new EntityCacheEntry(Country::CLASSNAME, $entities[0]));

View File

@@ -3,10 +3,12 @@
namespace Doctrine\Tests\ORM\Cache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\Cache\CollectionCacheEntry;
use Doctrine\ORM\Cache\Region\DefaultRegion;
use Doctrine\Tests\Mocks\CacheEntryMock;
use Doctrine\Tests\Mocks\CacheKeyMock;
/**
* @group DDC-2183
*/
@@ -72,4 +74,27 @@ class DefaultRegionTest extends AbstractRegionTest
$region->evictAll();
}
public function testGetMulti()
{
$key1 = new CacheKeyMock('key.1');
$value1 = new CacheEntryMock(array('id' => 1, 'name' => 'bar'));
$key2 = new CacheKeyMock('key.2');
$value2 = new CacheEntryMock(array('id' => 2, 'name' => 'bar'));
$this->assertFalse($this->region->contains($key1));
$this->assertFalse($this->region->contains($key2));
$this->region->put($key1, $value1);
$this->region->put($key2, $value2);
$this->assertTrue($this->region->contains($key1));
$this->assertTrue($this->region->contains($key2));
$actual = $this->region->getMultiple(new CollectionCacheEntry(array($key1, $key2)));
$this->assertEquals($value1, $actual[0]);
$this->assertEquals($value2, $actual[1]);
}
}

View File

@@ -22,10 +22,10 @@ class MultiGetRegionTest extends AbstractRegionTest
public function testGetMulti()
{
$key1 = new CacheKeyMock('key.1');
$value1 = new CacheEntryMock(array('id'=>1, 'name' => 'bar'));
$value1 = new CacheEntryMock(array('id' => 1, 'name' => 'bar'));
$key2 = new CacheKeyMock('key.2');
$value2 = new CacheEntryMock(array('id'=>2, 'name' => 'bar'));
$value2 = new CacheEntryMock(array('id' => 2, 'name' => 'bar'));
$this->assertFalse($this->region->contains($key1));
$this->assertFalse($this->region->contains($key2));
@@ -33,6 +33,9 @@ class MultiGetRegionTest extends AbstractRegionTest
$this->region->put($key1, $value1);
$this->region->put($key2, $value2);
$this->assertTrue($this->region->contains($key1));
$this->assertTrue($this->region->contains($key2));
$actual = $this->region->getMultiple(new CollectionCacheEntry(array($key1, $key2)));
$this->assertEquals($value1, $actual[0]);

View File

@@ -11,6 +11,7 @@ use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\Tests\Models\Cache\Country;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\PersistentCollection;
@@ -390,7 +391,7 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase
{
$mapping = $this->em->getClassMetadata('Doctrine\Tests\Models\Cache\Country');
$assoc = array('type' => 1);
$coll = new PersistentCollection($this->em, $mapping, null);
$coll = new PersistentCollection($this->em, $mapping, new ArrayCollection());
$persister = $this->createPersisterDefault();
$entity = new Country("Foo");
@@ -406,7 +407,7 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase
{
$mapping = $this->em->getClassMetadata('Doctrine\Tests\Models\Cache\Country');
$assoc = array('type' => 1);
$coll = new PersistentCollection($this->em, $mapping, null);
$coll = new PersistentCollection($this->em, $mapping, new ArrayCollection());
$persister = $this->createPersisterDefault();
$entity = new Country("Foo");

View File

@@ -636,11 +636,13 @@ class ExtraLazyCollectionTest extends OrmFunctionalTestCase
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
$user->groups->removeElement($group);
$this->assertTrue($user->groups->removeElement($group));
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a persisted entity should cause one query to be executed.");
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
$this->assertFalse($user->groups->removeElement($group), "Removing an already removed element returns false");
// Test Many to Many removal with Entity state as new
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
$group->name = "A New group!";

View File

@@ -0,0 +1,45 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Tests\Models\VersionedManyToOne\Article;
use Doctrine\Tests\Models\VersionedManyToOne\Category;
/**
* @group MergeVersionedOneToMany
*/
class MergeVersionedManyToOneTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('versioned_many_to_one');
parent::setUp();
}
/**
* This test case asserts that a detached and unmodified entity could be merge without firing
* OptimisticLockException.
*/
public function testSetVersionOnCreate()
{
$category = new Category();
$article = new Article();
$article->name = 'Article';
$article->category = $category;
$this->_em->persist($article);
$this->_em->flush();
$this->_em->clear();
$articleMerged = $this->_em->merge($article);
$articleMerged->name = 'Article Merged';
$this->_em->flush();
$this->assertEquals(2, $articleMerged->version);
}
}

View File

@@ -791,4 +791,20 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSQLEquals('u.id AS id0, u.status AS status1, u.username AS username2, u.name AS name3, u.email_id AS email_id4', (string)$rsm);
}
/**
* @group DDC-3899
*/
public function testGenerateSelectClauseWithDiscriminatorColumn()
{
$rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT);
$rsm->addEntityResult('Doctrine\Tests\Models\DDC3899\DDC3899User', 'u');
$rsm->addJoinedEntityResult('Doctrine\Tests\Models\DDC3899\DDC3899FixContract', 'c', 'u', 'contracts');
$rsm->addFieldResult('u', $this->platform->getSQLResultCasing('id'), 'id');
$rsm->setDiscriminatorColumn('c', $this->platform->getSQLResultCasing('discr'));
$selectClause = $rsm->generateSelectClause(array('u' => 'u1', 'c' => 'c1'));
$this->assertSQLEquals('u1.id as id, c1.discr as discr', $selectClause);
}
}

View File

@@ -669,6 +669,28 @@ class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertCount(9, $paginator);
}
/**
* @dataProvider useOutputWalkersAndFetchJoinCollection
*/
public function testPaginationWithSubSelectOrderByExpression($useOutputWalker, $fetchJoinCollection)
{
$query = $this->_em->createQuery(
"SELECT u,
(
SELECT MAX(a.version)
FROM Doctrine\\Tests\\Models\\CMS\\CmsArticle a
WHERE a.user = u
) AS HIDDEN max_version
FROM Doctrine\\Tests\\Models\\CMS\\CmsUser u
ORDER BY max_version DESC"
);
$paginator = new Paginator($query, $fetchJoinCollection);
$paginator->setUseOutputWalkers($useOutputWalker);
$this->assertCount(9, $paginator->getIterator());
}
public function populate()
{
$groups = array();

View File

@@ -45,12 +45,12 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setQueryCacheDriver($cache);
$query->getResult();
$this->assertEquals(2, $this->getCacheSize($cache));
$this->assertEquals(1, $this->getCacheSize($cache));
$query->setHint('foo', 'bar');
$query->getResult();
$this->assertEquals(3, $this->getCacheSize($cache));
$this->assertEquals(2, $this->getCacheSize($cache));
return $query;
}
@@ -105,16 +105,16 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$cache = new \Doctrine\Common\Cache\ArrayCache();
$cache = $this->getMock('Doctrine\Common\Cache\Cache');
$query->setQueryCacheDriver($cache);
$users = $query->getResult();
$cache
->expects(self::once())
->method('save')
->with(self::isType('string'), self::isInstanceOf('Doctrine\ORM\Query\ParserResult'));
$data = $this->cacheDataReflection->getValue($cache);
$this->assertEquals(2, count($data));
$this->assertInstanceOf('Doctrine\ORM\Query\ParserResult', array_pop($data));
$query->getResult();
}
public function testQueryCache_HitDoesNotSaveParserResult()

View File

@@ -61,7 +61,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionAbs()
{
$result = $this->_em->createQuery('SELECT m, ABS(m.salary * -1) AS abs FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, ABS(m.salary * -1) AS abs FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getResult();
$this->assertEquals(4, count($result));
@@ -73,7 +73,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionConcat()
{
$arg = $this->_em->createQuery('SELECT m, CONCAT(m.name, m.department) AS namedep FROM Doctrine\Tests\Models\Company\CompanyManager m')
$arg = $this->_em->createQuery('SELECT m, CONCAT(m.name, m.department) AS namedep FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getArrayResult();
$this->assertEquals(4, count($arg));
@@ -85,7 +85,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionLength()
{
$result = $this->_em->createQuery('SELECT m, LENGTH(CONCAT(m.name, m.department)) AS namedeplength FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, LENGTH(CONCAT(m.name, m.department)) AS namedeplength FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getArrayResult();
$this->assertEquals(4, count($result));
@@ -98,7 +98,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionLocate()
{
$dql = "SELECT m, LOCATE('e', LOWER(m.name)) AS loc, LOCATE('e', LOWER(m.name), 7) AS loc2 ".
"FROM Doctrine\Tests\Models\Company\CompanyManager m";
"FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC";
$result = $this->_em->createQuery($dql)
->getArrayResult();
@@ -116,7 +116,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionLower()
{
$result = $this->_em->createQuery("SELECT m, LOWER(m.name) AS lowername FROM Doctrine\Tests\Models\Company\CompanyManager m")
$result = $this->_em->createQuery("SELECT m, LOWER(m.name) AS lowername FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC")
->getArrayResult();
$this->assertEquals(4, count($result));
@@ -128,7 +128,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionMod()
{
$result = $this->_em->createQuery("SELECT m, MOD(m.salary, 3500) AS amod FROM Doctrine\Tests\Models\Company\CompanyManager m")
$result = $this->_em->createQuery("SELECT m, MOD(m.salary, 3500) AS amod FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC")
->getArrayResult();
$this->assertEquals(4, count($result));
@@ -140,7 +140,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionSqrt()
{
$result = $this->_em->createQuery("SELECT m, SQRT(m.salary) AS sqrtsalary FROM Doctrine\Tests\Models\Company\CompanyManager m")
$result = $this->_em->createQuery("SELECT m, SQRT(m.salary) AS sqrtsalary FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC")
->getArrayResult();
$this->assertEquals(4, count($result));
@@ -152,7 +152,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFunctionUpper()
{
$result = $this->_em->createQuery("SELECT m, UPPER(m.name) AS uppername FROM Doctrine\Tests\Models\Company\CompanyManager m")
$result = $this->_em->createQuery("SELECT m, UPPER(m.name) AS uppername FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC")
->getArrayResult();
$this->assertEquals(4, count($result));
@@ -186,7 +186,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
$dql = "SELECT m, TRIM(TRAILING '.' FROM m.name) AS str1, ".
" TRIM(LEADING '.' FROM m.name) AS str2, TRIM(CONCAT(' ', CONCAT(m.name, ' '))) AS str3 ".
"FROM Doctrine\Tests\Models\Company\CompanyManager m";
"FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC";
$result = $this->_em->createQuery($dql)->getArrayResult();
@@ -207,7 +207,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testOperatorAdd()
{
$result = $this->_em->createQuery('SELECT m, m.salary+2500 AS add FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, m.salary+2500 AS add FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getResult();
$this->assertEquals(4, count($result));
@@ -219,7 +219,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testOperatorSub()
{
$result = $this->_em->createQuery('SELECT m, m.salary-2500 AS sub FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, m.salary-2500 AS sub FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getResult();
$this->assertEquals(4, count($result));
@@ -231,7 +231,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testOperatorMultiply()
{
$result = $this->_em->createQuery('SELECT m, m.salary*2 AS op FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, m.salary*2 AS op FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getResult();
$this->assertEquals(4, count($result));
@@ -246,7 +246,7 @@ class QueryDqlFunctionTest extends \Doctrine\Tests\OrmFunctionalTestCase
*/
public function testOperatorDiv()
{
$result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\Tests\Models\Company\CompanyManager m')
$result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC')
->getResult();
$this->assertEquals(4, count($result));

View File

@@ -141,7 +141,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0, $this->getCacheSize($cache));
$query->getResult();
$this->assertEquals(2, $this->getCacheSize($cache));
$this->assertEquals(1, $this->getCacheSize($cache));
return $query;
}

View File

@@ -343,18 +343,18 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setQueryCacheDriver($cache);
$query->getResult();
$this->assertEquals(2, sizeof($cacheDataReflection->getValue($cache)));
$this->assertEquals(1, sizeof($cacheDataReflection->getValue($cache)));
$conf = $this->_em->getConfiguration();
$conf->addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter");
$this->_em->getFilters()->enable("locale");
$query->getResult();
$this->assertEquals(3, sizeof($cacheDataReflection->getValue($cache)));
$this->assertEquals(2, sizeof($cacheDataReflection->getValue($cache)));
// Another time doesn't add another cache entry
$query->getResult();
$this->assertEquals(3, sizeof($cacheDataReflection->getValue($cache)));
$this->assertEquals(2, sizeof($cacheDataReflection->getValue($cache)));
}
public function testQueryGeneration_DependsOnFilters()

View File

@@ -3,8 +3,8 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Cache\Attraction;
use Doctrine\Tests\Models\Cache\AttractionInfo;
use Doctrine\Tests\Models\Cache\AttractionContactInfo;
use Doctrine\Tests\Models\Cache\AttractionInfo;
use Doctrine\Tests\Models\Cache\AttractionLocationInfo;
/**
@@ -188,4 +188,47 @@ class SecondLevelCacheJoinTableInheritanceTest extends SecondLevelCacheAbstractT
$this->assertInstanceOf(AttractionContactInfo::CLASSNAME, $entity->getInfos()->get(0));
$this->assertEquals($this->attractionsInfo[0]->getFone(), $entity->getInfos()->get(0)->getFone());
}
}
public function testQueryCacheShouldBeEvictedOnTimestampUpdate()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->loadFixturesAttractions();
$this->loadFixturesAttractionsInfo();
$this->evictRegions();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$dql = 'SELECT attractionInfo FROM Doctrine\Tests\Models\Cache\AttractionInfo attractionInfo';
$result1 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(count($this->attractionsInfo), $result1);
$this->assertEquals($queryCount + 5, $this->getCurrentQueryCount());
$contact = new AttractionContactInfo(
'1234-1234',
$this->_em->find(Attraction::CLASSNAME, $this->attractions[5]->getId())
);
$this->_em->persist($contact);
$this->_em->flush();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$result2 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(count($this->attractionsInfo) + 1, $result2);
$this->assertEquals($queryCount + 6, $this->getCurrentQueryCount());
foreach ($result2 as $entity) {
$this->assertInstanceOf(AttractionInfo::CLASSNAME, $entity);
}
}
}

View File

@@ -2,10 +2,10 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Cache\City;
use Doctrine\Tests\Models\Cache\ComplexAction;
use Doctrine\Tests\Models\Cache\Country;
use Doctrine\Tests\Models\Cache\State;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Tests\Models\Cache\Token;
use Doctrine\Tests\Models\Cache\Action;
@@ -98,6 +98,40 @@ class SecondLevelCacheManyToOneTest extends SecondLevelCacheAbstractTest
$this->assertEquals($this->states[1]->getCountry()->getName(), $c4->getCountry()->getName());
}
public function testInverseSidePutShouldEvictCollection()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->_em->clear();
$this->cache->evictEntityRegion(State::CLASSNAME);
$this->cache->evictEntityRegion(Country::CLASSNAME);
//evict collection on add
$c3 = $this->_em->find(State::CLASSNAME, $this->states[0]->getId());
$prev = $c3->getCities();
$count = $prev->count();
$city = new City("Buenos Aires", $c3);
$c3->addCity($city);
$this->_em->persist($city);
$this->_em->persist($c3);
$this->_em->flush();
$this->_em->clear();
$state = $this->_em->find(State::CLASSNAME, $c3->getId());
$queryCount = $this->getCurrentQueryCount();
// Association was cleared from EM
$this->assertNotEquals($prev, $state->getCities());
// New association has one more item (cache was evicted)
$this->assertEquals($count + 1, $state->getCities()->count());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
}
public function testShouldNotReloadWhenAssociationIsMissing()
{
$this->loadFixturesCountries();

View File

@@ -14,18 +14,18 @@ use Doctrine\Tests\Models\Cache\Traveler;
*/
class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
{
public function testShouldNotPutCollectionInverseSideOnPersist()
public function testShouldPutCollectionInverseSideOnPersist()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->_em->clear();
$this->assertTrue($this->cache->containsEntity(State::CLASSNAME, $this->states[0]->getId()));
$this->assertTrue($this->cache->containsEntity(State::CLASSNAME, $this->states[1]->getId()));
$this->assertFalse($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[0]->getId()));
$this->assertFalse($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[1]->getId()));
$this->assertTrue($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[0]->getId()));
$this->assertTrue($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[1]->getId()));
}
public function testPutAndLoadOneToManyRelation()
@@ -187,6 +187,7 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->_em->clear();
$this->secondLevelCacheLogger->clearStats();
@@ -247,8 +248,8 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->_em->remove($city0);
$this->_em->persist($state);
$this->_em->flush();
$this->_em->clear();
$this->secondLevelCacheLogger->clearStats();
$queryCount = $this->getCurrentQueryCount();
@@ -261,19 +262,19 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->assertInstanceOf(City::CLASSNAME, $city1);
$this->assertEquals($entity->getCities()->get(1)->getName(), $city1->getName());
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::CLASSNAME)));
$this->assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
$state->getCities()->remove(0);
$this->_em->remove($city1);
$this->_em->persist($state);
$this->_em->flush();
$this->_em->clear();
$this->secondLevelCacheLogger->clearStats();
$queryCount = $this->getCurrentQueryCount();
@@ -281,9 +282,9 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->assertCount(0, $state->getCities());
$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::CLASSNAME)));
$this->assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
}
public function testOneToManyWithEmptyRelation()
@@ -346,11 +347,12 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
public function testCacheInitializeCollectionWithNewObjects()
{
$this->_em->clear();
$this->evictRegions();
$traveler = new Traveler("Doctrine Bot");
for ($i=0; $i<3; ++$i) {
for ($i = 0; $i < 3; ++$i) {
$traveler->getTravels()->add(new Travel($traveler));
}
@@ -373,7 +375,7 @@ class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
$this->assertFalse($entity->getTravels()->isInitialized());
$this->assertCount(4, $entity->getTravels());
$this->assertTrue($entity->getTravels()->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
$this->_em->flush();
$this->_em->clear();

View File

@@ -1035,4 +1035,37 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
->setCacheable(true)
->getResult();
}
}
public function testQueryCacheShouldBeEvictedOnTimestampUpdate()
{
$this->loadFixturesCountries();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$dql = 'SELECT country FROM Doctrine\Tests\Models\Cache\Country country';
$result1 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(2, $result1);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->_em->persist(new Country('France'));
$this->_em->flush();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$result2 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(3, $result2);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
foreach ($result2 as $entity) {
$this->assertInstanceOf(Country::CLASSNAME, $entity);
}
}
}

View File

@@ -210,4 +210,45 @@ class SecondLevelCacheSingleTableInheritanceTest extends SecondLevelCacheAbstrac
$this->assertEquals($this->attractions[0]->getName(), $entity->getAttractions()->get(0)->getName());
$this->assertEquals($this->attractions[1]->getName(), $entity->getAttractions()->get(1)->getName());
}
}
public function testQueryCacheShouldBeEvictedOnTimestampUpdate()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();
$this->loadFixturesAttractions();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$dql = 'SELECT attraction FROM Doctrine\Tests\Models\Cache\Attraction attraction';
$result1 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(count($this->attractions), $result1);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$contact = new Beach(
'Botafogo',
$this->_em->find(City::CLASSNAME, $this->cities[1]->getId())
);
$this->_em->persist($contact);
$this->_em->flush();
$this->_em->clear();
$queryCount = $this->getCurrentQueryCount();
$result2 = $this->_em->createQuery($dql)
->setCacheable(true)
->getResult();
$this->assertCount(count($this->attractions) + 1, $result2);
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
foreach ($result2 as $entity) {
$this->assertInstanceOf(Attraction::CLASSNAME, $entity);
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-3146
* @author Emiel Nijpels <emiel@silverstreet.com>
*/
class DDC3146Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* Verify that the number of added events to the event listener from the abstract hydrator class is equal to the number of removed events
*/
public function testEventListeners()
{
// Create mock connection to be returned from the entity manager interface
$mockConnection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock();
$mockEntityManagerInterface = $this->getMockBuilder('Doctrine\ORM\EntityManagerInterface')->disableOriginalConstructor()->getMock();
$mockEntityManagerInterface->expects($this->any())->method('getConnection')->will($this->returnValue($mockConnection));
// Create mock event manager to be returned from the entity manager interface
$mockEventManager = $this->getMockBuilder('Doctrine\Common\EventManager')->disableOriginalConstructor()->getMock();
$mockEntityManagerInterface->expects($this->any())->method('getEventManager')->will($this->returnValue($mockEventManager));
// Create mock statement and result mapping
$mockStatement = $this->getMockBuilder('Doctrine\DBAL\Driver\Statement')->disableOriginalConstructor()->getMock();
$mockStatement->expects($this->once())->method('fetch')->will($this->returnValue(false));
$mockResultMapping = $this->getMockBuilder('Doctrine\ORM\Query\ResultSetMapping')->disableOriginalConstructor()->getMock();
// Create mock abstract hydrator
$mockAbstractHydrator = $this->getMockBuilder('Doctrine\ORM\Internal\Hydration\AbstractHydrator')
->setConstructorArgs(array($mockEntityManagerInterface))
->setMethods(array('hydrateAllData'))
->getMock();
// Increase counter every time the event listener is added and decrease the counter every time the event listener is removed
$eventCounter = 0;
$mockEventManager->expects($this->any())
->method('addEventListener')
->will(
$this->returnCallback(
function () use (&$eventCounter) {
$eventCounter++;
}
)
);
$mockEventManager->expects($this->any())
->method('removeEventListener')
->will(
$this->returnCallback(
function () use (&$eventCounter) {
$eventCounter--;
}
)
);
// Create iterable result
$iterableResult = $mockAbstractHydrator->iterate($mockStatement, $mockResultMapping, array());
$iterableResult->next();
// Number of added events listeners should be equal or less than the number of removed events
$this->assertLessThanOrEqual(0, $eventCounter, 'More events added to the event listener than removed; this can create a memory leak when references are not cleaned up');
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
class DDC3303Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema([$this->_em->getClassMetadata(DDC3303Employee::CLASSNAME)]);
}
/**
* @group 4097
* @group 4277
* @group 5867
*
* When using an embedded field in an inheritance, private properties should also be inherited.
*/
public function testEmbeddedObjectsAreAlsoInherited()
{
$employee = new DDC3303Employee(
'John Doe',
new DDC3303Address('Somewhere', 123, 'Over the rainbow'),
'Doctrine Inc'
);
$this->_em->persist($employee);
$this->_em->flush();
$this->_em->clear();
self::assertEquals($employee, $this->_em->find(DDC3303Employee::CLASSNAME, 'John Doe'));
}
}
/** @MappedSuperclass */
abstract class DDC3303Person
{
/** @Id @GeneratedValue(strategy="NONE") @Column(type="string") @var string */
private $name;
/** @Embedded(class="DDC3303Address") @var DDC3303Address */
private $address;
public function __construct($name, DDC3303Address $address)
{
$this->name = $name;
$this->address = $address;
}
}
/**
* @Embeddable
*/
class DDC3303Address
{
/** @Column(type="string") @var string */
private $street;
/** @Column(type="integer") @var int */
private $number;
/** @Column(type="string") @var string */
private $city;
public function __construct($street, $number, $city)
{
$this->street = $street;
$this->number = $number;
$this->city = $city;
}
}
/**
* @Entity
* @Table(name="ddc3303_employee")
*/
class DDC3303Employee extends DDC3303Person
{
const CLASSNAME = __CLASS__;
/** @Column(type="string") @var string */
private $company;
public function __construct($name, DDC3303Address $address, $company)
{
parent::__construct($name, $address);
$this->company = $company;
}
}

View File

@@ -0,0 +1,104 @@
<?php
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Tests\Models\DDC3699\DDC3699Parent;
use Doctrine\Tests\Models\DDC3699\DDC3699RelationOne;
use Doctrine\Tests\Models\DDC3699\DDC3699RelationMany;
use Doctrine\Tests\Models\DDC3699\DDC3699Child;
/**
* @group DDC-3699
*/
class DDC3597Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
$this->useModelSet('ddc3699');
parent::setUp();
}
/**
* @group DDC-3699
*/
public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToOneUninitializedAssociations()
{
$id = 1;
$child = new DDC3699Child();
$child->id = $id;
$child->childField = 'childValue';
$child->parentField = 'parentValue';
$relation = new DDC3699RelationOne();
$relation->id = $id;
$relation->child = $child ;
$child->oneRelation = $relation;
$this->_em->persist($relation);
$this->_em->persist($child);
$this->_em->flush();
$this->_em->clear();
// fixtures loaded
/* @var $unManagedChild DDC3699Child */
$unManagedChild = $this->_em->find(DDC3699Child::CLASSNAME, $id);
$this->_em->detach($unManagedChild);
// make it managed again
$this->_em->find(DDC3699Child::CLASSNAME, $id);
$unManagedChild->childField = 'modifiedChildValue';
$unManagedChild->parentField = 'modifiedParentValue';
/* @var $mergedChild DDC3699Child */
$mergedChild = $this->_em->merge($unManagedChild);
$this->assertSame($mergedChild->childField, 'modifiedChildValue');
$this->assertSame($mergedChild->parentField, 'modifiedParentValue');
}
/**
* @group DDC-3699
*/
public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToManyUninitializedAssociations()
{
$id = 2;
$child = new DDC3699Child();
$child->id = $id;
$child->childField = 'childValue';
$child->parentField = 'parentValue';
$relation = new DDC3699RelationMany();
$relation->id = $id;
$relation->child = $child ;
$child->relations[] = $relation;
$this->_em->persist($relation);
$this->_em->persist($child);
$this->_em->flush();
$this->_em->clear();
/* @var $unmanagedChild DDC3699Child */
$unmanagedChild = $this->_em->find(DDC3699Child::CLASSNAME, $id);
$this->_em->detach($unmanagedChild);
// make it managed again
$this->_em->find(DDC3699Child::CLASSNAME, $id);
$unmanagedChild->childField = 'modifiedChildValue';
$unmanagedChild->parentField = 'modifiedParentValue';
/* @var $mergedChild DDC3699Child */
$mergedChild = $this->_em->merge($unmanagedChild);
$this->assertSame($mergedChild->childField, 'modifiedChildValue');
$this->assertSame($mergedChild->parentField, 'modifiedParentValue');
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* @author Marc Pantel <pantel.m@gmail.com>
*/
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Tests\ORM\Mapping\YamlMappingDriverTest;
class DDC3711Test extends YamlMappingDriverTest
{
public function testCompositeKeyForJoinTableInManyToManyCreation()
{
$yamlDriver = $this->_loadDriver();
$em = $this->_getTestEntityManager();
$em->getConfiguration()->setMetadataDriverImpl($yamlDriver);
$factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$factory->setEntityManager($em);
$entityA = new ClassMetadata('Doctrine\Tests\Models\DDC3711\DDC3711EntityA');
$entityA = $factory->getMetadataFor('Doctrine\Tests\Models\DDC3711\DDC3711EntityA');
$this->assertEquals(array('link_a_id1' => "id1", 'link_a_id2' => "id2"), $entityA->associationMappings['entityB']['relationToSourceKeyColumns']);
$this->assertEquals(array('link_b_id1' => "id1", 'link_b_id2' => "id2"), $entityA->associationMappings['entityB']['relationToTargetKeyColumns']);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\Cache\Country;
use Doctrine\Tests\ORM\Functional\SecondLevelCacheAbstractTest;
class DDC3967Test extends SecondLevelCacheAbstractTest
{
protected function setUp()
{
parent::setUp();
$this->loadFixturesCountries();
$this->_em->getCache()->evictEntityRegion(Country::CLASSNAME);
$this->_em->clear();
}
public function testIdentifierCachedWithProperType()
{
$country = array_pop($this->countries);
$id = $country->getId();
// First time, loaded from database
$this->_em->find(Country::CLASSNAME, "$id");
$this->_em->clear();
// Second time, loaded from cache
/** @var Country $country */
$country = $this->_em->find(Country::CLASSNAME, "$id");
// Identifier type should be integer
$this->assertSame($country->getId(), $id);
}
}

View File

@@ -49,7 +49,7 @@ class DDC767Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotNull($pUser, "User not retrieved from database.");
$groups = array(2, 3);
$groups = array($group2->id, $group3->id);
try {
$this->_em->beginTransaction();

View File

@@ -0,0 +1,193 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group GH-5762
*/
class GH5762Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(GH5762Driver::CLASSNAME),
$this->_em->getClassMetadata(GH5762DriverRide::CLASSNAME),
$this->_em->getClassMetadata(GH5762Car::CLASSNAME),
));
}
public function testIssue()
{
$result = $this->fetchData();
self::assertInstanceOf(GH5762Driver::CLASSNAME, $result);
self::assertInstanceOf('Doctrine\ORM\PersistentCollection', $result->driverRides);
self::assertInstanceOf(GH5762DriverRide::CLASSNAME, $result->driverRides->get(0));
self::assertInstanceOf(GH5762Car::CLASSNAME, $result->driverRides->get(0)->car);
$cars = array();
foreach ($result->driverRides as $ride) {
$cars[] = $ride->car->brand;
}
self::assertEquals(count($cars), count(array_unique($cars)));
self::assertContains('BMW', $cars);
self::assertContains('Crysler', $cars);
self::assertContains('Dodge', $cars);
self::assertContains('Mercedes', $cars);
self::assertContains('Volvo', $cars);
}
private function fetchData()
{
$this->createData();
$qb = $this->_em->createQueryBuilder();
$qb->select('d, dr, c')
->from(GH5762Driver::CLASSNAME, 'd')
->leftJoin('d.driverRides', 'dr')
->leftJoin('dr.car', 'c')
->where('d.id = 1');
return $qb->getQuery()->getSingleResult();
}
private function createData()
{
$car1 = new GH5762Car('BMW', '7 Series');
$car2 = new GH5762Car('Crysler', '300');
$car3 = new GH5762Car('Dodge', 'Dart');
$car4 = new GH5762Car('Mercedes', 'C-Class');
$car5 = new GH5762Car('Volvo', 'XC90');
$driver = new GH5762Driver(1, 'John Doe');
$ride1 = new GH5762DriverRide($driver, $car1);
$ride2 = new GH5762DriverRide($driver, $car2);
$ride3 = new GH5762DriverRide($driver, $car3);
$ride4 = new GH5762DriverRide($driver, $car4);
$ride5 = new GH5762DriverRide($driver, $car5);
$this->_em->persist($car1);
$this->_em->persist($car2);
$this->_em->persist($car3);
$this->_em->persist($car4);
$this->_em->persist($car5);
$this->_em->persist($driver);
$this->_em->persist($ride1);
$this->_em->persist($ride2);
$this->_em->persist($ride3);
$this->_em->persist($ride4);
$this->_em->persist($ride5);
$this->_em->flush();
$this->_em->clear();
}
}
/**
* @Entity
* @Table(name="driver")
*/
class GH5762Driver
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="NONE")
*/
public $id;
/**
* @Column(type="string", length=255);
*/
public $name;
/**
* @OneToMany(targetEntity="GH5762DriverRide", mappedBy="driver")
*/
public $driverRides;
public function __construct($id, $name)
{
$this->driverRides = new ArrayCollection();
$this->id = $id;
$this->name = $name;
}
}
/**
* @Entity
* @Table(name="driver_ride")
*/
class GH5762DriverRide
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @ManyToOne(targetEntity="GH5762Driver", inversedBy="driverRides")
* @JoinColumn(name="driver_id", referencedColumnName="id")
*/
public $driver;
/**
* @Id
* @ManyToOne(targetEntity="GH5762Car", inversedBy="carRides")
* @JoinColumn(name="car", referencedColumnName="brand")
*/
public $car;
function __construct(GH5762Driver $driver, GH5762Car $car)
{
$this->driver = $driver;
$this->car = $car;
$this->driver->driverRides->add($this);
$this->car->carRides->add($this);
}
}
/**
* @Entity
* @Table(name="car")
*/
class GH5762Car
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="string", length=25)
* @GeneratedValue(strategy="NONE")
*/
public $brand;
/**
* @Column(type="string", length=255);
*/
public $model;
/**
* @OneToMany(targetEntity="GH5762DriverRide", mappedBy="car")
*/
public $carRides;
public function __construct($brand, $model)
{
$this->carRides = new ArrayCollection();
$this->brand = $brand;
$this->model = $model;
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Tests\Mocks\HydratorMockStatement;
final class GH6362Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(
[
$this->_em->getClassMetadata(GH6362Start::CLASSNAME),
$this->_em->getClassMetadata(GH6362Base::CLASSNAME),
$this->_em->getClassMetadata(GH6362Child::CLASSNAME),
$this->_em->getClassMetadata(GH6362Join::CLASSNAME),
]
);
}
/**
* @group 6362
*
* SELECT a as base, b, c, d
* FROM Start a
* LEFT JOIN a.bases b
* LEFT JOIN Child c WITH b.id = c.id
* LEFT JOIN c.joins d
*/
public function testInheritanceJoinAlias()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult(GH6362Start::CLASSNAME, 'a', 'base');
$rsm->addJoinedEntityResult(GH6362Base::CLASSNAME, 'b', 'a', 'bases');
$rsm->addEntityResult(GH6362Child::CLASSNAME, 'c');
$rsm->addJoinedEntityResult(GH6362Join::CLASSNAME, 'd', 'c', 'joins');
$rsm->addFieldResult('a', 'id_0', 'id');
$rsm->addFieldResult('b', 'id_1', 'id');
$rsm->addFieldResult('c', 'id_2', 'id');
$rsm->addFieldResult('d', 'id_3', 'id');
$rsm->addMetaResult('a', 'bases_id_4', 'bases_id', false, 'integer');
$rsm->addMetaResult('b', 'type_5', 'type');
$rsm->addMetaResult('c', 'type_6', 'type');
$rsm->addMetaResult('d', 'child_id_7', 'child_id', false, 'integer');
$rsm->setDiscriminatorColumn('b', 'type_5');
$rsm->setDiscriminatorColumn('c', 'type_6');
$resultSet = [
[
'id_0' => '1',
'id_1' => '1',
'id_2' => '1',
'id_3' => '1',
'bases_id_4' => '1',
'type_5' => 'child',
'type_6' => 'child',
'child_id_7' => '1',
],
];
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, [Query::HINT_FORCE_PARTIAL_LOAD => true]);
$this->assertInstanceOf(GH6362Start::CLASSNAME, $result[0]['base']);
$this->assertInstanceOf(GH6362Child::CLASSNAME, $result[1][0]);
}
}
/**
* @Entity
*/
class GH6362Start
{
const CLASSNAME = __CLASS__;
/**
* @Column(type="integer")
* @Id
* @GeneratedValue
*/
protected $id;
/**
* @ManyToOne(targetEntity="GH6362Base", inversedBy="starts")
*/
private $bases;
}
/**
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="type", type="string")
* @DiscriminatorMap({"child" = "GH6362Child"})
* @Entity
*/
abstract class GH6362Base
{
const CLASSNAME = __CLASS__;
/**
* @Column(type="integer")
* @Id
* @GeneratedValue
*/
protected $id;
/**
* @OneToMany(targetEntity="GH6362Start", mappedBy="bases")
*/
private $starts;
}
/**
* @Entity
*/
class GH6362Child extends GH6362Base
{
const CLASSNAME = __CLASS__;
/**
* @OneToMany(targetEntity="GH6362Join", mappedBy="child")
*/
private $joins;
}
/**
* @Entity
*/
class GH6362Join
{
const CLASSNAME = __CLASS__;
/**
* @Column(type="integer")
* @Id
* @GeneratedValue
*/
private $id;
/**
* @ManyToOne(targetEntity="GH6362Child", inversedBy="joins")
*/
private $child;
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @group GH-6464
*/
class GH6464Test extends OrmFunctionalTestCase
{
/**
* {@inheritDoc}
*/
protected function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema([
$this->_em->getClassMetadata(GH6464Post::CLASS_NAME),
$this->_em->getClassMetadata(GH6464User::CLASS_NAME),
$this->_em->getClassMetadata(GH6464Author::CLASS_NAME),
]);
}
/**
* Verifies that SqlWalker generates valid SQL for an INNER JOIN to CTI table
*
* SqlWalker needs to generate nested INNER JOIN statements, otherwise there would be INNER JOIN
* statements without an ON clause, which are valid on e.g. MySQL but rejected by PostgreSQL.
*/
public function testIssue()
{
$query = $this->_em->createQueryBuilder()
->select('p')
->from(GH6464Post::CLASS_NAME, 'p')
->innerJoin(GH6464Author::CLASS_NAME, 'a', 'WITH', 'p.authorId = a.id')
->getQuery();
$this->assertNotRegExp(
'/INNER JOIN \w+ \w+ INNER JOIN/',
$query->getSQL(),
'As of GH-6464, every INNER JOIN should have an ON clause, which is missing here'
);
// Query shouldn't yield a result, yet it shouldn't crash (anymore)
$this->assertEquals([], $query->getResult());
}
}
/** @Entity */
class GH6464Post
{
const CLASS_NAME = __CLASS__;
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @Column(type="integer") */
public $authorId;
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"author" = "GH6464Author"})
*/
abstract class GH6464User
{
const CLASS_NAME = __CLASS__;
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
}
/** @Entity */
class GH6464Author extends GH6464User
{
const CLASS_NAME = __CLASS__;
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Tests\Models\Issue5989\Issue5989Employee;
use Doctrine\Tests\Models\Issue5989\Issue5989Manager;
use Doctrine\Tests\Models\Issue5989\Issue5989Person;
/**
* @group issue-5989
*/
class Issue5989Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('issue5989');
parent::setUp();
}
public function testSimpleArrayTypeHydratedCorrectlyInJoinedInheritance()
{
$manager = new Issue5989Manager();
$managerTags = ['tag1', 'tag2'];
$manager->tags = $managerTags;
$this->_em->persist($manager);
$employee = new Issue5989Employee();
$employeeTags =['tag2', 'tag3'];
$employee->tags = $employeeTags;
$this->_em->persist($employee);
$this->_em->flush();
$managerId = $manager->id;
$employeeId = $employee->id;
// clear entity manager so that $repository->find actually fetches them and uses the hydrator
// instead of just returning the existing managed entities
$this->_em->clear();
$repository = $this->_em->getRepository(Issue5989Person::CLASSNAME);
$manager = $repository->find($managerId);
$employee = $repository->find($employeeId);
static::assertEquals($managerTags, $manager->tags);
static::assertEquals($employeeTags, $employee->tags);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Events;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
use Doctrine\Tests\OrmFunctionalTestCase;
/**
* @covers \Doctrine\ORM\Internal\Hydration\AbstractHydrator
*/
class AbstractHydratorTest extends OrmFunctionalTestCase
{
/**
* @group DDC-3146
* @group #1515
*
* Verify that the number of added events to the event listener from the abstract hydrator class is equal to the
* number of removed events
*/
public function testOnClearEventListenerIsDetachedOnCleanup()
{
$mockConnection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock();
$mockEntityManagerInterface = $this->getMock('Doctrine\ORM\EntityManagerInterface');
$mockEventManager = $this->getMock('Doctrine\Common\EventManager');
$mockStatement = $this->getMock('Doctrine\DBAL\Driver\Statement');
$mockResultMapping = $this->getMock('Doctrine\ORM\Query\ResultSetMapping');
$mockEntityManagerInterface->expects(self::any())->method('getEventManager')->willReturn($mockEventManager);
$mockEntityManagerInterface->expects(self::any())->method('getConnection')->willReturn($mockConnection);
$mockStatement->expects(self::once())->method('fetch')->willReturn(false);
/* @var $mockAbstractHydrator AbstractHydrator */
$mockAbstractHydrator = $this
->getMockBuilder('Doctrine\ORM\Internal\Hydration\AbstractHydrator')
->setConstructorArgs([$mockEntityManagerInterface])
->setMethods(['hydrateAllData'])
->getMock();
$mockEventManager
->expects(self::at(0))
->method('addEventListener')
->with([Events::onClear], $mockAbstractHydrator);
$mockEventManager
->expects(self::at(1))
->method('removeEventListener')
->with([Events::onClear], $mockAbstractHydrator);
$mockEventManager
->expects(self::at(2))
->method('addEventListener')
->with([Events::onClear], $mockAbstractHydrator);
$mockEventManager
->expects(self::at(3))
->method('removeEventListener')
->with([Events::onClear], $mockAbstractHydrator);
iterator_to_array($mockAbstractHydrator->iterate($mockStatement, $mockResultMapping));
$mockAbstractHydrator->hydrateAll($mockStatement, $mockResultMapping);
}
}

View File

@@ -8,6 +8,8 @@ use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Mapping\AssociationMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\Tests\Models\Hydration\EntityWithArrayDefaultArrayValueM2M;
use Doctrine\Tests\Models\Hydration\SimpleEntity;
use Doctrine\Tests\Models\CMS\CmsUser;
@@ -1956,4 +1958,29 @@ class ObjectHydratorTest extends HydrationTestCase
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$hydrator->hydrateAll($stmt, $rsm);
}
public function testFetchJoinCollectionValuedAssociationWithDefaultArrayValue()
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult(EntityWithArrayDefaultArrayValueM2M::CLASSNAME, 'e1', null);
$rsm->addJoinedEntityResult(SimpleEntity::CLASSNAME, 'e2', 'e1', 'collection');
$rsm->addFieldResult('e1', 'a1__id', 'id');
$rsm->addFieldResult('e2', 'e2__id', 'id');
$result = (new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em))
->hydrateAll(
new HydratorMockStatement([[
'a1__id' => '1',
'e2__id' => '1',
]]),
$rsm
);
$this->assertCount(1, $result);
$this->assertInstanceOf(EntityWithArrayDefaultArrayValueM2M::CLASSNAME, $result[0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0]->collection);
$this->assertCount(1, $result[0]->collection);
$this->assertInstanceOf(SimpleEntity::CLASSNAME, $result[0]->collection[0]);
}
}

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