Compare commits

...

437 Commits
2.8.0 ... 2.9.5

Author SHA1 Message Date
Grégoire Paris
77cc86ed88 Merge pull request #8916 from greg0ire/failing-test-8914
Check current class' discriminator map
2021-08-23 12:20:22 +02:00
Grégoire Paris
1de28c2cab Merge pull request #8930 from doctrine/2.10-readme
Introduce 2.10 to readme
2021-08-21 16:05:17 +02:00
Claudio Zizza
565987f583 Introduce 2.10 to readme 2021-08-20 21:14:25 +02:00
Grégoire Paris
ae10af0259 Check current class' discriminator map
A class can be in its own discriminator map, as described in the
documentation example at
https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/inheritance-mapping.html#single-table-inheritance
Checking only the current class' discriminator map should be enough,
since it is set to a copy of its parent's discriminator map earlier.

Fixes #8914
2021-08-15 11:59:11 +02:00
Grégoire Paris
d636d79686 Merge pull request #8895 from derrabus/bugfix/serializable
Implement __serialize() and __unserialize()
2021-08-15 11:56:49 +02:00
Vincent Langlet
b19a13f4ed Override getAssociationTargetClass phpdoc (#8907)
The upstream interface now allows null to be returned, but this
implementation never returns null, and consumers are expecting it not
to.
2021-08-11 22:53:03 +02:00
Grégoire Paris
1e2c0ce72d Merge pull request #8909 from greg0ire/fix-build
Fix build
2021-08-11 22:08:05 +02:00
Grégoire Paris
6a6bcc1e2b Ignore error caused by upstream package
See https://github.com/doctrine/collections/pull/282
2021-08-11 21:48:09 +02:00
Grégoire Paris
e2f54f6fa6 Remove phpstan-specific annotation
This is a direct consequence of https://github.com/doctrine/collections/pull/274
2021-08-11 21:48:09 +02:00
Grégoire Paris
e16a768916 Adapt baseline to new error message
The signature has become more precise, and so has the error message.
2021-08-11 21:48:03 +02:00
Grégoire Paris
3b9e04e971 Merge pull request #8905 from nicolas-grekas/ret-types
Add explicit `@return` type next to `#[ReturnTypeWillChange]`
2021-08-11 00:13:16 +02:00
Nicolas Grekas
fca1f5240d Add explicit @return type next to #[ReturnTypeWillChange] 2021-08-10 18:51:46 +02:00
Grégoire Paris
4fa2f6baa4 Merge pull request #8896 from derrabus/bugfix/dont-pass-null
Don't pass null as parameter
2021-08-09 08:19:12 +02:00
Grégoire Paris
245563e1cf Merge pull request #8894 from derrabus/bugfix/return-type-will-change
Fix return types for PHP 8.1
2021-08-09 08:18:46 +02:00
Grégoire Paris
a5436be939 Merge pull request #8899 from simPod/psalm
Regenerate psalm baseline
2021-08-08 17:13:19 +02:00
Simon Podlipsky
1246b3b5c3 Regenerate psalm baseline 2021-08-08 16:24:49 +02:00
Alexander M. Turek
3464591763 Don't pass null as parameter
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-08 01:39:43 +02:00
Alexander M. Turek
ae4bcd61ee Implement __serialize() and __unserialize()
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-08 01:24:06 +02:00
Alexander M. Turek
dc960d7d96 Fix return types for PHP 8.1
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-08 01:10:58 +02:00
Grégoire Paris
7736429e9b Merge pull request #8892 from derrabus/bump/phpstan
Bump PHPStan to 0.12.94
2021-08-08 00:08:21 +02:00
Alexander M. Turek
7c6bea1307 Bump PHPStan to 0.12.94
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-08-07 22:12:21 +02:00
Grégoire Paris
106ed8009a Merge pull request #8883 from dopeh/patch-1
Fix getting-started example by including cache
2021-08-05 00:20:07 +02:00
Pep
b2e00f6086 Fix getting-started example by including cache
The getting started example does not work without a valid cache library, this adds symfony/cache.
2021-08-04 23:04:46 +02:00
Grégoire Paris
055b646d9a Merge pull request #8769 from greg0ire/remove-unhelpful-template
Make templating Psalm-specific
2021-08-04 07:42:29 +02:00
Grégoire Paris
c1c3c89836 Merge pull request #8855 from piowin/GH8443-failing-test
Failing test for GH8443
2021-08-03 07:53:38 +02:00
Grégoire Paris
6fc0176f87 Merge pull request #8872 from simPod/fix-build
Fix CI SA failures
2021-08-02 23:48:02 +02:00
Simon Podlipsky
42126dc1bd Fix CI SA failures 2021-08-02 18:30:31 +02:00
Grégoire Paris
bc9e0b3d2c Merge pull request #8860 from t-richard/patch-1
Fix typo in "Working with Objects"
2021-07-24 14:55:58 +02:00
Thibault RICHARD
aa9d0148d5 Fix typo in "Working with Objects" 2021-07-24 14:13:29 +02:00
21skills
86703cbc73 Extra brackets if no cti joins fix
Continuation of a problem from
https://github.com/doctrine/orm/pull/6812
The same problem appears if you add WITH condition to the joined entity with discriminator
2021-07-23 21:27:05 +02:00
piowin
0504c535f1 Failing test for GH8443 2021-07-23 21:27:05 +02:00
Grégoire Paris
3c4009df38 Merge pull request #8847 from greg0ire/adapt-tests-to-new-wrapping
Adapt tests to new way of wrapping
2021-07-18 12:04:40 +02:00
Grégoire Paris
0a1be2cc21 Adapt tests to new way of wrapping
Namespacing is configured here, which means instead of just one layer,
we have a PSR cache wrapped in a doctrine cache with namespacing, itself
wrapped again with a PSR cache.
2021-07-18 11:00:46 +02:00
Benjamin Eberlei
95408cd8e4 [docs] Fix sentence about how nullable types affecting column definitions. (#8835) 2021-07-08 22:22:34 +02:00
Thomas Landauer
182bdaac6b Adding fields to Index (#8830)
* Adding `fields` to Index

I guess this was just forgotten here.
Wording is taken from https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/annotations-reference.html#index

* Update attributes-reference.rst
2021-07-08 22:06:57 +02:00
Grégoire Paris
3c805b22b4 Merge pull request #8825 from greg0ire/github-templates
Backport Github issue and PR templates
2021-07-06 08:34:55 +02:00
Grégoire Paris
6a41ab56ce Backport Github issue and PR templates 2021-07-05 23:14:47 +02:00
fridde
802dd54f07 Referenced new support for PHP8 attributes (#8823) 2021-07-05 22:06:04 +02:00
Grégoire Paris
836c0d3803 Merge pull request #8814 from greg0ire/backport-7158
Proposed corrected typo in demo code.
2021-07-05 10:11:50 +02:00
Grégoire Paris
aa3ed91dd2 Merge pull request #8821 from greg0ire/fix-phpstan 2021-07-05 10:01:59 +02:00
Grégoire Paris
f542dde131 Pin PHPStan to current version
We want to control upgrades to avoid suddenly failing builds.
2021-07-05 09:22:54 +02:00
Grégoire Paris
a165d4af7c Adapt ignore rules to new PHPStan version 2021-07-05 09:13:37 +02:00
Cathy Guilbaud
ff13059ba2 Proposed corrected typo in demo code.
changed $payed to $paid.
2021-07-04 17:34:22 +02:00
Grégoire Paris
c3953435dd Merge pull request #8806 from greg0ire/resurrect-phpbench
Resurrect phpbench
2021-07-03 23:21:21 +02:00
Grégoire Paris
8f2aef5fa3 Merge pull request #8810 from greg0ire/update-baseline
Update Psalm baseline
2021-07-03 21:00:26 +02:00
Grégoire Paris
05be0e8bbd Update Psalm baseline
It seems some deprecations have been addressed recently.
2021-07-03 20:46:37 +02:00
Grégoire Paris
64cf6edea6 Use consistent style for the OR operator 2021-07-03 12:07:43 +02:00
Grégoire Paris
7608a40463 Run phpbench in the CI 2021-07-03 12:05:45 +02:00
Grégoire Paris
b1f6f9bfc3 Make phpbench runnable again
There seems to have been a new major version.
2021-07-03 11:57:32 +02:00
Grégoire Paris
3fe980de96 Merge pull request #8801 from derrabus/bugfix/ignore-return-type-will-change
Ignore errors about missing ReturnTypeWillChange class
2021-06-30 23:02:01 +02:00
Grégoire Paris
f5d988de4e Merge pull request #8784 from greg0ire/undeprecate-passing-null
Undeprecate targetEntity omission for some types
2021-06-30 21:42:13 +02:00
Grégoire Paris
e840aef630 Merge pull request #8798 from greg0ire/backport-7776
Remove hack to access class scope inside closures
2021-06-30 08:20:29 +02:00
Gabriel Caruso
2936bac7ec Remove hack to access class scope inside closures
This is possible since 5.4.
2021-06-29 23:56:37 +02:00
Grégoire Paris
1f6bfe1754 Merge pull request #8800 from jderusse/fix-exception
Fix exception not thrown by "getEntityIdentifier"
2021-06-29 23:50:41 +02:00
Alexander M. Turek
b0c7993dd6 Ignore errors about missing ReturnTypeWillChange class
Signed-off-by: Alexander M. Turek <me@derrabus.de>
2021-06-29 22:50:16 +02:00
Jérémy Derussé
796af72650 Fix exception not thrown by "getEntityIdentifier" 2021-06-29 22:32:29 +02:00
Claudio Zizza
233d9b0276 Update session examples in docs (#8795)
The session examples contained approaches that aren't favorable for an application
and needed an overhaul using either scalar values or an DTO instead of the whole
entity with detach() and merge().
2021-06-28 23:23:40 +02:00
Grégoire Paris
b6d7826dc7 Merge pull request #8789 from greg0ire/backport-7292
Use a more concrete, less confusing example
2021-06-28 23:09:24 +02:00
Grégoire Paris
b3ee7141eb Use a more concrete, less confusing example
I picked a toothbrush because you usually do not share it with other
persons, feel free to propose something else.
2021-06-28 00:03:29 +02:00
Grégoire Paris
73aa6e8352 Merge pull request #8791 from greg0ire/backport-docs-batch-2
Backport docs batch 2
2021-06-28 00:00:10 +02:00
Igor Pellegrini
f3e87d2c2f Fix bullet list not rendering correctly on Github
As *rst*, if reading the tutorial on the Github repository, the items
are not seen as elements of a bullet list, hence they are not rendered
and they are collapsed in a single line of text.

Fix won't affect how it renders on
[doctrine-project.org](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html).
2021-06-27 10:42:17 +02:00
Catalin Ciobanu
53ba6b9732 Updated docblock 2021-06-26 23:04:56 +02:00
Jachim Coudenys
b3f580bf5e Fix the link to the 'Change Tracking Policies' documentation 2021-06-26 23:04:12 +02:00
Alex Rock Ancelet
774b5cbdd4 document using DTOs and entities as Value Objects 2021-06-26 22:51:22 +02:00
Gabriel Caruso
7fa3e6ec7c Use HTTPS instead of HTTP 2021-06-26 22:43:16 +02:00
Grégoire Paris
e743981f8d Merge pull request #8790 from greg0ire/backport-docs-batch
Backport docs batch
2021-06-26 22:18:30 +02:00
Haralan Dobrev
a469514ef0 Remove obsolete backslashes from underscores
These backslashes were not correctly migrated when updating the website
and it prevents search for some important terms like the query hints.
2021-06-26 18:57:58 +02:00
Jachim Coudenys
9fb13dbe28 Fix the result cache documentation (it caches raw data, not hydrated entities) 2021-06-26 17:12:28 +02:00
Adrian Föder
fc97041e49 [TASK] Streamline code block type hints for readability
This corrects a mistake with a given type hint in
the PHP code blocks as well as adds further for
consistency and brevity.
2021-06-26 16:14:05 +02:00
Claudio Zizza
f8f3b196a1 Extend documentation of arbitrary joins 2021-06-26 16:13:56 +02:00
Claudio Zizza
c01961840e Fix of variable typo in cache usage example 2021-06-26 16:13:47 +02:00
Andreas Möller
6ef1367cde Fix: Consistently format inversedBy and mappedBy attributes 2021-06-26 16:13:39 +02:00
Adrian Föder
3572b49e6a [FEATURE] Mention and link identifier generation strategies 2021-06-26 16:13:30 +02:00
Maciej Malarz
587c5f5ad6 Fix Criteria's orderBy example 2021-06-26 16:13:23 +02:00
someniatko
4f0a04e0eb Better PHPDoc for AbstractQuery cache methods
Updated useResultCache() and setResultCacheLifetime() docs to clearly state $lifetime is measured in seconds;
Added description to useResultCache()'s params. Renamed $bool => $useCache.
2021-06-26 16:13:19 +02:00
Andreas Möller
4451019dc0 Fix: Attributes vs. annotations 2021-06-26 15:56:33 +02:00
Gabriel Ostrolucký
c021426613 Fix #2935 [DDC-2236] Add note that Paginator might screw aggreg. queries 2021-06-26 15:56:09 +02:00
Gabriel Ostrolucký
243c8bff1f Fix EBNF of InExpression (StateFieldPath -> Arithmetic) 2021-06-26 15:55:18 +02:00
Gert de Pagter
5bbad8c403 Require ctype extension 2021-06-26 15:53:07 +02:00
Thomas Landauer
6c6b919788 Added UNIQUE INDEX to generated SQL schema
When I tried the example code, I got this unique index too.

Plus my `ALTER TABLE` statement was a little different from the one in the docs:
> ALTER TABLE cart ADD CONSTRAINT FK_BA388B79395C3F3 FOREIGN KEY (customer_id) REFERENCES customer (id)
Should I change that too?
2021-06-26 15:51:51 +02:00
Thomas Bisignani
b37c433080 [Documentation] Fixed comments syntax 2021-06-26 15:50:34 +02:00
Claudio Zizza
173f31a14a Add readOnly attribute to annotation example 2021-06-26 15:48:33 +02:00
Guido Sangiovanni
319acb1076 typo in class description
fixed typo in class description
2021-06-26 15:46:45 +02:00
Arman
242d2c1c41 Remove year from license 2021-06-26 15:46:14 +02:00
Jérôme Vasseur
5c95ce5c21 Fix EntityRepository constructor phpdoc
Remove redundant phpdoc from EntityRepository constructor
2021-06-26 15:45:02 +02:00
Grégoire Paris
7a78fd2900 Undeprecate targetEntity omission for some types
Some relationship types are attached to properties that can be typed
since php 7.4.
To avoid repeating the property type in targetEntity, an autodetection
feature has been developed that allows building the mapping from that
type information and annotations.
Omitting the targetEntity stays deprecated for ManyToMany as there is no
auto-detection for that yet (although one could be built by analyzing
phpdoc annotations).
2021-06-24 23:41:05 +02:00
Grégoire Paris
f233e4cf6b Merge pull request #8781 from derrabus/bugfix/static-analysis
Fix Psalm/PHPStan errors
2021-06-23 08:20:28 +02:00
Alexander M. Turek
b9f7e09401 Fix Psalm/PHPStan errors 2021-06-23 01:12:54 +02:00
Julio Montoya
5e91eea726 Link to correct section (#8778) 2021-06-22 13:12:54 +02:00
Julio Montoya
10922a5329 Spell "entity" properly (#8777) 2021-06-22 13:09:57 +02:00
Grégoire Paris
fbf793af0e Merge pull request #8543 from simonberger/add_missing_param_and_return_attributes
Add missing @param and @return PHPDoc attributes
2021-06-20 21:51:38 +02:00
Simon Berger
a26ae0648f Add missing @param and @Result PHPDoc attributes 2021-06-20 21:42:50 +02:00
Grégoire Paris
8bb564d5fe Limit template annotations to Psalm
Phpstan requires that the template can be resolved in all cases, while
Psalm seems to tolerate this ambiguous situation.
See https://github.com/phpstan/phpstan/issues/5175#issuecomment-861437050
2021-06-16 08:13:57 +02:00
Matthias Pigulla
7827869191 Make ResolveTargetEntityListener deal with the DiscriminatorMap as well (#8402) 2021-06-15 08:08:15 +02:00
Philipp Fritsche
82e77cf508 Bugfix: handle repeatable attributes (#8756)
* fix: handle repeatable attributes

* Restore interface compatibility

A reader is supposed to only ever return objects. By introducing
RepeatableAttributeCollection, we fulfill the interface and improve
clarity.

* refactor: accurate type declarations and returns

* refactor: remove unused use

* Ignore AttributeReader in phpstan and psalm to pass on CI running PHP 7.4.

* test: isTransient

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-06-13 12:29:22 +02:00
Grégoire Paris
1518b40dd2 Merge pull request #8758 from greg0ire/restore-bc-for-annotations
Restore bc for annotations
2021-06-10 19:12:13 +02:00
Grégoire Paris
10e41ec8bc Deprecate required of mandatory arguments 2021-06-09 23:04:16 +02:00
Grégoire Paris
303e346390 Restore backwards-compatibility
There used to be no constructor in this class, adding one with mandatory
arguments was technically a BC-break.

Fixes #8753
2021-06-09 23:04:16 +02:00
Grégoire Paris
fc7db8f59e Merge pull request #8747 from greg0ire/fix-attributes-syntax
Use correct named argument syntax in docs
2021-06-07 22:41:13 +02:00
Grégoire Paris
ae7f04ea53 Use correct named argument syntax in docs 2021-06-07 21:06:44 +02:00
Grégoire Paris
b8808099ea Merge pull request #8742 from derrabus/bugfix/return-type-will-change
Add ReturnTypeWillChange to ReflectionEmbeddedProperty
2021-06-05 23:52:49 +02:00
Alexander M. Turek
6432a3eeb2 Add ReturnTypeWillChange to ReflectionEmbeddedProperty 2021-06-05 22:51:55 +02:00
Grégoire Paris
3a0f60d6c6 Merge pull request #8734 from VincentLanglet/fixMetadata
Fix metadata constructor inference by phpstan
2021-06-05 21:48:17 +02:00
Grégoire Paris
ee19cf5cfd Merge pull request #8740 from VincentLanglet/fixMetada2
Make ClassMetadata covariant
2021-06-03 10:21:39 +02:00
Vincent Langlet
66daafd597 Make ClassMetadata covariant 2021-06-03 09:04:46 +02:00
Vincent Langlet
249c4fe61b Remove currentWorkingDirectory 2021-06-01 09:28:45 +02:00
Vincent Langlet
89673c60bf Fix metadata constructor inference by phpstan 2021-05-31 23:53:58 +02:00
Yup
75b4b88c5b Add automatic type detection for Embedded. (#8724)
* Add automatic type detection for Embedded.

* Inline statement.

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-05-31 11:53:14 +02:00
Benjamin Eberlei
d9e59d6862 [GH-8723] Remove use of nullability to automatically detect nullable status (#8732)
* [GH-8723] Remove use of nullability to automatically detect nullable status.

* [GH-8723] Make Column::$nullable default to false again, fix tests.
2021-05-31 10:19:16 +02:00
Yup
5fa94969de Adapt flush($argument) in documentation as it's deprecated. (#8728) 2021-05-29 22:22:19 +02:00
Juan Iglesias
f2c3ddac97 Add note about performance and inheritance mapping (#8704)
Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2021-05-27 08:27:55 +02:00
Grégoire Paris
46f0da9ffa Merge pull request #8710 from franmomu/recompute
Handle generic parameters in UnitOfWork
2021-05-25 13:29:21 +02:00
Fran Moreno
1e832a6782 Add generics to parameters 2021-05-25 13:01:47 +02:00
Grégoire Paris
56bdb44efd Merge pull request #8722 from alcaeus/fix-metadata-cache-clear 2021-05-25 12:55:00 +02:00
Andreas Braun
fffac44991 Bump doctrine/cache patch dependency to fix build with lowest deps 2021-05-25 11:58:11 +02:00
Andreas Braun
e42b3d6584 Fix metadata cache compatibility layer 2021-05-25 10:38:00 +02:00
Grégoire Paris
7ab2c3abbd Merge pull request #8708 from VincentLanglet/patch-2
Fix ClassMetadaInfo template inference
2021-05-25 08:49:50 +02:00
Grégoire Paris
498c816b65 Merge pull request #8717 from greg0ire/update-branch-metadata
Mark 2.8.x as unmaintained, and 2.9.x as current
2021-05-25 00:01:21 +02:00
Vincent Langlet
eec740079d Fix ClassMetadataInfo template inference 2021-05-24 21:52:40 +02:00
Grégoire Paris
c359715a97 Mark 2.8.x as unmaintained, and 2.9.x as current 2021-05-24 21:32:26 +02:00
Benjamin Eberlei
f3e55fae9f Update .doctrine-project.json to include 2.9 stable and 2.10 upcoming 2021-05-24 17:54:12 +02:00
Michael Babker
91c3bd4121 Fix links to attribute sections (#8714) 2021-05-24 17:52:30 +02:00
Peter Gribanov
e6cf12c66f remove usage Webmozart (#8713) 2021-05-24 17:50:15 +02:00
Grégoire Paris
99d67cb77d Merge pull request #8705 from greg0ire/2.9.x
Merge 2.8.x into 2.9.x
2021-05-21 09:15:28 +02:00
Grégoire Paris
43f66d5808 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-05-21 09:04:27 +02:00
Grégoire Paris
a6577b89a2 Merge pull request #8701 from jderusse/symfony6
Allow Symfony 6.0
2021-05-20 07:58:49 +02:00
Jérémy Derussé
0ca87566a9 Allow Symfony 6.0 2021-05-20 07:48:21 +02:00
Grégoire Paris
5d01f94a36 Merge pull request #8699 from greg0ire/fix-psalm
Fix some static analysis issues
2021-05-20 07:43:12 +02:00
Grégoire Paris
3d02b02636 Update static analysis baseline files
These issues were not introduced with new code, but with upgrades.
2021-05-20 00:03:39 +02:00
Grégoire Paris
6de321cb09 Address Psalm issues introduced by persistence 2021-05-20 00:03:39 +02:00
Grégoire Paris
535bc92dc8 Merge pull request #8700 from deguif/fix-undefined-offset
Fix undefined index
2021-05-18 12:20:38 +02:00
François-Xavier de Guillebon
ebb5d03f7a Fix undefined offset 2021-05-18 10:00:19 +02:00
Grégoire Paris
8e13369621 Merge pull request #8698 from deguif/cache-deprecation
Fix cache deprecation
2021-05-17 22:15:13 +02:00
François-Xavier de Guillebon
8eff4b775a Fix cache deprecation 2021-05-17 21:33:52 +02:00
Grégoire Paris
b85403d0a2 Merge pull request #8691 from alcaeus/check-deprecations
Check for use of deprecated API with Psalm
2021-05-15 18:50:30 +02:00
Andreas Braun
22ce3adfce Move to psalm level 2
This converts more issues to errors, most notably around deprecations. This can be used to later remove deprecated API.
2021-05-15 17:01:43 +02:00
tweet9ra
3a194ad699 SimpleObjectHydrator: skip unsuit custom type before converting it (#8566)
When using inheritance, it is possible to map the same column to properties of
different child classes. This can result in the same column being selecting several
times with different aliases in one SQL query, and only one aliased field needs
to be hydrated per row.
We now check that such an aliased value is mapped to the class we are hydrated
before attempting to convert it as it might result in an error when using a custom
type that does not get the expected data to initialize php value.

Co-authored-by: Sergey Naumov <s.naumov@lamoda.ru>
2021-05-15 09:37:16 +02:00
Andreas Braun
d52dab54dd Merge pull request #8672 from alcaeus/allow-cache-2.0 2021-05-14 20:21:56 +02:00
Andreas Braun
b5ac7714bc Remove ignored phpstan errors related to doctrine/cache 2.0 2021-05-13 20:38:21 +02:00
Andreas Braun
590551d5c3 Fix setup tool tests 2021-05-13 20:16:52 +02:00
Andreas Braun
c9fb9fdb40 Improve BC layer for getMetadataCacheImpl 2021-05-13 20:16:52 +02:00
Andreas Braun
965926dcc8 Update phpstan baseline to account for doctrine/cache deprecation 2021-05-13 20:16:52 +02:00
Andreas Braun
a6e30c5f4c Fix checkstyle violations 2021-05-13 20:16:52 +02:00
Andreas Braun
30ab6f4cea Add upgrade note for cache changes 2021-05-13 20:16:52 +02:00
Andreas Braun
5e5a44dce2 Suggest using symfony/cache in setup tool 2021-05-13 20:16:52 +02:00
Andreas Braun
d7bf30b291 Fix setup tool tests 2021-05-13 20:16:51 +02:00
Andreas Braun
ce8da6623f Stop using doctrine/cache 2021-05-13 20:16:51 +02:00
Andreas Braun
2ecec0c5d6 Remove reliance on doctrine/cache implementations in tests 2021-05-13 20:16:51 +02:00
Andreas Braun
6f128e4515 Allow installing doctrine/cache 2.0 2021-05-13 20:11:14 +02:00
Benjamin Eberlei
e24b0f0be7 [GH-8589] A new approach to non-nullable typed associations for BC (#8678)
* [GH-8589] A new approach to non-nullable typed associations for BC

* Address review comment about keeping the target entity type detection.
2021-05-10 19:08:16 +02:00
Benjamin Eberlei
6753b26f73 [GH-8676] Allow nested annotations to work without parents as attributes (#8677)
* [GH-8676] Allow nested annotations to work without parents as attributes.

* Housekeeping
2021-05-09 19:58:44 +02:00
Grégoire Paris
4ccc4e19fc Merge pull request #8600 from VincentLanglet/computeChangeset
Remove internal tag from computeChangeSet
2021-05-05 19:27:39 +02:00
Simon Podlipsky
4e2009433b Reflect that default EntityManager is not always named default (#8671)
Follows up #8646
2021-05-05 18:53:34 +02:00
Grégoire Paris
c25b822217 Merge pull request #8673 from Seldaek/patch-1
Add hint for ->iterate() deprecation
2021-05-05 13:31:30 +02:00
Jordi Boggiano
c3dcc5af91 Add hint for ->iterate() deprecation 2021-05-05 10:31:33 +02:00
Grégoire Paris
b2f404b25f Merge pull request #8651 from alcaeus/deprecate-doctrine-metadata-cache
Introduce PSR-6 for metadata caching
2021-05-01 14:53:46 +02:00
Alexandr Li
d141f27875 ConvertDoctrine1Schema: Fix Doctrine 1 notnull field import (#8649)
* ConvertDoctrine1Schema: Fix Doctrine 1 `notnull` field import

* cs fix

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-05-01 13:56:41 +02:00
Andreas Braun
4691839201 Extend DoctrineTestCase to fix missing methods 2021-04-29 12:54:23 +02:00
Andreas Braun
91387382b7 Allow symfony/cache 4.4 to provide PHP 7.1 support 2021-04-29 09:48:27 +02:00
Andreas Braun
f634c64b7a Use stubs over mocks in tests
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-29 09:42:18 +02:00
Andreas Braun
7ba9c980b5 Introduce PSR-6 for metadata caching 2021-04-29 09:42:18 +02:00
Grégoire Paris
dacdcf2c7b Merge pull request #8662 from greg0ire/2.9.x
Merge 2.8.x into 2.9.x
2021-04-29 09:27:40 +02:00
Grégoire Paris
f296fee9e4 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-29 09:20:26 +02:00
Grégoire Paris
8555fc1d34 Merge pull request #8659 from greg0ire/make-tests-independent
Make tests independent
2021-04-29 09:07:26 +02:00
Grégoire Paris
b0826fd746 Merge pull request #8201 from oojacoboo/2.8.x
Throw Exception when unable to locate identifier
2021-04-28 15:57:50 +02:00
Jacob Thomason
fe93c2e9d5 Throw Exception that includes name of entity when unable to locate identifier 2021-04-28 15:33:03 +02:00
Grégoire Paris
850d57827f Make tests independent
It seems like IdentityMapTest cannot be run on its own when the second
level cache is enabled (with ENABLE_SECOND_LEVEL_CACHE=1).
It does work when running the whole test suite because
ExtraLazyCollectionTest disables part of that cache in its setUp()
method.
In this patch, we restore the class metadata as it was before running
setUp() and put the test in IdentityMapTest inside the group that is
excluded when running with ENABLE_SECOND_LEVEL_CACHE=1 on the CI.
2021-04-28 14:12:31 +02:00
Benjamin Eberlei
e1388fa986 [GH-8327] Make EntityManagerProvider compatible with expected DoctrineBundle usage (#8646)
* [GH-8327] Make EntityManagerProvider compatible with expected DoctrineBundle usage.

* phpcs

* [Gh-8327] Delegate to EntityManagerProvider::getDefaultManager in ConnectionFromManagerProvider
2021-04-25 19:44:12 +02:00
Grégoire Paris
9a48450481 Merge pull request #7608 from mavroprovato/patch-1
Avoid unnecessary flush after processing first row
2021-04-24 14:23:09 +02:00
Kostas Kokkoros
cff8b96dd6 Avoid unnecessary flush after processing first row
The code as is needlessly flushes after just one row is updated or
removed. It makes more sense to update after ``$batchSize`` elements are
updated or removed, just as it is in the insert case.
2021-04-24 13:01:33 +02:00
Grégoire Paris
996c1c74b3 Merge pull request #8644 from greg0ire/more-accurate-return-type
Describe return types more accurately
2021-04-22 22:31:05 +02:00
Grégoire Paris
48612e6dc6 Merge pull request #8641 from Jean85/remove-deprecated-proxy-usage
Replace deprecated Proxy usages with parent interface to reduce baseline
2021-04-19 23:37:22 +02:00
Benjamin Eberlei
ddfee26f80 Support for Array parameters in SQL filters (#8375)
* #1168 Add support for array parameters on the SQLFilter

* DDC-1168 Add support for array parameters on the SQLFilter

* [GH-2624] Rework array support to use new getParameterList()

* [DDC-1952] Change at() mocking to using returnCallback()

* [DDC-1952] Make arrays of values explicit with new setParameterList

* Adjust tests to use country as list and locale as single value.

* [DDC-1952] Add tests for new exxeption conditions.

* Apply suggestions from code review

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

Co-authored-by: Manuel Nogales <nogales.manuel@gmail.com>
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-19 19:46:22 +02:00
Grégoire Paris
eb860a704e Change incorrect DBAL return types 2021-04-19 19:27:00 +02:00
Grégoire Paris
51ffcb4891 Describe return types more accurately
This fixes an SA regression introduced when using stricter types in
SchemaTool.

Fixes #8642
2021-04-19 19:09:10 +02:00
Grégoire Paris
72f500318a Merge pull request #8544 from greg0ire/bc-type-declarations
Add type declarations where backwards-compatible
2021-04-19 14:02:17 +02:00
Grégoire Paris
55f030f66b Add type declarations where backwards-compatible
This includes:
- private methods
- return type declarations of final protected methods
- return type declarations of public and protected methods of final classes

Parameter type declarations are a more delicate matter and should
probably be handled separately to make it easier to catch issues during
code review.

Type declarations can be more trusted than simple phpdoc when it
comes to static analysis, having them means we can infer the phpdoc of
calling methods with confidence.

Note that it seems that some of the phpdoc I initially inferred these
declarations from were apparently wrong, in particular some mentioning
Doctrine\Dbal\Statement when was is really passed around is
Doctrine\Dbal\Driver\Statement.
2021-04-19 13:51:00 +02:00
orklah
95af30eb72 FileLockRegion::__construct 3rd param is meant to be an int (#8640) 2021-04-19 13:29:43 +02:00
Alessandro Lai
9ea0769d78 Replace deprecated Proxy usages with parent interface to reduce baseline 2021-04-19 10:43:49 +02:00
Benjamin Eberlei
22413453da Reintroduce PHP 7.1 support (#8613)
* Reintroduce PHP 7.1 support

* phpcs

* Another object typehint

* More compatibility

* phpcs

* Reduce doctrine/inflector versions again since 1.4.4 is released.

* Housekeeping: phpcbf

* Simplify PHPUnit Polyfill abstraction.

* Missing assertDoesNotMatchRegularExpression

* phpcs

* Add 7.1 on Github actions, since dependencies now supported.

* Simplify code to work with renamed phpunit assertions and document when to remove.

* phpcs

* Downgrade target phpstan version to 7.1.0

* Run --prefer-lowest with PHP 7.1 not 7.3
2021-04-18 21:24:51 +02:00
Grégoire Paris
06fadcdd8c Merge pull request #8630 from Jean85/reduce-baseline
Reduce baseline
2021-04-18 18:50:59 +02:00
Alessandro Lai
7c56aa2141 Reduce baseline with a nullable return where needed 2021-04-18 18:39:20 +02:00
Alessandro Lai
4cdcb5f760 Reduce baseline for AbstractCollectionPersister 2021-04-18 18:39:20 +02:00
Alessandro Lai
b542b36e45 Remove baseline for DefaultCacheFactory 2021-04-18 18:39:20 +02:00
Alessandro Lai
e5a7a13e1e Remove single baseline rule from DefaultCache 2021-04-18 18:39:20 +02:00
Alessandro Lai
8336dd3779 Remove baseline for AbstractQuery 2021-04-18 18:39:14 +02:00
Grégoire Paris
b04d7a62ae Merge pull request #8548 from orklah/test2
Adding details to types in PHPDoc
2021-04-18 18:09:28 +02:00
Grégoire Paris
a959a474fd Merge pull request #8636 from greg0ire/update-gitattributes
Update ignore rules to reflect current situation
2021-04-18 17:56:47 +02:00
Benjamin Eberlei
ce128e742b [GH-5202] Implement Query::HINT_READ_ONLY flag (#7936)
* Rebase QueryHintReadOnly on 2.9.x

* Housekeeping: phpcs

* [GH-5202] Dont mark known entities as read only.

* [GH-5202] Not mark objects read only when proxy before.

* phpcs
2021-04-18 16:45:25 +02:00
Benjamin Eberlei
dac87dae06 [GH-8327] Deprecate EntityManagerHelper for a provider abstraction. (#8524)
* cli config

* [GH-8327] Deprecate EntityManager HelperSet for a provider abstraction.

* Housekeeping: phpcs

* [GH-8327] Refactor tests towards use of SingleManagerProvider instead of HelperSet.

* [GH-8327] Refactor tests towards use of SingleManagerProvider instead of HelperSet.

* [GH-8327] Refactor tests towards use of SingleManagerProvider instead of HelperSet.

* Housekeeping: cs

* Update tests/Doctrine/Tests/ORM/Tools/Console/ConsoleRunnerTest.php

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

* [GH-8327] Change option from entity-manager to em for consistency with DoctrineBundle.

* Add final to new methods and classes

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

* [GH-8327] Bugfix: phpstan detected stricter type checks needed.

* phpcs

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-18 14:32:42 +02:00
Jakub Caban
a2230485b2 Fix typed properties for default metadata (#7939) (#8589)
* Make Column::$type, Column::$nullable and JoinColumn::$nullable nullable by default

* Add tests for mapped typed properties (type and nullable)

* Fix Yaml driver tests and remove driver exceptions thrown too early

* Fix PHP driver tests

* Fix static PHP driver tests

* Fix XML driver tests

* Coding Standards

* Deprecate unused MappingException method

* Add manyToOne test and check nullable at the right place

* Coding Standards

* Bugfix: Temporarily change association join columns in CascadeRemoveOrderTest to circumvent new CommitOrderCalculator bug.

* phpcs

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-04-18 14:26:41 +02:00
Benjamin Eberlei
a68aa580c5 [GH-8345] Fields for unique constraints (#8629)
* Add possibility to use fields instead of column for unique constraint and indexes (#8345)

* Document changes in annotation reference

* phpcs

* Ensure exactly one of fields/columns is set for index/uniqueConstraint

* Adapt docs to fields/columns changes

* phpcs

* Implement fields in Attribute driver and fix mapping classes constructors.

* Coding Standard

* Apply suggestions from code review

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

* phpcs

Co-authored-by: Jakub Caban <kuba.iluvatar@gmail.com>
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-18 14:06:30 +02:00
Grégoire Paris
5ee71c54d4 Update ignore rules to reflect current situation
We no longer use Travis, we do not use git submodules as far as I know,
and we now use baseline files as well as project metadata.
2021-04-18 10:49:48 +02:00
orklah
dc37c2cd2f psalm fixes 2021-04-17 17:11:04 +02:00
Grégoire Paris
261a405970 Merge pull request #8635 from greg0ire/2.9.x
Manually merge 2.8.x into 2.9.x
2021-04-17 16:42:07 +02:00
Grégoire Paris
1ea51d88c4 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-17 16:31:14 +02:00
Grégoire Paris
da3a9fa361 Merge pull request #8634 from orklah/static-upgrade
upgrade static tools
2021-04-17 15:41:51 +02:00
orklah
4fd81d26ff upgrade static tools 2021-04-17 13:12:35 +02:00
Benjamin Eberlei
f8e06ad31e [GH-6396] Allow custom hydrators access to meta columns via Query::HINT_INCLUDE_META_COLUMNS hint. (#8382) 2021-04-16 21:27:29 +02:00
Grégoire Paris
559c1ba806 Merge pull request #8628 from greg0ire/2.9.x
Merge 2.8.x up into 2.9.x
2021-04-16 20:08:51 +02:00
Grégoire Paris
4665758c44 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-16 19:59:27 +02:00
Grégoire Paris
e2e9f8fa97 Merge pull request #8627 from greg0ire/add-baselines
Add baseline files for static analyzers
2021-04-16 19:04:04 +02:00
Grégoire Paris
f7249ec709 Declare return type
This helps SA tools figure out that it is fine to call count on the
return value of that method.
As a side-effect, using $metadata->name is not really an option since it
is not part of the ClassMetadata interface.
2021-04-16 13:14:54 +02:00
Grégoire Paris
87dbcca454 Add baseline files for static analyzers
There are many CS and SA-related changes in the ORM as we pursue better
code quality, and easier contributions. These often involve huge
changes, which can be hard to review and inevitably lead to some
regressions. I know some of those could have been avoided if we were
using stricter levels for PHPStan and Psalm.

By adding baselines, we ensure new code is at level 5 for both tools,
which should allow us to catch the most interesting issues.
2021-04-16 09:23:11 +02:00
Grégoire Paris
ceeea8ccd1 Merge pull request #8620 from greg0ire/2.9.x
Manually merge 2.8.x into 2.9.x
2021-04-13 19:23:10 +02:00
Grégoire Paris
6e16ef8c31 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-13 18:51:39 +02:00
Grégoire Paris
305e0d6664 Merge pull request #8617 from greg0ire/cs9
Upgrade to doctrine/coding-standard 9
2021-04-13 18:48:12 +02:00
Grégoire Paris
199be94e6d Upgrade to doctrine/coding-standard 9 2021-04-13 09:00:33 +02:00
Benjamin Eberlei
09a7d9f18a [GH-6578] Add validation that inherited entity class is mapped in discriminator. (#8378) 2021-04-10 18:13:31 +02:00
Grégoire Paris
f57f33b67f Merge pull request #8606 from greg0ire/2.9.x 2021-04-09 13:33:34 +02:00
Grégoire Paris
e86cddb360 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-09 13:21:30 +02:00
Grégoire Paris
fa588af3b1 Merge pull request #8604 from janatjak/2.8.x
Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method
2021-04-09 12:57:12 +02:00
Grégoire Paris
d4741720fa Merge pull request #8605 from greg0ire/fix-phpdoc-lsp-violations 2021-04-09 11:59:28 +02:00
Grégoire Paris
343385d060 Pin squizlabs/php_codesniffer
We are referencing rules in phpcs.xml.dist, and may experience
unexpected BC-breaks because of that when they get renamed.
2021-04-09 09:27:44 +02:00
Grégoire Paris
6d04dced03 Address sniff rename
This sniff seems to have been renamed or split in the latest version of
phpcs.
2021-04-09 09:19:10 +02:00
Grégoire Paris
22fa3a8556 Document actual return types
Some executors may return integers, for instance executors that only
execute update or delete statements.
Also, in case an integer is not returned, what's actually returned is a
Doctrine\DBAL\Driver\ResultStatement, and not a Doctrine\DBAL\Driver\Statement
2021-04-09 08:52:56 +02:00
Jakub Janata
eb05756dc3 Fix psalm param typehint for OneToManyAssociationBuilder::setOrderBy method 2021-04-08 22:52:50 +02:00
Grégoire Paris
5bb7e20708 Merge pull request #8602 from NicoHaase/fix-8599
Adjusted return type annotation for getOriginalEntityData
2021-04-07 23:22:19 +02:00
Nico Haase
a9076313c7 Adjusted return type 2021-04-07 21:06:58 +02:00
Grégoire Paris
2a87821b28 Merge pull request #8552 from acoulton/maint-phpunit-upgrade 2021-04-07 15:33:06 +02:00
acoulton
da5877d60c Only polyfill older phpunit methods when required 2021-04-07 12:08:55 +01:00
Andrew Coulton
67dfe8e1af Simplify mock building calls
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-04-07 11:07:35 +01:00
Vincent Langlet
2dfe51b396 Remove internal tag 2021-04-07 11:41:34 +02:00
Grégoire Paris
5ac036de02 Merge pull request #8594 from greg0ire/make-sure-test-is-run 2021-04-06 17:47:17 +02:00
Grégoire Paris
fda0d7b440 Merge pull request #8596 from doctrine/2.8.x-merge-up-into-2.9.x_606c485ba431f2.86881997 2021-04-06 15:06:02 +02:00
Grégoire Paris
23e1fd8ad6 Drop assertion about not being an instance of proxy
We do not want to enforce it as it is an internal details that seems to
vary from environment to environment.
2021-04-06 14:55:25 +02:00
Benjamin Eberlei
f8fa0fe069 [GH-8592] Deprecated Named (Native) Queries in Metadata/EntityRepository (#8593)
* [GH-8592] Deprecated Named (Native) Queries in Metadata and EntityRepository.

* [GH-8592] Add deprecation notice for named queries in docs [ci-skip]
2021-04-05 21:59:08 +02:00
Grégoire Paris
a588555ecd Merge pull request #8586 from KartaviK/patch-3
Additional psalm param typehint for orderBy argument in findBy method
2021-04-05 20:38:36 +02:00
Grégoire Paris
501057da83 Ensure test is suffixed with Test 2021-04-05 14:42:17 +02:00
Grégoire Paris
7de84537f6 Merge pull request #8591 from DmitriiBezborodnikov/case_insensive_parenthesis
Return case insensitive check
2021-04-05 14:40:51 +02:00
Grégoire Paris
97f8325dad Make sure tests are suffixed with Test
They will not be taken into account when running vendor/bin/phpunit
otherwise.
2021-04-05 14:32:40 +02:00
Grégoire Paris
0ebd7052d7 Drop create table at shutdown
It makes tests more isolated from each other: another test relying on
some tables including some of the ones created here may fail creating
the tables it needs because they already exist.
2021-04-05 14:03:49 +02:00
Dmitrii Bezborodnikov
5d73378b92 Return case insensitive check 2021-04-05 14:03:49 +02:00
Grégoire Paris
10572ec441 Merge pull request #8590 from VincentLanglet/patch-2
Fix phpdoc of ClassMetadataInfo::getIdentifierValues
2021-04-04 23:47:09 +02:00
Vincent Langlet
76278d801d Fix phpdoc 2021-04-04 21:19:54 +02:00
Roman Varkuta
ca80830b26 Describe $orderBy parameter as a hash
A list of string is incorrect, it actually looks like this:
['someField' => 'DESC', 'someOtherField' => 'ASC'…]`
2021-04-03 12:45:24 +02:00
Grégoire Paris
1ed89c756a Merge pull request #8582 from doctrine/2.8.x-merge-up-into-2.9.x_6066399d9875f8.96494390
Merge release 2.8.3 into 2.9.x
2021-04-02 09:12:58 +02:00
Grégoire Paris
bb078b5cb7 Merge remote-tracking branch 'origin/2.8.x' into 2.8.x-merge-up-into-2.9.x_6066399d9875f8.96494390 2021-04-02 09:00:35 +02:00
Grégoire Paris
bcb4889a2c Merge pull request #8583 from greg0ire/sync-static-analysis-workflows
Synchronize static analysis jobs with upstream
2021-04-02 08:58:50 +02:00
Grégoire Paris
961da8b0cc Synchronize static analysis jobs with upstream 2021-04-01 23:32:04 +02:00
Benjamin Eberlei
657a30f8ce [GH-6394] Bugfix: IdentifierFlattener support for association non-object values. (#8384)
* [GH-6394] Bugfix: IdentifierFlattener support for association non-object values

* [GH-6394] Bugfix: BasicEntityPersister::update used wrong identifiers for version assignment.

* Exclude MissingNativeTypeHint phpcs rule as 7.4 is not lowest version.
2021-04-01 23:16:53 +02:00
Benjamin Eberlei
c3f8996af5 Bump requirement to DBAL 2.13 (#8577) 2021-04-01 21:26:25 +02:00
Grégoire Paris
0655083e50 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-04-01 09:02:08 +02:00
Grégoire Paris
0b25d4d8b0 Merge pull request #8573 from greg0ire/fix-build
Fix build issues
2021-04-01 07:49:27 +02:00
Grégoire Paris
a88242ee6c Adapt test logic to PHP and SQLite
There seems to be at least 2 camps in the software world when it comes
to the question "What's today minus one month", today being at the end
of march.

While PHP and SQLite agree that that would be the 2nd of March, other
RDBMS than SQLite and humans will tell you that it's the last day of
February.

This patch ensures that we check one logic for SQLite, and the other
logic for other platforms.
2021-03-30 21:08:29 +02:00
Grégoire Paris
fe4964008d Accommodate 2 behaviors of symfony/console in test
Decorated text used to be wrapped too early in SymfonyStyle->block()
See https://github.com/symfony/symfony/pull/40348
The fix was not contributed to version 3, which means we have to rewrite
the test so that it passes for both the correct and the buggy version.
2021-03-30 08:41:10 +02:00
Grégoire Paris
3f3de70c3e Merge pull request #8564 from cybercitizen7/featureIncludeDirectory
Adding DIR to include statement to fix issue with pathing
2021-03-26 19:46:40 +01:00
darkw1z
eb4e317144 Adding DIR to include statement to fix issue with pathing 2021-03-26 14:04:46 +01:00
Grégoire Paris
c8f2f61ea1 Merge pull request #8556 from VincentLanglet/patch-2
Fix fieldMapping phpdoc
2021-03-26 08:26:40 +01:00
Vincent Langlet
c9502d3d0b Fix fieldMapping phpdoc 2021-03-24 15:07:08 +01:00
Benjamin Eberlei
b6b3c97436 [GH-8265] Attribute Metadata Driver (#8266)
* [GH-8265] Prototype for Attribute Metadata Driver

* [GH-8265] Skip AttributeDriverTest on PHP 7.

* [GH-8265] Fill more test entities with Attribute declarations to pass tests.

* [GH-8265] More test entity attributes for passing AttributeDriverTest.

* [GH-8265] Final changes to get AttributeDriverTest passing with test entities.

* [GH-8265] automatically update cs for new code when possible.

* [GH-8265] exclude sniffs that break because of phpcs not knowing attributes.

* [GH-8265] Fix AttributeReader styles.

* [GH-8265] Fix AttributeReader styles.

* [GH-8265] Missing changes to AttributeDriver

* [GH-8265] Fix InverseJoinColumn attribute cs violations.

* [GH-8265] Fix AbstractMappingDriverTest::_loadDriver and other CS

* [GH-8265] Coding styles

* [GH-8265] Coding styles

* [GH-8265] Coding styles

* [GH-8265] Coding styles

* [GH-8265] Convert Cache, ChangeTrackingPolicy, Column to named annotations.

* [GH-8265] Convert all annotations to named constructor for attribute support.

* [GH-8265] Style after attribute changes.

* [GH-8265] more styles

* [GH-8265] Remove workaround code for attributes.

* More cs

* More cs

* More cs

* More cs

* Add Attribute Metadata driver reference.

* Housekeeping: phpcs

* More merge conflict resolutions

* phpcs

* fix broken merge

* Change NamedArgumentConstructorAnnotation interface to use NamedArgumentConstructor annotation instead.

* phpcs

* Housekeeping: cs

* Housekeeping: cs

* Update docs with review comments

* Improve attribute docs

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>

* Rename AttributesDriver to AttributeDriver

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-03-23 13:30:57 +01:00
Benjamin Eberlei
8f6d146bc4 Bump doctrine/deprecations to at least v0.5.3 (#8553) 2021-03-22 23:23:03 +01:00
Grégoire Paris
3358ccde39 Merge pull request #8547 from greg0ire/psalm-lv6-phpdoc
Make phpdoc types correct
2021-03-21 22:11:51 +01:00
acoulton
1f4e6ebeeb Add a forward-compatibility wrapper for phpunit8 assertions
While doctrine still supports php7.2 the test cases need to run
under phpunit8 as well. However some assertion methods produce
deprecation warnings in the test output with phpunit >= 9.

This commit adds a thin forward compatiblity wrapper for the
new assertion method names so that they can be used with both
supported phpunit versions.
2021-03-18 11:25:18 +00:00
acoulton
a94db4f5c0 Fix remaining warnings from the phpunit9 upgrade
Some tests were still using deprecated assertion and mocking methods,
resulting in a long list of warnings in the phpunit output.

This commit resolves all the warnings:

* Fixes a couple of test names in `@depends` tags (presumably these
  tests were renamed at some point for coding standards).

* Changes how mocks are configured when asserting the same method
  is called multiple times with a sequence of arguments / sequence
  of return values. The old `->at` expectation is deprecated because
  it can be brittle and give unreliable results. Some of this
  mocking could probably still be refactored further, but my focus
  was on solving the deprecation with the existing setup.

* Removes one use of prophecy for mocking, in favour of using
  phpunit mock objects. Prophecy now requires an extra composer
  dependency and a trait which seems overkill given it was only
  used in one place.

* Updates to the new names for assertFileExists and assertRegExp
  (and their `not` versions) - these are functionally equivalent.

* Replaces the last few references to old PHPUnit_Framework_XXX
  classes with their namespaced equivalent. These were mostly in
  comments, or in native php `assert()` statements that were sanity
  checking mocked object types. These asserts are probably redundant
  (and are clearly not running in CI since the classes they referenced
  no longer exist).
2021-03-18 10:51:43 +00:00
Grégoire Paris
47475f3a67 Merge pull request #8532 from acoulton/bug-fix-ci-db-connection
All CI runs are using the sqlite fallback connection instead of the expected driver
2021-03-17 19:49:56 +01:00
acoulton
61c4a5da0a Rename tmpdb_ to privileged_db in test config and TestUtil
To avoid confusion, the `tmpdb_` test config values are now named
`privileged_db_` and better documented in the phpunit.xml.dist.

The TestUtil class has been refactored to more closely mirror the
structure and method / variable naming of the equivalent in
doctrine/dbal. This does not introduce any significant functional
changes. The only real difference is that the test output now prints
the selected database driver the first time it is referenced,
rather than repeating this through the test run.
2021-03-17 10:31:22 +00:00
acoulton
dd34bca4eb Upgrade previously-skipped tests to phpunit 9
These tests had not been running in CI so missed the previous
phpunit upgrade.

Note that assertions in decimal/floaty values in GH7941Test have
been changed to compare numerically with a reasonable level of
precision, instead of using regex. This is because the types
returned by the different drivers are inconsistent, and phpunit
now only allows regex comparisons on strings.
2021-03-17 10:31:22 +00:00
acoulton
3e21c50f61 Fix unit test and CI database driver / credential configuration
Builds using the github actions phpunit.xml files were not properly
recognising driver-specific configuration values, so were all
falling back to use the in-memory sqlite database instead of the
expected driver. This also meant a number of tests were skipped
as they rely on functionality not available in sqlite.

This commit addresses that by:

* REMOVING the automatic fallback to the sqlite memory database -
  phpunit.xml must now always specify explicit parameters for the
  desired connection.

* Displaying the active driver in the build output for visibility
  and debugging.

* Changing the way TestUtil loads the database config in line
  with the equivalent logic in doctrine/dbal, and to support the
  way that the config is/was specified in the phpunit.xml files
  for CI.

Note that this means a couple of the expected config variable names
have changed. Developers that are using customised phpunit.xml files
locally will need to update them to provide:

* Database config variables if they want to use the sqlite/memory
  driver - see phpunit.xml.dist for details.
* `db_driver` instead of `db_type`
* `db_user` instead of `db_username`
* `db_dbname` instead of `db_name`
* And, if in use, the equivalent changes to the `tmpdb_` values

The other change is that now if you provide any value for
`db_driver` we will attempt to create that connection type and
that will throw if other details (username / password / etc as
required by the driver) are not provided. Previously providing
partial configuration would cause TestUtil to silently fall back
to the in-memory sqlite driver.
2021-03-17 10:31:22 +00:00
Grégoire Paris
bc3592bcc8 Make phpdoc type correct 2021-03-16 19:20:11 +01:00
Grégoire Paris
4fccec1322 Merge pull request #8545 from orklah/psalm-plugins-2
remove some empty() use when safe
2021-03-15 23:51:09 +01:00
orklah
0177133385 remove unwanted check with 0 2021-03-15 23:28:20 +01:00
orklah
8df5cb84fa remove some empty() use when safe 2021-03-15 23:28:19 +01:00
Grégoire Paris
3b7275e183 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-03-14 18:57:41 +01:00
Grégoire Paris
5247c56fce Merge pull request #8539 from greg0ire/cs-20210311
🎉 Final CS batch 🎉
2021-03-14 18:47:03 +01:00
Grégoire Paris
cc37c490c2 Synchronize coding standard workflow with upstream
Now that there no longer are cs issues, we can thank diff-sniffer and
kiss it goodbye!
2021-03-13 09:55:38 +01:00
Grégoire Paris
95824efd61 Manually fix cs 2021-03-13 09:46:38 +01:00
Grégoire Paris
44d4712e64 Ignore rule about annotation phpdoc
These phpdoc is parsed by doctrine/annotations, and that package does
not understand things like array<string, mixed> yet.
2021-03-13 09:46:38 +01:00
Grégoire Paris
930f44c02f Ignore rule for externally-defined property 2021-03-13 00:08:03 +01:00
Grégoire Paris
6e3c011e65 Ignore rule about superflous comment
We can fix it with a breaking change.
2021-03-13 00:08:03 +01:00
Grégoire Paris
a82de0d422 Ignore rule about unused method
It cannot work when you call the private method like a callable.
2021-03-13 00:08:03 +01:00
Grégoire Paris
9917488179 Ignore rule about empty statements
This should be implemented in a separate pull request.
2021-03-12 08:20:46 +01:00
Grégoire Paris
93f31d2c33 Merge pull request #8533 from acoulton/test-string-lock-version
Add test coverage for passing optimistic lock version as string
2021-03-11 21:41:32 +01:00
acoulton
77356b954f Add test coverage for passing optimistic lock version as string
As discussed in #8527, when using optimistic locking with integer
version columns, Doctrine has always supported passing the lock
version as a string. For example when passing in a version
received in POST / GET.

Technically speaking this does not comply with the docs and phdoc
(which show the app explicitly casting to int before passing).

Nonetheless the maintainers decided it should continue to be valid
for now and reinstated the old soft-equals logic with #8531.

This modified test just avoids accidental changes in future.
2021-03-11 13:24:53 +00:00
Grégoire Paris
92f764206e Ignore broken rule 2021-03-11 09:11:24 +01:00
Grégoire Paris
141539673e Merge pull request #8530 from doctrine/cs-20210310
CS-batch 25/26 🤩
2021-03-11 00:05:31 +01:00
Grégoire Paris
23dc804c9b Merge pull request #8531 from beberlei/GH-8527-RevertLockEquals
[GH-8527] Revert cs fixes for entity version compares in lock+merge
2021-03-10 23:30:25 +01:00
Benjamin Eberlei
9e3baa7baa [GH-8527] Revert cs fixes for entity version compares in lock+merge 2021-03-10 22:46:07 +01:00
Grégoire Paris
322ea51ecf Manually fix cs 2021-03-10 22:17:31 +01:00
Grégoire Paris
21b046452b Merge pull request #8529 from greg0ire/cs-20210308
CS batch 24/26 🤞
2021-03-10 19:53:52 +01:00
Grégoire Paris
c57b81ada4 Manually fix cs 2021-03-09 21:15:33 +01:00
Grégoire Paris
4fa7c9c6de Merge pull request #8521 from greg0ire/cs-20210228
CS batch 23/an estimated 26
2021-03-08 19:57:18 +01:00
Benjamin Eberlei
2685b65c2b [GH-6855] Trigger deprecation for unsupported lifecycle callback mapping on embedded classes.(#8381) 2021-03-04 22:08:11 +01:00
Benjamin Eberlei
3902a4eb6e [GH-8458] Properly deprecate ConvertDoctrine1Schema (#8517) 2021-03-02 09:38:09 +01:00
Jakub Caban
b3ed525d4d Use typed properties for default metadata for #7939 (#8439)
[GH-7939] Detect column and association types from typed properties.

* Use typed properties for default metadata for #7939

* Coding Standards

* Remove $name from CmsUserTypes and adapt tests

* Factor out conditions required for typed property

* Factor out typed validation and completion methods

* Move Typed tests model to separate namespace

* Don't pass by reference, return array

* Document changes to default mapping for typed properties

* Better wording in annotation reference

* Add missing targetEntity assertion on typed association

* Try to comply with CS

* USe constants instead of strings

* Use one-line comments for single line content

* phpcs

* phpcs

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-03-01 22:13:58 +01:00
Grégoire Paris
3580517aac Manually fix cs 2021-03-01 21:49:10 +01:00
Benjamin Eberlei
4cdc6b1a71 [GH-7128] Improve OneToManyRequiresMappedBy Exception message (#8380) 2021-03-01 21:42:57 +01:00
plfort
38ccbd8638 DDC-2076 - MEMBER OF - Remove useless join over target table of ManyToMany relationship (#8438)
Co-authored-by: Pierre-Louis FORT <pierre-louis.fort@theia.fr>
2021-02-28 23:37:40 +01:00
Grégoire Paris
f9e7c3c2d8 Merge pull request #8516 from greg0ire/cs-20210227
CS batch 22/an estimated 26
2021-02-28 21:27:52 +01:00
Grégoire Paris
3600c0fbca Manually fix cs 2021-02-28 18:31:38 +01:00
Grégoire Paris
f779513042 Remove unused properties
They should have been removed as part of a6b43b93ac
2021-02-28 18:31:38 +01:00
Benjamin Eberlei
b4e6530d2d [GH-8471] Deprecate Partial DQL syntax and forcing partial loads. (#8472)
* [GH-8471] Deprecate Partial DQL syntax and forcing partial loads.
2021-02-28 18:17:12 +01:00
Diego Rin Martín
07d426edf5 Changed lock function to compare timestamps instead of DateTimeInterface objects directly. (#8508)
When using optimistic lock with DateTimeInterface based version field a bug appears due to the use of the === operator for comparing the lock version and the entity version. This comparison always resolves to false because the === operator when comparing objects is only true when both sides are the exact same instance of the object.

To fix the issue I have decided to compare timestamps instead the DateTimeInterface based objects directly, calling getTimestamp() method and doing a strict comparison.

Modified OptimisticLockException to use DateTimeInterface instead of DateTime class.

Added test suite to cover case.

Fixes #8499
2021-02-27 18:40:48 +01:00
Grégoire Paris
4afd4069be Merge pull request #8515 from greg0ire/cs-20210226
CS batch 21/an estimated 26
2021-02-27 14:30:25 +01:00
Grégoire Paris
239215c2e5 Merge pull request #8502 from greg0ire/rework-contributing-md
Rework CONTRIBUTING.md
2021-02-27 13:03:20 +01:00
Grégoire Paris
e6f11652d2 Add section for 2.9.x branch 2021-02-27 12:16:49 +01:00
Grégoire Paris
2910a73927 Rework badges urls
Some .x were missing, and ugly urlencoding can be avoided.
2021-02-27 12:10:47 +01:00
Grégoire Paris
3959b2743c Remove references to Travis 2021-02-27 12:03:35 +01:00
Grégoire Paris
658e54027e Remove trailing whitespace 2021-02-27 12:01:06 +01:00
Grégoire Paris
ba882451b0 Refer to our actual coding/standard
We do much more than just PSR 1 and 2
2021-02-27 12:01:05 +01:00
Grégoire Paris
1ed9840123 Refer to global workflow policy
We are doing things differently now, and the how is already documented.
2021-02-27 12:01:05 +01:00
Grégoire Paris
71044894a1 Manually fix cs 2021-02-26 21:30:49 +01:00
Grégoire Paris
1a41d6b87c Fix configuration mix up 2021-02-26 08:25:44 +01:00
Grégoire Paris
5dfcb08999 Merge pull request #8512 from greg0ire/cs-20210225
CS batch 20/an estimated 26
2021-02-25 23:23:24 +01:00
Grégoire Paris
284bd6fd03 Manually fix cs 2021-02-25 21:22:50 +01:00
Grégoire Paris
e40ac3e1d0 Merge pull request #8510 from greg0ire/cs-20210224
CS batch 19/an estimated 26
2021-02-24 22:41:28 +01:00
Grégoire Paris
0bce2472f2 Manually fix cs 2021-02-24 18:51:02 +01:00
Grégoire Paris
89f57de884 Merge pull request #8504 from greg0ire/cs-20210223
CS batch 18/an estimated 26
2021-02-23 23:18:42 +01:00
Grégoire Paris
ae19f40958 Merge pull request #8495 from Warxcell/fix_to_iterable_with_cache
Fix bug when using Result Cache with Query::toIterable
2021-02-23 20:16:39 +01:00
Grégoire Paris
c2d69a3c48 Merge pull request #8507 from greg0ire/address-move-away-from-master
Address move away from master
2021-02-23 18:19:47 +01:00
Grégoire Paris
6ce91dd37b Address move away from master 2021-02-23 09:24:40 +01:00
Grégoire Paris
57e6ba25c9 Merge pull request #8505 from dbu/patch-1
fix typo in changelog
2021-02-23 08:56:31 +01:00
David Buchmann
9d2e67bbb4 fix typo in changelog 2021-02-23 08:17:31 +01:00
Grégoire Paris
2dce5b20ad Manually fix cs 2021-02-22 23:50:09 +01:00
Warxcell
930859f803 Fix bug when using ResultCache with Query::toIterable.
Signed-off-by: Warxcell <warxcell@gmail.com>
2021-02-22 23:35:22 +02:00
Yup
a70c73ae3a Use RegEx to match if queryPart contains OR/AND (#8453)
This allows fixes cases of queries that contain line feeds or tabs but
do not benefit from automatic wrapping of parenthesis.
2021-02-22 20:58:06 +01:00
David Buchmann
074346b8d5 Note deprecation of AbstractQuery::iterator (#8497) 2021-02-22 20:30:05 +01:00
Grégoire Paris
9ed4a8c043 Merge pull request #8498 from greg0ire/cs-20210222
CS batch 17/an estimated 26
2021-02-22 20:25:06 +01:00
Grégoire Paris
9c917811e5 Manually fix cs 2021-02-22 09:12:04 +01:00
Grégoire Paris
f883820257 Ignore rule about wrong comment style 2021-02-22 08:48:46 +01:00
Grégoire Paris
a32045dd51 Ignore rule violated by external package 2021-02-22 07:52:57 +01:00
Grégoire Paris
672b04a55d Merge pull request #8483 from olsavmic/fix-single-scalar-hydrator-memory-leak-on-exception
Fix single scalar hydrator memory leak on exception
2021-02-21 21:12:50 +01:00
Grégoire Paris
261334aca2 Merge pull request #8494 from greg0ire/cs-20210221
CS batch 16/an estimated 26
2021-02-21 20:29:47 +01:00
Warxcell
7f6ed094cd Add test to verify that using ResultCache with Query::toIterable is failing. 2021-02-21 21:12:52 +02:00
Grégoire Paris
a792655813 Manually fix cs 2021-02-21 12:20:19 +01:00
Grégoire Paris
fb71204910 Relax assertion (#8493)
EntityManager is too restrictive, any implementation can actually be
returned here.

Closes #8488
2021-02-21 07:43:56 +01:00
Grégoire Paris
b918661cf1 Merge pull request #8492 from greg0ire/cs-20210220
CS batch 15/an estimated 26
2021-02-20 20:49:25 +01:00
Michael Olšavský
7971a53164 Method hydrateAll() does not take into account possible exception
from hydrateAllData() which in turn does not call cleanup()
2021-02-20 18:58:50 +01:00
Grégoire Paris
a175f96ae8 Manually fix cs 2021-02-20 15:37:15 +01:00
Grégoire Paris
7c1cde6471 Ignore rule that triggers on external property 2021-02-20 15:32:26 +01:00
Grégoire Paris
1ffc0cacf4 Merge pull request #8491 from greg0ire/cs-20210219
CS batch 14/an estimated 26
2021-02-20 11:10:27 +01:00
Grégoire Paris
e979d0d50f Manually fix cs 2021-02-20 00:01:41 +01:00
Grégoire Paris
553ea03079 Ignore rule about case mismatch
@group does not have to do with the Group entity at all.
2021-02-19 23:33:25 +01:00
Grégoire Paris
149014879d Ignore rule about property defined externally 2021-02-19 23:11:43 +01:00
Grégoire Paris
b991c58988 Merge pull request #8490 from greg0ire/cs-20210218
CS batch 13/an estimated 26
2021-02-19 23:08:25 +01:00
Aleksandr Frolov
ee9627b82e Update QueryBuilder::setParameters docs (#8487)
Use `ArrayCollection` instead of plain array (which is supported only for bc)
2021-02-19 01:45:14 +01:00
Grégoire Paris
e3f03414f9 Manually fix cs 2021-02-18 23:17:03 +01:00
Grégoire Paris
1f406fd3df Merge pull request #8484 from greg0ire/cs-20210217
CS batch 12/an estimated 26
2021-02-18 21:54:43 +01:00
Grégoire Paris
0ae53a6703 Merge pull request #8485 from doctrine/2.8.x-merge-up-into-2.9.x_602d59a0aa86b9.63081052
Merge release 2.8.2 into 2.9.x
2021-02-17 20:50:44 +01:00
Grégoire Paris
49864a7f57 Merge remote-tracking branch 'origin/2.8.x' into 2.8.x-merge-up-into-2.9.x_602d59a0aa86b9.63081052 2021-02-17 20:39:28 +01:00
Grégoire Paris
b747bf15ff Manually fix cs 2021-02-17 16:32:39 +01:00
Grégoire Paris
5fe85bfc03 Ignore rule about underscore in method name
We inherit from a class defined in another package.
2021-02-17 15:55:21 +01:00
Grégoire Paris
0dccf05ca8 Automatically fix cs 2021-02-17 15:55:21 +01:00
Grégoire Paris
ebae57eb96 Merge pull request #8480 from greg0ire/cs-20210216
CS batch 11/an estimated 26
2021-02-16 23:10:18 +01:00
Benjamin Eberlei
30a7c2aa67 [GH-8410] Fix memory leak in new toIterable and state bug. (#8467)
* [GH-8410] Fix memory leak in new toIterable and state bug.

The new AbstractQuery::toIterable() had a memory leak that
AbstractQuery::iterable() did not have. This leak is now fixed.

After fixing the leak, one test failed where the identity map in
ObjectHydrator triggered and lead to a notice. Introduced a new
AbstractHydrator::cleanupAfterRowIteration() that the ObjectHydrator
uses to cleanup the state.

* [GH-8413] Bugfix: Iterating with multiple, mixed results

When multiple entity results are part of a row, the result handling
must be different. In addition mixed results with scalars are broken
and now throw an exception as illegal operation.

* Housekeeping: phpcs

* [GH-8413] Add assertions for entity alias iteration.

* [GH-8387] Missing @deprecated on Query::iterate
2021-02-16 17:52:20 +01:00
Benjamin Eberlei
3a9b8fde9b Housekeeping: Fix cs 2021-02-16 16:24:19 +01:00
Yosh
4f864bc178 Identifier type is not set when many2many relations are deleted (#8401)
* Ensure identifier type is set on deleteJoinTableRecords

* Housekeeping: phpcs

* Housekeeping: phpcs

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-02-16 16:12:39 +01:00
Grégoire Paris
d76cbd755f Manually fix cs 2021-02-16 09:07:26 +01:00
Grégoire Paris
4aece04ae7 Automatically fix cs 2021-02-16 08:48:48 +01:00
Grégoire Paris
f31dbf8d4e Merge pull request #8479 from greg0ire/cs-20210215
CS batch 10/an estimated 27
2021-02-15 23:44:27 +01:00
Grégoire Paris
416f35dba9 Manually fix cs 2021-02-14 21:00:58 +01:00
Grégoire Paris
c29370e061 Automatically fix cs 2021-02-14 14:39:35 +01:00
Grégoire Paris
4e0f6837d0 Merge pull request #8478 from beberlei/CsFixes2
CS Fixes 2 string interpolation to sprintf
2021-02-14 14:39:00 +01:00
Benjamin Eberlei
e45d212f02 Housekeeping: CS fixes Doctrine\ORM\Mapping\MappingException 2021-02-14 14:28:42 +01:00
Benjamin Eberlei
8f62bd39b5 Housekeeping: CS fixes Doctrine\ORM\ORMException 2021-02-14 09:53:19 +01:00
Benjamin Eberlei
5e11afcdf1 Housekeeping: CS fixes Doctrine\ORM\Mapping\MappingException 2021-02-14 09:49:39 +01:00
Benjamin Eberlei
f833222017 Cs fixes1 (#8475)
* Housekeeping: CS Query, AbstractQuery, NativeQuery.

* Housekeeping: phpcs TreeWalker

* Housekeeping: CS Doctrine\ORM\EntityManager

* Housekeeping: CS Doctrine\ORM\Cache

* Upgrade git-phpcs

* Drop unused method parameter

* Describe types more precisely

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-02-14 09:23:15 +01:00
Grégoire Paris
c7f39ebbde Merge pull request #8477 from greg0ire/cs-20210214
CS batch 9/an estimated 28
2021-02-14 09:20:11 +01:00
Grégoire Paris
15f08ed006 Manually fix cs 2021-02-14 00:04:18 +01:00
Grégoire Paris
b6fd4b5ef3 Automatically fix cs 2021-02-14 00:04:11 +01:00
Benjamin Eberlei
e2e59e94f5 [GH-8383] deprecate notify change tracking policy (#8473)
* [GH-8383] Deprecate notify change tracking policy.

* [GH-8383] Add warning to documentation about notify change tracking policy deprecation.
2021-02-13 23:14:28 +01:00
Grégoire Paris
5e4dae88f3 Merge pull request #8476 from greg0ire/cs-20210213
CS batch 8/an estimated 30
2021-02-13 17:26:58 +01:00
Grégoire Paris
7312ddeda7 Manually fix cs 2021-02-13 13:46:01 +01:00
Grégoire Paris
9a67b6f699 Merge pull request #8474 from greg0ire/cs-20210212
CS batch 7/an estimated 30
2021-02-12 23:03:28 +01:00
Grégoire Paris
f59a0c349b Manually fix cs 2021-02-12 20:43:17 +01:00
Grégoire Paris
4c8831f716 Remove unused helper method 2021-02-12 20:04:56 +01:00
Benjamin Eberlei
6fe388a705 Introduce doctrine/deprecations (#8466)
* Introduce doctrine/deprecations, empty out VerifyDeprecations trait for now.

* Replace more usages of VerifyDeprecations (akwardkly for now)

* Update doctrine/deprecations to v0.2.0

* Remove ORM VerifyDeprecations trait and its use.

* Use doctrine/deprecatios VerifyDeprecations trait where useful

* Housekeeping: phpcs

* Fix reference link for toIterable/iterate deprecation
2021-02-12 18:14:34 +01:00
Vincent Langlet
172a8d9414 Restrict EntityManagerInterface::getRepository (#8417) 2021-02-12 17:44:22 +01:00
Grégoire Paris
01ca442be7 Ignore error about no assignment
These files must be require'd
2021-02-12 08:45:07 +01:00
Grégoire Paris
01374ca2ab Ignore rule about lone comment
That comment is mistaken for a method comment because it precedes one.
The issue was reported but will most likely not be fixed.
2021-02-12 07:39:56 +01:00
Grégoire Paris
94e8b1d43c Merge pull request #8470 from greg0ire/cs-20210211
CS batch 6/an estimated 30
2021-02-12 07:25:43 +01:00
Grégoire Paris
61d0f96c17 Manually fix cs 2021-02-11 21:02:10 +01:00
Grégoire Paris
41729be80a Spell "first" properly 2021-02-11 18:24:56 +01:00
Grégoire Paris
6dbaa39016 Merge pull request #8469 from greg0ire/cs-20210209
CS batch 5/an estimated 30
2021-02-10 21:11:11 +01:00
Grégoire Paris
58c95a92d1 Manually fix cs 2021-02-09 22:58:05 +01:00
Grégoire Paris
4958180b02 Merge pull request #8468 from greg0ire/cs-20210208
CS batch 4/an estimated 30
2021-02-09 22:22:41 +01:00
Grégoire Paris
d00dbf7e2d Merge pull request #8357 from snapshotpl/add-psalm-annotation
Add psalm annotation to ArrayCollection of Parameters
2021-02-08 21:25:58 +01:00
Grégoire Paris
8312ff0cb5 Merge pull request #8353 from fezfez/patch-1
Add docs to export-ignore
2021-02-08 21:23:35 +01:00
Witold Wasiczko
17012f1fea Improve psalm types 2021-02-08 21:16:56 +01:00
Witold Wasiczko
324ac3972f Add psalm annotation for parameters 2021-02-08 21:16:25 +01:00
Grégoire Paris
fb9b9b276e Manually fix cs 2021-02-08 13:53:04 +01:00
Benjamin Eberlei
323469cdb7 Add /*.phpunit.xml to .gitignore 2021-02-07 19:38:52 +01:00
Grégoire Paris
792a9a9149 Merge pull request #8464 from greg0ire/cs-20210207
CS batch 3/many
2021-02-07 14:21:09 +01:00
Grégoire Paris
0f655f9fb6 Manually fix cs 2021-02-07 12:21:40 +01:00
Grégoire Paris
2d7acbd07f Merge pull request #8457 from greg0ire/cs-20210206
CS Batch 2/many
2021-02-06 12:22:10 +01:00
andrews05
835030297a Add support for INDEX BY an associated entity (2.9.x) (#7918)
* Add support for INDEX BY an associated entity

This allows specifying an association in the INDEX BY clause of a query
which will index by the association's join column.

Related to #7661.

* Reintroduce IndexBy#simpleStateFieldPathExpression as deprecated property.

* Housekeeping: phpcs

* Housekeeping: phpcs

Co-authored-by: Benjamin Eberlei <kontakt@beberlei.de>
2021-02-06 11:46:18 +01:00
Grégoire Paris
b06679cc14 Manually fix cs 2021-02-06 11:44:32 +01:00
Julian Ullrich
2693a93aed fixed entity generation for numeric values (#8434)
* fixed entity generation for numeric values

* fixed entity generation for numeric values

Co-authored-by: julian <julian@ullrichmail.net>
2021-02-06 00:35:45 +01:00
Benjamin Eberlei
8724589c6e Housekeeping: Fix wrong typehint - Closes #8421 2021-02-06 00:23:45 +01:00
Grégoire Paris
4cc78d9478 Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-02-05 23:40:59 +01:00
Grégoire Paris
424305ef38 Merge pull request #8455 from greg0ire/cs
Cs
2021-02-05 23:40:01 +01:00
Grégoire Paris
9d01f6a45c Run phpcbf after running git-phpcs
git-phpcs can publish comments to Github, that will be helpful.
phpcbf will still be helpful to ensure no one upgrades the coding
standard without also fixing issues that can be autofixed.
2021-02-05 21:39:13 +01:00
Grégoire Paris
7ed487b534 Manually fix CS 2021-02-05 21:38:33 +01:00
Grégoire Paris
68d24288ce Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-02-05 20:06:04 +01:00
Grégoire Paris
40f3925589 Merge pull request #8144 from greg0ire/cs
Automated fixes with phpcbf + manual fixes
2021-02-05 08:44:58 +01:00
Alexander Schranz
f92c3dba32 Fix --complete flag in orm:ensure-production-settings command (#8426) 2021-02-01 22:08:04 +01:00
Grégoire Paris
5abad7c0af Merge remote-tracking branch 'origin/2.8.x' into 2.9.x 2021-01-31 00:01:45 +01:00
Grégoire Paris
bcbd4401b8 Ignore export directory 2021-01-30 23:50:15 +01:00
Grégoire Paris
d6aca8e146 Fix proxy file exclude pattern
__CG__ is a file prefix, not a directory
2021-01-30 23:45:46 +01:00
Grégoire Paris
8f1911a4fe Fix cs by hand 2021-01-30 23:29:40 +01:00
Grégoire Paris
7f30cd3102 Require doctrine/common ^3.0.3
That release comes with a fix for a bug that affects us since we are
using return type declarations for wakeUp() in proxyfied classes in on
of our tests.
2021-01-30 18:55:32 +01:00
Grégoire Paris
f01fe3e050 Fix or remove wrong assertions 2021-01-30 18:55:32 +01:00
Grégoire Paris
210c2ee6a4 Remove strict types 2021-01-30 18:55:32 +01:00
Grégoire Paris
497dfd1a84 Avoid covariant return types
It's not supported by PHP 7.2/7.3
2021-01-30 18:55:31 +01:00
Grégoire Paris
d9f0e2a27f Add exclude rules for tricky cases 2021-01-30 18:55:31 +01:00
Grégoire Paris
9a40ac6e2a Restore weird phpdoc
Tests do not pass when I format that phpdoc nicely.
2021-01-30 18:55:31 +01:00
Grégoire Paris
1687d9c479 Restore version annotation, but capitalized
@version is commonly used for svn ids and is forbidden
2021-01-30 18:55:31 +01:00
Gabriel Ostrolucký
1a46ed8901 Relax contract of EntityListenerResolver so it doesn't require class name (#8448)
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2021-01-30 13:37:26 +01:00
Grégoire Paris
36d0352c01 Add missing use statements 2021-01-30 11:21:44 +01:00
Grégoire Paris
15eacd787b Remove weird extra argument 2021-01-30 11:21:44 +01:00
Grégoire Paris
5b3f9bdd7b Fix type declarations 2021-01-30 11:21:44 +01:00
Grégoire Paris
e00dba94f4 Remove strict types 2021-01-30 11:21:44 +01:00
Grégoire Paris
ab0e4007a5 Use interface instead of concretion 2021-01-30 11:17:40 +01:00
Grégoire Paris
32266c54f9 Fix compatibility with parent signature 2021-01-30 11:17:39 +01:00
Grégoire Paris
dd2120cd41 Make default value compatible with phpdoc 2021-01-30 11:17:39 +01:00
Grégoire Paris
8991df0785 Add missing return types 2021-01-30 11:17:39 +01:00
Grégoire Paris
ca31923a39 Run phpcbf in the CI
There are still many CS issues to fix, but with this, we now know people
can run vendor/bin/phpcbf to fix their issues and only their issues.
2021-01-30 11:17:39 +01:00
Grégoire Paris
68bc00b6c6 Automatically fix CS 2021-01-30 10:58:42 +01:00
Grégoire Paris
5b55b8c6cf Disable rules that would result in BC-breaks 2021-01-29 23:21:12 +01:00
Grégoire Paris
10f381bc95 Restrict fixes to be compatible with PHP 7.2 2021-01-29 21:16:18 +01:00
Nikolay Gagarinov
40aa8fe5db update getting-started fix type, improve doc (#8440)
* update getting-started fix type, improve doc

* Update getting-started.rst
2021-01-26 21:40:41 +01:00
Simon Podlipsky
5801474ba3 Catch doctrine/persistence MappingException (#8264)
When driver chain is used doctrine/persistence MappingException is thrown instead of doctrine/orm MappingException
2021-01-11 09:07:06 +01:00
Claudio Zizza
9dbd960631 Update website config to current repository branches (#8420) 2021-01-11 09:06:06 +01:00
Grégoire Paris
544df89055 Use proper workflow name (#8418)
This must have been a copy/paste/adapt mistake.
2021-01-11 09:05:18 +01:00
orklah
f0ad5f72b2 bump psalm and fix some issues on level 6 (#8409) 2021-01-08 20:30:15 +01:00
azjezz
378944dd27 remove T from class metadata (#8398) 2020-12-19 20:48:16 +01:00
Benjamin Eberlei
8b749642cd [GH-8231] Bugfix: Missed dirty check synchronization check. (#8392)
When an entity with change tracking policy "deferred explicit" gets
removed, then persisted again, it is not schedulded for a dirty check
synchronization. This is not the case for entities that are persisted
and are already in the managed state.
2020-12-14 11:00:07 +01:00
Nic Wortel
277b53a970 Use the ramsey/composer-install action to install dependencies (#8388)
Follow-up of https://github.com/doctrine/.github/pull/16.
2020-12-10 20:03:47 +01:00
Benjamin Morel
2febb4509a Fix Psalm param (#8386) 2020-12-09 11:03:01 +01:00
Benjamin Eberlei
cbc252f3b7 Add Doctrine\ORM\Query\Expr::mod() (#8377)
* Add Doctrine\ORM\Query\Expr::mod()

Co-authored-by: Menno Holtkamp <menno.holtkamp@shopforce.nl>

* [GH-6739] Add entry to documentation

Co-authored-by: Menno Holtkamp <menno.holtkamp@shopforce.nl>
2020-12-06 23:09:20 +01:00
Benjamin Eberlei
21d2c88013 [GH-7486] Bugfix: failing the command when clearing empty cache is wrong semantics. (#8376) 2020-12-06 22:00:00 +01:00
Vašek Henzl
e7d33eb1a9 Infer datetime_immutable DBAL type for \DateTimeImmutable instance parameters (#8328)
The support for passing \DateTimeImmutable instance as a query parameter has
been added to ORM in #1333 (the year 2015), a long time before immutable date
types (datetime_immutable etc) were introduced to DBAL in doctrine/dbal#2450
(2017).

Back then, it made sense to treat \DateTimeImmutable (or any
\DateTimeInterface) in the same way as \DateTime and infer parameter type as
datetime. However, when immutable date types were later added to DBAL, it
wasn't reflected anyhow in type inference in ORM and \DateTimeImmmutable
instances are still inferred as datetime DBAL type.

This PR fixes this IMO incorrect behaviour of
ParameterTypeInferer::inferType(): for a \DateTimeImmmutable parameter, it now
returns datetime_immutable DBAL type; for \DateTime or any other types
implementing \DateTimeInterface, it returns datetime DBAL type as it did
before.

This behaviour is in line with DateTimeImmutableType handling only
\DateTimeImmutable and DateTimeType handling any \DateTimeInterface.

Why? In most cases, it doesn't matter and datetime works for \DateTimeImmutable
parameters just fine. But it does matter if using custom implementation of
datetime_immutable type like UTCDateTimeImmutableType from
simpod/doctrine-utcdatetime. Then the broken type inference is revealed.

This is partially related to #6443, however, this PR isn't about custom DBAL
types but about correct type inference for build-in types.
2020-12-05 23:36:33 +01:00
Michel Hunziker
cab7a4558d Fix invalid psalm annotation (#8374) 2020-12-05 14:47:49 +01:00
Benjamin Eberlei
242cf1a33d Fix ambiguous case where an entity is also a Traversable (#8371)
* Fix ambiguous case where an entity is also a Traversable

* Address phpcs violations.

* Address phpcs violations.

* Address phpcs violations.

Co-authored-by: Laurent VOULLEMIER <laurent.voullemier@gmail.com>
2020-12-04 20:53:07 +01:00
Grégoire Paris
da225a0db8 Drop step that switches the release branch (#8372)
ORM is a repository where we use the stable branch as the default
branch, that step is not appropriate here.
2020-12-04 20:46:09 +01:00
Benjamin Eberlei
3ef5a30102 [GH-8366] Catch additional Persistence MappingException (#8370) 2020-12-04 20:16:50 +01:00
Stéphane
35cf4810c1 Add docs to export-ignore 2020-11-27 18:50:57 +01:00
1308 changed files with 45459 additions and 29093 deletions

View File

@@ -7,26 +7,38 @@
"versions": [
{
"name": "3.0",
"branchName": "master",
"branchName": "3.0.x",
"slug": "latest",
"upcoming": true
},
{
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"name": "2.10",
"branchName": "2.10.x",
"slug": "2.10",
"upcoming": true
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"name": "2.9",
"branchName": "2.9.x",
"slug": "2.9",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "2.8",
"branchName": "2.8.x",
"slug": "2.8",
"maintained": false
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"maintained": false
},
{
"name": "2.6",
"branchName": "2.6",

6
.gitattributes vendored
View File

@@ -1,10 +1,10 @@
/tests export-ignore
/tools export-ignore
/docs export-ignore
/.github export-ignore
.doctrine-project.json export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.travis.yml export-ignore
build.properties export-ignore
build.properties.dev export-ignore
build.xml export-ignore
@@ -14,4 +14,6 @@ run-all.sh export-ignore
phpcs.xml.dist export-ignore
phpbench.json export-ignore
phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore

37
.github/ISSUE_TEMPLATE/BC_Break.md vendored Normal file
View File

@@ -0,0 +1,37 @@
---
name: 💥 BC Break
about: Have you encountered an issue during upgrade? 💣
---
<!--
Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/2.9.x/UPGRADE.md
-->
### BC Break Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Previous behavior
<!-- What was the previous (working) behavior? -->
#### Current behavior
<!-- What is the current (broken) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the BC break.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit it in a Pull Request separately, referencing this bug report.
-->

34
.github/ISSUE_TEMPLATE/Bug.md vendored Normal file
View File

@@ -0,0 +1,34 @@
---
name: 🐞 Bug Report
about: Something is broken? 🔨
---
### Bug Report
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes/no
| Version | x.y.z
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Current behavior
<!-- What is the current (buggy) behavior? -->
#### How to reproduce
<!--
Provide steps to reproduce the bug.
If possible, also add a code snippet with relevant configuration, entity mappings, DQL etc.
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
-->
#### Expected behavior
<!-- What was the expected (correct) behavior? -->

View File

@@ -0,0 +1,18 @@
---
name: 🎉 Feature Request
about: You have a neat idea that should be implemented? 🎩
---
### Feature Request
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the feature you would like to see implemented. -->

View File

@@ -0,0 +1,6 @@
---
name: ❓ Support Question
about: Have a problem that you can't figure out? 🤔
---
Please use https://github.com/doctrine/orm/discussions instead.

View File

@@ -0,0 +1,19 @@
---
name: 🐞 Failing Test
about: You found a bug and have a failing Unit or Functional test? 🔨
---
### Failing Test
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| BC Break | yes/no
| Version | x.y.z
#### Summary
<!-- Provide a summary of the failing scenario. -->

View File

@@ -0,0 +1,18 @@
---
name: ⚙ Improvement
about: You have some improvement to make Doctrine better? 🎁
---
### Improvement
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the improvement you are submitting. -->

View File

@@ -0,0 +1,26 @@
---
name: 🎉 New Feature
about: You have implemented some neat idea that you want to make part of Doctrine? 🎩
---
<!--
Thank you for submitting new feature!
Pick the target branch based according to these criteria:
* submitting a bugfix: target the lowest active stable branch: 2.9.x
* submitting a new feature: target the next minor branch: 2.10.x
* submitting a BC-breaking change: target the next major branch: 3.0.x
-->
### New Feature
<!-- Fill in the relevant information below to help triage your issue. -->
| Q | A
|------------ | ------
| New Feature | yes
| RFC | yes/no
| BC Break | yes/no
#### Summary
<!-- Provide a summary of the feature you have implemented. -->

View File

@@ -1,12 +1,17 @@
name: Static Analysis
name: "Coding Standards"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
coding-standards:
name: "Coding Standards"
runs-on: "ubuntu-latest"
runs-on: "ubuntu-20.04"
strategy:
matrix:
@@ -16,8 +21,6 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 10
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
@@ -26,21 +29,11 @@ jobs:
php-version: "${{ matrix.php-version }}"
tools: "cs2pr"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
dependency-versions: "highest"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Install git-phpcs"
run: "wget https://github.com/diff-sniffer/git/releases/download/0.3.2/git-phpcs.phar"
- name: "Fetch head branch"
run: "git remote set-branches --add origin $GITHUB_BASE_REF && git fetch origin $GITHUB_BASE_REF"
- name: "Run git-phpcs"
run: "php git-phpcs.phar origin/$GITHUB_BASE_REF...$GITHUB_SHA --report=checkstyle | cs2pr"
# https://github.com/doctrine/.github/issues/3
- name: "Run PHP_CodeSniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"

View File

@@ -19,11 +19,6 @@ jobs:
- "7.3"
- "7.4"
- "8.0"
deps:
- "normal"
include:
- deps: "low"
php-version: "7.3"
steps:
- name: "Checkout"
@@ -39,20 +34,8 @@ jobs:
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
if: "${{ matrix.deps == 'normal' }}"
- name: "Install lowest possible dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-lowest"
if: "${{ matrix.deps == 'low' }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml --coverage-clover=coverage-no-cache.xml"
@@ -67,7 +50,7 @@ jobs:
- name: "Upload coverage file"
uses: "actions/upload-artifact@v2"
with:
name: "phpunit-sqlite-${{ matrix.deps }}-${{ matrix.php-version }}-coverage"
name: "phpunit-sqlite-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
@@ -109,15 +92,8 @@ jobs:
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
@@ -171,15 +147,8 @@ jobs:
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
@@ -233,15 +202,8 @@ jobs:
ini-values: "zend.assertions=1"
extensions: "${{ matrix.extension }}"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage-no-cache.xml"
@@ -259,6 +221,38 @@ jobs:
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-coverage"
path: "coverage*.xml"
phpunit-lower-php-versions:
name: "PHPUnit with SQLite"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.1"
deps:
- "highest"
- "lowest"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
ini-values: "zend.assertions=1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
dependency-versions: "${{ matrix.deps }}"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit/sqlite.xml"
upload_coverage:
name: "Upload coverage to Codecov"
runs-on: "ubuntu-20.04"

49
.github/workflows/phpbench.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: "Performance benchmark"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
env:
fail-fast: true
jobs:
phpbench:
name: "PHPBench"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
with:
fetch-depth: 2
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
ini-values: "zend.assertions=1"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress"
- name: "Run PHPBench"
run: "vendor/bin/phpbench run --report=default"

View File

@@ -35,16 +35,6 @@ jobs:
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create and/or Switch to new Release Branch"
uses: "laminas/automatic-releases@v1"
with:
command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
env:
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create new milestones"
uses: "laminas/automatic-releases@v1"
with:

View File

@@ -1,17 +1,23 @@
name: Static Analysis
name: "Static Analysis"
on:
pull_request:
branches:
- "*.x"
push:
branches:
- "*.x"
jobs:
static-analysis-phpstan:
name: "PHPStan"
runs-on: "ubuntu-latest"
name: "Static Analysis with PHPStan"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
- "8.0"
steps:
- name: "Checkout code"
@@ -22,29 +28,23 @@ jobs:
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
tools: cs2pr
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-progress --no-suggest --no-interaction --prefer-dist"
dependency-versions: "highest"
- name: "Run a static analysis with phpstan/phpstan"
run: "php vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
run: "vendor/bin/phpstan analyse"
static-analysis-psalm:
name: "Psalm"
runs-on: "ubuntu-latest"
name: "Static Analysis with Psalm"
runs-on: "ubuntu-20.04"
strategy:
matrix:
php-version:
- "7.4"
- "8.0"
steps:
- name: "Checkout code"
@@ -56,15 +56,10 @@ jobs:
coverage: "none"
php-version: "${{ matrix.php-version }}"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
dependency-versions: "highest"
- name: "Run a static analysis with vimeo/psalm"
run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)"

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ vendor/
/.phpcs-cache
composer.lock
/.phpunit.result.cache
/*.phpunit.xml

View File

@@ -6,30 +6,17 @@ Before we can merge your Pull-Request here are some guidelines that you need to
These guidelines exist not to annoy you, but to keep the code base clean,
unified and future proof.
## We only accept PRs to "master"
Doctrine has [general contributing guidelines][contributor workflow], make
sure you follow them.
Our branching strategy is "everything to master first", even
bugfixes and we then merge them into the stable branches. You should only
open pull requests against the master branch. Otherwise we cannot accept the PR.
There is one exception to the rule, when we merged a bug into some stable branches
we do occasionally accept pull requests that merge the same bug fix into earlier
branches.
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
## Coding Standard
We use PSR-1 and PSR-2:
This project follows [`doctrine/coding-standard`][coding standard homepage].
You may fix many some of the issues with `vendor/bin/phpcbf`.
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
with some exceptions/differences:
* Keep the nesting of control structures per method as small as possible
* Align equals (=) signs
* Add spaces between assignment, control and return statements
* Prefer early exit over nesting conditions
* Add spaces around a negation if condition ``if ( ! $cond)``
[coding standard homepage]: https://github.com/doctrine/coding-standard
## Unit-Tests
@@ -56,25 +43,19 @@ curl -sS https://getcomposer.org/installer | php --
To run the testsuite against another database, copy the ``phpunit.xml.dist``
to for example ``mysql.phpunit.xml`` and edit the parameters. You can
take a look at the ``tests/travis`` folder for some examples. Then run:
take a look at the ``ci/github/phpunit`` directory for some examples. Then run:
vendor/bin/phpunit -c mysql.phpunit.xml
If you do not provide these parameters, the test suite will use an in-memory
sqlite database.
Tips for creating unit tests:
1. If you put a test into the `Ticket` namespace as described above, put the testcase and all entities into the same class.
See `https://github.com/doctrine/orm/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
See `https://github.com/doctrine/orm/tree/2.8.x/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php` for an
example.
## Travis
We automatically run your pull request through [Travis CI](http://www.travis-ci.org)
against SQLite, MySQL and PostgreSQL. If you break the tests, we cannot merge your code,
so please make sure that your code is working before opening up a Pull-Request.
## Getting merged
Please allow us time to review your pull requests. We will give our best to review

View File

@@ -1,4 +1,4 @@
Copyright (c) 2006-2015 Doctrine Project
Copyright (c) Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -1,7 +1,7 @@
| [Master][Master] | [2.8.x][2.8] |
|:----------------:|:----------:|
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] |
| [![Coverage Status][Master coverage image]][Master coverage] | [![Coverage Status][2.8 coverage image]][2.8 coverage] |
| [3.0.x][3.0] | [2.10.x][2.10] | [2.9.x][2.9] |
|:----------------:|:----------------:|:----------:|
| [![Build status][3.0 image]][3.0] | [![Build status][2.10 image]][2.10] | [![Build status][2.9 image]][2.9] |
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.10 coverage image]][2.10 coverage] | [![Coverage Status][2.9 coverage image]][2.9 coverage] |
Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
@@ -16,11 +16,15 @@ without requiring unnecessary code duplication.
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
[Master image]: https://img.shields.io/travis/doctrine/orm/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/orm
[Master coverage image]: https://codecov.io/gh/doctrine/orm/branch/master/graph/badge.svg
[Master coverage]: https://codecov.io/gh/doctrine/orm/branch/master
[2.8 image]: https://img.shields.io/travis/doctrine/orm/2.8.svg?style=flat-square
[2.8]: https://github.com/doctrine/orm/tree/2.8
[2.8 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.8/graph/badge.svg
[2.8 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.8
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
[2.9 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.9.x
[2.9]: https://github.com/doctrine/orm/tree/2.9.x
[2.9 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.9.x/graph/badge.svg
[2.9 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.9.x
[2.10 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.10.x
[2.10]: https://github.com/doctrine/orm/tree/2.10.x
[2.10 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.10.x/graph/badge.svg
[2.10 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.10.x

View File

@@ -10,8 +10,8 @@ we cannot protect you from SQL injection.
Please read the documentation chapter on Security in Doctrine DBAL and ORM to
understand the assumptions we make.
- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
- [ORM Security Page](https://github.com/doctrine/orm/blob/master/docs/en/reference/security.rst)
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html)
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core

View File

@@ -1,3 +1,31 @@
# Upgrade to 2.9
## Minor BC BREAK: Setup tool needs cache implementation
With the deprecation of doctrine/cache, the setup tool might no longer work as expected without a different cache
implementation. To work around this:
* Install symfony/cache: `composer require symfony/cache`. This will keep previous behaviour without any changes
* Instantiate caches yourself: to use a different cache implementation, pass a cache instance when calling any
configuration factory in the setup tool:
```diff
- $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir);
+ $cache = \Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($anyPsr6Implementation);
+ $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
```
* As a quick workaround, you can lock the doctrine/cache dependency to work around this: `composer require doctrine/cache ^1.11`.
Note that this is only recommended as a bandaid fix, as future versions of ORM will no longer work with doctrine/cache
1.11.
## Deprecated: doctrine/cache for metadata caching
The `Doctrine\ORM\Configuration#setMetadataCacheImpl()` method is deprecated and should no longer be used. Please use
`Doctrine\ORM\Configuration#setMetadataCache()` with any PSR-6 cache adapter instead.
## Removed: flushing metadata cache
To support PSR-6 caches, the `--flush` option for the `orm:clear-cache:metadata` command is ignored. Metadata cache is
now always cleared regardless of the cache adapter being used.
# Upgrade to 2.8
## Minor BC BREAK: Failed commit now throw OptimisticLockException
@@ -5,6 +33,11 @@
Method `Doctrine\ORM\UnitOfWork#commit()` can throw an OptimisticLockException when a commit silently fails and returns false
since `Doctrine\DBAL\Connection#commit()` signature changed from returning void to boolean
## Deprecated: `Doctrine\ORM\AbstractQuery#iterator()`
The method `Doctrine\ORM\AbstractQuery#iterator()` is deprecated in favor of `Doctrine\ORM\AbstractQuery#toIterable()`.
Note that `toIterable()` yields results of the query, unlike `iterator()` which yielded each result wrapped into an array.
# Upgrade to 2.7
## Added `Doctrine\ORM\AbstractQuery#enableResultCache()` and `Doctrine\ORM\AbstractQuery#disableResultCache()` methods

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env php
<?php
include('doctrine.php');
include(__DIR__ . '/doctrine.php');

View File

@@ -7,6 +7,10 @@
failOnRisky="true"
>
<php>
<!-- use an in-memory sqlite database -->
<var name="db_driver" value="pdo_sqlite"/>
<var name="db_memory" value="true"/>
<!-- necessary change for some CLI/console output test assertions -->
<env name="COLUMNS" value="120"/>
</php>

View File

@@ -16,29 +16,36 @@
"sort-packages": true
},
"require": {
"php": "^7.2|^8.0",
"php": "^7.1 ||^8.0",
"ext-ctype": "*",
"ext-pdo": "*",
"composer/package-versions-deprecated": "^1.8",
"doctrine/annotations": "^1.11.1",
"doctrine/cache": "^1.9.1",
"doctrine/annotations": "^1.13",
"doctrine/cache": "^1.12.1 || ^2.1.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^3.0",
"doctrine/dbal": "^2.10.0",
"doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.13.0",
"doctrine/deprecations": "^0.5.3",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.4|^2.0",
"doctrine/inflector": "^1.4 || ^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.0",
"symfony/console": "^3.0|^4.0|^5.0"
"doctrine/persistence": "^2.2",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"doctrine/coding-standard": "^8.0",
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^8.5|^9.4",
"symfony/yaml": "^3.4|^4.0|^5.0",
"vimeo/psalm": "4.1.1"
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "0.12.94",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.0",
"symfony/cache": "^4.4 || ^5.2",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.7.0"
},
"suggest": {
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
},
"autoload": {
@@ -47,16 +54,12 @@
"autoload-dev": {
"psr-4": {
"Doctrine\\Tests\\": "tests/Doctrine/Tests",
"Doctrine\\StaticAnalysis\\": "tests/Doctrine/StaticAnalysis",
"Doctrine\\Performance\\": "tests/Doctrine/Performance"
}
},
"bin": ["bin/doctrine"],
"extra": {
"branch-alias": {
"dev-master": "2.7.x-dev"
}
},
"archive": {
"exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
"exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"]
}
}

View File

@@ -1,4 +1,4 @@
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
The Doctrine ORM documentation is licensed under [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US)
Creative Commons Legal Code
@@ -359,5 +359,4 @@ Creative Commons Notice
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.
Creative Commons may be contacted at https://creativecommons.org/.

View File

@@ -193,7 +193,7 @@ object into a string representation before saving to the database (in the
value from the database (in the ``convertToPHPValue`` method).
The format of the string representation format is called
`Well-known text (WKT) <http://en.wikipedia.org/wiki/Well-known_text>`_.
`Well-known text (WKT) <https://en.wikipedia.org/wiki/Well-known_text>`_.
The advantage of this format is, that it is both human readable and parsable by MySQL.
Internally, MySQL stores geometry values in a binary format that is not

View File

@@ -5,7 +5,7 @@ Persisting the Decorator Pattern
This recipe will show you a simple example of how you can use
Doctrine ORM to persist an implementation of the
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
`Decorator Pattern <https://en.wikipedia.org/wiki/Decorator_pattern>`_
Component
---------

View File

@@ -14,7 +14,7 @@ In Doctrine 1 the DQL language was not implemented using a real
parser. This made modifications of the DQL by the user impossible.
Doctrine ORM in contrast has a real parser for the DQL language,
which transforms the DQL statement into an
`Abstract Syntax Tree <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
`Abstract Syntax Tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`_
and generates the appropriate SQL statement for it. Since this
process is deterministic Doctrine heavily caches the SQL that is
generated from any given DQL query, which reduces the performance

View File

@@ -132,7 +132,7 @@ dql statement.
The ``ArithmeticPrimary`` method call is the most common
denominator of valid EBNF tokens taken from the
`DQL EBNF grammar <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
`DQL EBNF grammar <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
that matches our requirements for valid input into the DateDiff Dql
function. Picking the right tokens for your methods is a tricky
business, but the EBNF grammar is pretty helpful finding it, as is
@@ -246,6 +246,6 @@ vendor sql functions and extend the DQL languages scope.
Code for this Extension to DQL and other Doctrine Extensions can be
found
`in the GitHub DoctrineExtensions repository <http://github.com/beberlei/DoctrineExtensions>`_.
`in the GitHub DoctrineExtensions repository <https://github.com/beberlei/DoctrineExtensions>`_.

View File

@@ -3,42 +3,69 @@ Entities in the Session
There are several use-cases to save entities in the session, for example:
1. User object
1. User data
2. Multi-step forms
To achieve this with Doctrine you have to pay attention to some details to get
this working.
Merging entity into an EntityManager
------------------------------------
Updating an entity
------------------
In Doctrine an entity objects has to be "managed" by an EntityManager to be
updateable. Entities saved into the session are not managed in the next request
anymore. This means that you have to register these entities with an
EntityManager again if you want to change them or use them as part of
references between other entities. You can achieve this by calling
``EntityManager#merge()``.
updatable. Entities saved into the session are not managed in the next request
anymore. This means that you have to update the entities with the stored session
data after you fetch the entities from the EntityManager again.
For a representative User object the code to get turn an instance from
the session into a managed Doctrine object looks like this:
For a representative User object the code to get data from the session into a
managed Doctrine object can look like these examples:
Working with scalars
~~~~~~~~~~~~~~~~~~~~
In simpler applications there is no need to work with objects in sessions and you can use
separate session elements.
.. code-block:: php
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
session_start();
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof User) {
$user = $_SESSION['user'];
$user = $em->merge($user);
if (isset($_SESSION['userId']) && is_int($_SESSION['userId'])) {
$userId = $_SESSION['userId'];
$em = GetEntityManager(); // creates an EntityManager
$user = $em->find(User::class, $userId);
$user->setValue($_SESSION['storedValue']);
$em->flush();
}
.. note::
Working with custom data transfer objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A frequent mistake is not to get the merged user object from the return
value of ``EntityManager#merge()``. The entity object passed to merge is
not necessarily the same object that is returned from the method.
If objects are needed, we discourage the storage of entity objects in the session. It's
preferable to use a `DTO (data transfer object) <https://en.wikipedia.org/wiki/Data_transfer_object>`_
instead and merge the DTO data later with the entity.
.. code-block:: php
<?php
require_once 'bootstrap.php';
session_start();
if (isset($_SESSION['user']) && $_SESSION['user'] instanceof UserDto) {
$userDto = $_SESSION['user'];
$em = GetEntityManager(); // creates an EntityManager
$userEntity = $em->find(User::class, $userDto->getId());
$userEntity->populateFromDto($userDto);
$em->flush();
}
Serializing entity into the session
-----------------------------------
@@ -47,22 +74,20 @@ Entities that are serialized into the session normally contain references to
other entities as well. Think of the user entity has a reference to their
articles, groups, photos or many other different entities. If you serialize
this object into the session then you don't want to serialize the related
entities as well. This is why you should call ``EntityManager#detach()`` on this
object or implement the __sleep() magic method on your entity.
entities as well. This is why you shouldn't serialize an entity and use
only the needed values of it. This can happen with the help of a DTO.
.. code-block:: php
<?php
require_once 'bootstrap.php';
$em = GetEntityManager(); // creates an EntityManager
$user = $em->find("User", 1);
$em->detach($user);
$_SESSION['user'] = $user;
$userDto = new UserDto($user->getId(), $user->getFirstName(), $user->getLastName());
// or "UserDto::createFrom($user);", but don't store an entity in a property. Only its values without relations.
.. note::
$_SESSION['user'] = $userDto;
When you called detach on your objects they get "unmanaged" with that
entity manager. This means you cannot use them as part of write operations
during ``EntityManager#flush()`` anymore in this request.

View File

@@ -6,7 +6,7 @@ Implementing ArrayAccess for Domain Objects
This recipe will show you how to implement ArrayAccess for your
domain objects in order to allow more uniform access, for example
in templates. In these examples we will implement ArrayAccess on a
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
Option 1

View File

@@ -7,9 +7,14 @@ The NOTIFY change-tracking policy is the most effective
change-tracking policy provided by Doctrine but it requires some
boilerplate code. This recipe will show you how this boilerplate
code should look like. We will implement it on a
`Layer Supertype <http://martinfowler.com/eaaCatalog/layerSupertype.html>`_
`Layer Supertype <https://martinfowler.com/eaaCatalog/layerSupertype.html>`_
for all our domain objects.
.. note::
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
Implementing NotifyPropertyChanged
----------------------------------

View File

@@ -4,7 +4,7 @@ Implementing Wakeup or Clone
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
As explained in the
`restrictions for entity classes in the manual <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
`restrictions for entity classes in the manual <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
it is usually not allowed for an entity to implement ``__wakeup``
or ``__clone``, because Doctrine makes special use of them.
However, it is quite easy to make use of these methods in a safe
@@ -23,7 +23,7 @@ implementation code in an identity check as follows:
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
// ...
public function __wakeup()
{
@@ -34,7 +34,7 @@ implementation code in an identity check as follows:
// otherwise do nothing, do NOT throw an exception!
}
//...
// ...
}
Safely implementing __clone
@@ -48,7 +48,7 @@ Safely implementing ``__clone`` is pretty much the same:
class MyEntity
{
private $id; // This is the identifier of the entity.
//...
// ...
public function __clone()
{
@@ -59,7 +59,7 @@ Safely implementing ``__clone`` is pretty much the same:
// otherwise do nothing, do NOT throw an exception!
}
//...
// ...
}
Summary

View File

@@ -61,7 +61,7 @@ to manage this mess,
however let me crush your expectations fast. There is not a single database out there (supported by Doctrine ORM)
that supports timezones correctly. Correctly here means that you can cover all the use-cases that
can come up with timezones. If you don't believe me you should read up on `Storing DateTime
in Databases <http://derickrethans.nl/storing-date-time-in-database.html>`_.
in Databases <https://derickrethans.nl/storing-date-time-in-database.html>`_.
The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC.
However with frequent daylight saving and political timezone changes you can have a UTC offset that moves

View File

@@ -41,6 +41,7 @@ Mapping Objects onto a Database
* **Drivers**:
:doc:`Docblock Annotations <reference/annotations-reference>` |
:doc:`Attributes <reference/attributes-reference>` |
:doc:`XML <reference/xml-mapping>` |
:doc:`YAML <reference/yaml-mapping>` |
:doc:`PHP <reference/php-mapping>`

View File

@@ -50,7 +50,7 @@ steps of configuration.
conversions with the query cache. These 2 caches require only an
absolute minimum of memory yet they heavily improve the runtime
performance of Doctrine. The recommended cache driver to use with
Doctrine is `APC <http://www.php.net/apc>`_. APC provides you with
Doctrine is `APC <https://php.net/apc>`_. APC provides you with
an opcode-cache (which is highly recommended anyway) and a very
fast in-memory cache storage that you can use for the metadata and
query caches as seen in the previous code snippet.
@@ -101,10 +101,11 @@ Gets or sets the metadata driver implementation that is used by
Doctrine to acquire the object-relational metadata for your
classes.
There are currently 4 available implementations:
There are currently 5 available implementations:
- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver``
- ``Doctrine\ORM\Mapping\Driver\AttributeDriver``
- ``Doctrine\ORM\Mapping\Driver\XmlDriver``
- ``Doctrine\ORM\Mapping\Driver\YamlDriver``
- ``Doctrine\ORM\Mapping\Driver\DriverChain``

View File

@@ -89,7 +89,7 @@ as part of the lifecycle of the instance variables entity-class.
Required attributes:
- **type**: Name of the Doctrine Type which is converted between PHP
and Database representation.
and Database representation. Default to ``string`` or :ref:`Type from PHP property type <reference-php-mapping-types>`
Optional attributes:
@@ -350,7 +350,7 @@ in order to specify that it is an embedded class.
Required attributes:
- **class**: The embeddable class
- **class**: The embeddable class. You can omit this value if you use a PHP property type instead.
.. code-block:: php
@@ -398,11 +398,11 @@ Example:
<?php
/**
* @Entity(repositoryClass="MyProject\UserRepository")
* @Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
*/
class User
{
//...
// ...
}
.. _annref_entity_result:
@@ -455,7 +455,8 @@ Optional attributes:
- **strategy**: Set the name of the identifier generation strategy.
Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE.
Valid values are ``AUTO``, ``SEQUENCE``, ``TABLE``, ``IDENTITY``, ``UUID``, ``CUSTOM`` and ``NONE``, explained
in the :ref:`Identifier Generation Strategies <identifier-generation-strategies>` section.
If not specified, default value is AUTO.
Example:
@@ -513,7 +514,8 @@ Required attributes:
- **name**: Name of the Index
- **columns**: Array of columns.
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
Optional attributes:
@@ -535,6 +537,19 @@ Basic example:
{
}
Basic example using fields:
.. code-block:: php
<?php
/**
* @Entity
* @Table(name="ecommerce_products",indexes={@Index(name="search_idx", fields={"name", "email"})})
*/
class ECommerceProduct
{
}
Example with partial indexes:
.. code-block:: php
@@ -715,6 +730,7 @@ Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
You can omit this value if you use a PHP property type instead.
*IMPORTANT:* No leading backslash!
Optional attributes:
@@ -840,6 +856,11 @@ Example:
@NamedNativeQuery
~~~~~~~~~~~~~~~~~
.. note::
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
Is used to specify a native SQL named query.
The NamedNativeQuery annotation can be applied to an entity or mapped superclass.
@@ -923,6 +944,7 @@ Required attributes:
- **targetEntity**: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.
When typed properties are used it is inherited from PHP type.
*IMPORTANT:* No leading backslash!
Optional attributes:
@@ -1263,7 +1285,8 @@ Required attributes:
- **name**: Name of the Index
- **columns**: Array of columns.
- **fields**: Array of fields. Exactly one of **fields**, **columns** is required.
- **columns**: Array of columns. Exactly one of **fields**, **columns** is required.
Optional attributes:
@@ -1285,6 +1308,19 @@ Basic example:
{
}
Basic example using fields:
.. code-block:: php
<?php
/**
* @Entity
* @Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", fields={"name", "email"})})
*/
class ECommerceProduct
{
}
Example with partial indexes:
.. code-block:: php

View File

@@ -83,7 +83,7 @@ be any regular PHP class observing the following restrictions:
- An entity class must not implement ``__wakeup`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
Also consider implementing
`Serializable <http://php.net/manual/en/class.serializable.php>`_
`Serializable <https://php.net/manual/en/class.serializable.php>`_
instead.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
@@ -189,7 +189,7 @@ The Unit of Work
Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a
typical implementation of the
`Unit of Work pattern <http://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
`Unit of Work pattern <https://martinfowler.com/eaaCatalog/unitOfWork.html>`_,
to keep track of all the things that need to be done the next time
``flush`` is invoked. You usually do not directly interact with a
``UnitOfWork`` but with the ``EntityManager`` instead.

View File

@@ -22,9 +22,9 @@ One tip for working with relations is to read the relation from left to right, w
- ManyToOne - Many instances of the current Entity refer to One instance of the referred Entity.
- OneToOne - One instance of the current Entity refers to One instance of the referred Entity.
See below for all the possible relations.
See below for all the possible relations.
An association is considered to be unidirectional if only one side of the association has
An association is considered to be unidirectional if only one side of the association has
a property referring to the other side.
To gain a full understanding of associations you should also read about :doc:`owning and
@@ -182,7 +182,7 @@ Here is a one-to-one relationship between a ``Customer`` and a
``Cart``. The ``Cart`` has a reference back to the ``Customer`` so
it is bidirectional.
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
Here we see the ``mappedBy`` and ``inversedBy`` attributes for the first time.
They are used to tell Doctrine which property on the other side refers to the
object.
@@ -259,6 +259,7 @@ Generated MySQL Schema:
CREATE TABLE Cart (
id INT AUTO_INCREMENT NOT NULL,
customer_id INT DEFAULT NULL,
UNIQUE INDEX UNIQ_BA388B79395C3F3 (customer_id),
PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE Customer (
@@ -977,10 +978,10 @@ similar defaults. As an example, consider this mapping:
<?php
class User
{
//...
// ...
/** @ManyToMany(targetEntity="Group") */
private $groups;
//...
// ...
}
.. code-block:: xml
@@ -1008,7 +1009,7 @@ This is essentially the same as the following, more verbose, mapping:
<?php
class User
{
//...
// ...
/**
* Many Users have Many Groups.
* @ManyToMany(targetEntity="Group")
@@ -1018,7 +1019,7 @@ This is essentially the same as the following, more verbose, mapping:
* )
*/
private $groups;
//...
// ...
}
.. code-block:: xml
@@ -1061,6 +1062,70 @@ join columns default to the simple, unqualified class name of the
targeted class followed by "\_id". The referencedColumnName always
defaults to "id", just as in one-to-one or many-to-one mappings.
Additionally, when using typed properties with Doctrine 2.9 or newer
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
associations as they will be set based on type. Also ``nullable``
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
.. configuration-block::
.. code-block:: php
<?php
/** @OneToOne */
private Shipment $shipment;
.. code-block:: xml
<doctrine-mapping>
<entity class="Product">
<one-to-one field="shipment" />
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToOne:
shipment: ~
Is essentially the same as following:
.. configuration-block::
.. code-block:: php
<?php
/**
* One Product has One Shipment.
* @OneToOne(targetEntity="Shipment")
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
*/
private Shipment $shipment;
.. code-block:: xml
<doctrine-mapping>
<entity class="Product">
<one-to-one field="shipment" target-entity="Shipment">
<join-column name="shipment_id" referenced-column-name="id" nulable=false />
</one-to-one>
</entity>
</doctrine-mapping>
.. code-block:: yaml
Product:
type: entity
oneToOne:
shipment:
targetEntity: Shipment
joinColumn:
name: shipment_id
referencedColumnName: id
nullable: false
If you accept these defaults, you can reduce the mapping code to a
minimum.

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,7 @@ Doctrine provides several different ways to specify object-relational
mapping metadata:
- :doc:`Docblock Annotations <annotations-reference>`
- :doc:`Attributes <attributes-reference>`
- :doc:`XML <xml-mapping>`
- :doc:`YAML <yaml-mapping>`
- :doc:`PHP code <php-mapping>`
@@ -76,7 +77,7 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
/** @Entity */
class Message
{
//...
// ...
}
.. code-block:: xml
@@ -108,7 +109,7 @@ You can change this by configuring information about the table:
*/
class Message
{
//...
// ...
}
.. code-block:: xml
@@ -211,6 +212,27 @@ list:
- ``options``: (optional) Key-value pairs of options that get passed
to the underlying database platform when generating DDL statements.
.. _reference-php-mapping-types:
PHP Types Mapping
_________________
Since version 2.9 Doctrine can determine usable defaults from property types
on entity classes. When property type is nullable this has no effect on
``nullable`` Column attribute at the moment for backwards compatibility
reasons.
Additionally, Doctrine will map PHP types to ``type`` attribute as follows:
- ``DateInterval``: ``dateinterval``
- ``DateTime``: ``datetime``
- ``DateTimeImmutable``: ``datetime_immutable``
- ``array``: ``json``
- ``bool``: ``boolean``
- ``float``: ``float``
- ``int``: ``integer``
- ``string`` or any other type: ``string``
.. _reference-mapping-types:
Doctrine Mapping Types
@@ -270,7 +292,7 @@ A cookbook article shows how to define :doc:`your own custom mapping types
.. warning::
All Date types assume that you are exclusively using the default timezone
set by `date_default_timezone_set() <http://php.net/manual/en/function.date-default-timezone-set.php>`_
set by `date_default_timezone_set() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
or by the php.ini configuration ``date.timezone``. Working with
different timezones will cause troubles and unexpected behavior.
@@ -300,7 +322,7 @@ annotation.
* @GeneratedValue
*/
private $id;
//...
// ...
}
.. code-block:: xml
@@ -328,9 +350,11 @@ annotation.
In most cases using the automatic generator strategy (``@GeneratedValue``) is
what you want. It defaults to the identifier generation mechanism your current
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
database vendor prefers: AUTO_INCREMENT with MySQL, sequences with PostgreSQL
and Oracle and so on.
.. _identifier-generation-strategies:
Identifier Generation Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -387,7 +411,7 @@ besides specifying the sequence's name:
* @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
*/
protected $id = null;
//...
// ...
}
.. code-block:: xml

View File

@@ -51,7 +51,7 @@ internally but also mean more work during ``flush``.
$em->clear(); // Detaches all objects from Doctrine!
}
}
$em->flush(); //Persist objects that did not make up an entire batch
$em->flush(); // Persist objects that did not make up an entire batch
$em->clear();
Bulk Updates
@@ -89,11 +89,11 @@ with the batching strategy that was already used for bulk inserts:
foreach ($q->toIterable() as $user) {
$user->increaseCredit();
$user->calculateNewBonuses();
++$i;
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all updates.
$em->clear(); // Detaches all objects from Doctrine!
}
++$i;
}
$em->flush();
@@ -147,11 +147,11 @@ The following example shows how to do this:
$q = $em->createQuery('select u from MyProject\Model\User u');
foreach($q->toIterable() as $row) {
$em->remove($row);
++$i;
if (($i % $batchSize) === 0) {
$em->flush(); // Executes all deletions.
$em->clear(); // Detaches all objects from Doctrine!
}
++$i;
}
$em->flush();

View File

@@ -43,7 +43,7 @@ these methods.
This documentation does not cover every single cache driver included
with Doctrine. For an up-to-date-list, see the
`cache directory on GitHub <https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache>`_.
`cache directory on GitHub <https://github.com/doctrine/cache/tree/2.8.x/lib/Doctrine/Common/Cache>`_.
PhpFileCache
~~~~~~~~~~~~
@@ -74,7 +74,7 @@ Memcache
In order to use the Memcache cache driver you must have it compiled
and enabled in your php.ini. You can read about Memcache
`on the PHP website <http://php.net/memcache>`_. It will
`on the PHP website <https://php.net/memcache>`_. It will
give you a little background information about what it is and how
you can use it as well as how to install it.
@@ -99,7 +99,7 @@ Memcache.
In order to use the Memcached cache driver you must have it compiled
and enabled in your php.ini. You can read about Memcached
`on the PHP website <http://php.net/memcached>`_. It will
`on the PHP website <https://php.net/memcached>`_. It will
give you a little background information about what it is and how
you can use it as well as how to install it.
@@ -121,7 +121,7 @@ Redis
In order to use the Redis cache driver you must have it compiled
and enabled in your php.ini. You can read about what Redis is
`from here <http://redis.io/>`_. Also check
`from here <https://redis.io/>`_. Also check
`A PHP extension for Redis <https://github.com/nicolasff/phpredis/>`_ for how you can use
and install the Redis PHP extension.
@@ -286,9 +286,8 @@ Result Cache
~~~~~~~~~~~~
The result cache can be used to cache the results of your queries
so that we don't have to query the database or hydrate the data
again after the first time. You just need to configure the result
cache implementation.
so that we don't have to query the database again after the first time.
You just need to configure the result cache implementation.
.. code-block:: php

View File

@@ -61,6 +61,11 @@ This policy can be configured as follows:
Notify
~~~~~~
.. note::
The notify change tracking policy is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8383>`_)
This policy is based on the assumption that the entities notify
interested listeners of changes to their properties. For that
purpose, a class that wants to use this policy needs to implement

View File

@@ -95,7 +95,7 @@ If you want to configure Doctrine in more detail, take a look at the :doc:`Advan
.. note::
You can learn more about the database connection configuration in the
`Doctrine DBAL connection configuration reference <http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
`Doctrine DBAL connection configuration reference <https://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html>`_.
Setting up the Commandline Tool
-------------------------------

View File

@@ -1,5 +1,5 @@
Doctrine Query Language
===========================
=======================
DQL stands for Doctrine Query Language and is an Object
Query Language derivative that is very similar to the Hibernate
@@ -487,6 +487,26 @@ where you can generate an arbitrary join with the following syntax:
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN Banlist b WITH u.email = b.email');
With an arbitrary join the result differs from the joins using a mapped property.
The result of an arbitrary join is an one dimensional array with a mix of the entity from the ``SELECT``
and the joined entity fitting to the filtering of the query. In case of the example with ``User``
and ``Blacklist``, it can look like this:
- User
- Blacklist
- Blacklist
- User
- Blacklist
- User
- Blacklist
- Blacklist
- Blacklist
In this form of join, the ``Blacklist`` entities found by the filtering in the ``WITH`` part are not fetched by an accessor
method on ``User``, but are already part of the result. In case the accessor method for Blacklists is invoked on a User instance,
it loads all the related ``Blacklist`` objects corresponding to this ``User``. This change of behaviour needs to be considered
when the DQL is switched to an arbitrary join.
.. note::
The differences between WHERE, WITH and HAVING clauses may be
confusing.
@@ -603,6 +623,13 @@ then phonenumber-id:
...
'nameUpper' => string 'JWAGE' (length=5)
You can also index by a to-one association, which will use the id of
the associated entity (the join column) as the key in the result set:
.. code-block:: sql
SELECT p, u FROM Participant INDEX BY p.user JOIN p.user u WHERE p.event = 3
UPDATE queries
--------------
@@ -673,29 +700,35 @@ The following functions are supported in SELECT, WHERE and HAVING
clauses:
- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side
- ABS(arithmetic\_expression)
- CONCAT(str1, str2)
- CURRENT\_DATE() - Return the current date
- CURRENT\_TIME() - Returns the current time
- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date
- ``IDENTITY(single_association_path_expression [, fieldMapping])`` -
Retrieve the foreign key column of association of the owning side
- ``ABS(arithmetic_expression)``
- ``CONCAT(str1, str2)``
- ``CURRENT_DATE()`` - Return the current date
- ``CURRENT_TIME()`` - Returns the current time
- ``CURRENT_TIMESTAMP()`` - Returns a timestamp of the current date
and time.
- LENGTH(str) - Returns the length of the given string
- LOCATE(needle, haystack [, offset]) - Locate the first
- ``LENGTH(str)`` - Returns the length of the given string
- ``LOCATE(needle, haystack [, offset])`` - Locate the first
occurrence of the substring in the string.
- LOWER(str) - returns the string lowercased.
- MOD(a, b) - Return a MOD b.
- SIZE(collection) - Return the number of elements in the
- ``LOWER(str)`` - returns the string lowercased.
- ``MOD(a, b)`` - Return a MOD b.
- ``SIZE(collection)`` - Return the number of elements in the
specified collection
- SQRT(q) - Return the square-root of q.
- SUBSTRING(str, start [, length]) - Return substring of given
- ``SQRT(q)`` - Return the square-root of q.
- ``SUBSTRING(str, start [, length])`` - Return substring of given
string.
- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim
- ``TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str)`` - Trim
the string by the given trim char, defaults to whitespaces.
- UPPER(str) - Return the upper-case of the given string.
- DATE_ADD(date, value, unit) - Add the given time to a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
- DATE_SUB(date, value, unit) - Subtract the given time from a given date. (Supported units are SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR)
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
- ``UPPER(str)`` - Return the upper-case of the given string.
- ``DATE_ADD(date, value, unit)`` - Add the given time to a given date.
(Supported units are ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``,
``WEEK``, ``MONTH``, ``YEAR``)
- ``DATE_SUB(date, value, unit)`` - Subtract the given time from a
given date. (Supported units are ``SECOND``, ``MINUTE``, ``HOUR``,
``DAY``, ``WEEK``, ``MONTH``, ``YEAR``)
- ``DATE_DIFF(date1, date2)`` - Calculate the difference in days
between date1-date2.
Arithmetic operators
~~~~~~~~~~~~~~~~~~~~
@@ -805,7 +838,7 @@ what type of results to expect.
Single Table
~~~~~~~~~~~~
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
is an inheritance mapping strategy where all classes of a hierarchy
are mapped to a single database table. In order to distinguish
which row represents which type in the hierarchy a so-called
@@ -898,7 +931,7 @@ entities:
Class Table Inheritance
~~~~~~~~~~~~~~~~~~~~~~~
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
is an inheritance mapping strategy where each class in a hierarchy
is mapped to several tables: its own table and the tables of all
parent classes. The table of a child class is linked to the table
@@ -1147,10 +1180,10 @@ make best use of the different result formats:
The constants for the different hydration modes are:
- Query::HYDRATE\_OBJECT
- Query::HYDRATE\_ARRAY
- Query::HYDRATE\_SCALAR
- Query::HYDRATE\_SINGLE\_SCALAR
- ``Query::HYDRATE_OBJECT``
- ``Query::HYDRATE_ARRAY``
- ``Query::HYDRATE_SCALAR``
- ``Query::HYDRATE_SINGLE_SCALAR``
Object Hydration
^^^^^^^^^^^^^^^^
@@ -1216,7 +1249,7 @@ Scalar Hydration:
1. Fields from classes are prefixed by the DQL alias in the result.
A query of the kind 'SELECT u.name ..' returns a key 'u\_name' in
A query of the kind 'SELECT u.name ..' returns a key 'u_name' in
the result rows.
Single Scalar Hydration
@@ -1364,21 +1397,22 @@ userland. However the following few hints are to be used in
userland:
- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects
- ``Query::HINT_FORCE_PARTIAL_LOAD`` - Allows to hydrate objects
although not all their columns are fetched. This query hint can be
used to handle memory consumption problems with large result-sets
that contain char or binary data. Doctrine has no way of implicitly
reloading this data. Partially loaded objects have to be passed to
``EntityManager::refresh()`` if they are to be reloaded fully from
the database.
- Query::HINT\_REFRESH - This query is used internally by
the database. This query hint is deprecated and will be removed
in the future (`Details <https://github.com/doctrine/orm/issues/8471>`_)
- ``Query::HINT_REFRESH`` - This query is used internally by
``EntityManager::refresh()`` and can be used in userland as well.
If you specify this hint and a query returns the data for an entity
that is already managed by the UnitOfWork, the fields of the
existing entity will be refreshed. In normal operation a result-set
that loads data of an already existing entity is discarded in favor
of the already existing entity.
- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional
- ``Query::HINT_CUSTOM_TREE_WALKERS`` - An array of additional
``Doctrine\ORM\Query\TreeWalker`` instances that are attached to
the DQL query parsing process.
@@ -1615,7 +1649,7 @@ From, Join and Index by
RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression]
IndexBy ::= "INDEX" "BY" StateFieldPathExpression
IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
Select Expressions
~~~~~~~~~~~~~~~~~~
@@ -1733,7 +1767,7 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InExpression ::= ArithmeticExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
InstanceOfParameter ::= AbstractSchemaName | InputParameter
LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]

View File

@@ -329,9 +329,9 @@ XML would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User">

View File

@@ -52,7 +52,7 @@ or adding entities to a collection twice. You have to check for both conditions
in the code before calling ``$em->flush()`` if you know that unique constraint failures
can occur.
In `Symfony2 <http://www.symfony.com>`_ for example there is a Unique Entity Validator
In `Symfony2 <https://www.symfony.com>`_ for example there is a Unique Entity Validator
to achieve this task.
For collections you can check with ``$collection->contains($entity)`` if an entity is already
@@ -112,8 +112,8 @@ over this collection using a LIMIT statement (or vendor equivalent).
Doctrine does not offer a solution for this out of the box but there are several extensions
that do:
* `DoctrineExtensions <http://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <http://github.com/whiteoctober/pagerfanta>`_
* `DoctrineExtensions <https://github.com/beberlei/DoctrineExtensions>`_
* `Pagerfanta <https://github.com/whiteoctober/pagerfanta>`_
Why does pagination not work correctly with fetch joins?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -53,6 +53,9 @@ proper quoting of parameters.
}
}
If the parameter is an array and should be quoted as a list of values for an IN query
this is possible with the alternative ``SQLFilter#setParameterList()`` and
``SQLFilter#getParameterList()`` functions.
Configuration
-------------

View File

@@ -44,13 +44,35 @@ Read-Only Entities
------------------
You can mark entities as read only (See metadata mapping
references for details). This means that the entity marked as read only is never considered
for updates, which means when you call flush on the EntityManager these entities are skipped
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
ones, they are just not considered for updates.
references for details).
This means that the entity marked as read only is never considered for updates.
During flush on the EntityManager these entities are skipped even if properties
changed.
Read-Only allows to persist new entities of a kind and remove existing ones,
they are just not considered for updates.
See :ref:`annref_entity`
You can also explicitly mark individual entities read only directly on the
UnitOfWork via a call to ``markReadOnly()``:
.. code-block:: php
$user = $entityManager->find(User::class, $id);
$entityManager->getUnitOfWork()->markReadOnly($user);
Or you can set all objects that are the result of a query hydration to be
marked as read only with the following query hint:
.. code-block:: php
$query = $entityManager->createQuery('SELECT u FROM App\\Entity\\User u');
$query->setHint(Query::HINT_READ_ONLY, true);
$users = $query->getResult();
Extra-Lazy Collections
----------------------
@@ -75,4 +97,4 @@ See :doc:`Best Practices <reference/best-practices>`
Change Tracking policies
------------------------
See: :doc:`Change Tracking Policies <reference/change-tracking-policies>`
See: :doc:`Change Tracking Policies <change-tracking-policies>`

View File

@@ -31,24 +31,31 @@ Example:
.. code-block:: php
<?php
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\OneToOne;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\MappedSuperclass;
use Doctrine\ORM\Mapping\Entity;
/** @MappedSuperclass */
class MappedSuperclassBase
class Person
{
/** @Column(type="integer") */
protected $mapped1;
/** @Column(type="string") */
protected $mapped2;
/**
* @OneToOne(targetEntity="MappedSuperclassRelated1")
* @JoinColumn(name="related1_id", referencedColumnName="id")
* @OneToOne(targetEntity="Toothbrush")
* @JoinColumn(name="toothbrush_id", referencedColumnName="id")
*/
protected $mappedRelated1;
protected $toothbrush;
// ... more fields and methods
}
/** @Entity */
class EntitySubClass extends MappedSuperclassBase
class Employee extends Person
{
/** @Id @Column(type="integer") */
private $id;
@@ -58,6 +65,15 @@ Example:
// ... more fields and methods
}
/** @Entity */
class Toothbrush
{
/** @Id @Column(type="integer") */
private $id;
// ... more fields and methods
}
The DDL for the corresponding database schema would look something
like this (this is for SQLite):
@@ -73,7 +89,7 @@ defined on that class directly.
Single Table Inheritance
------------------------
`Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
`Single Table Inheritance <https://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
is an inheritance mapping strategy where all classes of a hierarchy
are mapped to a single database table. In order to distinguish
which row represents which type in the hierarchy a so-called
@@ -181,7 +197,7 @@ the root entity of the single-table inheritance hierarchy.
Class Table Inheritance
-----------------------
`Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
`Class Table Inheritance <https://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
is an inheritance mapping strategy where each class in a hierarchy
is mapped to several tables: its own table and the tables of all
parent classes. The table of a child class is linked to the table
@@ -274,6 +290,9 @@ be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
Otherwise Doctrine *CANNOT* create proxy instances
of this entity and will *ALWAYS* load the entity eagerly.
There is also another important performance consideration that it is *NOT POSSIBLE*
to query for the base entity without any LEFT JOINs to the sub-types.
SQL Schema considerations
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -321,7 +340,7 @@ Example:
*/
class User
{
//other fields mapping
// other fields mapping
/**
* @ManyToMany(targetEntity="Group", inversedBy="users")
@@ -460,7 +479,7 @@ Things to note:
- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany)
- The association type *CANNOT* be changed.
- The override could redefine the joinTables or joinColumns depending on the association type.
- The override could redefine inversedBy to reference more than one extended entity.
- The override could redefine ``inversedBy`` to reference more than one extended entity.
- The override could redefine fetch to modify the fetch strategy of the extended entity.
Attribute Override

View File

@@ -112,10 +112,10 @@ in the core library. We don't think behaviors add more value than
they cost pain and debugging hell. Please see the many different
blog posts we have written on this topics:
- `Doctrine2 "Behaviors" in a Nutshell <http://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
- `A re-usable Versionable behavior for Doctrine2 <http://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
- `Write your own ORM on top of Doctrine2 <http://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
- `Doctrine ORM Behavioral Extensions <http://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
- `Doctrine2 "Behaviors" in a Nutshell <https://www.doctrine-project.org/2010/02/17/doctrine2-behaviours-nutshell.html>`_
- `A re-usable Versionable behavior for Doctrine2 <https://www.doctrine-project.org/2010/02/24/doctrine2-versionable.html>`_
- `Write your own ORM on top of Doctrine2 <https://www.doctrine-project.org/2010/07/19/your-own-orm-doctrine2.html>`_
- `Doctrine ORM Behavioral Extensions <https://www.doctrine-project.org/2010/11/18/doctrine2-behavioral-extensions.html>`_
Doctrine ORM has enough hooks and extension points so that **you** can
add whatever you want on top of it. None of this will ever become
@@ -131,8 +131,8 @@ extensions out there that offer support for Nested Set with
ORM:
- `Doctrine2 Hierarchical-Structural Behavior <http://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <http://github.com/blt04/doctrine2-nestedset>`_
- `Doctrine2 Hierarchical-Structural Behavior <https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior>`_
- `Doctrine2 NestedSet <https://github.com/blt04/doctrine2-nestedset>`_
Known Issues
------------

View File

@@ -14,6 +14,7 @@ metadata:
- **XML files** (XmlDriver)
- **Class DocBlock Annotations** (AnnotationDriver)
- **Attributes** (AttributeDriver)
- **YAML files** (YamlDriver)
- **PHP Code in files or static functions** (PhpDriver)

View File

@@ -71,7 +71,7 @@ with inheritance hierarchies.
use Doctrine\ORM\Query\ResultSetMappingBuilder;
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
"FROM users u INNER JOIN address a ON u.address_id = a.id";
$rsm = new ResultSetMappingBuilder($entityManager);
@@ -265,7 +265,7 @@ detail:
<?php
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
*
* @param string $alias
* @param string $columnAlias
* @param string $columnName
@@ -320,10 +320,10 @@ entity.
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
The result would look like this:
@@ -356,10 +356,10 @@ thus owns the foreign key.
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'address_id', 'address_id');
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
Foreign keys are used by Doctrine for lazy-loading purposes when
@@ -385,12 +385,12 @@ associations that are lazy.
$rsm->addFieldResult('a', 'address_id', 'id');
$rsm->addFieldResult('a', 'street', 'street');
$rsm->addFieldResult('a', 'city', 'city');
$sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' .
'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?';
$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
In this case the nested entity ``Address`` is registered with the
@@ -420,10 +420,10 @@ to map the hierarchy (both use a discriminator column).
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
$rsm->setDiscriminatorColumn('u', 'discr');
$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
Note that in the case of Class Table Inheritance, an example as
@@ -435,6 +435,10 @@ strategy but with native SQL it is your responsibility.
Named Native Query
------------------
.. note::
Named Native Queries are deprecated as of version 2.9 and will be removed in ORM 3.0
You can also map a native query using a named native query mapping.
To achieve that, you must describe the SQL resultset structure
@@ -789,7 +793,7 @@ followed by a dot ("."), followed by the name or the field or property of the pr
6:
name: address.country
column: a_country
If you retrieve a single entity and if you use the default mapping,

View File

@@ -1,6 +1,14 @@
Partial Objects
===============
.. note::
Creating Partial Objects through DQL is deprecated and
will be removed in the future, use data transfer object
support in DQL instead. (`Details
<https://github.com/doctrine/orm/issues/8471>`_)
A partial object is an object whose state is not fully initialized
after being reconstituted from the database and that is
disconnected from the rest of its data. The following section will

View File

@@ -277,10 +277,17 @@ following syntax:
.. code-block:: php
<?php
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Parameter;
// $qb instanceof QueryBuilder
// Query here...
$qb->setParameters(array(1 => 'value for ?1', 2 => 'value for ?2'));
$qb->setParameters(new ArrayCollection([
new Parameter('1', 'value for ?1'),
new Parameter('2', 'value for ?2')
]));
Getting already bound parameters is easy - simply use the above
mentioned syntax with "getParameter()" or "getParameters()":
@@ -513,6 +520,9 @@ complete list of supported helper methods available:
// Example - $qb->expr()->sqrt('u.currentBalance')
public function sqrt($x); // Returns Expr\Func
// Example - $qb->expr()->mod('u.currentBalance', '10')
public function mod($x); // Returns Expr\Func
// Example - $qb->expr()->count('u.firstname')
public function count($x); // Returns Expr\Func
@@ -533,7 +543,7 @@ using ``addCriteria``:
// ...
$criteria = Criteria::create()
->orderBy(['firstName', 'ASC']);
->orderBy(['firstName' => Criteria::ASC]);
// $qb instanceof QueryBuilder
$qb->addCriteria($criteria);

View File

@@ -114,7 +114,7 @@ Timestamp region
Tracks the timestamps of the most recent updates to particular entity.
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/TimestampRegion.html>`_.
.. _reference-second-level-cache-mode:
@@ -177,10 +177,11 @@ To enable the second-level-cache, you should provide a cache factory.
.. code-block:: php
<?php
/* @var $config \Doctrine\ORM\Cache\RegionsConfiguration */
/* @var $cache \Doctrine\Common\Cache\Cache */
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $cacheConfig */
/** @var \Doctrine\Common\Cache\Cache $cache */
/** @var \Doctrine\ORM\Configuration $config */
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($config, $cache);
$factory = new \Doctrine\ORM\Cache\DefaultCacheFactory($cacheConfig, $cache);
// Enable second-level-cache
$config->setSecondLevelCacheEnabled();
@@ -208,7 +209,7 @@ It allows you to provide a specific implementation of the following components :
``CollectionHydrator``
transforms collections into cache entries and cache entries into collections
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/DefaultCacheFactory.html>`_.
Region Lifetime
~~~~~~~~~~~~~~~
@@ -218,8 +219,9 @@ To specify a default lifetime for all regions or specify a different lifetime fo
.. code-block:: php
<?php
/* @var $config \Doctrine\ORM\Configuration */
/* @var $cacheConfig \Doctrine\ORM\Cache\CacheConfiguration */
/** @var \Doctrine\ORM\Configuration $config */
/** @var \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig */
/** @var \Doctrine\ORM\Cache\RegionsConfiguration $regionConfig */
$cacheConfig = $config->getSecondLevelCacheConfiguration();
$regionConfig = $cacheConfig->getRegionsConfiguration();
@@ -270,7 +272,7 @@ If you want to get more information you should implement
``\Doctrine\ORM\Cache\Logging\CacheLogger`` and collect
all the information you want.
`See API Doc <http://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
`See API Doc <https://www.doctrine-project.org/api/orm/current/Doctrine/ORM/Cache/Logging/CacheLogger.html>`_.
Entity cache definition
@@ -313,7 +315,7 @@ level cache region.
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Country">
<cache usage="READ_ONLY" region="my_entity_region" />
<id name="id" type="integer" column="id">
@@ -389,7 +391,7 @@ It caches the primary keys of association and cache each element will be cached
.. code-block:: xml
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="State">
<cache usage="NONSTRICT_READ_WRITE" />
@@ -464,8 +466,8 @@ Basic entity cache
$country1 = $em->find('Country', 1); // Retrieve item from cache
$country->setName("New Name");
$em->persist($country);
$country1->setName("New Name");
$em->flush(); // Hit database to update the row and update cache
$em->clear(); // Clear entity manager

View File

@@ -10,7 +10,7 @@ we cannot protect you from SQL injection.
Please also read the documentation chapter on Security in Doctrine DBAL. This
page only handles Security issues in the ORM.
- `DBAL Security Page <http://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
- `DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>`
If you find a Security bug in Doctrine, please report it on Jira and change the
Security Level to "Security Issues". It will be visible to Doctrine Core

View File

@@ -68,7 +68,7 @@ looks like this:
// $em instanceof EntityManager
$em->getConnection()->beginTransaction(); // suspend auto-commit
try {
//... do some work
// ... do some work
$user = new User;
$user->setName('George');
$em->persist($user);
@@ -98,7 +98,7 @@ functionally equivalent to the previously shown code looks as follows:
<?php
// $em instanceof EntityManager
$em->transactional(function($em) {
//... do some work
// ... do some work
$user = new User;
$user->setName('George');
$em->persist($user);

View File

@@ -16,11 +16,11 @@ Bidirectional Associations
The following rules apply to **bidirectional** associations:
- The inverse side has to have the ``mappedBy`` attribute of the OneToOne,
OneToMany, or ManyToMany mapping declaration. The mappedBy
OneToMany, or ManyToMany mapping declaration. The ``mappedBy``
attribute contains the name of the association-field on the owning side.
- The owning side has to have the ``inversedBy`` attribute of the
OneToOne, ManyToOne, or ManyToMany mapping declaration.
The inversedBy attribute contains the name of the association-field
OneToOne, ManyToOne, or ManyToMany mapping declaration.
The ``inversedBy`` attribute contains the name of the association-field
on the inverse-side.
- ManyToOne is always the owning side of a bidirectional association.
- OneToMany is always the inverse side of a bidirectional association.

View File

@@ -134,6 +134,10 @@ optimize the performance of the Flush Operation:
explicit strategies of notifying the UnitOfWork what objects/properties
changed.
.. note::
Flush only a single entity with ``$entityManager->flush($entity)`` is deprecated and will be removed in ORM 3.0.
(`Details <https://github.com/doctrine/orm/issues/8459>`_)
Query Internals
---------------

View File

@@ -291,7 +291,7 @@ example that encapsulate much of the association management code:
<?php
class User
{
//...
// ...
public function markCommentRead(Comment $comment) {
// Collections implement ArrayAccess
$this->commentsRead[] = $comment;
@@ -463,14 +463,14 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
<?php
class User
{
//...
// ...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
*/
private $commentsAuthored;
//...
// ...
}
...you can now create a user and an associated comment like this:

View File

@@ -338,7 +338,7 @@ in multiple ways with very different performance impacts.
.. note::
Calling ``remove`` on an entity will remove the object from the identiy
Calling ``remove`` on an entity will remove the object from the identity
map and therefore detach it. Querying the same entity again, for example
via a lazy loaded relation, will return a new object.
@@ -388,8 +388,7 @@ automatically without invoking the ``detach`` method:
currently managed by the EntityManager instance become detached.
- When serializing an entity. The entity retrieved upon subsequent
unserialization will be detached (This is the case for all entities
that are serialized and stored in some cache, i.e. when using the
Query Result Cache).
that are serialized and stored in some cache).
The ``detach`` operation is usually not as frequently needed and
used as ``persist`` and ``remove``.

View File

@@ -16,9 +16,9 @@ setup for the latest code in trunk.
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
...
@@ -102,9 +102,9 @@ of several common elements:
// Doctrine.Tests.ORM.Mapping.User.dcm.xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\ORM\Mapping\User" table="cms_users">
@@ -763,9 +763,9 @@ entity relationship. You can define this in XML with the "association-key" attri
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">

View File

@@ -41,9 +41,10 @@
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
reference/improving-performance

View File

@@ -43,9 +43,10 @@ Reference Guide
reference/native-sql
reference/change-tracking-policies
reference/partial-objects
reference/annotations-reference
reference/attributes-reference
reference/xml-mapping
reference/yaml-mapping
reference/annotations-reference
reference/php-mapping
reference/caching
reference/improving-performance

View File

@@ -58,9 +58,9 @@ and year of production as primary keys:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="VehicleCatalogue\Model\Car">
@@ -194,9 +194,9 @@ We keep up the example of an Article with arbitrary attributes, the mapping look
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Application\Model\ArticleAttribute">
@@ -302,7 +302,7 @@ of products purchased and maybe even the current price.
private $items;
/** @Column(type="boolean") */
private $payed = false;
private $paid = false;
/** @Column(type="boolean") */
private $shipped = false;
/** @Column(type="datetime") */

View File

@@ -71,9 +71,9 @@ switch to extra lazy as shown in these examples:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">

View File

@@ -82,7 +82,8 @@ that directory with the following contents:
{
"require": {
"doctrine/orm": "^2.6.2",
"symfony/yaml": "2.*"
"symfony/yaml": "2.*",
"symfony/cache": "^5.3"
},
"autoload": {
"psr-0": {"": "src/"}
@@ -138,8 +139,8 @@ step:
$useSimpleAnnotationReader = false;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode, $proxyDir, $cache, $useSimpleAnnotationReader);
// or if you prefer yaml or XML
//$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
//$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// $config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
// $config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// database configuration parameters
$conn = array(
@@ -235,44 +236,246 @@ entity definition:
/**
* @var int
*/
protected $id;
private $id;
/**
* @var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
private $name;
}
When creating entity classes, all of the fields should be ``protected`` or ``private``
(not ``public``), with getter and setter methods for each one (except ``$id``).
The use of mutators allows Doctrine to hook into calls which
manipulate the entities in ways that it could not if you just
directly set the values with ``entity#field = foo;``
When creating entity classes, all of the fields should be ``private``.
Use ``protected`` when strictly needed and very rarely if not ever ``public``.
Adding behavior to Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are two options to define methods in entities:
**getters/setters**, or **mutators and DTOs**,
respectively for **anemic entities** or **rich entities**.
**Anemic entities: Getters and setters**
The most popular method is to create two kinds of methods to
**read** (getter) and **update** (setter) the object's properties.
The id field has no setter since, generally speaking, your code
should not set this value since it represents a database id value.
(Note that Doctrine itself can still set the value using the
Reflection API instead of a defined setter function.)
The next step for persistence with Doctrine is to describe the
structure of the ``Product`` entity to Doctrine using a metadata
language. The metadata language describes how entities, their
properties and references should be persisted and what constraints
should be applied to them.
.. note::
Doctrine ORM does not use any of the methods you defined: it uses
reflection to read and write values to your objects, and will never
call methods, not even ``__construct``.
This approach is mostly used when you want to focus on behavior-less
entities, and when you want to have all your business logic in your
services rather than in the objects themselves.
Getters and setters are a common convention which makes it possible to
expose each field of your entity to the external world, while allowing
you to keep some type safety in place.
Such an approach is a good choice for RAD (rapid application development),
but may lead to problems later down the road, because providing such an
easy way to modify any field in your entity means that the entity itself
cannot guarantee validity of its internal state. Having any object in
invalid state is dangerous:
- An invalid state can bring bugs in your business logic.
- The state can be implicitly saved in the database: any forgotten ``flush``
can persist the broken state.
- If persisted, the corrupted data will be retrieved later in your application
when the data is loaded again, thereby leading to bugs in your business logic.
- When bugs occur after corrupted data is persisted, troubleshooting will
become much harder, and you might be aware of the bug too late to fix it in a
proper manner.
implicitly saved in database, thereby leading to corrupted or inconsistent
data in your storage, and later in your application when the data is loaded again.
.. note::
This method, although very common, is inappropriate for Domain Driven
Design (`DDD <https://en.wikipedia.org/wiki/Domain-driven_design>`)
where methods should represent real business operations and not simple
property change, And business invariants should be maintained both in the
application state (entities in this case) and in the database, with no
space for data corruption.
Here is an example of a simple **anemic entity**:
.. configuration-block::
.. code-block:: php
<?php
class User
{
private $username;
private $passwordHash;
private $bans;
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): void
{
$this->username = $username;
}
public function getPasswordHash(): string
{
return $this->passwordHash;
}
public function setPasswordHash(string $passwordHash): void
{
$this->passwordHash = $passwordHash;
}
public function getBans(): array
{
return $this->bans;
}
public function addBan(Ban $ban): void
{
$this->bans[] = $ban;
}
}
In the example above, we avoid all possible logic in the entity and only care
about putting and retrieving data into it without validation (except the one
provided by type-hints) nor consideration about the object's state.
As Doctrine ORM is a persistence tool for your domain, the state of an object is
really important. This is why we strongly recommend using rich entities.
**Rich entities: Mutators and DTOs**
We recommend using a rich entity design and rely on more complex mutators,
and if needed based on DTOs.
In this design, you should **not** use getters nor setters, and instead,
implement methods that represent the **behavior** of your domain.
For example, when having a ``User`` entity, we could foresee
the following kind of optimization.
Example of a rich entity with proper accessors and mutators:
.. configuration-block::
.. code-block:: php
<?php
class User
{
private $banned;
private $username;
private $passwordHash;
private $bans;
public function toNickname(): string
{
return $this->username;
}
public function authenticate(string $password, callable $checkHash): bool
{
return $checkHash($password, $this->passwordHash) && ! $this->hasActiveBans();
}
public function changePassword(string $password, callable $hash): void
{
$this->passwordHash = $hash($password);
}
public function ban(\DateInterval $duration): void
{
assert($duration->invert !== 1);
$this->bans[] = new Ban($this);
}
}
.. note::
Please note that this example is only a stub. When going further in the
documentation, we will update this object with more behavior and maybe
update some methods.
The entities should only mutate state after checking that all business logic
invariants are being respected.
Additionally, our entities should never see their state change without
validation. For example, creating a ``new Product()`` object without any data
makes it an **invalid object**.
Rich entities should represent **behavior**, not **data**, therefore
they should be valid even after a ``__construct()`` call.
To help creating such objects, we can rely on ``DTOs``, and/or make
our entities always up-to-date. This can be performed with static constructors,
or rich mutators that accept ``DTOs`` as parameters.
The role of the ``DTO`` is to maintain the entity's state and to help us rely
upon objects that correctly represent the data that is used to mutate the
entity.
.. note::
A `DTO <https://en.wikipedia.org/wiki/Data_transfer_object>` is an object
that only carries data without any logic. Its only goal is to be transferred
from one service to another.
A ``DTO`` often represents data sent by a client and that has to be validated,
but can also be used as simple data carrier for other cases.
By using ``DTOs``, if we take our previous ``User`` example, we could create
a ``ProfileEditingForm`` DTO that will be a plain model, totally unrelated to
our database, that will be populated via a form and validated.
Then we can add a new mutator to our ``User``:
.. configuration-block::
.. code-block:: php
<?php
class User
{
public function updateFromProfile(ProfileEditingDTO $profileFormDTO): void
{
// ...
}
public static function createFromRegistration(UserRegistrationDTO $registrationDTO): self
{
// ...
}
}
There are several advantages to using such a model:
* **Entity state is always valid.** Since no setters exist, this means that we
only update portions of the entity that should already be valid.
* Instead of having plain getters and setters, our entity now has
**real behavior**: it is much easier to determine the logic in the domain.
* DTOs can be reused in other components, for example deserializing mixed
content, using forms...
* Classic and static constructors can be used to manage different ways to
create our objects, and they can also use DTOs.
* Anemic entities tend to isolate the entity from logic, whereas rich
entities allow putting the logic in the object itself, including data
validation.
The next step for persistence with Doctrine is to describe the structure of
the ``Product`` entity to Doctrine using a metadata language. The metadata
language describes how entities, their properties and references should be
persisted and what constraints should be applied to them.
Metadata for an Entity can be configured using DocBlock annotations directly
in the Entity class itself, or in an external XML or YAML file. This Getting
@@ -299,11 +502,11 @@ but you only need to choose one.
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
*/
protected $name;
private $name;
// .. (other code)
}
@@ -311,9 +514,9 @@ but you only need to choose one.
.. code-block:: xml
<!-- config/xml/Product.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Product" table="products">
@@ -483,6 +686,7 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="bugs")
*/
class Bug
@@ -493,25 +697,25 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
* @ORM\GeneratedValue
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $description;
private $description;
/**
* @ORM\Column(type="datetime")
* @var DateTime
*/
protected $created;
private $created;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $status;
private $status;
public function getId()
{
@@ -568,13 +772,13 @@ classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
* @ORM\Column(type="integer")
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $name;
private $name;
public function getId()
{
@@ -621,7 +825,7 @@ domain model to match the requirements:
{
// ... (previous code)
protected $products;
private $products;
public function __construct()
{
@@ -639,8 +843,8 @@ domain model to match the requirements:
{
// ... (previous code)
protected $reportedBugs;
protected $assignedBugs;
private $reportedBugs;
private $assignedBugs;
public function __construct()
{
@@ -661,12 +865,12 @@ domain model to match the requirements:
Lazy load proxies always contain an instance of
Doctrine's EntityManager and all its dependencies. Therefore a
var\_dump() will possibly dump a very large recursive structure
``var_dump()`` will possibly dump a very large recursive structure
which is impossible to render and read. You have to use
``Doctrine\Common\Util\Debug::dump()`` to restrict the dumping to a
human readable level. Additionally you should be aware that dumping
the EntityManager to a Browser may take several minutes, and the
Debug::dump() method just ignores any occurrences of it in Proxy
``Debug::dump()`` method just ignores any occurrences of it in Proxy
instances.
Because we only work with collections for the references we must be
@@ -715,8 +919,8 @@ the bi-directional reference:
{
// ... (previous code)
protected $engineer;
protected $reporter;
private $engineer;
private $reporter;
public function setEngineer(User $engineer)
{
@@ -749,8 +953,8 @@ the bi-directional reference:
{
// ... (previous code)
protected $reportedBugs;
protected $assignedBugs;
private $reportedBugs;
private $assignedBugs;
public function addReportedBug(Bug $bug)
{
@@ -801,7 +1005,7 @@ the database that points from Bugs to Products.
{
// ... (previous code)
protected $products;
private $products;
public function assignToProduct(Product $product)
{
@@ -837,37 +1041,37 @@ the ``Product`` before:
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
*/
protected $description;
private $description;
/**
* @ORM\Column(type="datetime")
*/
protected $created;
private $created;
/**
* @ORM\Column(type="string")
*/
protected $status;
private $status;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="assignedBugs")
*/
protected $engineer;
private $engineer;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="reportedBugs")
*/
protected $reporter;
private $reporter;
/**
* @ORM\ManyToMany(targetEntity="Product")
*/
protected $products;
private $products;
// ... (other code)
}
@@ -875,9 +1079,9 @@ the ``Product`` before:
.. code-block:: xml
<!-- config/xml/Bug.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs">
@@ -975,25 +1179,25 @@ Finally, we'll add metadata mappings for the ``User`` entity.
* @ORM\Column(type="integer")
* @var int
*/
protected $id;
private $id;
/**
* @ORM\Column(type="string")
* @var string
*/
protected $name;
private $name;
/**
* @ORM\OneToMany(targetEntity="Bug", mappedBy="reporter")
* @var Bug[] An ArrayCollection of Bug objects.
*/
protected $reportedBugs;
private $reportedBugs;
/**
* @ORM\OneToMany(targetEntity="Bug", mappedBy="engineer")
* @var Bug[] An ArrayCollection of Bug objects.
*/
protected $assignedBugs;
private $assignedBugs;
// .. (other code)
}
@@ -1001,9 +1205,9 @@ Finally, we'll add metadata mappings for the ``User`` entity.
.. code-block:: xml
<!-- config/xml/User.dcm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="User" table="users">
@@ -1187,9 +1391,9 @@ The console output of this script is then:
use-case. Don't we use an ORM to get rid of all the endless
hand-writing of SQL? Doctrine introduces DQL which is best
described as **object-query-language** and is a dialect of
`OQL <http://en.wikipedia.org/wiki/Object_Query_Language>`_ and
`OQL <https://en.wikipedia.org/wiki/Object_Query_Language>`_ and
similar to `HQL <http://www.hibernate.org>`_ or
`JPQL <http://en.wikipedia.org/wiki/Java_Persistence_Query_Language>`_.
`JPQL <https://en.wikipedia.org/wiki/Java_Persistence_Query_Language>`_.
It does not know the concept of columns and tables, but only those
of Entity-Class and property. Using the Metadata we defined before
it allows for very short distinctive and powerful queries.
@@ -1443,7 +1647,7 @@ Entity Repositories
For now we have not discussed how to separate the Doctrine query logic from your model.
In Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this
separation. The similar concept in Doctrine2 is called Entity Repositories, integrating
the `repository pattern <http://martinfowler.com/eaaCatalog/repository.html>`_ at the heart of Doctrine.
the `repository pattern <https://martinfowler.com/eaaCatalog/repository.html>`_ at the heart of Doctrine.
Every Entity uses a default repository by default and offers a bunch of convenience
methods that you can use to query for instances of that Entity. Take for example
@@ -1539,14 +1743,14 @@ we have to adjust the metadata slightly.
**/
class Bug
{
//...
// ...
}
.. code-block:: xml
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Bug" table="bugs" repository-class="BugRepository">

View File

@@ -39,3 +39,7 @@ collection. You can disable this behavior by setting the
``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries
described are executed. We hope to automate the detection for this in
the future.
.. note::
``$fetchJoinCollection`` flag set to ``true`` might affect results if you use aggregations in your query.

View File

@@ -100,9 +100,9 @@ The code and mappings for the Market entity looks like this:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Market">
@@ -186,9 +186,9 @@ here are the code and mappings for it:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
<doctrine-mapping xmlns="https://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\StockExchange\Stock">

View File

@@ -309,7 +309,7 @@
<xs:complexType name="embedded">
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="optional" />
<xs:attribute name="column-prefix" type="xs:string" use="optional" />
<xs:attribute name="use-column-prefix" type="xs:boolean" default="true" use="optional" />
</xs:complexType>
@@ -332,7 +332,8 @@
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:attribute name="columns" type="xs:string" use="optional"/>
<xs:attribute name="fields" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
@@ -351,6 +352,7 @@
</xs:sequence>
<xs:attribute name="name" type="xs:NMTOKEN" use="optional"/>
<xs:attribute name="columns" type="xs:string" use="required"/>
<xs:attribute name="fields" type="xs:string" use="optional"/>
<xs:attribute name="flags" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other"/>
</xs:complexType>
@@ -539,7 +541,7 @@
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="target-entity" type="xs:string" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
<xs:anyAttribute namespace="##other"/>
@@ -557,7 +559,7 @@
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
</xs:sequence>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="target-entity" type="xs:string" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -21,11 +22,21 @@ namespace Doctrine\ORM;
use Countable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Cache\TimestampCacheKey;
use Doctrine\ORM\Internal\Hydration\IterableResult;
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Cache\QueryCacheKey;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Persistence\Mapping\MappingException;
use Traversable;
use function array_map;
use function array_shift;
use function count;
@@ -39,19 +50,11 @@ use function ksort;
use function reset;
use function serialize;
use function sha1;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
*/
abstract class AbstractQuery
{
@@ -60,27 +63,27 @@ abstract class AbstractQuery
/**
* Hydrates an object graph. This is the default behavior.
*/
const HYDRATE_OBJECT = 1;
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
const HYDRATE_ARRAY = 2;
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
const HYDRATE_SCALAR = 3;
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
const HYDRATE_SINGLE_SCALAR = 4;
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
const HYDRATE_SIMPLEOBJECT = 5;
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* The parameter map of this query.
@@ -93,7 +96,7 @@ abstract class AbstractQuery
/**
* The user-specified ResultSetMapping to use.
*
* @var \Doctrine\ORM\Query\ResultSetMapping
* @var ResultSetMapping
*/
protected $_resultSetMapping;
@@ -107,7 +110,7 @@ abstract class AbstractQuery
/**
* The map of query hints.
*
* @var array
* @psalm-var array<string, mixed>
*/
protected $_hints = [];
@@ -118,33 +121,27 @@ abstract class AbstractQuery
*/
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* @var \Doctrine\DBAL\Cache\QueryCacheProfile
*/
/** @var QueryCacheProfile|null */
protected $_queryCacheProfile;
/**
* Whether or not expire the result cache.
*
* @var boolean
* @var bool
*/
protected $_expireResultCache = false;
/**
* @var \Doctrine\DBAL\Cache\QueryCacheProfile
*/
/** @var QueryCacheProfile */
protected $_hydrationCacheProfile;
/**
* Whether to use second level cache, if available.
*
* @var boolean
* @var bool
*/
protected $cacheable = false;
/**
* @var boolean
*/
/** @var bool */
protected $hasCache = false;
/**
@@ -157,31 +154,25 @@ abstract class AbstractQuery
/**
* Second level query cache mode.
*
* @var integer|null
* @var int|null
*/
protected $cacheMode;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
*/
/** @var CacheLogger|null */
protected $cacheLogger;
/**
* @var integer
*/
/** @var int */
protected $lifetime = 0;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
* @param \Doctrine\ORM\EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->_em = $em;
$this->parameters = new ArrayCollection();
$this->_hints = $em->getConfiguration()->getDefaultQueryHints();
$this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
$this->_em = $em;
$this->parameters = new ArrayCollection();
$this->_hints = $em->getConfiguration()->getDefaultQueryHints();
$this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
if ($this->hasCache) {
$this->cacheLogger = $em->getConfiguration()
@@ -193,19 +184,19 @@ abstract class AbstractQuery
/**
* Enable/disable second level query (result) caching for this query.
*
* @param boolean $cacheable
* @param bool $cacheable
*
* @return static This query instance.
*/
public function setCacheable($cacheable)
{
$this->cacheable = (boolean) $cacheable;
$this->cacheable = (bool) $cacheable;
return $this;
}
/**
* @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise.
* @return bool TRUE if the query results are enable for second level cache, FALSE otherwise.
*/
public function isCacheable()
{
@@ -225,17 +216,17 @@ abstract class AbstractQuery
}
/**
* Obtain the name of the second level query cache region in which query results will be stored
*
* @return string|null The cache region name; NULL indicates the default region.
*/
* Obtain the name of the second level query cache region in which query results will be stored
*
* @return string|null The cache region name; NULL indicates the default region.
*/
public function getCacheRegion()
{
return $this->cacheRegion;
}
/**
* @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise.
* @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise.
*/
protected function isCacheEnabled()
{
@@ -243,7 +234,7 @@ abstract class AbstractQuery
}
/**
* @return integer
* @return int
*/
public function getLifetime()
{
@@ -253,19 +244,19 @@ abstract class AbstractQuery
/**
* Sets the life-time for this query into second level cache.
*
* @param integer $lifetime
* @param int $lifetime
*
* @return static This query instance.
*/
public function setLifetime($lifetime)
{
$this->lifetime = (integer) $lifetime;
$this->lifetime = (int) $lifetime;
return $this;
}
/**
* @return integer
* @return int
*/
public function getCacheMode()
{
@@ -273,13 +264,13 @@ abstract class AbstractQuery
}
/**
* @param integer $cacheMode
* @param int $cacheMode
*
* @return static This query instance.
*/
public function setCacheMode($cacheMode)
{
$this->cacheMode = (integer) $cacheMode;
$this->cacheMode = (int) $cacheMode;
return $this;
}
@@ -296,7 +287,7 @@ abstract class AbstractQuery
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return \Doctrine\ORM\EntityManager
* @return EntityManagerInterface
*/
public function getEntityManager()
{
@@ -321,6 +312,7 @@ abstract class AbstractQuery
* Get all defined parameters.
*
* @return ArrayCollection The defined query parameters.
* @psalm-return ArrayCollection<int, Parameter>
*/
public function getParameters()
{
@@ -332,14 +324,14 @@ abstract class AbstractQuery
*
* @param mixed $key The key (index or name) of the bound parameter.
*
* @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
* @return Parameter|null The value of the bound parameter, or NULL if not available.
*/
public function getParameter($key)
{
$key = Query\Parameter::normalizeName($key);
$filteredParameters = $this->parameters->filter(
function (Query\Parameter $parameter) use ($key) : bool {
static function (Query\Parameter $parameter) use ($key): bool {
$parameterName = $parameter->getName();
return $key === $parameterName;
@@ -353,10 +345,9 @@ abstract class AbstractQuery
* Sets a collection of query parameters.
*
* @param ArrayCollection|mixed[] $parameters
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*
* @return static This query instance.
*
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*/
public function setParameters($parameters)
{
@@ -409,10 +400,9 @@ abstract class AbstractQuery
* @param mixed $value
*
* @return mixed[]|string|int|float|bool
*
* @throws \Doctrine\ORM\ORMInvalidArgumentException
*
* @psalm-return array|scalar
*
* @throws ORMInvalidArgumentException
*/
public function processParameterValue($value)
{
@@ -420,15 +410,12 @@ abstract class AbstractQuery
return $value;
}
if ($value instanceof Traversable) {
if ($value instanceof Collection) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
$value = $this->processArrayParameterValue($value);
return $value;
}
@@ -447,10 +434,48 @@ abstract class AbstractQuery
if ($value === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
} catch (ORMMappingException $e) {
// Silence any mapping exceptions. These can occur if the object in
// question is not a mapped entity, in which case we just don't do
// any preparation on the value.
} catch (MappingException | ORMMappingException $e) {
/* Silence any mapping exceptions. These can occur if the object in
question is not a mapped entity, in which case we just don't do
any preparation on the value.
Depending on MappingDriver, either MappingException or
ORMMappingException is thrown. */
$value = $this->potentiallyProcessIterable($value);
}
return $value;
}
/**
* If no mapping is detected, trying to resolve the value as a Traversable
*
* @param mixed $value
*
* @return mixed
*/
private function potentiallyProcessIterable($value)
{
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
$value = $this->processArrayParameterValue($value);
}
return $value;
}
/**
* Process a parameter value which was previously identified as an array
*
* @param mixed[] $value
*
* @return mixed[]
*/
private function processArrayParameterValue(array $value): array
{
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
return $value;
@@ -459,8 +484,6 @@ abstract class AbstractQuery
/**
* Sets the ResultSetMapping that should be used for hydration.
*
* @param \Doctrine\ORM\Query\ResultSetMapping $rsm
*
* @return static This query instance.
*/
public function setResultSetMapping(Query\ResultSetMapping $rsm)
@@ -474,7 +497,7 @@ abstract class AbstractQuery
/**
* Gets the ResultSetMapping used for hydration.
*
* @return \Doctrine\ORM\Query\ResultSetMapping
* @return ResultSetMapping
*/
protected function getResultSetMapping()
{
@@ -483,18 +506,14 @@ abstract class AbstractQuery
/**
* Allows to translate entity namespaces to full qualified names.
*
* @param Query\ResultSetMapping $rsm
*
* @return void
*/
private function translateNamespaces(Query\ResultSetMapping $rsm)
private function translateNamespaces(Query\ResultSetMapping $rsm): void
{
$translate = function ($alias) : string {
$translate = function ($alias): string {
return $this->_em->getClassMetadata($alias)->getName();
};
$rsm->aliasMap = array_map($translate, $rsm->aliasMap);
$rsm->aliasMap = array_map($translate, $rsm->aliasMap);
$rsm->declaringClasses = array_map($translate, $rsm->declaringClasses);
}
@@ -510,21 +529,19 @@ abstract class AbstractQuery
* some form of caching with UnitOfWork registration you should use
* {@see AbstractQuery::setResultCacheProfile()}.
*
* @return static This query instance.
*
* @example
* $lifetime = 100;
* $resultKey = "abc";
* $query->setHydrationCacheProfile(new QueryCacheProfile());
* $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
*
* @return static This query instance.
*/
public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
public function setHydrationCacheProfile(?QueryCacheProfile $profile = null)
{
if ($profile !== null && ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_hydrationCacheProfile = $profile;
@@ -533,7 +550,7 @@ abstract class AbstractQuery
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
* @return QueryCacheProfile
*/
public function getHydrationCacheProfile()
{
@@ -546,15 +563,13 @@ abstract class AbstractQuery
* If no result cache driver is set in the QueryCacheProfile, the default
* result cache driver is used from the configuration.
*
* @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
*
* @return static This query instance.
*/
public function setResultCacheProfile(QueryCacheProfile $profile = null)
public function setResultCacheProfile(?QueryCacheProfile $profile = null)
{
if ($profile !== null && ! $profile->getResultCacheDriver()) {
$resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
$profile = $profile->setResultCacheDriver($resultCacheDriver);
$profile = $profile->setResultCacheDriver($resultCacheDriver);
}
$this->_queryCacheProfile = $profile;
@@ -573,6 +588,7 @@ abstract class AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
/** @phpstan-ignore-next-line */
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
@@ -606,9 +622,9 @@ abstract class AbstractQuery
*
* @deprecated 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.
*
* @param bool $useCache
* @param int $lifetime
* @param string $resultCacheId
* @param bool $useCache Whether or not to cache the results of this query.
* @param int $lifetime How long the cache entry is valid, in seconds.
* @param string $resultCacheId ID to use for the cache entry.
*
* @return static This query instance.
*/
@@ -628,7 +644,7 @@ abstract class AbstractQuery
*
* @return static This query instance.
*/
public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null) : self
public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null): self
{
$this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId);
@@ -641,7 +657,7 @@ abstract class AbstractQuery
*
* @return static This query instance.
*/
public function disableResultCache() : self
public function disableResultCache(): self
{
$this->_queryCacheProfile = null;
@@ -651,13 +667,13 @@ abstract class AbstractQuery
/**
* Defines how long the result cache will be active before expire.
*
* @param int|null $lifetime How long the cache entry is valid.
* @param int|null $lifetime How long the cache entry is valid, in seconds.
*
* @return static This query instance.
*/
public function setResultCacheLifetime($lifetime)
{
$lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
$lifetime = $lifetime !== null ? (int) $lifetime : 0;
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setLifetime($lifetime)
@@ -671,7 +687,7 @@ abstract class AbstractQuery
*
* @deprecated
*
* @return integer
* @return int
*/
public function getResultCacheLifetime()
{
@@ -681,7 +697,7 @@ abstract class AbstractQuery
/**
* Defines if the result cache is active or not.
*
* @param boolean $expire Whether or not to force resultset cache expiration.
* @param bool $expire Whether or not to force resultset cache expiration.
*
* @return static This query instance.
*/
@@ -695,7 +711,7 @@ abstract class AbstractQuery
/**
* Retrieves if the resultset cache is active or not.
*
* @return boolean
* @return bool
*/
public function getExpireResultCache()
{
@@ -703,7 +719,7 @@ abstract class AbstractQuery
}
/**
* @return QueryCacheProfile
* @return QueryCacheProfile|null
*/
public function getQueryCacheProfile()
{
@@ -776,7 +792,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_ARRAY).
*
* @return array
* @return mixed[]
*/
public function getArrayResult()
{
@@ -788,7 +804,7 @@ abstract class AbstractQuery
*
* Alias for execute(null, HYDRATE_SCALAR).
*
* @return array
* @return mixed[]
*/
public function getScalarResult()
{
@@ -812,17 +828,16 @@ abstract class AbstractQuery
return null;
}
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
return null;
}
if ( ! is_array($result)) {
if (! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
throw new NonUniqueResultException();
}
return array_shift($result);
@@ -841,22 +856,22 @@ abstract class AbstractQuery
* @return mixed
*
* @throws NonUniqueResultException If the query result is not unique.
* @throws NoResultException If the query returned no result and hydration mode is not HYDRATE_SINGLE_SCALAR.
* @throws NoResultException If the query returned no result.
*/
public function getSingleResult($hydrationMode = null)
{
$result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
throw new NoResultException();
}
if ( ! is_array($result)) {
if (! is_array($result)) {
return $result;
}
if (count($result) > 1) {
throw new NonUniqueResultException;
throw new NonUniqueResultException();
}
return array_shift($result);
@@ -871,6 +886,7 @@ abstract class AbstractQuery
*
* @throws NoResultException If the query returned no result.
* @throws NonUniqueResultException If the query result is not unique.
* @throws NoResultException If the query returned no result.
*/
public function getSingleScalarResult()
{
@@ -901,7 +917,7 @@ abstract class AbstractQuery
*/
public function getHint($name)
{
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
return $this->_hints[$name] ?? false;
}
/**
@@ -919,7 +935,7 @@ abstract class AbstractQuery
/**
* Return the key value map of query hints that are currently set.
*
* @return array
* @return array<string,mixed>
*/
public function getHints()
{
@@ -930,25 +946,27 @@ abstract class AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
*
* @deprecated
* @deprecated 2.8 Use {@see toIterable} instead. See https://github.com/doctrine/orm/issues/8463
*
* @param ArrayCollection|array|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
* @param ArrayCollection|mixed[]|null $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
*
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
* @return IterableResult
*/
public function iterate($parameters = null, $hydrationMode = null)
{
@trigger_error(
'Method ' . __METHOD__ . '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
E_USER_DEPRECATED
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/8463',
'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
__METHOD__
);
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if ( ! empty($parameters)) {
if (! empty($parameters)) {
$this->setParameters($parameters);
}
@@ -962,24 +980,31 @@ abstract class AbstractQuery
* Executes the query and returns an iterable that can be used to incrementally
* iterate over the result.
*
* @param ArrayCollection|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
* @param ArrayCollection|array|mixed[] $parameters The query parameters.
* @param string|int|null $hydrationMode The hydration mode to use.
* @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
*
* @return iterable<mixed>
*/
public function toIterable(iterable $parameters = [], $hydrationMode = null) : iterable
public function toIterable(iterable $parameters = [], $hydrationMode = null): iterable
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
if (($this->isCountable($parameters) && count($parameters) !== 0)
if (
($this->isCountable($parameters) && count($parameters) !== 0)
|| ($parameters instanceof Traversable && iterator_count($parameters) !== 0)
) {
$this->setParameters($parameters);
}
$rsm = $this->getResultSetMapping();
$rsm = $this->getResultSetMapping();
if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
throw QueryException::iterateWithMixedResultNotAllowed();
}
$stmt = $this->_doExecute();
return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);
@@ -988,8 +1013,9 @@ abstract class AbstractQuery
/**
* Executes the query.
*
* @param ArrayCollection|array|null $parameters Query parameters.
* @param string|int|null $hydrationMode Processing mode to be used during the hydration process.
* @param ArrayCollection|mixed[]|null $parameters Query parameters.
* @param string|int|null $hydrationMode Processing mode to be used during the hydration process.
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
*
* @return mixed
*/
@@ -1005,8 +1031,9 @@ abstract class AbstractQuery
/**
* Execute query ignoring second level cache.
*
* @param ArrayCollection|array|null $parameters
* @param string|int|null $hydrationMode
* @param ArrayCollection|mixed[]|null $parameters
* @param string|int|null $hydrationMode
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
*
* @return mixed
*/
@@ -1016,11 +1043,11 @@ abstract class AbstractQuery
$this->setHydrationMode($hydrationMode);
}
if ( ! empty($parameters)) {
if (! empty($parameters)) {
$this->setParameters($parameters);
}
$setCacheEntry = static function () : void {
$setCacheEntry = static function ($data): void {
};
if ($this->_hydrationCacheProfile !== null) {
@@ -1034,11 +1061,11 @@ abstract class AbstractQuery
return $result[$realCacheKey];
}
if ( ! $result) {
if (! $result) {
$result = [];
}
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) : void {
$setCacheEntry = static function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile): void {
$result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
@@ -1064,8 +1091,9 @@ abstract class AbstractQuery
/**
* Load from second level cache or executes the query and put into cache.
*
* @param ArrayCollection|array|null $parameters
* @param string|int|null $hydrationMode
* @param ArrayCollection|mixed[]|null $parameters
* @param string|int|null $hydrationMode
* @psalm-param ArrayCollection<int, Parameter>|mixed[]|null $parameters
*
* @return mixed
*/
@@ -1080,7 +1108,7 @@ abstract class AbstractQuery
$this->getTimestampKey()
);
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
$result = $queryCache->get($queryKey, $rsm, $this->_hints);
if ($result !== null) {
if ($this->cacheLogger) {
@@ -1104,10 +1132,7 @@ abstract class AbstractQuery
return $result;
}
/**
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
*/
private function getTimestampKey()
private function getTimestampKey(): ?TimestampCacheKey
{
$entityName = reset($this->_resultSetMapping->aliasMap);
@@ -1178,7 +1203,9 @@ abstract class AbstractQuery
/**
* Executes the query and returns a the resulting Statement object.
*
* @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
* @return ResultStatement|int The executed database statement that holds
* the results, or an integer indicating how
* many rows were affected.
*/
abstract protected function _doExecute();
@@ -1204,10 +1231,12 @@ abstract class AbstractQuery
{
$query = $this->getSQL();
$hints = $this->getHints();
$params = array_map(function(Parameter $parameter) {
$params = array_map(function (Parameter $parameter) {
$value = $parameter->getValue();
// Small optimization
// Does not invoke processParameterValue for scalar values
if (is_scalar($value = $parameter->getValue())) {
// Does not invoke processParameterValue for scalar value
if (is_scalar($value)) {
return $value;
}
@@ -1220,7 +1249,7 @@ abstract class AbstractQuery
}
/** @param iterable<mixed> $subject */
private function isCountable(iterable $subject) : bool
private function isCountable(iterable $subject): bool
{
return $subject instanceof Countable || is_array($subject);
}

View File

@@ -20,44 +20,44 @@
namespace Doctrine\ORM;
use Doctrine\ORM\Cache\QueryCache;
use Doctrine\ORM\Cache\Region;
/**
* Provides an API for querying/managing the second level cache regions.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface Cache
{
const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
public const DEFAULT_QUERY_REGION_NAME = 'query_cache_region';
const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
public const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region';
/**
* May read items from the cache, but will not add items.
*/
const MODE_GET = 1;
public const MODE_GET = 1;
/**
* Will never read items from the cache,
* but will add items to the cache as it reads them from the database.
*/
const MODE_PUT = 2;
public const MODE_PUT = 2;
/**
* May read items from the cache, and add items to the cache.
*/
const MODE_NORMAL = 3;
public const MODE_NORMAL = 3;
/**
* The query will never read items from the cache,
* but will refresh items to the cache as it reads them from the database.
*/
const MODE_REFRESH = 4;
public const MODE_REFRESH = 4;
/**
* @param string $className The entity class.
*
* @return \Doctrine\ORM\Cache\Region|null
* @return Region|null
*/
public function getEntityCacheRegion($className);
@@ -65,7 +65,7 @@ interface Cache
* @param string $className The entity class.
* @param string $association The field name that represents the association.
*
* @return \Doctrine\ORM\Cache\Region|null
* @return Region|null
*/
public function getCollectionCacheRegion($className, $association);
@@ -75,7 +75,7 @@ interface Cache
* @param string $className The entity class.
* @param mixed $identifier The entity identifier
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsEntity($className, $identifier);
@@ -112,7 +112,7 @@ interface Cache
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsCollection($className, $association, $ownerIdentifier);
@@ -149,7 +149,7 @@ interface Cache
*
* @param string $regionName The cache name given to the query.
*
* @return boolean true if the underlying cache contains corresponding data; false otherwise.
* @return bool true if the underlying cache contains corresponding data; false otherwise.
*/
public function containsQuery($regionName);
@@ -172,7 +172,7 @@ interface Cache
*
* @param string|null $regionName Query cache region name, or default query cache if the region name is NULL.
*
* @return \Doctrine\ORM\Cache\QueryCache The Query Cache associated with the region name.
* @return QueryCache The Query Cache associated with the region name.
*/
public function getQueryCache($regionName = null);
}

View File

@@ -22,16 +22,13 @@ namespace Doctrine\ORM\Cache;
/**
* Association cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class AssociationCacheEntry implements CacheEntry
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity identifier
* @var array<string, mixed> The entity identifier
*/
public $identifier;
@@ -43,13 +40,13 @@ class AssociationCacheEntry implements CacheEntry
public $class;
/**
* @param string $class The entity class.
* @param array $identifier The entity identifier.
* @param string $class The entity class.
* @param array<string, mixed> $identifier The entity identifier.
*/
public function __construct($class, array $identifier)
{
$this->class = $class;
$this->identifier = $identifier;
$this->class = $class;
$this->identifier = $identifier;
}
/**
@@ -57,7 +54,7 @@ class AssociationCacheEntry implements CacheEntry
*
* This method allow Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string, mixed> $values array containing property values
*
* @return AssociationCacheEntry
*/

View File

@@ -24,34 +24,23 @@ use Doctrine\ORM\Cache\Logging\CacheLogger;
/**
* Configuration container for second-level cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheConfiguration
{
/**
* @var \Doctrine\ORM\Cache\CacheFactory|null
*/
/** @var CacheFactory|null */
private $cacheFactory;
/**
* @var \Doctrine\ORM\Cache\RegionsConfiguration|null
*/
/** @var RegionsConfiguration|null */
private $regionsConfig;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
*/
/** @var CacheLogger|null */
private $cacheLogger;
/**
* @var \Doctrine\ORM\Cache\QueryCacheValidator|null
*/
/** @var QueryCacheValidator|null */
private $queryValidator;
/**
* @return \Doctrine\ORM\Cache\CacheFactory|null
* @return CacheFactory|null
*/
public function getCacheFactory()
{
@@ -59,8 +48,6 @@ class CacheConfiguration
}
/**
* @param \Doctrine\ORM\Cache\CacheFactory $factory
*
* @return void
*/
public function setCacheFactory(CacheFactory $factory)
@@ -69,7 +56,7 @@ class CacheConfiguration
}
/**
* @return \Doctrine\ORM\Cache\Logging\CacheLogger|null
* @return CacheLogger|null
*/
public function getCacheLogger()
{
@@ -77,7 +64,7 @@ class CacheConfiguration
}
/**
* @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger
* @return void
*/
public function setCacheLogger(CacheLogger $logger)
{
@@ -85,7 +72,7 @@ class CacheConfiguration
}
/**
* @return \Doctrine\ORM\Cache\RegionsConfiguration
* @return RegionsConfiguration
*/
public function getRegionsConfiguration()
{
@@ -97,7 +84,7 @@ class CacheConfiguration
}
/**
* @param \Doctrine\ORM\Cache\RegionsConfiguration $regionsConfig
* @return void
*/
public function setRegionsConfiguration(RegionsConfiguration $regionsConfig)
{
@@ -105,7 +92,7 @@ class CacheConfiguration
}
/**
* @return \Doctrine\ORM\Cache\QueryCacheValidator
* @return QueryCacheValidator
*/
public function getQueryValidator()
{
@@ -119,7 +106,7 @@ class CacheConfiguration
}
/**
* @param \Doctrine\ORM\Cache\QueryCacheValidator $validator
* @return void
*/
public function setQueryValidator(QueryCacheValidator $validator)
{

View File

@@ -26,11 +26,7 @@ namespace Doctrine\ORM\Cache;
* <b>IMPORTANT NOTE:</b>
*
* Fields of classes that implement CacheEntry are public for performance reason.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheEntry
{
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -21,11 +22,10 @@ namespace Doctrine\ORM\Cache;
use Doctrine\ORM\ORMException;
use function sprintf;
/**
* Exception for cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheException extends ORMException
{
@@ -33,7 +33,7 @@ class CacheException extends ORMException
* @param string $sourceEntity
* @param string $fieldName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function updateReadOnlyCollection($sourceEntity, $fieldName)
{
@@ -43,7 +43,7 @@ class CacheException extends ORMException
/**
* @param string $entityName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function updateReadOnlyEntity($entityName)
{
@@ -53,7 +53,7 @@ class CacheException extends ORMException
/**
* @param string $entityName
*
* @return \Doctrine\ORM\Cache\CacheException
* @return CacheException
*/
public static function nonCacheableEntity($entityName)
{

View File

@@ -20,93 +20,91 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
/**
* Contract for building second level cache regions components.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheFactory
{
/**
* Build an entity persister for the given entity metadata.
*
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister that will be cached.
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param EntityManagerInterface $em The entity manager.
* @param EntityPersister $persister The entity persister that will be cached.
* @param ClassMetadata $metadata The entity metadata.
*
* @return \Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister
* @return CachedEntityPersister
*/
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata);
/**
* Build a collection persister for the given relation mapping.
*
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached.
* @param array $mapping The association mapping.
* @param EntityManagerInterface $em The entity manager.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param mixed[] $mapping The association mapping.
*
* @return \Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister
* @return CachedCollectionPersister
*/
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping);
/**
* Build a query cache based on the given region name
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param string $regionName The region name.
* @param EntityManagerInterface $em The Entity manager.
* @param string $regionName The region name.
*
* @return \Doctrine\ORM\Cache\QueryCache The built query cache.
* @return QueryCache The built query cache.
*/
public function buildQueryCache(EntityManagerInterface $em, $regionName = null);
/**
* Build an entity hydrator
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param EntityManagerInterface $em The Entity manager.
* @param ClassMetadata $metadata The entity metadata.
*
* @return \Doctrine\ORM\Cache\EntityHydrator The built entity hydrator.
* @return EntityHydrator The built entity hydrator.
*/
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata);
/**
* Build a collection hydrator
*
* @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager.
* @param array $mapping The association mapping.
* @param EntityManagerInterface $em The Entity manager.
* @param mixed[] $mapping The association mapping.
*
* @return \Doctrine\ORM\Cache\CollectionHydrator The built collection hydrator.
* @return CollectionHydrator The built collection hydrator.
*/
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping);
/**
* Build a cache region
*
* @param array $cache The cache configuration.
* @param array<string,mixed> $cache The cache configuration.
*
* @return \Doctrine\ORM\Cache\Region The cache region.
* @return Region The cache region.
*/
public function getRegion(array $cache);
/**
* Build timestamp cache region
*
* @return \Doctrine\ORM\Cache\TimestampRegion The timestamp region.
* @return TimestampRegion The timestamp region.
*/
public function getTimestampRegion();
/**
* Build \Doctrine\ORM\Cache
*
* @param EntityManagerInterface $entityManager
*
* @return \Doctrine\ORM\Cache
* @return Cache
*/
public function createCache(EntityManagerInterface $entityManager);
}

View File

@@ -23,9 +23,6 @@ namespace Doctrine\ORM\Cache;
/**
* Defines entity / collection / query key to be stored in the cache region.
* Allows multiple roles to be stored in the same cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
abstract class CacheKey
{

View File

@@ -22,9 +22,6 @@ namespace Doctrine\ORM\Cache;
/**
* Collection cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CollectionCacheEntry implements CacheEntry
{
@@ -48,7 +45,7 @@ class CollectionCacheEntry implements CacheEntry
*
* This method allows for Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string, mixed> $values array containing property values
*
* @return CollectionCacheEntry
*/

View File

@@ -20,18 +20,20 @@
namespace Doctrine\ORM\Cache;
use function implode;
use function ksort;
use function str_replace;
use function strtolower;
/**
* Defines entity collection roles to be stored in the cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CollectionCacheKey extends CacheKey
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The owner entity identifier
* @var array<string, mixed> The owner entity identifier
*/
public $ownerIdentifier;
@@ -50,17 +52,17 @@ class CollectionCacheKey extends CacheKey
public $association;
/**
* @param string $entityClass The entity class.
* @param string $association The field name that represents the association.
* @param array $ownerIdentifier The identifier of the owning entity.
* @param string $entityClass The entity class.
* @param string $association The field name that represents the association.
* @param array<string, mixed> $ownerIdentifier The identifier of the owning entity.
*/
public function __construct($entityClass, $association, array $ownerIdentifier)
{
ksort($ownerIdentifier);
$this->ownerIdentifier = $ownerIdentifier;
$this->entityClass = (string) $entityClass;
$this->association = (string) $association;
$this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association;
$this->ownerIdentifier = $ownerIdentifier;
$this->entityClass = (string) $entityClass;
$this->association = (string) $association;
$this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association;
}
}

View File

@@ -20,33 +20,31 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
/**
* Hydrator cache entry for collections
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CollectionHydrator
{
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key.
* @param array|\Doctrine\Common\Collections\Collection $collection The collection.
* @param ClassMetadata $metadata The entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param array|mixed[]|Collection $collection The collection.
*
* @return \Doctrine\ORM\Cache\CollectionCacheEntry
* @return CollectionCacheEntry
*/
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection);
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The owning entity metadata.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key.
* @param \Doctrine\ORM\Cache\CollectionCacheEntry $entry The cached collection entry.
* @param \Doctrine\ORM\PersistentCollection $collection The collection to load the cache into.
* @param ClassMetadata $metadata The owning entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param CollectionCacheEntry $entry The cached collection entry.
* @param PersistentCollection $collection The collection to load the cache into.
*
* @return array
* @return mixed[]
*/
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection);
}

View File

@@ -26,32 +26,29 @@ namespace Doctrine\ORM\Cache;
*
* When a entry is locked another process should not be able to read or write the entry.
* All evict operation should not consider locks, even though an entry is locked evict should be able to delete the entry and its lock.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface ConcurrentRegion extends Region
{
/**
* Attempts to read lock the mapping for the given key.
*
* @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock.
* @param CacheKey $key The key of the item to lock.
*
* @return \Doctrine\ORM\Cache\Lock A lock instance or NULL if the lock already exists.
* @return Lock A lock instance or NULL if the lock already exists.
*
* @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region.
* @throws LockException Indicates a problem accessing the region.
*/
public function lock(CacheKey $key);
/**
* Attempts to read unlock the mapping for the given key.
*
* @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to unlock.
* @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained from {@link readLock}
* @param CacheKey $key The key of the item to unlock.
* @param Lock $lock The lock previously obtained from {@link readLock}
*
* @return void
*
* @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region.
* @throws LockException Indicates a problem accessing the region.
*/
public function unlock(CacheKey $key, Lock $lock);
}

View File

@@ -20,44 +20,35 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMInvalidArgumentException;
use Doctrine\ORM\UnitOfWork;
use function is_array;
use function is_object;
/**
* Provides an API for querying/managing the second level cache regions.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCache implements Cache
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var \Doctrine\ORM\Cache\CacheFactory
*/
/** @var CacheFactory */
private $cacheFactory;
/**
* @var \Doctrine\ORM\Cache\QueryCache[]
*/
/** @var QueryCache[] */
private $queryCaches = [];
/**
* @var \Doctrine\ORM\Cache\QueryCache
*/
/** @var QueryCache|null */
private $defaultQueryCache;
public function __construct(EntityManagerInterface $em)
@@ -77,7 +68,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return null;
}
@@ -92,7 +83,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return null;
}
@@ -104,10 +95,10 @@ class DefaultCache implements Cache
*/
public function containsEntity($className, $identifier)
{
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return false;
}
@@ -122,7 +113,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -137,7 +128,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -154,7 +145,7 @@ class DefaultCache implements Cache
foreach ($metadatas as $metadata) {
$persister = $this->uow->getEntityPersister($metadata->rootEntityName);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
continue;
}
@@ -170,7 +161,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return false;
}
@@ -185,7 +176,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -200,7 +191,7 @@ class DefaultCache implements Cache
$metadata = $this->em->getClassMetadata($className);
$persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association));
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
return;
}
@@ -215,16 +206,14 @@ class DefaultCache implements Cache
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
foreach ($metadatas as $metadata) {
foreach ($metadata->associationMappings as $association) {
if ( ! $association['type'] & ClassMetadata::TO_MANY) {
if (! $association['type'] & ClassMetadata::TO_MANY) {
continue;
}
$persister = $this->uow->getCollectionPersister($association);
if ( ! ($persister instanceof CachedPersister)) {
if (! ($persister instanceof CachedPersister)) {
continue;
}
@@ -279,7 +268,7 @@ class DefaultCache implements Cache
$this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em);
}
if ( ! isset($this->queryCaches[$regionName])) {
if (! isset($this->queryCaches[$regionName])) {
$this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName);
}
@@ -287,14 +276,12 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*
* @return \Doctrine\ORM\Cache\EntityCacheKey
*/
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier)
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*/
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): EntityCacheKey
{
if ( ! is_array($identifier)) {
if (! is_array($identifier)) {
$identifier = $this->toIdentifierArray($metadata, $identifier);
}
@@ -302,15 +289,16 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*
* @return \Doctrine\ORM\Cache\CollectionCacheKey
* @param ClassMetadata $metadata The entity metadata.
* @param string $association The field name that represents the association.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*/
private function buildCollectionCacheKey(ClassMetadata $metadata, $association, $ownerIdentifier)
{
if ( ! is_array($ownerIdentifier)) {
private function buildCollectionCacheKey(
ClassMetadata $metadata,
string $association,
$ownerIdentifier
): CollectionCacheKey {
if (! is_array($ownerIdentifier)) {
$ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);
}
@@ -318,12 +306,12 @@ class DefaultCache implements Cache
}
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
*
* @return array
* @return array<string, mixed>
*/
private function toIdentifierArray(ClassMetadata $metadata, $identifier)
private function toIdentifierArray(ClassMetadata $metadata, $identifier): array
{
if (is_object($identifier) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($identifier))) {
$identifier = $this->uow->getSingleIdentifierValue($identifier);
@@ -335,5 +323,4 @@ class DefaultCache implements Cache
return [$metadata->identifier[0] => $identifier];
}
}

View File

@@ -38,42 +38,30 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use InvalidArgumentException;
use LogicException;
use function sprintf;
use const DIRECTORY_SEPARATOR;
/**
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCacheFactory implements CacheFactory
{
/**
* @var CacheAdapter
*/
/** @var CacheAdapter */
private $cache;
/**
* @var \Doctrine\ORM\Cache\RegionsConfiguration
*/
/** @var RegionsConfiguration */
private $regionsConfig;
/**
* @var \Doctrine\ORM\Cache\TimestampRegion|null
*/
/** @var TimestampRegion|null */
private $timestampRegion;
/**
* @var \Doctrine\ORM\Cache\Region[]
*/
/** @var Region[] */
private $regions = [];
/**
* @var string|null
*/
/** @var string|null */
private $fileLockRegionDirectory;
/**
* @param RegionsConfiguration $cacheConfig
* @param CacheAdapter $cache
*/
public function __construct(RegionsConfiguration $cacheConfig, CacheAdapter $cache)
{
$this->cache = $cache;
@@ -82,6 +70,8 @@ class DefaultCacheFactory implements CacheFactory
/**
* @param string $fileLockRegionDirectory
*
* @return void
*/
public function setFileLockRegionDirectory($fileLockRegionDirectory)
{
@@ -97,7 +87,7 @@ class DefaultCacheFactory implements CacheFactory
}
/**
* @param \Doctrine\ORM\Cache\Region $region
* @return void
*/
public function setRegion(Region $region)
{
@@ -105,7 +95,7 @@ class DefaultCacheFactory implements CacheFactory
}
/**
* @param \Doctrine\ORM\Cache\TimestampRegion $region
* @return void
*/
public function setTimestampRegion(TimestampRegion $region)
{
@@ -117,8 +107,8 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata)
{
$region = $this->getRegion($metadata->cache);
$usage = $metadata->cache['usage'];
$region = $this->getRegion($metadata->cache);
$usage = $metadata->cache['usage'];
if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {
return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata);
@@ -129,10 +119,14 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata);
}
throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage));
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
}
/**
@@ -140,8 +134,8 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping)
{
$usage = $mapping['cache']['usage'];
$region = $this->getRegion($mapping['cache']);
$usage = $mapping['cache']['usage'];
$region = $this->getRegion($mapping['cache']);
if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) {
return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping);
@@ -152,10 +146,14 @@ class DefaultCacheFactory implements CacheFactory
}
if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (! $region instanceof ConcurrentRegion) {
throw new InvalidArgumentException(sprintf('Unable to use access strategy type of [%s] without a ConcurrentRegion', $usage));
}
return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping);
}
throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage));
throw new InvalidArgumentException(sprintf('Unrecognized access strategy type [%s]', $usage));
}
/**
@@ -168,7 +166,7 @@ class DefaultCacheFactory implements CacheFactory
$this->getRegion(
[
'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME,
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE
'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE,
]
)
);
@@ -203,45 +201,39 @@ class DefaultCacheFactory implements CacheFactory
$cacheAdapter = $this->createRegionCache($name);
$lifetime = $this->regionsConfig->getLifetime($cache['region']);
$region = ($cacheAdapter instanceof MultiGetCache)
$region = $cacheAdapter instanceof MultiGetCache
? new DefaultMultiGetRegion($name, $cacheAdapter, $lifetime)
: new DefaultRegion($name, $cacheAdapter, $lifetime);
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {
if (
'' === $this->fileLockRegionDirectory ||
null === $this->fileLockRegionDirectory
$this->fileLockRegionDirectory === '' ||
$this->fileLockRegionDirectory === null
) {
throw new \LogicException(
throw new LogicException(
'If you want to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' .
'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you want to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). '
);
}
$directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region'];
$region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region']));
$region = new FileLockRegion($region, $directory, (string) $this->regionsConfig->getLockLifetime($cache['region']));
}
return $this->regions[$cache['region']] = $region;
}
/**
* @param string $name
*
* @return CacheAdapter
*/
private function createRegionCache($name)
private function createRegionCache(string $name): CacheAdapter
{
$cacheAdapter = clone $this->cache;
if (!$cacheAdapter instanceof CacheProvider) {
if (! $cacheAdapter instanceof CacheProvider) {
return $cacheAdapter;
}
$namespace = $cacheAdapter->getNamespace();
if ('' !== $namespace) {
if ($namespace !== '') {
$namespace .= ':';
}

View File

@@ -20,36 +20,32 @@
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Query;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use function array_walk;
use function assert;
/**
* Default hydrator cache for collections
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultCollectionHydrator implements CollectionHydrator
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param EntityManagerInterface $em The entity manager.
*/
public function __construct(EntityManagerInterface $em)
{
@@ -77,23 +73,23 @@ class DefaultCollectionHydrator implements CollectionHydrator
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection)
{
$assoc = $metadata->associationMappings[$key->association];
/* @var $targetPersister \Doctrine\ORM\Cache\Persister\CachedPersister */
$targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
$targetRegion = $targetPersister->getCacheRegion();
$list = [];
assert($targetPersister instanceof CachedPersister);
$targetRegion = $targetPersister->getCacheRegion();
$list = [];
/** @var EntityCacheEntry[]|null $entityEntries */
$entityEntries = $targetRegion->getMultiple($entry);
if ($entityEntries === null) {
return null;
}
/* @var $entityEntries \Doctrine\ORM\Cache\EntityCacheEntry[] */
foreach ($entityEntries as $index => $entityEntry) {
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
}
array_walk($list, function($entity, $index) use ($collection) {
array_walk($list, static function ($entity, $index) use ($collection) {
$collection->hydrateSet($index, $entity);
});

View File

@@ -21,49 +21,45 @@
namespace Doctrine\ORM\Cache;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Query;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Utility\IdentifierFlattener;
use function array_merge;
use function is_array;
use function is_object;
use function reset;
/**
* Default hydrator cache for entities
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultEntityHydrator implements EntityHydrator
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* The IdentifierFlattener used for manipulating identifiers
*
* @var \Doctrine\ORM\Utility\IdentifierFlattener
* @var IdentifierFlattener
*/
private $identifierFlattener;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param EntityManagerInterface $em The entity manager.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->uow = $em->getUnitOfWork();
$this->em = $em;
$this->uow = $em->getUnitOfWork();
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
}
@@ -80,19 +76,19 @@ class DefaultEntityHydrator implements EntityHydrator
}
foreach ($metadata->associationMappings as $name => $assoc) {
if ( ! isset($data[$name])) {
if (! isset($data[$name])) {
continue;
}
if ( ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
if (! ($assoc['type'] & ClassMetadata::TO_ONE)) {
unset($data[$name]);
continue;
}
if ( ! isset($assoc['cache'])) {
if (! isset($assoc['cache'])) {
$targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']);
$owningAssociation = ( ! $assoc['isOwningSide'])
$owningAssociation = ! $assoc['isOwningSide']
? $targetClassMetadata->associationMappings[$assoc['mappedBy']]
: $assoc;
$associationIds = $this->identifierFlattener->flattenIdentifier(
@@ -113,7 +109,7 @@ class DefaultEntityHydrator implements EntityHydrator
$targetAssoc = $targetClassMetadata->associationMappings[$fieldName];
foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) {
foreach ($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) {
if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) {
$data[$localColumn] = $fieldValue;
}
@@ -123,7 +119,7 @@ class DefaultEntityHydrator implements EntityHydrator
continue;
}
if ( ! isset($assoc['id'])) {
if (! isset($assoc['id'])) {
$targetClass = ClassUtils::getClass($data[$name]);
$targetId = $this->uow->getEntityIdentifier($data[$name]);
$data[$name] = new AssociationCacheEntry($targetClass, $targetId);
@@ -138,7 +134,7 @@ class DefaultEntityHydrator implements EntityHydrator
// @TODO - fix it !
// handle UnitOfWork#createEntity hash generation
if ( ! is_array($targetId)) {
if (! is_array($targetId)) {
$data[reset($assoc['joinColumnFieldNames'])] = $targetId;
$targetEntity = $this->em->getClassMetadata($assoc['targetEntity']);
@@ -160,20 +156,20 @@ class DefaultEntityHydrator implements EntityHydrator
$hints = self::$hints;
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
$hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
}
foreach ($metadata->associationMappings as $name => $assoc) {
if ( ! isset($assoc['cache']) || ! isset($data[$name])) {
if (! isset($assoc['cache']) || ! isset($data[$name])) {
continue;
}
$assocClass = $data[$name]->class;
$assocId = $data[$name]->identifier;
$isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide']));
$assocClass = $data[$name]->class;
$assocId = $data[$name]->identifier;
$isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide']));
if ( ! $isEagerLoad) {
if (! $isEagerLoad) {
$data[$name] = $this->em->getReference($assocClass, $assocId);
continue;

View File

@@ -21,68 +21,63 @@
namespace Doctrine\ORM\Cache;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\UnitOfWork;
use function array_key_exists;
use function array_map;
use function array_shift;
use function array_unshift;
use function assert;
use function count;
use function is_array;
use function key;
use function reset;
/**
* Default query cache implementation.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class DefaultQueryCache implements QueryCache
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
/** @var EntityManagerInterface */
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
private $uow;
/**
* @var \Doctrine\ORM\Cache\Region
*/
/** @var Region */
private $region;
/**
* @var \Doctrine\ORM\Cache\QueryCacheValidator
*/
/** @var QueryCacheValidator */
private $validator;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
*/
/** @var CacheLogger */
protected $cacheLogger;
/**
* @var array
*/
/** @var array<string,mixed> */
private static $hints = [Query::HINT_CACHE_ENABLED => true];
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param \Doctrine\ORM\Cache\Region $region The query region.
* @param EntityManagerInterface $em The entity manager.
* @param Region $region The query region.
*/
public function __construct(EntityManagerInterface $em, Region $region)
{
$cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration();
$this->em = $em;
$this->region = $region;
$this->uow = $em->getUnitOfWork();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->validator = $cacheConfig->getQueryValidator();
$this->em = $em;
$this->region = $region;
$this->uow = $em->getUnitOfWork();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->validator = $cacheConfig->getQueryValidator();
}
/**
@@ -90,17 +85,17 @@ class DefaultQueryCache implements QueryCache
*/
public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = [])
{
if ( ! ($key->cacheMode & Cache::MODE_GET)) {
if (! ($key->cacheMode & Cache::MODE_GET)) {
return null;
}
$cacheEntry = $this->region->get($key);
if ( ! $cacheEntry instanceof QueryCacheEntry) {
if (! $cacheEntry instanceof QueryCacheEntry) {
return null;
}
if ( ! $this->validator->isValid($key, $cacheEntry)) {
if (! $this->validator->isValid($key, $cacheEntry)) {
$this->region->evict($key);
return null;
@@ -117,7 +112,7 @@ class DefaultQueryCache implements QueryCache
$cm = $this->em->getClassMetadata($entityName);
$generateKeys = static function (array $entry) use ($cm) : EntityCacheKey {
$generateKeys = static function (array $entry) use ($cm): EntityCacheKey {
return new EntityCacheKey($cm->rootEntityName, $entry['identifier']);
};
@@ -140,8 +135,8 @@ class DefaultQueryCache implements QueryCache
$this->cacheLogger->entityCacheHit($regionName, $cacheKeys->identifiers[$index]);
}
if ( ! $hasRelation) {
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
if (! $hasRelation) {
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
continue;
}
@@ -156,9 +151,10 @@ class DefaultQueryCache implements QueryCache
$assocMetadata = $this->em->getClassMetadata($assoc['targetEntity']);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']);
$assocEntry = $assocRegion->get($assocKey);
if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assoc['identifier']))) === null) {
if ($assocEntry === null) {
if ($this->cacheLogger !== null) {
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
}
@@ -177,11 +173,11 @@ class DefaultQueryCache implements QueryCache
continue;
}
if ( ! isset($assoc['list']) || empty($assoc['list'])) {
if (! isset($assoc['list']) || empty($assoc['list'])) {
continue;
}
$generateKeys = function ($id) use ($assocMetadata): EntityCacheKey {
$generateKeys = static function ($id) use ($assocMetadata): EntityCacheKey {
return new EntityCacheKey($assocMetadata->rootEntityName, $id);
};
@@ -249,29 +245,29 @@ class DefaultQueryCache implements QueryCache
public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = [])
{
if ($rsm->scalarMappings) {
throw new CacheException("Second level cache does not support scalar results.");
throw new CacheException('Second level cache does not support scalar results.');
}
if (count($rsm->entityMappings) > 1) {
throw new CacheException("Second level cache does not support multiple root entities.");
throw new CacheException('Second level cache does not support multiple root entities.');
}
if ( ! $rsm->isSelect) {
throw new CacheException("Second-level cache query supports only select statements.");
if (! $rsm->isSelect) {
throw new CacheException('Second-level cache query supports only select statements.');
}
if (($hints[Query\SqlWalker::HINT_PARTIAL] ?? false) === true || ($hints[Query::HINT_FORCE_PARTIAL_LOAD] ?? false) === true) {
throw new CacheException("Second level cache does not support partial entities.");
throw new CacheException('Second level cache does not support partial entities.');
}
if ( ! ($key->cacheMode & Cache::MODE_PUT)) {
if (! ($key->cacheMode & Cache::MODE_PUT)) {
return false;
}
$data = [];
$entityName = reset($rsm->aliasMap);
$rootAlias = key($rsm->aliasMap);
$persister = $this->uow->getEntityPersister($entityName);
$data = [];
$entityName = reset($rsm->aliasMap);
$rootAlias = key($rsm->aliasMap);
$persister = $this->uow->getEntityPersister($entityName);
if (! $persister instanceof CachedEntityPersister) {
throw CacheException::nonCacheableEntity($entityName);
@@ -288,7 +284,7 @@ class DefaultQueryCache implements QueryCache
if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey)) {
// Cancel put result if entity put fail
if ( ! $persister->storeEntityCache($entity, $entityKey)) {
if (! $persister->storeEntityCache($entity, $entityKey)) {
return false;
}
}
@@ -298,11 +294,11 @@ class DefaultQueryCache implements QueryCache
// @TODO - move to cache hydration components
foreach ($rsm->relationMap as $alias => $name) {
$parentAlias = $rsm->parentAliasMap[$alias];
$parentClass = $rsm->aliasMap[$parentAlias];
$metadata = $this->em->getClassMetadata($parentClass);
$assoc = $metadata->associationMappings[$name];
$assocValue = $this->getAssociationValue($rsm, $alias, $entity);
$parentAlias = $rsm->parentAliasMap[$alias];
$parentClass = $rsm->aliasMap[$parentAlias];
$metadata = $this->em->getClassMetadata($parentClass);
$assoc = $metadata->associationMappings[$name];
$assocValue = $this->getAssociationValue($rsm, $alias, $entity);
if ($assocValue === null) {
continue;
@@ -311,7 +307,8 @@ class DefaultQueryCache implements QueryCache
// root entity association
if ($rootAlias === $parentAlias) {
// Cancel put result if association put fail
if ( ($assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue)) === null) {
$assocInfo = $this->storeAssociationCache($key, $assoc, $assocValue);
if ($assocInfo === null) {
return false;
}
@@ -321,7 +318,7 @@ class DefaultQueryCache implements QueryCache
}
// store single nested association
if ( ! is_array($assocValue)) {
if (! is_array($assocValue)) {
// Cancel put result if association put fail
if ($this->storeAssociationCache($key, $assoc, $assocValue) === null) {
return false;
@@ -344,15 +341,13 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param \Doctrine\ORM\Cache\QueryCacheKey $key
* @param array $assoc
* @param mixed $assocValue
* @param array<string,mixed> $assoc
* @param mixed $assocValue
*
* @return mixed[]|null
*
* @psalm-return array{targetEntity: string, type: mixed, list?: array[], identifier?: array}|null
* @psalm-return array{targetEntity: class-string, type: mixed, list?: array[], identifier?: array}|null
*/
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue)
private function storeAssociationCache(QueryCacheKey $key, array $assoc, $assocValue): ?array
{
$assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
$assocMetadata = $assocPersister->getClassMetadata();
@@ -363,9 +358,9 @@ class DefaultQueryCache implements QueryCache
$assocIdentifier = $this->uow->getEntityIdentifier($assocValue);
$entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier);
if ( ! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
if (! $assocValue instanceof Proxy && ($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
// Entity put fail
if ( ! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
if (! $assocPersister->storeEntityCache($assocValue, $entityKey)) {
return null;
}
}
@@ -373,7 +368,7 @@ class DefaultQueryCache implements QueryCache
return [
'targetEntity' => $assocMetadata->rootEntityName,
'identifier' => $assocIdentifier,
'type' => $assoc['type']
'type' => $assoc['type'],
];
}
@@ -386,7 +381,7 @@ class DefaultQueryCache implements QueryCache
if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey)) {
// Entity put fail
if ( ! $assocPersister->storeEntityCache($assocItem, $entityKey)) {
if (! $assocPersister->storeEntityCache($assocItem, $entityKey)) {
return null;
}
}
@@ -402,14 +397,15 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param \Doctrine\ORM\Query\ResultSetMapping $rsm
* @param string $assocAlias
* @param object $entity
* @param object $entity
*
* @return array|object
* @return array<object>|object
*/
private function getAssociationValue(ResultSetMapping $rsm, $assocAlias, $entity)
{
private function getAssociationValue(
ResultSetMapping $rsm,
string $assocAlias,
$entity
) {
$path = [];
$alias = $assocAlias;
@@ -420,9 +416,8 @@ class DefaultQueryCache implements QueryCache
array_unshift($path, [
'field' => $field,
'class' => $class
]
);
'class' => $class,
]);
$alias = $parent;
}
@@ -431,10 +426,10 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param mixed $value
* @param array $path
* @param mixed $value
* @param array<mixed> $path
*
* @return array|object|null
* @return mixed
*/
private function getAssociationPathValue($value, array $path)
{
@@ -447,7 +442,7 @@ class DefaultQueryCache implements QueryCache
return null;
}
if (empty($path)) {
if ($path === []) {
return $value;
}

View File

@@ -22,18 +22,17 @@ namespace Doctrine\ORM\Cache;
use Doctrine\ORM\EntityManagerInterface;
use function array_map;
/**
* Entity cache entry
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class EntityCacheEntry implements CacheEntry
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity map data
* @var array<string,mixed> The entity map data
*/
public $data;
@@ -41,12 +40,14 @@ class EntityCacheEntry implements CacheEntry
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var string The entity class name
* @psalm-var class-string
*/
public $class;
/**
* @param string $class The entity class.
* @param array $data The entity data.
* @param string $class The entity class.
* @param array<string,mixed> $data The entity data.
* @psalm-param class-string $class
*/
public function __construct($class, array $data)
{
@@ -59,7 +60,7 @@ class EntityCacheEntry implements CacheEntry
*
* This method allow Doctrine\Common\Cache\PhpFileCache compatibility
*
* @param array $values array containing property values
* @param array<string,mixed> $values array containing property values
*
* @return EntityCacheEntry
*/
@@ -71,14 +72,12 @@ class EntityCacheEntry implements CacheEntry
/**
* Retrieves the entity data resolving cache entries
*
* @param \Doctrine\ORM\EntityManagerInterface $em
*
* @return array
* @return array<string, mixed>
*/
public function resolveAssociationEntries(EntityManagerInterface $em)
{
return array_map(function($value) use ($em) {
if ( ! ($value instanceof AssociationCacheEntry)) {
return array_map(static function ($value) use ($em) {
if (! ($value instanceof AssociationCacheEntry)) {
return $value;
}

View File

@@ -20,18 +20,20 @@
namespace Doctrine\ORM\Cache;
use function implode;
use function ksort;
use function str_replace;
use function strtolower;
/**
* Defines entity classes roles to be stored in the cache region.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class EntityCacheKey extends CacheKey
{
/**
* READ-ONLY: Public only for performance reasons, it should be considered immutable.
*
* @var array The entity identifier
* @var array<string, mixed> The entity identifier
*/
public $identifier;
@@ -43,8 +45,8 @@ class EntityCacheKey extends CacheKey
public $entityClass;
/**
* @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param array $identifier The entity identifier
* @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class.
* @param array<string, mixed> $identifier The entity identifier
*/
public function __construct($entityClass, array $identifier)
{

View File

@@ -24,26 +24,23 @@ use Doctrine\ORM\Mapping\ClassMetadata;
/**
* Hydrator cache entry for entities
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface EntityHydrator
{
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key.
* @param object $entity The entity.
* @param ClassMetadata $metadata The entity metadata.
* @param EntityCacheKey $key The entity cache key.
* @param object $entity The entity.
*
* @return \Doctrine\ORM\Cache\EntityCacheEntry
* @return EntityCacheEntry
*/
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity);
/**
* @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key.
* @param \Doctrine\ORM\Cache\EntityCacheEntry $entry The entity cache entry.
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
* @param ClassMetadata $metadata The entity metadata.
* @param EntityCacheKey $key The entity cache key.
* @param EntityCacheEntry $entry The entity cache entry.
* @param object $entity The entity to load the cache into. If not specified, a new entity is created.
*/
public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null);
}

View File

@@ -20,27 +20,23 @@
namespace Doctrine\ORM\Cache;
use function time;
use function uniqid;
/**
* Cache Lock
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class Lock
{
/**
* @var string
*/
/** @var string */
public $value;
/**
* @var integer
*/
/** @var int */
public $time;
/**
* @param string $value
* @param integer $time
* @param string $value
* @param int $time
*/
public function __construct($value, $time = null)
{
@@ -49,7 +45,7 @@ class Lock
}
/**
* @return \Doctrine\ORM\Cache\Lock
* @return Lock
*/
public static function createLockRead()
{

View File

@@ -22,11 +22,7 @@ namespace Doctrine\ORM\Cache;
/**
* Lock exception for cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class LockException extends CacheException
{
}

View File

@@ -26,81 +26,78 @@ use Doctrine\ORM\Cache\QueryCacheKey;
/**
* Interface for logging.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CacheLogger
{
/**
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCachePut($regionName, EntityCacheKey $key);
/**
* Log an entity get from second level cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCacheHit($regionName, EntityCacheKey $key);
/**
* Log an entity get from second level cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity.
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*/
public function entityCacheMiss($regionName, EntityCacheKey $key);
/**
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
*/
* Log an entity put into second level cache.
*
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCachePut($regionName, CollectionCacheKey $key);
/**
* Log an entity get from second level cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCacheHit($regionName, CollectionCacheKey $key);
/**
* Log an entity get from second level cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection.
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*/
public function collectionCacheMiss($regionName, CollectionCacheKey $key);
/**
* Log a query put into the query cache.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCachePut($regionName, QueryCacheKey $key);
/**
* Log a query get from the query cache resulted in a hit.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCacheHit($regionName, QueryCacheKey $key);
/**
* Log a query get from the query cache resulted in a miss.
*
* @param string $regionName The name of the cache region.
* @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query.
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*/
public function queryCacheMiss($regionName, QueryCacheKey $key);
}

View File

@@ -26,20 +26,16 @@ use Doctrine\ORM\Cache\QueryCacheKey;
/**
* Cache logger chain
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class CacheLoggerChain implements CacheLogger
{
/**
* @var array<\Doctrine\ORM\Cache\Logging\CacheLogger>
*/
/** @var array<CacheLogger> */
private $loggers = [];
/**
* @param string $name
* @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger
* @param string $name
*
* @return void
*/
public function setLogger($name, CacheLogger $logger)
{
@@ -49,15 +45,15 @@ class CacheLoggerChain implements CacheLogger
/**
* @param string $name
*
* @return \Doctrine\ORM\Cache\Logging\CacheLogger|null
* @return CacheLogger|null
*/
public function getLogger($name)
{
return isset($this->loggers[$name]) ? $this->loggers[$name] : null;
return $this->loggers[$name] ?? null;
}
/**
* @return array<\Doctrine\ORM\Cache\Logging\CacheLogger>
* @return array<CacheLogger>
*/
public function getLoggers()
{

View File

@@ -24,27 +24,20 @@ use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\QueryCacheKey;
use function array_sum;
/**
* Provide basic second level cache statistics.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class StatisticsCacheLogger implements CacheLogger
{
/**
* @var int[]
*/
/** @var int[] */
private $cacheMissCountMap = [];
/**
* @var int[]
*/
/** @var int[] */
private $cacheHitCountMap = [];
/**
* @var int[]
*/
/** @var int[] */
private $cachePutCountMap = [];
/**
@@ -146,7 +139,7 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionHitCount($regionName)
{
return isset($this->cacheHitCountMap[$regionName]) ? $this->cacheHitCountMap[$regionName] : 0;
return $this->cacheHitCountMap[$regionName] ?? 0;
}
/**
@@ -158,7 +151,7 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionMissCount($regionName)
{
return isset($this->cacheMissCountMap[$regionName]) ? $this->cacheMissCountMap[$regionName] : 0;
return $this->cacheMissCountMap[$regionName] ?? 0;
}
/**
@@ -170,11 +163,11 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function getRegionPutCount($regionName)
{
return isset($this->cachePutCountMap[$regionName]) ? $this->cachePutCountMap[$regionName] : 0;
return $this->cachePutCountMap[$regionName] ?? 0;
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsMiss()
{
@@ -182,7 +175,7 @@ class StatisticsCacheLogger implements CacheLogger
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsHit()
{
@@ -190,7 +183,7 @@ class StatisticsCacheLogger implements CacheLogger
}
/**
* @return array
* @return array<string, int>
*/
public function getRegionsPut()
{
@@ -201,6 +194,8 @@ class StatisticsCacheLogger implements CacheLogger
* Clear region statistics
*
* @param string $regionName The name of the cache region.
*
* @return void
*/
public function clearRegionStats($regionName)
{
@@ -211,6 +206,8 @@ class StatisticsCacheLogger implements CacheLogger
/**
* Clear all statistics
*
* @return void
*/
public function clearStats()
{

View File

@@ -24,9 +24,6 @@ namespace Doctrine\ORM\Cache;
* Defines a region that supports multi-get reading.
*
* With one method call we can get multiple items.
*
* @since 2.5
* @author Asmir Mustafic
*/
interface MultiGetRegion
{

View File

@@ -1,4 +1,5 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -19,11 +20,10 @@
namespace Doctrine\ORM\Cache\Persister;
use Doctrine\ORM\Cache\Region;
/**
* Interface for persister that support second level cache.
*
* @since 2.5
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
interface CachedPersister
{
@@ -40,7 +40,7 @@ interface CachedPersister
/**
* Gets the The region access.
*
* @return \Doctrine\ORM\Cache\Region
* @return Region
*/
public function getCacheRegion();
}

View File

@@ -21,99 +21,82 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\Region;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\CollectionHydrator;
use Doctrine\ORM\Cache\EntityCacheKey;
use Doctrine\ORM\Cache\Logging\CacheLogger;
use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\UnitOfWork;
use function array_values;
use function assert;
use function count;
use function is_array;
/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
abstract class AbstractCollectionPersister implements CachedCollectionPersister
{
/**
* @var \Doctrine\ORM\UnitOfWork
*/
/** @var UnitOfWork */
protected $uow;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
/** @var ClassMetadataFactory */
protected $metadataFactory;
/**
* @var \Doctrine\ORM\Persisters\Collection\CollectionPersister
*/
/** @var CollectionPersister */
protected $persister;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
/** @var ClassMetadata */
protected $sourceEntity;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadata
*/
/** @var ClassMetadata */
protected $targetEntity;
/**
* @var array
*/
/** @var mixed[] */
protected $association;
/**
* @var array
*/
/** @var mixed[] */
protected $queuedCache = [];
/**
* @var \Doctrine\ORM\Cache\Region
*/
/** @var Region */
protected $region;
/**
* @var string
*/
/** @var string */
protected $regionName;
/**
* @var \Doctrine\ORM\Cache\CollectionHydrator
*/
/** @var CollectionHydrator */
protected $hydrator;
/**
* @var \Doctrine\ORM\Cache\Logging\CacheLogger
*/
/** @var CacheLogger|null */
protected $cacheLogger;
/**
* @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached.
* @param \Doctrine\ORM\Cache\Region $region The collection region.
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
* @param array $association The association mapping.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param Region $region The collection region.
* @param EntityManagerInterface $em The entity manager.
* @param mixed[] $association The association mapping.
*/
public function __construct(CollectionPersister $persister, Region $region, EntityManagerInterface $em, array $association)
{
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$configuration = $em->getConfiguration();
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();
$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association);
$this->sourceEntity = $em->getClassMetadata($association['sourceEntity']);
$this->targetEntity = $em->getClassMetadata($association['targetEntity']);
$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association);
$this->sourceEntity = $em->getClassMetadata($association['sourceEntity']);
$this->targetEntity = $em->getClassMetadata($association['targetEntity']);
}
/**
@@ -141,22 +124,17 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
}
/**
* @param \Doctrine\ORM\PersistentCollection $collection
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
*
* @return object[]|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
{
if (($cache = $this->region->get($key)) === null) {
$cache = $this->region->get($key);
if ($cache === null) {
return null;
}
if (($cache = $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection)) === null) {
return null;
}
return $cache;
return $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection);
}
/**
@@ -164,14 +142,14 @@ 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();
assert($targetPersister instanceof CachedEntityPersister);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
// Only preserve ordering if association configured it
if ( ! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
if (! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
// Elements may be an array or a Collection
$elements = array_values(is_array($elements) ? $elements : $elements->getValues());
}
@@ -183,15 +161,15 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
continue;
}
$class = $this->targetEntity;
$className = ClassUtils::getClass($elements[$index]);
$class = $this->targetEntity;
$className = ClassUtils::getClass($elements[$index]);
if ($className !== $this->targetEntity->name) {
$class = $this->metadataFactory->getMetadataFor($className);
}
$entity = $elements[$index];
$entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
$entity = $elements[$index];
$entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity);
$targetRegion->put($entityKey, $entityEntry);
}
@@ -262,7 +240,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/**
* Clears cache entries related to the current collection
*
* @param PersistentCollection $collection
* @return void
*/
protected function evictCollectionCache(PersistentCollection $collection)
{
@@ -282,13 +260,15 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/**
* @param string $targetEntity
* @param object $element
*
* @return void
*/
protected function evictElementCache($targetEntity, $element)
{
/* @var $targetPersister CachedEntityPersister */
$targetPersister = $this->uow->getEntityPersister($targetEntity);
$targetRegion = $targetPersister->getCacheRegion();
$key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
assert($targetPersister instanceof CachedEntityPersister);
$targetRegion = $targetPersister->getCacheRegion();
$key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element));
$targetRegion->evict($key);

View File

@@ -20,44 +20,39 @@
namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
/**
* Interface for second level cache collection persisters.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.5
*/
interface CachedCollectionPersister extends CachedPersister, CollectionPersister
{
/**
* @return \Doctrine\ORM\Mapping\ClassMetadata
* @return ClassMetadata
*/
public function getSourceEntityMetadata();
/**
* @return \Doctrine\ORM\Mapping\ClassMetadata
* @return ClassMetadata
*/
public function getTargetEntityMetadata();
/**
* Loads a collection from cache
*
* @param \Doctrine\ORM\PersistentCollection $collection
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
*
* @return \Doctrine\ORM\PersistentCollection|null
* @return PersistentCollection|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key);
/**
* Stores a collection into cache
*
* @param \Doctrine\ORM\Cache\CollectionCacheKey $key
* @param array|\Doctrine\Common\Collections\Collection $elements
* @param array|mixed[]|Collection $elements
*
* @return void
*/

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