Compare commits

...

193 Commits

Author SHA1 Message Date
Alexander M. Turek
8291a7f09b Allow doctrine/deprecations 1.0 (#9723) 2022-05-02 21:10:07 +02:00
Grégoire Paris
26e274e373 Merge pull request #9708 from michnovka/2.12.x-fix-psalm-repository 2022-05-02 15:26:47 +02:00
Alexander M. Turek
5209184a60 Psalm 4.23, PHPStan 1.6.3 (#9718) 2022-05-02 11:07:33 +02:00
Sergei Morozov
23f54885bc Merge pull request #9706 from morozov/cleanup-test-entity-manager
Remove unused OrmTestCase::getTestEntityManager() parameters
2022-05-01 11:43:07 -07:00
Sergei Morozov
7f29b576d8 Remove some ConnectionMock methods (#9707) 2022-05-01 17:52:10 +02:00
Tomas
a8425a5248 Fix psalm annotation 2022-05-01 16:40:44 +02:00
Pierre B
86ce0e5e35 Update ClassMetadataInfo::table definition (#9703)
Everything except the name key might be undefined when accessing to this public property, for instance in a LoadMetadataEvent

Co-authored-by: Pierre Bourdet <pbourdet@worldia.com>
2022-05-01 09:40:00 +02:00
Sergei Morozov
39fd5f4d46 Remove unused OrmTestCase::getTestEntityManager() parameters 2022-04-30 12:35:05 -07:00
Thomas Landauer
a9309d748b Add missing use statement (#9699) 2022-04-30 08:04:44 +02:00
Grégoire Paris
fe09af6df1 Merge pull request #9701 from doctrine/2.11.x
2.11.x
2022-04-29 22:45:58 +02:00
Grégoire Paris
ed50e3d967 Merge pull request #9698 from ThomasLandauer/patch-8 2022-04-29 16:33:49 +02:00
Thomas Landauer
1d59e46245 use valid link syntax 2022-04-29 16:25:10 +02:00
Grégoire Paris
a0a0b0e476 Merge pull request #9692 from greg0ire/reference-cmi-even-less
Use ClassMetadata over ClassMetadataInfo in tests
2022-04-28 23:07:56 +02:00
Grégoire Paris
0078a67786 Use ClassMetadata over ClassMetadataInfo in tests
ClassMetadataInfo is deprecated in favor of ClassMetadata.
2022-04-28 21:40:23 +02:00
Grégoire Paris
38d1124be9 Merge pull request #9691 from greg0ire/reference-cmi-less
Reference ClassMetadaInfo less
2022-04-28 07:58:51 +02:00
Grégoire Paris
e9d3c218ef Remove explanation about inexistent distinction
ClassMetadataInfo used to be useful during entity generation, because it
allowed the entity not to exist. We no longer do entity generation, and
even if we did, the reflection methods have been moved to
ClassMetadataInfo as of 76e4f5a80b .
2022-04-27 23:28:58 +02:00
Grégoire Paris
6d2ca8fe40 Address mapping driver extraction
This documentation must be very old because this is no longer valid as
of e9e36dcf32 . The interface and abstract
file driver have since then been moved to doctrine/common, and the to
doctrine/persistence.
2022-04-27 23:28:43 +02:00
Grégoire Paris
a06011daf3 Refer to ClassMetadata instead of ClassMetadataInfo
Although properties and methods are currently located in
ClassMetadataInfo, it is better to refer to ClassMetadata as the former
is deprecated in favor of the latter.
2022-04-27 23:28:40 +02:00
Alexander M. Turek
0846b8b102 PHPStan 1.6.1 (#9688) 2022-04-26 23:52:07 +02:00
Simon Podlipsky
1dd2b44982 Drop SymfonyStyle[listing] for sqls (#9679)
This was super annoying as UpdateCommand printed sqls prefixed with `*` so it was not possible to copy statements anymore without manually removing those asterisks.

This removes prefixing sqls and makes it consistent with Create and Drop commands.
2022-04-26 15:33:18 +02:00
Alexander M. Turek
d9508e97df Remove dynamic property declarations (#9683) 2022-04-26 11:10:50 +02:00
Alexander M. Turek
534ed9c3c2 PHPStan 1.6.0 (#9682) 2022-04-26 11:01:11 +02:00
Grégoire Paris
a552df66a9 Merge pull request #9677 from greg0ire/assert-drivers-are-orm-specific
Document ORM drivers only really load ORM metadata
2022-04-24 20:55:13 +02:00
Grégoire Paris
85238d4d98 Document ORM drivers only really load ORM metadata 2022-04-24 20:05:37 +02:00
Fran Moreno
b7e9dd023c Fix HydrationException::invalidDiscriminatorValue parameter type (#9676) 2022-04-24 19:32:57 +02:00
Fran Moreno
1ac05f5e4e Fix type on ClassMetadata discriminatorMap (#9675) 2022-04-24 17:52:28 +02:00
Alexander M. Turek
2e4a872272 Conditionally extend the old AnnotationDriver class (#9671) 2022-04-22 19:46:03 +02:00
Alexander M. Turek
d550364431 Deprecate the doctrine binary (#9661) 2022-04-19 20:34:28 +02:00
Alexander M. Turek
5b2bf9d74c Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  ScalarColumnHydrator: prevent early-bail on falsy values (#9663)
2022-04-19 11:27:11 +02:00
Mitch
4af1aa3177 ScalarColumnHydrator: prevent early-bail on falsy values (#9663)
* add failing test for issue #9230

* ScalarColumnHydrator: prevent early-bail on falsy values, fix #9230

Co-authored-by: Mickael GOETZ <contact@mickael-goetz.com>
2022-04-19 09:01:33 +00:00
michnovka
2fe40679f4 Fix enum hydration when fetching partial results (#9657) 2022-04-16 20:49:28 +02:00
Alexander M. Turek
7029965d3a Indicate support for doctrine/persistence 3 (#9656) 2022-04-15 13:00:03 +02:00
michnovka
7e49c70320 Fix tests for enum ID hydration (#9658) 2022-04-13 12:58:20 +02:00
Grégoire Paris
f7fe5ad1bb Merge pull request #9654 from greg0ire/revert-9636
Revert "Use charset/collation from column or table default when creatng relations (#9636)"
2022-04-12 07:19:51 +02:00
Grégoire Paris
035c52ce3c Revert "Use charset/collation from column or table default when creating relations (#9636)"
This reverts commit 03f4468be2.
The inferring process seems fragile and MySQL-specific. The ORM might
not be the correct place to fix this issue (if it needs to be fixed at
all).
2022-04-11 20:26:17 +02:00
michnovka
7e7e38b60e Fix test file/class names (#9649) 2022-04-11 12:15:45 +02:00
Alexander M. Turek
36ab133e62 Leverage generic persistence event classes (#9633) 2022-04-11 11:58:42 +02:00
Alexander M. Turek
e13422ab5e Merge 2.11.x into 2.12.x (#9650)
* Fix composer install in contributing readme

People that contribute know how to use composer.

* Fix static analysis for Persistence 2.5 (#9648)

Co-authored-by: Ruud Kamphuis <ruudk@users.noreply.github.com>
Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2022-04-10 23:45:04 +02:00
Alexander M. Turek
f4d5283f70 Fix static analysis for Persistence 2.5 (#9648) 2022-04-10 23:31:12 +02:00
Grégoire Paris
fda79b8e21 Improve exception message (#9646)
In setups where you have many parameters, or do not even realise you are
using an entity, that additional piece of context can be helpful. The
parameter name is not always available where the old exception was
called though.
2022-04-10 23:08:52 +02:00
Alexander M. Turek
5a345b01dc Deprecate console helper (#9641) 2022-04-10 20:59:10 +02:00
Ruud Kamphuis
03f4468be2 Use charset/collation from column or table default when creating relations (#9636)
Fixes #6823
2022-04-10 14:34:21 +02:00
michnovka
a3d82f8e2f Support Enum IDs and search by Enum fields (#9629) 2022-04-09 23:40:41 +02:00
Grégoire Paris
976fe5bc0d Merge pull request #9639 from ruudk/patch-1
Fix composer install in contributing readme
2022-04-09 16:41:21 +02:00
Ruud Kamphuis
582b222b00 Fix composer install in contributing readme
People that contribute know how to use composer.
2022-04-09 15:29:45 +02:00
Alexander M. Turek
d9e8e839fe Deprecate custom ObjectRepository implementations (#9533) 2022-04-06 13:51:12 +02:00
Alexander M. Turek
e8472c8f1a Fix types on walkLiteral() and walkLikeExpression() (#9566) 2022-04-06 10:48:54 +02:00
Alexander M. Turek
deaab5133e Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  explicitly use the non-deprecated ORMException
2022-04-05 13:02:01 +02:00
Sander
cffe31fc9d Add support for array of enums (#9497)
* Add support for array of enums

This allows the use of 'array' and 'simple_array' in combination
with the enumType parameter.

* Reference is_array and array_map through a use statement nstead of global fallback

* Return the value of an array of enums correctly

* Add enum array mapping test

* Fix order of use parameters

* Fix return type docblock

* Apply phpcs feedback

* Fix static closure

* Add missing return type to static closure

* Add helper method for enum initialization to reduce code duplication

* Fix CS

* Replace mixed typehints with more specific ones

* Update docblock type hint to allow for array of string/int

* Fix types

* Fix types

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2022-04-05 01:29:40 +02:00
Alexander M. Turek
0e9c7533fb Fix types on ResultSetMapping (#9621) 2022-04-04 21:58:25 +02:00
Grégoire Paris
1ffb9152f7 Merge pull request #9623 from BenoitDuffez/dont-use-depecated-ormexception
explicitly use the non-deprecated ORMException
2022-04-02 11:52:48 +02:00
Benoit Duffez
51faa6ddb7 explicitly use the non-deprecated ORMException 2022-04-01 13:23:05 -07:00
Alexander M. Turek
18d6bc3757 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Remove "Description of" PHPDoc (#9611)
2022-03-31 00:14:34 +02:00
Alexander M. Turek
7c4ae58517 Support enums as default values (#9616) 2022-03-28 22:36:45 +02:00
Alexander M. Turek
05f8fcf836 Skip tests requiring ObjectManagerAware (#9612) 2022-03-28 13:50:45 +00:00
Alexander M. Turek
692c3e1b45 Remove "Description of" PHPDoc (#9611) 2022-03-28 13:47:30 +02:00
Alexander M. Turek
acff29fddd Update psalm.xml 2022-03-28 10:48:23 +02:00
Alexander M. Turek
58659f6c4f Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  PHPStan 1.5.0 (#9607)
  Remove Sphinx config
  Use correct syntax for external links
  Update XmlExporter.php - Type problem in php8.x (#9589)
  Ignore deprecation from Persistence
  Stands with Ukraine (#9567)
  Use internal links when self-referencing
  Link to docs for the stable version
2022-03-28 10:46:50 +02:00
Alexander M. Turek
e410180c6e PHPStan 1.5.0 (#9607) 2022-03-28 10:37:03 +02:00
Grégoire Paris
4476b05d59 Merge pull request #9608 from greg0ire/remove-python-config
Remove Sphinx config
2022-03-26 17:15:36 +01:00
Grégoire Paris
343b0ae576 Remove Sphinx config
I do not think this file is still useful, since AFAIK we are not using
Sphinx anymore. Besides, this is the only Doctrine project I could find
that still has that file. It was last updated 6 years ago.
2022-03-26 12:07:22 +01:00
Grégoire Paris
9952350c64 Merge pull request #9604 from greg0ire/improve-exception-message
Indicate what feature is deprecated
2022-03-24 08:46:03 +01:00
Grégoire Paris
3bc78caba9 Indicate what feature is deprecated 2022-03-23 18:39:33 +01:00
Grégoire Paris
0f1c9ec72a Merge pull request #9603 from greg0ire/int-mask-of 2022-03-22 14:18:14 +01:00
Grégoire Paris
80f65d6f77 Implement int-mask-of where appropriate
With Psalm, you can specify that an integer should be a bitmask of
constants. Doing so allows to make some types more precise.
2022-03-22 14:02:31 +01:00
Grégoire Paris
de69f60c6a Merge pull request #9598 from greg0ire/fix-event-table
Use correct syntax for external links
2022-03-20 19:32:10 +01:00
Grégoire Paris
2a653b05a0 Use correct syntax for external links
There is no leading underscore, and the trailing underscore should not
be forgotten.
2022-03-20 19:13:19 +01:00
Grégoire Paris
0f04a82857 Merge pull request #9595 from greg0ire/deprecate-more-ns-aliases
Deprecate more occurrences of namespace aliases
2022-03-20 14:34:16 +01:00
Grégoire Paris
17903346cf Deprecate more occurrences of namespace aliases 2022-03-20 14:26:13 +01:00
Alexander M. Turek
98b468da57 Fix type on SqlWalker::walkPathExpression() (#9565) 2022-03-20 13:42:41 +01:00
Grégoire Paris
bccb4c7bd9 Merge pull request #9592 from greg0ire/fix-persistence-compat
Deprecate or throw on namespace alias usage
2022-03-20 12:26:28 +01:00
Grégoire Paris
dc53628faf Deprecate or throw on namespace alias usage
This feature has been deprecated and removed in doctrine/persistence.
It was already deprecated in doctrine/orm for other APIs.
2022-03-20 11:44:42 +01:00
Grégoire Paris
21f339e6eb Merge pull request #9528 from greg0ire/get-rid-of-persistent-object
Implement forward compatibility with Persistence 3
2022-03-19 13:18:06 +01:00
Jan Záruba
c6831c6b07 Update XmlExporter.php - Type problem in php8.x (#9589)
Please see PHP interface SimpleXMLElement::addAttribute(string $name, string $value = null, string $namespace = null): void .... 
The $value must be string or null.
2022-03-19 13:03:28 +01:00
Grégoire Paris
33da4d84eb Merge pull request #9590 from greg0ire/fix-build
Ignore deprecation from Persistence
2022-03-18 22:56:08 +01:00
Grégoire Paris
08de12e962 Merge pull request #9580 from klammbueddel/bug/duplicate-object-in-nested-collections
Add test to reproduce #9579
2022-03-18 22:54:19 +01:00
Grégoire Paris
7c83373f1e Add specific CI jobs for Persistence 3 2022-03-18 22:25:47 +01:00
Grégoire Paris
021164fbe5 throw when attempting to use partial clearing 2022-03-18 22:21:59 +01:00
Grégoire Paris
b2d0c21fe0 Workaround the impossibility of unsetting metadata 2022-03-18 21:49:08 +01:00
Grégoire Paris
7391e2586a Mock ClassMetadata::getName()
It is supposed to return a string.
2022-03-18 21:49:08 +01:00
Grégoire Paris
3532ce9a25 Remove useless calls 2022-03-18 21:49:08 +01:00
Grégoire Paris
2c769acf8c Implement forward compatbility with Persistence 3 2022-03-18 21:49:08 +01:00
Grégoire Paris
c1b373b931 Ignore deprecation from Persistence
The deprecation is already addressed in the next minor branch.
2022-03-18 19:19:15 +01:00
Christian Bartels
61cb557b18 Check if association already contains object (#9579) 2022-03-18 11:00:33 +01:00
Maxime Veber
b6cff1aa1c Stands with Ukraine (#9567) 2022-03-18 10:32:03 +01:00
Grégoire Paris
4471ad9f6b Merge pull request #9587 from greg0ire/implement-colocated-driver 2022-03-16 10:54:06 +01:00
Grégoire Paris
cd57768b08 Implement colocated mapping driver
This allows us to decouple further from doctrine/annotations, and to fix
some static analysis issues.

The assumption being made here is that the abstract class we are no
longer extending is not used in type declarations and instanceof checks.
2022-03-15 12:42:27 +01:00
Grégoire Paris
d2206152bb Merge pull request #9585 from greg0ire/hunt-down-latest 2022-03-13 17:11:29 +01:00
Grégoire Paris
a34dc0a0e3 Use internal links when self-referencing
This should result in links with the current version of the docs.
2022-03-13 14:36:37 +01:00
Grégoire Paris
881a7b3b69 Link to docs for the stable version
When we do not know what version people intend to browse, it seems more
sensible to assume they want to see the docs for the stable version.
2022-03-13 14:35:15 +01:00
Alexander M. Turek
b64824addb Leverage MemcachedAdapter::isSupported() (#9578) 2022-03-10 23:37:36 +01:00
Alexander M. Turek
c7104c9471 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Baseline Psalm errors caused by DBAL 3.3.3 (#9577)
  Make sure MemcachedAdapter is supported before tring to use it (#9574)
  Fixing `:doc:` link (#9569)
  Adding PHP attributes (#9555)
  Remove reference to removed class
2022-03-09 17:20:17 +01:00
Alexander M. Turek
82bbb1dc4a Baseline Psalm errors caused by DBAL 3.3.3 (#9577) 2022-03-09 17:18:11 +01:00
flack
9c351e0444 Make sure MemcachedAdapter is supported before tring to use it (#9574) 2022-03-09 16:23:58 +01:00
Thomas Landauer
5ed5383338 Fixing :doc: link (#9569) 2022-03-08 22:31:56 +01:00
Thomas Landauer
eb1d54871b Adding PHP attributes (#9555)
Co-authored-by: Alexander M. Turek <me@derrabus.de>
Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>
2022-03-08 01:40:01 +01:00
Grégoire Paris
e148c838b0 Merge pull request #9563 from greg0ire/address-sqllogger-deprecation
Remove reference to removed class
2022-03-03 22:27:51 +01:00
Grégoire Paris
c73df2a7b4 Remove reference to removed class
EchoSQLLogger is deprecated in DBAL 2, and removed in DBAL 3.
2022-03-03 22:12:04 +01:00
Alexander M. Turek
bc6c6c9f0c Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Parser: SimpleArithmeticExpression should return ArithmeticTerm (#9557)
2022-03-03 19:55:19 +01:00
Loïc Vernet
89d0a6a67c validate schema command: allow to debug missing schema updates list (#9019) 2022-03-03 18:35:32 +00:00
Jan Barášek
38682e93db Parser: SimpleArithmeticExpression should return ArithmeticTerm (#9557) 2022-03-03 00:25:27 +01:00
Alexander M. Turek
1febeaca7f Document tree walker class strings (#9553) 2022-03-01 20:14:46 +01:00
Alexander M. Turek
229dcb082b Use literal types for JOIN_TYPE_* constants (#9552) 2022-03-01 15:10:51 +01:00
Grégoire Paris
3849aed6fb Merge pull request #9549 from derrabus/improvement/leverage-token-type
Leverage Lexer's Token type
2022-02-28 20:56:19 +01:00
Alexander M. Turek
f82db6a894 Leverage Lexer's Token type 2022-02-28 20:27:19 +01:00
Alexander M. Turek
a8a859cf5e Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Update baselines for Lexer 1.2.3 (#9546)
2022-02-28 14:11:53 +01:00
Alexander M. Turek
84df37de97 Update baselines for Lexer 1.2.3 (#9546) 2022-02-28 14:06:31 +01:00
Alexander M. Turek
7be96f64ab Document QueryComponent array shape (#9527) 2022-02-25 00:21:29 +01:00
Grégoire Paris
947935e4c9 Merge pull request #9541 from greg0ire/improve-templating 2022-02-24 20:25:13 +01:00
Grégoire Paris
40af1fcfc6 Improve templating
This is helpful for static analysis
2022-02-24 18:19:30 +01:00
Alexander M. Turek
021444b322 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Fix bug-#9536
2022-02-24 11:24:13 +01:00
Alexander M. Turek
ec7c637cf2 Un-deprecate the current proxy mechanism (#9532) 2022-02-24 11:17:05 +01:00
Grégoire Paris
0a0779c4a9 Remove unused methods 2022-02-22 20:26:18 +01:00
Grégoire Paris
856c3143f8 Merge pull request #9537 from kiler129/fix-bug-9536
Make error message suggestion accurate
2022-02-22 20:08:49 +01:00
kiler129
79f73a23f3 Fix bug-#9536
Wrong validation message is displayed when an incorrect bidirectional
bi-directional mapping is set up. When the owning side is configured
correctly and the target side is missing the back reference, the ORM
suggests adding inverseBy instead of mappedBy, with the field name
missing. This commit fixes this problem.
2022-02-22 12:05:35 -06:00
Grégoire Paris
a52d9880cc Merge pull request #9542 from doctrine/2.11.x
Merge 2.11.x up into 2.12.x
2022-02-22 18:11:25 +01:00
Grégoire Paris
4af912f712 Merge pull request #9539 from greg0ire/use-stable-dbal
Drop minor version number
2022-02-22 18:01:26 +01:00
Grégoire Paris
65f48e0ecd Drop minor version number
We should make it explicit that we mean to test with whatever is the
latest 3.x
2022-02-22 17:25:21 +01:00
Alexander M. Turek
08eaba44ca Fix more types on EntityRepository and FilterCollection (#9525) 2022-02-20 21:09:41 +01:00
Alexander M. Turek
05c35c398f Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Bring `FilterCollection` to a "clean" state after hash computation (#9523)
2022-02-20 14:19:47 +01:00
Matthias Pigulla
193c3abf0e Bring FilterCollection to a "clean" state after hash computation (#9523)
Co-authored-by: Alexander M. Turek <me@derrabus.de>
2022-02-20 13:09:05 +00:00
Alexander M. Turek
dac1875a79 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Make creating test models more straightforward
  Trigger the desired code path
  Fix syntax typo in attributes reference (#9513)
  Constructor-Argument "options" has the same type as the associated property. (#9501)
2022-02-20 11:52:36 +01:00
Grégoire Paris
5b8263e8fb Merge pull request #9526 from greg0ire/better-model-setup
Make creating test models more straightforward and revert to swallowing exceptions
2022-02-20 09:35:21 +01:00
Grégoire Paris
26e85b8c88 Make creating test models more straightforward
In https://github.com/doctrine/orm/pull/8962, I established that
swallowing exceptions while creating model was bad because it could hide
other helpful exceptions.
As it turns out however, swallowing exceptions is really the way to go
here, because of the performance implication of calling dropSchema(). It
is possible to catch a more precise exception as well, which should
preserve the benefits of not swallowing them.

It looks like I based my grep on the comment inside the catch and today,
I found many other occurrences of this pattern, without the easy to grep
comment.

I decided to fix them as well, but in a lazier way: one no longer has to
remember to call dropSchema in tearDown() now, it is automated.
2022-02-20 09:26:57 +01:00
Grégoire Paris
152c04c03d Merge pull request #9519 from lcobucci/fix-pagination-test
Trigger the desired code path
2022-02-16 09:24:32 +01:00
Luís Cobucci
12ab6fa43f Trigger the desired code path
Since v2.7.0 the ORM avoids using extra queries via the paginator
component when maximum results isn't set on the original query. With
that change, this test was not executing the code path that it was
expected to run.

This makes sure we trigger the forcing of custom DBAL types when
hydrating the identifiers, making sure we don't introduce bugs.

More info:
- Forcing DBAL type conversion: https://github.com/doctrine/orm/pull/7905
- Issue on optimisation: https://github.com/doctrine/orm/issues/7829
- PR on optimisation: https://github.com/doctrine/orm/pull/7863
- Minor BC-break docs: https://github.com/doctrine/orm/blob/2.11.x/UPGRADE.md#minor-bc-break-paginator-output-walkers-arent-be-called-anymore-on-sub-queries-for-queries-without-max-results
2022-02-15 22:59:09 +01:00
Alexander M. Turek
5a55772559 Document deprecation of AbstractCollectionPersister helpers (#9512) 2022-02-15 22:54:30 +01:00
Yann Rabiller
e8e61cbbd5 Fix syntax typo in attributes reference (#9513)
Curly brackets are the annotation way of declaring array. Probably a
mistake while copy pasting some examples from annotations.
2022-02-15 14:32:33 +00:00
Alexander M. Turek
d78fa52ad7 Replace TreeWalkerChainIterator with a generator (#9511) 2022-02-15 13:00:15 +01:00
Alexander M. Turek
4ddaa5fc20 Fix types on caches (#9507) 2022-02-13 22:50:21 +01:00
Tony Lemke
8f847cb5aa Constructor-Argument "options" has the same type as the associated property. (#9501) 2022-02-13 21:42:15 +01:00
Alexander M. Turek
d7abcb01bc Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Fix AbstractQuery::setParameter phpdoc (#9504)
2022-02-13 11:02:56 +01:00
David Maicher
599832cb81 Fix AbstractQuery::setParameter phpdoc (#9504)
* Fix AbstractQuery::setParameter phpdoc

* Fix AbstractQuery::setParameter phpdoc
2022-02-12 21:49:30 +01:00
Alexander M. Turek
8cff7dcdaf Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Added "false" value to $columnPrefix type declaration. (#9493)
2022-02-09 14:56:45 +01:00
Dmytro Hordinskyi
530f515556 Added "false" value to $columnPrefix type declaration. (#9493) 2022-02-09 09:40:16 +01:00
Alexander M. Turek
601728045c Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  PHPStan 1.4.6, Psalm 4.20.0 (#9491)
  Fix `#[DiscriminatorMap]` params (#9487)
  Run tests with stricter error handling (#9482)
2022-02-09 00:51:01 +01:00
Alexander M. Turek
b18cd893be Fix types on QueryBuilder (#9492) 2022-02-09 00:48:36 +01:00
Alexander M. Turek
21390a12b9 Fix types on EntityRepository (#9474) 2022-02-09 00:44:30 +01:00
Alexander M. Turek
182bcbae23 Avoid calling merge() (#9489) 2022-02-09 00:42:49 +01:00
Alexander M. Turek
1c55025b12 PHPStan 1.4.6, Psalm 4.20.0 (#9491) 2022-02-09 00:42:34 +01:00
Steve
0900d4bc97 Fix #[DiscriminatorMap] params (#9487)
Fix `#[DiscriminatorMap]` params
2022-02-08 08:35:46 +00:00
Alexander M. Turek
be2518d784 Run tests with stricter error handling (#9482) 2022-02-07 22:08:34 +00:00
Alexander M. Turek
978f687df9 Modernize strpos() calls (#9480) 2022-02-07 09:34:45 +01:00
Alexander M. Turek
fd1690431f Fix types on persisters (#9466) 2022-02-07 09:26:15 +01:00
Grégoire Paris
3cfcd4ad13 Merge pull request #9479 from derrabus/improvement/useless-catch
Remove useless catches
2022-02-06 16:07:47 +01:00
Alexander M. Turek
69b0b764e3 Rename DoctrineSetup to ORMSetup (#9481) 2022-02-06 15:22:58 +01:00
Alexander M. Turek
e11cef5fca Remove useless catches 2022-02-06 00:20:25 +01:00
Alexander M. Turek
395c02caf4 Deprecate methods removed in 3.0 (#9475) 2022-02-05 22:41:33 +01:00
Alexander M. Turek
0c4e739e94 Merge 2.11.x into 2.12.x (#9473) 2022-02-05 20:15:38 +01:00
Alexander M. Turek
7a72526e47 Skip tests related to PersistentObject if that class is missing (#9472) 2022-02-05 19:31:42 +01:00
Alexander M. Turek
bdd8883d12 Run Postgres 14 and MariaDB 10.6 in CI (#9470) 2022-02-05 19:28:13 +01:00
Alexander M. Turek
5f882b1cdd Check requirements for metadata drivers (#9459) 2022-02-01 19:19:40 +01:00
Alexander M. Turek
b3d849dd38 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  PDO is not a required extension (#9457)
  Check requirements for metadata drivers (#9452)
  Remove trailing underscore (#9446)
2022-02-01 14:16:15 +01:00
Alexander M. Turek
536b65f02b PDO is not a required extension (#9457) 2022-02-01 14:13:50 +01:00
Alexander M. Turek
103c42cdb7 Check requirements for metadata drivers (#9452) 2022-02-01 13:48:03 +01:00
Alexander M. Turek
aa1dd881d8 Support enums in findBy() calls (#9453) 2022-01-31 23:02:58 +01:00
Alexander M. Turek
92d27f2fea Streamline cache creation in tests (#9451) 2022-01-31 21:55:39 +01:00
Alexander M. Turek
f8de44c35f Document the new DoctrineSetup class (#9448) 2022-01-31 08:13:10 +01:00
Grégoire Paris
cdaf7b5308 Remove trailing underscore (#9446)
It looks like there was confusion between the syntax for external links
and the syntax for internal links, which does not mention underscores.
See https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-doc
versus https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#external-links
2022-01-30 23:01:35 +00:00
Alexander M. Turek
f81980e1fa Introduce DoctrineSetup as a replacement for Setup (#9443) 2022-01-30 22:38:57 +00:00
Alexander M. Turek
e9e54d8f65 Merge release 2.11.1 into 2.12.x (#9444) 2022-01-30 23:04:07 +01:00
Claudio Zizza
4b88ce787d Introduce __unserialize behaviour in docs (#9390)
* Introduce __unserialize behaviour in docs

* Mention deprecation of Serializable interface

* Add link to __unserialize method
2022-01-30 22:47:06 +01:00
Alexander M. Turek
04bfdf85de Merge 2.11.x up into 2.12.x (#9441) 2022-01-30 18:06:16 +01:00
Grégoire Paris
f9c3470a8d Adapt test logic to PHP and SQLite II (#9442)
In a88242ee6c, testDateSub() was modified
in order to satisfy different opinions to the question "What is now
minus one month", that has different answers for different pieces of
software on March 30th.

Today, we are January 30th, we need to do the same for testDateAdd().
Hopefully this is the last time we hear about this.
2022-01-30 17:48:10 +01:00
Grégoire Paris
c1b131b67e Merge pull request #9440 from sir-kain/php-8.1-ci
Added php 8.1 to CI
2022-01-30 09:55:04 +01:00
Grégoire Paris
16b82ea061 Use the identify generator strategy
It is a better default, and should fix tests for PostgreSQL
2022-01-29 11:33:13 +01:00
Waly
f8f370ace6 Added php 8.1 to CI 2022-01-28 22:55:25 +00:00
Alexander M. Turek
43f67c6164 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Psalm 4.19.0, PHPStan 1.4.3 (#9438)
  Ignore PHPUnit result cache everywhere (#9425)
2022-01-28 23:08:59 +01:00
Alexander M. Turek
d5c69fb73f Psalm 4.19.0, PHPStan 1.4.3 (#9438) 2022-01-28 21:54:10 +00:00
Alexander M. Turek
93f9eb7af2 Ignore PHPUnit result cache everywhere (#9425) 2022-01-24 12:35:44 +01:00
Alexander M. Turek
f5be4183ce Introduce assertQueryCount (#9423) 2022-01-24 09:39:48 +01:00
Alexander M. Turek
eed031fab0 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Add support for PHP 8.1 enums in embedded classes (#9419)
  Added class-string typehint on $targetEntity (#9415)
  Allow DiscriminatorColumn with length=0 (#9410)
  Move UnderscoreNamingStrategyTest to correct namespace (#9414)
2022-01-24 00:05:44 +01:00
HypeMC
6d5da83c68 Add support for PHP 8.1 enums in embedded classes (#9419) 2022-01-23 23:56:36 +01:00
Alexander M. Turek
328f36846e Switch tests to the middleware logging system (#9418) 2022-01-23 23:55:07 +01:00
jworman
5f01dd8d09 Added class-string typehint on $targetEntity (#9415) 2022-01-23 20:09:41 +01:00
Benjamin Cremer
b596e6a665 Allow DiscriminatorColumn with length=0 (#9410) 2022-01-21 10:27:29 +01:00
Alexander M. Turek
79d3cf5880 Move UnderscoreNamingStrategyTest to correct namespace (#9414) 2022-01-20 20:49:11 +01:00
Alexander M. Turek
f7822c775d Fix types on CacheLogger implementations (#9401) 2022-01-20 00:29:39 +01:00
Sergei Morozov
8c08792f0e Rework some tests that use hardcoded DBAL mocks (#9404) 2022-01-19 17:11:57 +01:00
Alexander M. Turek
026bba23f1 Merge branch '2.11.x' into 2.12.x
* 2.11.x:
  Fix type on loadCacheEntry (#9398)
2022-01-18 23:35:01 +01:00
Alexander M. Turek
4305cb9230 Deprecate MultiGetRegion (#9397) 2022-01-18 22:50:26 +01:00
Alexander M. Turek
d7b7c28ae5 Fix type on loadCacheEntry (#9398) 2022-01-18 22:49:52 +01:00
Alexander M. Turek
2886d0dc92 Merge 2.11.x into 2.12.x (#9394)
* Expose enumType to DBAL to make native DB Enum possible (#9382)

* Accessing private properties and methods from the same class is forbidden (#9311)

Resolves issue https://github.com/doctrine/common/issues/934

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Fix review issues

* Update baselines for DBAL 3.3 (#9393)

Co-authored-by: Vadim Borodavko <vadim.borodavko@gmail.com>
Co-authored-by: olsavmic <molsavsky1@gmail.com>
2022-01-18 09:45:05 +01:00
Alexander M. Turek
d6fd510c49 Update baselines for DBAL 3.3 (#9393) 2022-01-18 09:13:14 +01:00
olsavmic
a2a7d5bb01 Accessing private properties and methods from the same class is forbidden (#9311)
Resolves issue https://github.com/doctrine/common/issues/934

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Update docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst

Co-authored-by: Claudio Zizza <859964+SenseException@users.noreply.github.com>

Fix review issues
2022-01-17 23:15:31 +01:00
Vadim Borodavko
223b2650c4 Expose enumType to DBAL to make native DB Enum possible (#9382) 2022-01-17 10:39:16 +01:00
Alexander M. Turek
07f1c4e8f8 Merge pull request #9387 from doctrine/2.11.x 2022-01-16 21:39:56 +01:00
Vadim Borodavko
01c1644d9c Allow using Enum from different namespace than Entity (#9384) 2022-01-16 13:08:30 +01:00
Sukhdev Mohan
3eff2d4b3f Corrected ORM version and added missing dependency (#9386)
* Corrected ORM version and added missing dependency

Noticed that the version wasn't updated, pointing to 2.11.0 instead of 2.10.2. 
Also when following this tutotial ran into missing dependency for "doctrine/annotation" so added that too.

* Tutorial: Bump DBAL, YAML and Cache

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2022-01-16 02:28:30 +01:00
Alexander M. Turek
9ddf8b96f8 PHPStan 1.4.0 (#9385) 2022-01-16 01:22:41 +01:00
Benjamin Eberlei
3d00fa817a [GH-9380] Bugfix: Delegate ReflectionEnumProperty::getAttributes(). (#9381)
* [GH-9380] Bugfix: Delegate ReflectionEnumProperty::getAttributes().

* [GH-9380] Add test for retrieving attributes via enum property.

* [GH-9380] Add test for retrieving attributes via enum property.

* [GH-9380] Call parent ReflectionProperty ctor for best behavior.

* Update tests/Doctrine/Tests/ORM/Functional/EnumTest.php

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

Co-authored-by: Grégoire Paris <postmaster@greg0ire.fr>
2022-01-16 00:06:59 +01:00
Alexander M. Turek
0809a2b671 Support enum cases as parameters (#9373) 2022-01-13 13:11:13 +01:00
Andrii Dembitskyi
c0a1404e4c Add detach as of list cascade-all operations (#9357) 2022-01-12 22:33:11 +01:00
422 changed files with 6342 additions and 4607 deletions

View File

@@ -30,7 +30,7 @@ jobs:
- php-version: "8.0"
dbal-version: "2.13"
- php-version: "8.1"
dbal-version: "3.2@dev"
dbal-version: "3@dev"
steps:
- name: "Checkout"
@@ -42,7 +42,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "pdo, pdo_sqlite"
extensions: "apcu, pdo, pdo_sqlite"
coverage: "pcov"
ini-values: "zend.assertions=1"
@@ -78,16 +78,16 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
postgres-version:
- "9.6"
- "13"
- "14"
include:
- php-version: "8.0"
dbal-version: "2.13"
postgres-version: "13"
postgres-version: "14"
services:
postgres:
@@ -139,18 +139,18 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
mariadb-version:
- "10.5"
- "10.6"
extension:
- "mysqli"
- "pdo_mysql"
include:
- php-version: "8.0"
dbal-version: "2.13"
mariadb-version: "10.5"
mariadb-version: "10.6"
extension: "pdo_mysql"
services:
@@ -205,7 +205,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.1"
dbal-version:
- "default"
mysql-version:

View File

@@ -21,7 +21,15 @@ jobs:
- "8.1"
dbal-version:
- "default"
- "2.13"
persistence-version:
- "default"
include:
- php-version: "8.1"
dbal-version: "2.13"
persistence-version: "default"
- php-version: "8.1"
dbal-version: "default"
persistence-version: "2.5"
steps:
- name: "Checkout code"
@@ -37,6 +45,10 @@ jobs:
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
if: "${{ matrix.dbal-version != 'default' }}"
- name: "Require specific persistence version"
run: "composer require doctrine/persistence ^${{ matrix.persistence-version }} --no-update"
if: "${{ matrix.persistence-version != 'default' }}"
- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v1"
with:
@@ -44,12 +56,16 @@ jobs:
- name: "Run a static analysis with phpstan/phpstan"
run: "vendor/bin/phpstan analyse"
if: "${{ matrix.dbal-version == 'default' }}"
if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version == 'default'}}"
- name: "Run a static analysis with phpstan/phpstan"
run: "vendor/bin/phpstan analyse -c phpstan-dbal2.neon"
if: "${{ matrix.dbal-version == '2.13' }}"
- name: "Run a static analysis with phpstan/phpstan"
run: "vendor/bin/phpstan analyse -c phpstan-persistence2.neon"
if: "${{ matrix.dbal-version == 'default' && matrix.persistence-version != 'default'}}"
static-analysis-psalm:
name: "Static Analysis with Psalm"
runs-on: "ubuntu-20.04"

2
.gitignore vendored
View File

@@ -15,5 +15,5 @@ vendor/
/tests/Doctrine/Performance/history.db
/.phpcs-cache
composer.lock
/.phpunit.result.cache
.phpunit.result.cache
/*.phpunit.xml

View File

@@ -37,8 +37,7 @@ will have to run a composer installation in the project:
```sh
git clone git@github.com:doctrine/orm.git
cd orm
curl -sS https://getcomposer.org/installer | php --
./composer.phar install
composer install
```
To run the testsuite against another database, copy the ``phpunit.xml.dist``

View File

@@ -3,6 +3,8 @@
| [![Build status][3.0 image]][3.0] | [![Build status][2.12 image]][2.12] | [![Build status][2.11 image]][2.11] |
| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.12 coverage image]][2.12 coverage] | [![Coverage Status][2.11 coverage image]][2.11 coverage] |
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
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
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
@@ -13,7 +15,7 @@ without requiring unnecessary code duplication.
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/index.html)
* [Documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/index.html)
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.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://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)
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/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,104 @@
# Upgrade to 2.12
## Deprecated the `doctrine` binary.
The documentation explains how the console tools can be bootstrapped for
standalone usage.
The method `ConsoleRunner::printCliConfigTemplate()` is deprecated because it
was only useful in the context of the `doctrine` binary.
## Deprecate omitting `$class` argument to `ORMInvalidArgumentException::invalidIdentifierBindingEntity()`
To make it easier to identify understand the cause for that exception, it is
deprecated to omit the class name when calling
`ORMInvalidArgumentException::invalidIdentifierBindingEntity()`.
## Deprecate `Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper`
Using a console helper to provide the ORM's console commands with one or
multiple entity managers had been deprecated with 2.9 already. This leaves
The `EntityManagerHelper` class with no purpose which is why it is now
deprecated too. Applications that still rely on the `em` console helper, can
easily recreate that class in their own codebase.
## Deprecate custom repository classes that don't extend `EntityRepository`
Although undocumented, it is currently possible to configure a custom repository
class that implements `ObjectRepository` but does not extend the
`EntityRepository` base class.
This is now deprecated. Please extend `EntityRepository` instead.
## Deprecated more APIs related to entity namespace aliases
```diff
-$config = $entityManager->getConfiguration();
-$config->addEntityNamespace('CMS', 'My\App\Cms');
+use My\App\Cms\CmsUser;
-$entityManager->getRepository('CMS:CmsUser');
+$entityManager->getRepository(CmsUser::class);
```
## Deprecate `AttributeDriver::getReader()` and `AnnotationDriver::getReader()`
That method was inherited from the abstract `AnnotationDriver` class of
`doctrine/persistence`, and does not seem to serve any purpose.
## Un-deprecate `Doctrine\ORM\Proxy\Proxy`
Because no forward-compatible new proxy solution had been implemented yet, the
current proxy mechanism is not considered deprecated anymore for the time
being. This applies to the following interfaces/classes:
* `Doctrine\ORM\Proxy\Proxy`
* `Doctrine\ORM\Proxy\ProxyFactory`
These methods have been un-deprecated:
* `Doctrine\ORM\Configuration::getAutoGenerateProxyClasses()`
* `Doctrine\ORM\Configuration::getProxyDir()`
* `Doctrine\ORM\Configuration::getProxyNamespace()`
Note that the `Doctrine\ORM\Proxy\Autoloader` remains deprecated and will be removed in 3.0.
## Deprecate helper methods from `AbstractCollectionPersister`
The following protected methods of
`Doctrine\ORM\Cache\Persister\Collection\AbstractCollectionPersister`
are not in use anymore and will be removed.
* `evictCollectionCache()`
* `evictElementCache()`
## Deprecate `Doctrine\ORM\Query\TreeWalkerChainIterator`
This class won't have a replacement.
## Deprecate `OnClearEventArgs::getEntityClass()` and `OnClearEventArgs::clearsAllEntities()`
These methods will be removed in 3.0 along with the ability to partially clear
the entity manager.
## Deprecate `Doctrine\ORM\Configuration::newDefaultAnnotationDriver`
This functionality has been moved to the new `ORMSetup` class. Call
`Doctrine\ORM\ORMSetup::createDefaultAnnotationDriver()` to create
a new annotation driver.
## Deprecate `Doctrine\ORM\Tools\Setup`
In our effort to migrate from Doctrine Cache to PSR-6, the `Setup` class which
accepted a Doctrine Cache instance in each method has been deprecated.
The replacement is `Doctrine\ORM\ORMSetup` which accepts a PSR-6
cache instead.
## Deprecate `Doctrine\ORM\Cache\MultiGetRegion`
The interface will be merged with `Doctrine\ORM\Cache\Region` in 3.0.
# Upgrade to 2.11
## Rename `AbstractIdGenerator::generate()` to `generateId()`
@@ -289,8 +390,8 @@ If you would still like to perform batching operations over small `UnitOfWork`
instances, it is suggested to follow these paths instead:
* eagerly use `EntityManager#clear()` in conjunction with a specific second level
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/second-level-cache.html)
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html)
cache configuration (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/second-level-cache.html)
* use an explicit change tracking policy (see http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/change-tracking-policies.html)
## Deprecated `YAML` mapping drivers.

View File

@@ -1,5 +1,15 @@
<?php
fwrite(
STDERR,
'[Warning] The use of this script is discouraged. See'
. ' https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/tools.html#doctrine-console'
. ' for instructions on bootstrapping the console runner.'
. PHP_EOL
);
echo PHP_EOL . PHP_EOL;
require_once 'Doctrine/Common/ClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine');

View File

@@ -3,6 +3,16 @@
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
fwrite(
STDERR,
'[Warning] The use of this script is discouraged. See'
. ' https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/tools.html#doctrine-console'
. ' for instructions on bootstrapping the console runner.'
. PHP_EOL
);
echo PHP_EOL . PHP_EOL;
$autoloadFiles = [
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php'

View File

@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
convertDeprecationsToExceptions="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_driver" value="mysqli"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>

View File

@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
convertDeprecationsToExceptions="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_port" value="3306"/>

View File

@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
convertDeprecationsToExceptions="true"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_driver" value="pdo_pgsql"/>
<var name="db_host" value="localhost" />
<var name="db_user" value="postgres" />

View File

@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
convertDeprecationsToExceptions="true"
>
<php>
<ini name="error_reporting" value="-1" />
<!-- use an in-memory sqlite database -->
<var name="db_driver" value="pdo_sqlite"/>
<var name="db_memory" value="true"/>

View File

@@ -23,32 +23,32 @@
"php": "^7.1 || ^8.0",
"composer-runtime-api": "^2",
"ext-ctype": "*",
"ext-pdo": "*",
"doctrine/cache": "^1.12.1 || ^2.1.1",
"doctrine/collections": "^1.5",
"doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.13.1 || ^3.2",
"doctrine/deprecations": "^0.5.3",
"doctrine/deprecations": "^0.5.3 || ^1",
"doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.4 || ^2.0",
"doctrine/instantiator": "^1.3",
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.2",
"doctrine/lexer": "^1.2.3",
"doctrine/persistence": "^2.4 || ^3",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"symfony/polyfill-php72": "^1.23",
"symfony/polyfill-php80": "^1.15"
"symfony/polyfill-php80": "^1.16"
},
"require-dev": {
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "1.3.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"phpstan/phpstan": "~1.4.10 || 1.6.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.18.1"
"vimeo/psalm": "4.23.0"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 2.0"

View File

@@ -1,201 +0,0 @@
# -*- coding: utf-8 -*-
#
# Doctrine 2 ORM documentation build configuration file, created by
# sphinx-quickstart on Fri Dec 3 18:10:24 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os, datetime
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('_exts'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['configurationblock']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Doctrine 2 ORM'
copyright = u'2010-%y, Doctrine Project Team'.format(datetime.date.today)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2'
# The full version, including alpha/beta/rc tags.
release = '2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'doctrine'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_theme']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'Doctrine2ORMdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation',
u'Doctrine Project Team', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
primary_domain = "dcorm"
def linkcode_resolve(domain, info):
if domain == 'dcorm':
return 'http://'
return None

View File

@@ -0,0 +1,74 @@
Accessing private/protected properties/methods of the same class from different instance
========================================================================================
.. sectionauthor:: Michael Olsavsky (olsavmic)
As explained in the :doc:`restrictions for entity classes in the manual <../reference/architecture>`,
it is dangerous to access private/protected properties of different entity instance of the same class because of lazy loading.
The proxy instance that's injected instead of the real entity may not be initialized yet
and therefore not contain expected data which may result in unexpected behavior.
That's a limitation of current proxy implementation - only public methods automatically initialize proxies.
It is usually preferable to use a public interface to manipulate the object from outside the `$this`
context but it may not be convenient in some cases. The following example shows how to do it safely.
Safely accessing private properties from different instance of the same class
-----------------------------------------------------------------------------
To safely access private property of different instance of the same class, make sure to initialise
the proxy before use manually as follows:
.. code-block:: php
<?php
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Entity
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Entity")
* @ORM\JoinColumn(nullable=false)
*/
private self $parent;
/**
* @ORM\Column(type="string", nullable=false)
*/
private string $name;
// ...
public function doSomethingWithParent()
{
// Always initializing the proxy before use
if ($this->parent instanceof Proxy) {
$this->parent->__load();
}
// Accessing the `$this->parent->name` property without loading the proxy first
// may throw error in case the Proxy has not been initialized yet.
$this->parent->name;
}
public function doSomethingWithAnotherInstance(self $instance)
{
// Always initializing the proxy before use
if ($instance instanceof Proxy) {
$instance->__load();
}
// Accessing the `$instance->name` property without loading the proxy first
// may throw error in case the Proxy has not been initialized yet.
$instance->name;
}
// ...
}

View File

@@ -131,8 +131,8 @@ generation of a DateDiff FunctionNode somewhere in the AST of the
dql statement.
The ``ArithmeticPrimary`` method call is the most common
denominator of valid EBNF tokens taken from the
`DQL EBNF grammar <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
denominator of valid EBNF tokens taken from the :ref:`DQL EBNF grammar
<dql_ebnf_grammar>`
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

View File

@@ -3,8 +3,8 @@ Implementing Wakeup or Clone
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
As explained in the
`restrictions for entity classes in the manual <https://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
As explained in the :ref:`restrictions for entity classes in the manual
<terminology_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

View File

@@ -47,7 +47,7 @@ appropriate autoloaders.
}
foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) {
$mappedTableName = $mapping['joinTable']['name'];
$classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName;
}

View File

@@ -12,6 +12,7 @@ steps of configuration.
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
@@ -27,7 +28,7 @@ steps of configuration.
$config = new Configuration;
$config->setMetadataCache($metadataCache);
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
$driverImpl = ORMSetup::createDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
$config->setMetadataDriverImpl($driverImpl);
$config->setQueryCache($queryCache);
$config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies');
@@ -129,7 +130,9 @@ the ``Doctrine\ORM\Configuration``:
.. code-block:: php
<?php
$driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
use Doctrine\ORM\ORMSetup;
$driverImpl = ORMSetup::createDefaultAnnotationDriver('/path/to/lib/MyProject/Entities');
$config->setMetadataDriverImpl($driverImpl);
The path information to the entities is required for the annotation
@@ -195,10 +198,7 @@ SQL Logger (***Optional***)
Gets or sets the logger to use for logging all SQL statements
executed by Doctrine. The logger class must implement the
``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default
implementation that logs to the standard output using ``echo`` and
``var_dump`` can be found at
``Doctrine\DBAL\Logging\EchoSQLLogger``.
deprecated ``Doctrine\DBAL\Logging\SQLLogger`` interface.
Auto-generating Proxy Classes (***OPTIONAL***)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -457,18 +457,5 @@ Setting up the Console
----------------------
Doctrine uses the Symfony Console component for generating the command
line interface. You can take a look at the ``vendor/bin/doctrine.php``
script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command
for inspiration how to setup the cli.
In general the required code looks like this:
.. code-block:: php
<?php
$cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli);
$cli->run();
line interface. You can take a look at the
:doc:`tools chapter <../reference/tools>` for inspiration how to setup the cli.

View File

@@ -66,6 +66,8 @@ The root namespace of the ORM package is ``Doctrine\ORM``.
Terminology
-----------
.. _terminology_entities:
Entities
~~~~~~~~
@@ -82,9 +84,13 @@ be any regular PHP class observing the following restrictions:
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
- An entity class must not implement ``__wakeup`` or
:doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`.
Also consider implementing
`Serializable <https://php.net/manual/en/class.serializable.php>`_
instead.
You can also consider implementing
`Serializable <https://php.net/manual/en/class.serializable.php>`_,
but be aware that it is deprecated since PHP 8.1. We do not recommend its usage.
- PHP 7.4 introduces :doc:`the new magic method <https://php.net/manual/en/language.oop5.magic.php#object.unserialize>`
``__unserialize``, which changes the execution priority between
``__wakeup`` and itself when used. This can cause unexpected behaviour in
an Entity.
- Any two entity classes in a class hierarchy that inherit
directly or indirectly from one another must not have a mapped
property with the same name. That is, if B inherits from A then B
@@ -93,6 +99,7 @@ be any regular PHP class observing the following restrictions:
- An entity cannot make use of func_get_args() to implement variable parameters.
Generated proxies do not support this for performance reasons and your code might
actually fail to work when violating this restriction.
- Entity cannot access private/protected properties/methods of another entity of the same class or :doc:`do so safely <../cookbook/accessing-private-properties-of-the-same-class-from-different-instance>`.
Entities support inheritance, polymorphic associations, and
polymorphic queries. Both abstract and concrete classes can be
@@ -161,7 +168,8 @@ possible for ``__sleep`` to return names of private properties in
parent classes. On the other hand it is not a solution for proxy
objects to implement ``Serializable`` because Serializable does not
work well with any potential cyclic object references (at least we
did not find a way yet, if you did, please contact us).
did not find a way yet, if you did, please contact us). The
``Serializable`` interface is also deprecated beginning with PHP 8.1.
The EntityManager
~~~~~~~~~~~~~~~~~

View File

@@ -570,7 +570,7 @@ Example with partial indexes:
<?php
use Doctrine\ORM\Mapping\Index;
#[Index(name: "search_idx", columns: {"category"},
#[Index(name: "search_idx", columns: ["category"],
options: [
"where": "((category IS NOT NULL))"
]
@@ -628,7 +628,7 @@ Examples:
#[Entity]
#[InheritanceType("SINGLE_TABLE")]
#[DiscriminatorColumn(name: "discr", type: "string")]
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
#[DiscriminatorMap(["person" => "Person", "employee" => "Employee"])]
class Person
{
// ...
@@ -637,7 +637,7 @@ Examples:
#[Entity]
#[InheritanceType("JOINED")]
#[DiscriminatorColumn(name: "discr", type: "string")]
#[DiscriminatorMap({"person" = "Person", "employee" = "Employee"})]
#[DiscriminatorMap(["person" => "Person", "employee" => "Employee"])]
class Person
{
// ...

View File

@@ -14,17 +14,11 @@ After working through this guide you should know:
Mapping of associations will be covered in the next chapter on
:doc:`Association Mapping <association-mapping>`.
Guide Assumptions
-----------------
You should have already :doc:`installed and configure <configuration>`
Doctrine.
Creating Classes for the Database
---------------------------------
Every PHP object that you want to save in the database using Doctrine
is called an "Entity". The term "Entity" describes objects
is called an *Entity*. The term "Entity" describes objects
that have an identity over many independent requests. This identity is
usually achieved by assigning a unique identifier to an entity.
In this tutorial the following ``Message`` PHP class will serve as the
@@ -50,11 +44,11 @@ that describes your entity.
Doctrine provides several different ways to specify object-relational
mapping metadata:
- :doc:`Docblock Annotations <annotations-reference>`
- :doc:`Attributes <attributes-reference>`
- :doc:`Docblock Annotations <annotations-reference>`
- :doc:`XML <xml-mapping>`
- :doc:`YAML <yaml-mapping>`
- :doc:`PHP code <php-mapping>`
- :doc:`YAML <yaml-mapping>` (deprecated and will be removed in ``doctrine/orm`` 3.0.)
This manual will usually show mapping metadata via docblock annotations, though
many examples also show the equivalent configuration in YAML and XML.
@@ -62,8 +56,8 @@ many examples also show the equivalent configuration in YAML and XML.
.. note::
All metadata drivers perform equally. Once the metadata of a class has been
read from the source (annotations, xml or yaml) it is stored in an instance
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are
read from the source (attributes, annotations, XML, etc.) it is stored in an instance
of the ``Doctrine\ORM\Mapping\ClassMetadata`` class which are
stored in the metadata cache. If you're not using a metadata cache (not
recommended!) then the XML driver is the fastest.
@@ -71,9 +65,22 @@ Marking our ``Message`` class as an entity for Doctrine is straightforward:
.. configuration-block::
.. code-block:: php
.. code-block:: attribute
<?php
use Doctrine\ORM\Mapping\Entity;
#[Entity]
class Message
{
// ...
}
.. code-block:: annotation
<?php
use Doctrine\ORM\Mapping\Entity;
/** @Entity */
class Message
{
@@ -100,9 +107,25 @@ You can change this by configuring information about the table:
.. configuration-block::
.. code-block:: php
.. code-block:: attribute
<?php
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
#[Entity]
#[Table(name: 'message')]
class Message
{
// ...
}
.. code-block:: annotation
<?php
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Table;
/**
* @Entity
* @Table(name="message")
@@ -132,19 +155,38 @@ Now the class ``Message`` will be saved and fetched from the table ``message``.
Property Mapping
----------------
The next step after marking a PHP class as an entity is mapping its properties
to columns in a table.
The next step is mapping its properties to columns in the table.
To configure a property use the ``@Column`` docblock annotation. The ``type``
To configure a property use the ``Column`` docblock annotation. The ``type``
attribute specifies the :ref:`Doctrine Mapping Type <reference-mapping-types>`
to use for the field. If the type is not specified, ``string`` is used as the
default.
.. configuration-block::
.. code-block:: php
.. code-block:: attribute
<?php
use Doctrine\ORM\Mapping\Column;
use Doctrine\DBAL\Types\Types;
#[Entity]
class Message
{
#[Column(type: Types::INTEGER)]
private $id;
#[Column(length: 140)]
private $text;
#[Column(name: 'posted_at', type: Types::DATETIME)]
private $postedAt;
}
.. code-block:: annotation
<?php
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Column;
/** @Entity */
class Message
{
@@ -180,42 +222,34 @@ default.
column: posted_at
When we don't explicitly specify a column name via the ``name`` option, Doctrine
assumes the field name is also the column name. This means that:
assumes the field name is also the column name. So in this example:
* the ``id`` property will map to the column ``id`` using the type ``integer``;
* the ``text`` property will map to the column ``text`` with the default mapping type ``string``;
* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type.
The Column annotation has some more attributes. Here is a complete
list:
Here is a complete list of ``Column``s attributes (all optional):
- ``type``: (optional, defaults to 'string') The mapping type to
use for the column.
- ``name``: (optional, defaults to field name) The name of the
column in the database.
- ``length``: (optional, default 255) The length of the column in
the database. (Applies only if a string-valued column is used).
- ``unique``: (optional, default FALSE) Whether the column is a
unique key.
- ``nullable``: (optional, default FALSE) Whether the database
column is nullable.
- ``insertable``: (optional, default TRUE) Whether the database
column should be inserted.
- ``updatable``: (optional, default TRUE) Whether the database
column should be updated.
- ``enumType``: (optional, requires PHP 8.1 and ORM 2.11) The PHP enum type
name to convert the database value into.
- ``precision``: (optional, default 0) The precision for a decimal
(exact numeric) column (applies only for decimal column),
- ``type`` (default: 'string'): The mapping type to use for the column.
- ``name`` (default: name of property): The name of the column in the database.
- ``length`` (default: 255): The length of the column in the database.
Applies only if a string-valued column is used.
- ``unique`` (default: ``false``): Whether the column is a unique key.
- ``nullable`` (default: ``false``): Whether the column is nullable.
- ``insertable`` (default: ``true``): Whether the column should be inserted.
- ``updatable`` (default: ``true``): Whether the column should be updated.
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
(applies only for decimal column),
which is the maximum number of digits that are stored for the values.
- ``scale``: (optional, default 0) The scale for a decimal (exact
- ``scale`` (default: 0): The scale for a decimal (exact
numeric) column (applies only for decimal column), which represents
the number of digits to the right of the decimal point and must
not be greater than *precision*.
- ``columnDefinition``: (optional) Allows to define a custom
not be greater than ``precision``.
- ``columnDefinition``: Allows to define a custom
DDL snippet that is used to create the column. Warning: This normally
confuses the SchemaTool to always detect the column as changed.
- ``options``: (optional) Key-value pairs of options that get passed
confuses the :doc:`SchemaTool <tools>` to always detect the column as changed.
- ``options``: Key-value pairs of options that get passed
to the underlying database platform when generating DDL statements.
.. _reference-php-mapping-types:
@@ -223,21 +257,32 @@ list:
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.
.. versionadded:: 2.9
Additionally, Doctrine will map PHP types to ``type`` attribute as follows:
The column types can be inferred automatically from PHP's property types.
However, when the property type is nullable this has no effect on the ``nullable`` Column attribute.
- ``DateInterval``: ``dateinterval``
- ``DateTime``: ``datetime``
- ``DateTimeImmutable``: ``datetime_immutable``
- ``array``: ``json``
- ``bool``: ``boolean``
- ``float``: ``float``
- ``int``: ``integer``
- ``string`` or any other type: ``string``
These are the "automatic" mapping rules:
+-----------------------+-------------------------------+
| PHP property type | Doctrine column type |
+=======================+===============================+
| ``DateInterval`` | ``Types::DATEINTERVAL`` |
+-----------------------+-------------------------------+
| ``DateTime`` | ``Types::DATETIME_MUTABLE`` |
+-----------------------+-------------------------------+
| ``DateTimeImmutable`` | ``Types::DATETIME_IMMUTABLE`` |
+-----------------------+-------------------------------+
| ``array`` | ``Types::JSON`` |
+-----------------------+-------------------------------+
| ``bool`` | ``Types::BOOLEAN`` |
+-----------------------+-------------------------------+
| ``float`` | ``Types::FLOAT`` |
+-----------------------+-------------------------------+
| ``int`` | ``Types::INTEGER`` |
+-----------------------+-------------------------------+
| Any other type | ``Types::STRING`` |
+-----------------------+-------------------------------+
As of version 2.11 Doctrine can also automatically map typed properties using a
PHP 8.1 enum to set the right ``type`` and ``enumType``.

View File

@@ -41,8 +41,8 @@ access point to ORM functionality provided by Doctrine.
// bootstrap.php
require_once "vendor/autoload.php";
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
$paths = array("/path/to/entity-files");
$isDevMode = false;
@@ -55,16 +55,21 @@ access point to ORM functionality provided by Doctrine.
'dbname' => 'foo',
);
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
$config = ORMSetup::createAnnotationMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
.. note::
The ``ORMSetup`` class has been introduced with ORM 2.12. It's predecessor ``Setup`` is deprecated and will
be removed in version 3.0.
Or if you prefer XML:
.. code-block:: php
<?php
$paths = array("/path/to/xml-mappings");
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
$config = ORMSetup::createXMLMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
Or if you prefer YAML:
@@ -73,7 +78,7 @@ Or if you prefer YAML:
<?php
$paths = array("/path/to/yml-mappings");
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
$config = ORMSetup::createYAMLMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
.. note::
@@ -83,44 +88,29 @@ Or if you prefer YAML:
"symfony/yaml": "*"
Inside the ``Setup`` methods several assumptions are made:
Inside the ``ORMSetup`` methods several assumptions are made:
- If ``$isDevMode`` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
- If ``$isDevMode`` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
- If ``$isDevMode`` is true caching is done in memory with the ``ArrayAdapter``. Proxy objects are recreated on every request.
- If ``$isDevMode`` is false, check for Caches in the order APCu, Redis (127.0.0.1:6379), Memcache (127.0.0.1:11211) unless `$cache` is passed as fourth argument.
- If ``$isDevMode`` is false, set then proxy classes have to be explicitly created through the command line.
- If third argument `$proxyDir` is not set, use the systems temporary directory.
.. note::
In order to have ``ORMSetup`` configure the cache automatically, the library ``symfony/cache``
has to be installed as a dependency.
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced Configuration <reference/advanced-configuration>` section.
.. note::
You can learn more about the database connection configuration in the
`Doctrine DBAL connection configuration reference <https://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/stable/reference/configuration.html>`_.
Setting up the Commandline Tool
-------------------------------
Doctrine ships with a number of command line tools that are very helpful
during development. You can call this command from the Composer binary
directory:
.. code-block:: sh
$ php vendor/bin/doctrine
You need to register your applications EntityManager to the console tool
to make use of the tasks by creating a ``cli-config.php`` file with the
following content:
.. code-block:: php
<?php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
// replace with file to your own project bootstrap
require_once 'bootstrap.php';
// replace with mechanism to retrieve EntityManager in your app
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
during development. In order to make use of them, create an executable PHP
script in your project as described in the
:doc:`tools chapter <../reference/tools>`.

View File

@@ -1531,6 +1531,8 @@ Given that there are 10 users and corresponding addresses in the database the ex
a one-by-one basis once they are accessed.
.. _dql_ebnf_grammar:
EBNF
----

View File

@@ -79,7 +79,9 @@ array of events it should be subscribed to.
.. code-block:: php
<?php
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
use Doctrine\Common\EventSubscriber;
class TestEventSubscriber implements EventSubscriber
{
public $preFooInvoked = false;
@@ -142,33 +144,33 @@ Events Overview
| Event | Dispatched by | Lifecycle | Passed |
| | | Callback | Argument |
+=================================================================+=======================+===========+=====================================+
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `_LifecycleEventArgs`_ |
| :ref:`preRemove<reference-events-pre-remove>` | ``$em->remove()`` | Yes | `LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
| :ref:`postRemove<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `_LifecycleEventArgs`_ |
| :ref:`prePersist<reference-events-pre-persist>` | ``$em->persist()`` | Yes | `LifecycleEventArgs`_ |
| | on *initial* persist | | |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
| :ref:`postPersist<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `_PreUpdateEventArgs`_ |
| :ref:`preUpdate<reference-events-pre-update>` | ``$em->flush()`` | Yes | `PreUpdateEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `_LifecycleEventArgs`_ |
| :ref:`postUpdate<reference-events-post-update-remove-persist>` | ``$em->flush()`` | Yes | `LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `_LifecycleEventArgs`_ |
| :ref:`postLoad<reference-events-post-load>` | Loading from database | Yes | `LifecycleEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `_LoadClassMetadataEventArgs` |
| :ref:`loadClassMetadata<reference-events-load-class-metadata>` | Loading of mapping | No | `LoadClassMetadataEventArgs`_ |
| | metadata | | |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `_OnClassMetadataNotFoundEventArgs` |
| ``onClassMetadataNotFound`` | ``MappingException`` | No | `OnClassMetadataNotFoundEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `_PreFlushEventArgs`_ |
| :ref:`preFlush<reference-events-pre-flush>` | ``$em->flush()`` | Yes | `PreFlushEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `_OnFlushEventArgs` |
| :ref:`onFlush<reference-events-on-flush>` | ``$em->flush()`` | No | `OnFlushEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `_PostFlushEventArgs` |
| :ref:`postFlush<reference-events-post-flush>` | ``$em->flush()`` | No | `PostFlushEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `_OnClearEventArgs` |
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+
Naming convention
@@ -211,7 +213,6 @@ specific to a particular entity class's lifecycle.
.. code-block:: attribute
<?php
use Doctrine\DBAL\Types\Types;
use Doctrine\Persistence\Event\LifecycleEventArgs;
@@ -245,7 +246,6 @@ specific to a particular entity class's lifecycle.
.. code-block:: annotation
<?php
use Doctrine\Persistence\Event\LifecycleEventArgs;
/**
@@ -504,7 +504,6 @@ result in an infinite loop.
.. code-block:: php
<?php
use Doctrine\ORM\Event\PreFlushEventArgs;
class PreFlushExampleListener
@@ -590,7 +589,6 @@ This event is not a lifecycle callback.
.. code-block:: php
<?php
use Doctrine\ORM\Event\PostFlushEventArgs;
class PostFlushExampleListener
@@ -615,7 +613,7 @@ Changes to associations of the updated entity are never allowed in
this event, since Doctrine cannot guarantee to correctly handle
referential integrity at this point of the flush operation. This
event has a powerful feature however, it is executed with a
`_PreUpdateEventArgs`_ instance, which contains a reference to the
`PreUpdateEventArgs`_ instance, which contains a reference to the
computed change-set of this entity.
This means you have access to all the fields that have changed for
@@ -637,6 +635,8 @@ A simple example for this event looks like:
.. code-block:: php
<?php
use Doctrine\ORM\Event\PreUpdateEventArgs;
class NeverAliceOnlyBobListener
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
@@ -656,6 +656,8 @@ lifecycle callback when there are expensive validations to call:
.. code-block:: php
<?php
use Doctrine\ORM\Event\PreUpdateEventArgs;
class ValidCreditCardListener
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
@@ -807,6 +809,8 @@ An ``Entity Listener`` could be any class, by default it should be a class with
.. code-block:: php
<?php
use Doctrine\ORM\Event\PreUpdateEventArgs;
class UserListener
{
public function preUpdate(User $user, PreUpdateEventArgs $event)
@@ -823,6 +827,10 @@ you need to map the listener method using the event type mapping:
.. code-block:: php
<?php
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
class UserListener
{
/** @PrePersist */
@@ -907,6 +915,8 @@ Specifying an entity listener instance :
.. code-block:: php
<?php
use Doctrine\ORM\Event\PreUpdateEventArgs;
// User.php
/** @Entity @EntityListeners({"UserListener"}) */
@@ -933,12 +943,14 @@ Specifying an entity listener instance :
$listener = $container->get('user_listener');
$em->getConfiguration()->getEntityListenerResolver()->register($listener);
Implementing your own resolver :
Implementing your own resolver:
.. code-block:: php
<?php
class MyEntityListenerResolver extends \Doctrine\ORM\Mapping\DefaultEntityListenerResolver
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
class MyEntityListenerResolver extends DefaultEntityListenerResolver
{
public function __construct($container)
{
@@ -972,13 +984,15 @@ This event is not a lifecycle callback.
.. code-block:: php
<?php
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
$test = new TestEventListener();
$evm = $em->getEventManager();
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $test);
class TestEventListener
{
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$fieldMapping = array(
@@ -1011,13 +1025,16 @@ instance and class metadata.
.. code-block:: php
<?php
use Doctrine\ORM\Tools\ToolEvents;
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
$test = new TestEventListener();
$evm = $em->getEventManager();
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchemaTable, $test);
$evm->addEventListener(ToolEvents::postGenerateSchemaTable, $test);
class TestEventListener
{
public function postGenerateSchemaTable(\Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs $eventArgs)
public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$schema = $eventArgs->getSchema();
@@ -1035,13 +1052,16 @@ and the EntityManager.
.. code-block:: php
<?php
use Doctrine\ORM\Tools\ToolEvents;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
$test = new TestEventListener();
$evm = $em->getEventManager();
$evm->addEventListener(\Doctrine\ORM\Tools\ToolEvents::postGenerateSchema, $test);
$evm->addEventListener(ToolEvents::postGenerateSchema, $test);
class TestEventListener
{
public function postGenerateSchema(\Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs $eventArgs)
public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs)
{
$schema = $eventArgs->getSchema();
$em = $eventArgs->getEntityManager();

View File

@@ -55,51 +55,66 @@ Implementing Metadata Drivers
In addition to the included metadata drivers you can very easily
implement your own. All you need to do is define a class which
implements the ``Driver`` interface:
implements the ``MappingDriver`` interface:
.. code-block:: php
<?php
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
interface Driver
declare(strict_types=1);
namespace Doctrine\Persistence\Mapping\Driver;
use Doctrine\Persistence\Mapping\ClassMetadata;
/**
* Contract for metadata drivers.
*/
interface MappingDriver
{
/**
* Loads the metadata for the specified class into the provided container.
*
* @param string $className
* @param ClassMetadataInfo $metadata
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @return void
*
* @template T of object
*/
function loadMetadataForClass($className, ClassMetadataInfo $metadata);
public function loadMetadataForClass(string $className, ClassMetadata $metadata);
/**
* Gets the names of all mapped classes known to this driver.
*
* @return array The names of all mapped classes known to this driver.
*/
function getAllClassNames();
/**
* Whether the class with the specified name should have its metadata loaded.
* This is only the case if it is either mapped as an Entity or a
* MappedSuperclass.
*
* @param string $className
* @return boolean
* @return array<int, string> The names of all mapped classes known to this driver.
* @psalm-return list<class-string>
*/
function isTransient($className);
public function getAllClassNames();
/**
* Returns whether the class with the specified name should have its metadata loaded.
* This is only the case if it is either mapped as an Entity or a MappedSuperclass.
*
* @psalm-param class-string $className
*
* @return bool
*/
public function isTransient(string $className);
}
If you want to write a metadata driver to parse information from
some file format we've made your life a little easier by providing
the ``AbstractFileDriver`` implementation for you to extend from:
the ``FileDriver`` implementation for you to extend from:
.. code-block:: php
<?php
class MyMetadataDriver extends AbstractFileDriver
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
class MyMetadataDriver extends FileDriver
{
/**
* {@inheritdoc}
@@ -109,11 +124,11 @@ the ``AbstractFileDriver`` implementation for you to extend from:
/**
* {@inheritdoc}
*/
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
$data = $this->_loadMappingFile($file);
// populate ClassMetadataInfo instance from $data
// populate ClassMetadata instance from $data
}
/**
@@ -127,13 +142,12 @@ the ``AbstractFileDriver`` implementation for you to extend from:
.. note::
When using the ``AbstractFileDriver`` it requires that you
only have one entity defined per file and the file named after the
class described inside where namespace separators are replaced by
periods. So if you have an entity named ``Entities\User`` and you
wanted to write a mapping file for your driver above you would need
to name the file ``Entities.User.dcm.ext`` for it to be
recognized.
When using the ``FileDriver`` it requires that you only have one
entity defined per file and the file named after the class described
inside where namespace separators are replaced by periods. So if you
have an entity named ``Entities\User`` and you wanted to write a
mapping file for your driver above you would need to name the file
``Entities.User.dcm.ext`` for it to be recognized.
Now you can use your ``MyMetadataDriver`` implementation by setting
@@ -156,14 +170,6 @@ entity when needed.
You have all the methods you need to manually specify the mapping
information instead of using some mapping file to populate it from.
The base ``ClassMetadataInfo`` class is responsible for only data
storage and is not meant for runtime use. It does not require that
the class actually exists yet so it is useful for describing some
entity before it exists and using that information to generate for
example the entities themselves. The class ``ClassMetadata``
extends ``ClassMetadataInfo`` and adds some functionality required
for runtime usage and requires that the PHP class is present and
can be autoloaded.
You can read more about the API of the ``ClassMetadata`` classes in
the PHP Mapping chapter.

View File

@@ -185,13 +185,12 @@ It also has several methods that create builders (which are necessary for advanc
- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance
- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance
ClassMetadataInfo API
---------------------
ClassMetadata API
-----------------
The ``ClassMetadataInfo`` class is the base data object for storing
the mapping metadata for a single entity. It contains all the
getters and setters you need populate and retrieve information for
an entity.
The ``ClassMetadata`` class is the data object for storing the mapping
metadata for a single entity. It contains all the getters and setters
you need populate and retrieve information for an entity.
General Setters
~~~~~~~~~~~~~~~
@@ -309,13 +308,11 @@ Lifecycle Callback Getters
- ``hasLifecycleCallbacks($lifecycleEvent)``
- ``getLifecycleCallbacks($event)``
ClassMetadata API
-----------------
Runtime reflection methods
~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds
the runtime functionality required by Doctrine. It adds a few extra
methods related to runtime reflection for working with the entities
themselves.
These are methods related to runtime reflection for working with the
entities themselves.
- ``getReflectionClass()``

View File

@@ -13,7 +13,7 @@ as you want, or just pick a preferred one.
The ``QueryBuilder`` is not an abstraction of DQL, but merely a tool to dynamically build it.
You should still use plain DQL when you can, as it is simpler and more readable.
More about this in the :doc:`FAQ <faq>`_.
More about this in the :doc:`FAQ <faq>`.
Constructing a new QueryBuilder object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -7,24 +7,10 @@ Doctrine Console
The Doctrine Console is a Command Line Interface tool for simplifying common
administration tasks during the development of a project that uses ORM.
Take a look at the :doc:`Installation and Configuration <configuration>`
chapter for more information how to setup the console command.
For the following examples, we will set up the CLI as ``bin/doctrine``.
Display Help Information
~~~~~~~~~~~~~~~~~~~~~~~~
Type ``php vendor/bin/doctrine`` on the command line and you should see an
overview of the available commands or use the --help flag to get
information on the available commands. If you want to know more
about the use of generate entities for example, you can call:
.. code-block:: php
$> php vendor/bin/doctrine orm:generate-entities --help
Configuration
~~~~~~~~~~~~~
Setting Up the Console
~~~~~~~~~~~~~~~~~~~~~~
Whenever the ``doctrine`` command line tool is invoked, it can
access all Commands that were registered by a developer. There is no
@@ -34,25 +20,33 @@ Doctrine DBAL and ORM. If you want to use additional commands you
have to register them yourself.
All the commands of the Doctrine Console require access to the
``EntityManager``. You have to inject it into the console application with
``ConsoleRunner::createHelperSet``. Whenever you invoke the Doctrine
binary, it searches the current directory for the file ``cli-config.php``.
This file contains the project-specific configuration.
``EntityManager``. You have to inject it into the console application.
Here is an example of a the project-specific ``cli-config.php``:
Here is an example of a the project-specific ``bin/doctrine`` binary.
.. code-block:: php
#!/usr/bin/env php
<?php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
// replace this with the path to your own project bootstrap file.
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
// replace with path to your own project bootstrap file
require_once 'bootstrap.php';
// replace with mechanism to retrieve EntityManager in your app
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
$commands = [
// If you want to add your own custom console commands,
// you can do so here.
];
ConsoleRunner::run(
new SingleManagerProvider($entityManager),
$commands
);
.. note::
@@ -60,6 +54,18 @@ Here is an example of a the project-specific ``cli-config.php``:
and use their facilities to access the Doctrine EntityManager and
Connection Resources.
Display Help Information
~~~~~~~~~~~~~~~~~~~~~~~~
Type ``php bin/doctrine`` on the command line and you should see an
overview of the available commands or use the ``--help`` flag to get
information on the available commands. If you want to know more
about the use of generate entities for example, you can call:
::
$> php bin/doctrine orm:generate-entities --help
Command Overview
~~~~~~~~~~~~~~~~
@@ -143,7 +149,7 @@ When using the SchemaTool class directly, create your schema using
the ``createSchema()`` method. First create an instance of the
``SchemaTool`` and pass it an instance of the ``EntityManager``
that you want to use to create the schema. This method receives an
array of ``ClassMetadataInfo`` instances.
array of ``ClassMetadata`` instances.
.. code-block:: php
@@ -174,8 +180,8 @@ tables of the current model to clean up with orphaned tables.
You can also use database introspection to update your schema
easily with the ``updateSchema()`` method. It will compare your
existing database schema to the passed array of
``ClassMetadataInfo`` instances.
existing database schema to the passed array of ``ClassMetadata``
instances.
.. code-block:: php
@@ -189,38 +195,35 @@ To create the schema use the ``create`` command:
.. code-block:: php
$ php doctrine orm:schema-tool:create
$ php bin/doctrine orm:schema-tool:create
To drop the schema use the ``drop`` command:
.. code-block:: php
$ php doctrine orm:schema-tool:drop
$ php bin/doctrine orm:schema-tool:drop
If you want to drop and then recreate the schema then use both
options:
.. code-block:: php
$ php doctrine orm:schema-tool:drop
$ php doctrine orm:schema-tool:create
$ php bin/doctrine orm:schema-tool:drop
$ php bin/doctrine orm:schema-tool:create
As you would think, if you want to update your schema use the
``update`` command:
.. code-block:: php
$ php doctrine orm:schema-tool:update
$ php bin/doctrine orm:schema-tool:update
All of the above commands also accept a ``--dump-sql`` option that
will output the SQL for the ran operation.
.. code-block:: php
$ php doctrine orm:schema-tool:create --dump-sql
Before using the orm:schema-tool commands, remember to configure
your cli-config.php properly.
$ php bin/doctrine orm:schema-tool:create --dump-sql
Entity Generation
-----------------
@@ -229,9 +232,9 @@ Generate entity classes and method stubs from your mapping information.
.. code-block:: php
$ php doctrine orm:generate-entities
$ php doctrine orm:generate-entities --update-entities
$ php doctrine orm:generate-entities --regenerate-entities
$ php bin/doctrine orm:generate-entities
$ php bin/doctrine orm:generate-entities --update-entities
$ php bin/doctrine orm:generate-entities --regenerate-entities
This command is not suited for constant usage. It is a little helper and does
not support all the mapping edge cases very well. You still have to put work
@@ -316,14 +319,14 @@ convert to and the path to generate it:
.. code-block:: php
$ php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml
$ php bin/doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml
Reverse Engineering
-------------------
You can use the ``DatabaseDriver`` to reverse engineer a database
to an array of ``ClassMetadataInfo`` instances and generate YAML,
XML, etc. from them.
You can use the ``DatabaseDriver`` to reverse engineer a database to an
array of ``ClassMetadata`` instances and generate YAML, XML, etc. from
them.
.. note::
@@ -366,7 +369,7 @@ You can also reverse engineer a database using the
.. code-block:: php
$ php doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml
$ php bin/doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml
.. note::
@@ -393,6 +396,11 @@ You can either use the Doctrine Command Line Tool:
doctrine orm:validate-schema
If the validation fails, you can change the verbosity level to
check the detected errors:
doctrine orm:validate-schema -v
Or you can trigger the validation manually:
.. code-block:: php

View File

@@ -694,6 +694,7 @@ specified by their respective tags:
- ``<cascade-merge />``
- ``<cascade-remove />``
- ``<cascade-refresh />``
- ``<cascade-detach />``
Join Column Element
~~~~~~~~~~~~~~~~~~~

View File

@@ -81,10 +81,11 @@ that directory with the following contents:
{
"require": {
"doctrine/orm": "^2.10.2",
"doctrine/dbal": "^3.1.1",
"symfony/yaml": "2.*",
"symfony/cache": "^5.3"
"doctrine/orm": "^2.11.0",
"doctrine/dbal": "^3.2",
"doctrine/annotations": "1.13.2",
"symfony/yaml": "^5.4",
"symfony/cache": "^5.4"
},
"autoload": {
"psr-0": {"": "src/"}
@@ -136,8 +137,8 @@ step:
<?php
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
require_once "vendor/autoload.php";
@@ -146,10 +147,10 @@ step:
$proxyDir = null;
$cache = null;
$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 = ORMSetup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), $isDevMode, $proxyDir, $cache, $useSimpleAnnotationReader);
// or if you prefer YAML or XML
// $config = ORMSetup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
// $config = ORMSetup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
// database configuration parameters
$conn = array(
@@ -172,7 +173,7 @@ The ``require_once`` statement sets up the class autoloading for Doctrine and
its dependencies using Composer's autoloader.
The second block consists of the instantiation of the ORM
``Configuration`` object using the Setup helper. It assumes a bunch
``Configuration`` object using the ``ORMSetup`` helper. It assumes a bunch
of defaults that you don't have to bother about for now. You can
read up on the configuration details in the
:doc:`reference chapter on configuration <../reference/configuration>`.
@@ -190,22 +191,35 @@ Generating the Database Schema
Doctrine has a command-line interface that allows you to access the SchemaTool,
a component that can generate a relational database schema based entirely on the
defined entity classes and their metadata. For this tool to work, a
``cli-config.php`` file must exist in the project root directory:
defined entity classes and their metadata. For this tool to work, you need to
create an executable console script as described in the
:doc:`tools chapter <../reference/tools>`.
If you created the ``bootstrap.php`` file as described in the previous section,
that script could look like this:
.. code-block:: php
#!/usr/bin/env php
<?php
// cli-config.php
require_once "bootstrap.php";
// bin/doctrine
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
Now call the Doctrine command-line tool:
// Adjust this path to your actual bootstrap.php
require __DIR__ . 'path/to/your/bootstrap.php';
ConsoleRunner::run(
new SingleManagerProvider($entityManager)
);
In the following examples, we will assume that this script has been created as
``bin/doctrine``.
::
$ vendor/bin/doctrine orm:schema-tool:create
$ php bin/doctrine orm:schema-tool:create
Since we haven't added any entity metadata in ``src`` yet, you'll see a message
stating "No Metadata Classes to process." In the next section, we'll create a
@@ -217,14 +231,14 @@ You can easily recreate the database using the following commands:
::
$ vendor/bin/doctrine orm:schema-tool:drop --force
$ vendor/bin/doctrine orm:schema-tool:create
$ php bin/doctrine orm:schema-tool:drop --force
$ php bin/doctrine orm:schema-tool:create
Or you can use the update functionality:
::
$ vendor/bin/doctrine orm:schema-tool:update --force
$ php bin/doctrine orm:schema-tool:update --force
The updating of databases uses a diff algorithm for a given
database schema. This is a cornerstone of the ``Doctrine\DBAL`` package,
@@ -570,7 +584,7 @@ let's update the database schema:
::
$ vendor/bin/doctrine orm:schema-tool:update --force --dump-sql
$ php bin/doctrine orm:schema-tool:update --force --dump-sql
Specifying both flags ``--force`` and ``--dump-sql`` will cause the DDL
statements to be executed and then printed to the screen.
@@ -1267,7 +1281,7 @@ class that holds the owning sides.
Update your database schema by running:
::
$ vendor/bin/doctrine orm:schema-tool:update --force
$ php bin/doctrine orm:schema-tool:update --force
Implementing more Requirements

View File

@@ -4,11 +4,13 @@ declare(strict_types=1);
namespace Doctrine\ORM;
use BackedEnum;
use Countable;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Result;
use Doctrine\Deprecations\Deprecation;
@@ -371,11 +373,11 @@ abstract class AbstractQuery
/**
* Sets a query parameter.
*
* @param string|int $key The parameter position or name.
* @param mixed $value The parameter value.
* @param string|null $type The parameter type. If specified, the given value will be run through
* the type conversion of this type. This is usually not needed for
* strings and numeric types.
* @param string|int $key The parameter position or name.
* @param mixed $value The parameter value.
* @param string|int|null $type The parameter type. If specified, the given value will be run through
* the type conversion of this type. This is usually not needed for
* strings and numeric types.
*
* @return $this
*/
@@ -424,15 +426,20 @@ abstract class AbstractQuery
return $value->name;
}
if ($value instanceof BackedEnum) {
return $value->value;
}
if (! is_object($value)) {
return $value;
}
try {
$class = ClassUtils::getClass($value);
$value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
if ($value === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
}
} catch (MappingException | ORMMappingException $e) {
/* Silence any mapping exceptions. These can occur if the object in

View File

@@ -141,6 +141,8 @@ interface Cache
* Evicts all cached query results under the given name, or default query cache if the region name is NULL.
*
* @param string|null $regionName The cache name associated to the queries being cached.
*
* @return void
*/
public function evictQueryRegion($regionName = null);

View File

@@ -10,20 +10,26 @@ namespace Doctrine\ORM\Cache;
class AssociationCacheEntry implements CacheEntry
{
/**
* The entity identifier
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var array<string, mixed> The entity identifier
* @var array<string, mixed>
*/
public $identifier;
/**
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string The entity class name
* @var string
* @psalm-var class-string
*/
public $class;
/**
* @param string $class The entity class.
* @param array<string, mixed> $identifier The entity identifier.
* @psalm-param class-string $class
*/
public function __construct($class, array $identifier)
{

View File

@@ -25,6 +25,8 @@ class CacheException extends ORMException
}
/**
* @deprecated This method is not used anymore.
*
* @param string $entityName
*
* @return CacheException
@@ -45,6 +47,8 @@ class CacheException extends ORMException
}
/**
* @deprecated This method is not used anymore.
*
* @param string $entityName
* @param string $field
*

View File

@@ -31,9 +31,7 @@ interface CacheFactory
/**
* Build a collection persister for the given relation mapping.
*
* @param EntityManagerInterface $em The entity manager.
* @param CollectionPersister $persister The collection persister that will be cached.
* @param mixed[] $mapping The association mapping.
* @param mixed[] $mapping The association mapping.
*
* @return CachedCollectionPersister
*/
@@ -42,8 +40,7 @@ interface CacheFactory
/**
* Build a query cache based on the given region name
*
* @param EntityManagerInterface $em The Entity manager.
* @param string $regionName The region name.
* @param string|null $regionName The region name.
*
* @return QueryCache The built query cache.
*/
@@ -52,9 +49,6 @@ interface CacheFactory
/**
* Build an entity hydrator
*
* @param EntityManagerInterface $em The Entity manager.
* @param ClassMetadata $metadata The entity metadata.
*
* @return EntityHydrator The built entity hydrator.
*/
public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata);
@@ -62,8 +56,7 @@ interface CacheFactory
/**
* Build a collection hydrator
*
* @param EntityManagerInterface $em The Entity manager.
* @param mixed[] $mapping The association mapping.
* @param mixed[] $mapping The association mapping.
*
* @return CollectionHydrator The built collection hydrator.
*/

View File

@@ -11,8 +11,10 @@ namespace Doctrine\ORM\Cache;
abstract class CacheKey
{
/**
* Unique identifier
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string Unique identifier
* @var string
*/
public $hash;
}

View File

@@ -10,8 +10,10 @@ namespace Doctrine\ORM\Cache;
class CollectionCacheEntry implements CacheEntry
{
/**
* The list of entity identifiers hold by the collection
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var CacheKey[] The list of entity identifiers hold by the collection
* @var CacheKey[]
*/
public $identifiers;

View File

@@ -15,20 +15,27 @@ use function strtolower;
class CollectionCacheKey extends CacheKey
{
/**
* The owner entity identifier
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var array<string, mixed> The owner entity identifier
* @var array<string, mixed>
*/
public $ownerIdentifier;
/**
* The owner entity class
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string The owner entity class
* @var string
* @psalm-var class-string
*/
public $entityClass;
/**
* The association name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string The association name
* @var string
*/
public $association;
@@ -36,6 +43,7 @@ class CollectionCacheKey extends CacheKey
* @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.
* @psalm-param class-string $entityClass
*/
public function __construct($entityClass, $association, array $ownerIdentifier)
{

View File

@@ -14,8 +14,6 @@ use Doctrine\ORM\PersistentCollection;
interface CollectionHydrator
{
/**
* @param ClassMetadata $metadata The entity metadata.
* @param CollectionCacheKey $key The cached collection key.
* @param array|mixed[]|Collection $collection The collection.
*
* @return CollectionCacheEntry
@@ -23,11 +21,6 @@ interface CollectionHydrator
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection);
/**
* @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 mixed[]|null
*/
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection);

View File

@@ -29,7 +29,10 @@ class DefaultCache implements Cache
/** @var CacheFactory */
private $cacheFactory;
/** @var QueryCache[] */
/**
* @var QueryCache[]
* @psalm-var array<string, QueryCache>
*/
private $queryCaches = [];
/** @var QueryCache|null */
@@ -260,8 +263,7 @@ class DefaultCache implements Cache
}
/**
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
* @param mixed $identifier The entity identifier.
*/
private function buildEntityCacheKey(ClassMetadata $metadata, $identifier): EntityCacheKey
{
@@ -273,9 +275,7 @@ class DefaultCache implements Cache
}
/**
* @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.
* @param mixed $ownerIdentifier The identifier of the owning entity.
*/
private function buildCollectionCacheKey(
ClassMetadata $metadata,
@@ -290,18 +290,20 @@ class DefaultCache implements Cache
}
/**
* @param ClassMetadata $metadata The entity metadata.
* @param mixed $identifier The entity identifier.
* @param mixed $identifier The entity identifier.
*
* @return array<string, mixed>
*/
private function toIdentifierArray(ClassMetadata $metadata, $identifier): array
{
if (is_object($identifier) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($identifier))) {
$identifier = $this->uow->getSingleIdentifierValue($identifier);
if (is_object($identifier)) {
$class = ClassUtils::getClass($identifier);
if ($this->em->getMetadataFactory()->hasMetadataFor($class)) {
$identifier = $this->uow->getSingleIdentifierValue($identifier);
if ($identifier === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
if ($identifier === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($class);
}
}
}

View File

@@ -384,7 +384,8 @@ class DefaultQueryCache implements QueryCache
/**
* @param object $entity
*
* @return array<object>|object
* @return mixed[]|object|null
* @psalm-return list<mixed>|object|null
*/
private function getAssociationValue(
ResultSetMapping $rsm,
@@ -411,10 +412,11 @@ class DefaultQueryCache implements QueryCache
}
/**
* @param mixed $value
* @param array<mixed> $path
* @param mixed $value
* @psalm-param array<array-key, array{field: string, class: string}> $path
*
* @return mixed
* @return mixed[]|object|null
* @psalm-return list<mixed>|object|null
*/
private function getAssociationPathValue($value, array $path)
{

View File

@@ -14,14 +14,18 @@ use function array_map;
class EntityCacheEntry implements CacheEntry
{
/**
* The entity map data
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var array<string,mixed> The entity map data
* @var array<string,mixed>
*/
public $data;
/**
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string The entity class name
* @var string
* @psalm-var class-string
*/
public $class;

View File

@@ -15,20 +15,26 @@ use function strtolower;
class EntityCacheKey extends CacheKey
{
/**
* The entity identifier
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var array<string, mixed> The entity identifier
* @var array<string, mixed>
*/
public $identifier;
/**
* The entity class name
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var string The entity class name
* @var string
* @psalm-var class-string
*/
public $entityClass;
/**
* @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
* @psalm-param class-string $entityClass
*/
public function __construct($entityClass, array $identifier)
{

View File

@@ -24,7 +24,7 @@ interface EntityHydrator
* @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.
* @param object|null $entity The entity to load the cache into. If not specified, a new entity is created.
*
* @return object|null
*/

View File

@@ -18,6 +18,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*
* @return void
*/
public function entityCachePut($regionName, EntityCacheKey $key);
@@ -26,6 +28,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*
* @return void
*/
public function entityCacheHit($regionName, EntityCacheKey $key);
@@ -34,6 +38,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param EntityCacheKey $key The cache key of the entity.
*
* @return void
*/
public function entityCacheMiss($regionName, EntityCacheKey $key);
@@ -42,6 +48,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*
* @return void
*/
public function collectionCachePut($regionName, CollectionCacheKey $key);
@@ -50,6 +58,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*
* @return void
*/
public function collectionCacheHit($regionName, CollectionCacheKey $key);
@@ -58,6 +68,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param CollectionCacheKey $key The cache key of the collection.
*
* @return void
*/
public function collectionCacheMiss($regionName, CollectionCacheKey $key);
@@ -66,6 +78,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*
* @return void
*/
public function queryCachePut($regionName, QueryCacheKey $key);
@@ -74,6 +88,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*
* @return void
*/
public function queryCacheHit($regionName, QueryCacheKey $key);
@@ -82,6 +98,8 @@ interface CacheLogger
*
* @param string $regionName The name of the cache region.
* @param QueryCacheKey $key The cache key of the query.
*
* @return void
*/
public function queryCacheMiss($regionName, QueryCacheKey $key);
}

View File

@@ -10,7 +10,7 @@ use Doctrine\ORM\Cache\QueryCacheKey;
class CacheLoggerChain implements CacheLogger
{
/** @var array<CacheLogger> */
/** @var array<string, CacheLogger> */
private $loggers = [];
/**
@@ -34,7 +34,7 @@ class CacheLoggerChain implements CacheLogger
}
/**
* @return array<CacheLogger>
* @return array<string, CacheLogger>
*/
public function getLoggers()
{

View File

@@ -15,13 +15,13 @@ use function array_sum;
*/
class StatisticsCacheLogger implements CacheLogger
{
/** @var int[] */
/** @var array<string, int> */
private $cacheMissCountMap = [];
/** @var int[] */
/** @var array<string, int> */
private $cacheHitCountMap = [];
/** @var int[] */
/** @var array<string, int> */
private $cachePutCountMap = [];
/**
@@ -29,9 +29,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function collectionCacheMiss($regionName, CollectionCacheKey $key)
{
$this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName])
? $this->cacheMissCountMap[$regionName] + 1
: 1;
$this->cacheMissCountMap[$regionName]
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -39,9 +38,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function collectionCacheHit($regionName, CollectionCacheKey $key)
{
$this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName])
? $this->cacheHitCountMap[$regionName] + 1
: 1;
$this->cacheHitCountMap[$regionName]
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -49,9 +47,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function collectionCachePut($regionName, CollectionCacheKey $key)
{
$this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName])
? $this->cachePutCountMap[$regionName] + 1
: 1;
$this->cachePutCountMap[$regionName]
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -59,9 +56,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function entityCacheMiss($regionName, EntityCacheKey $key)
{
$this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName])
? $this->cacheMissCountMap[$regionName] + 1
: 1;
$this->cacheMissCountMap[$regionName]
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -69,9 +65,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function entityCacheHit($regionName, EntityCacheKey $key)
{
$this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName])
? $this->cacheHitCountMap[$regionName] + 1
: 1;
$this->cacheHitCountMap[$regionName]
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -79,9 +74,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function entityCachePut($regionName, EntityCacheKey $key)
{
$this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName])
? $this->cachePutCountMap[$regionName] + 1
: 1;
$this->cachePutCountMap[$regionName]
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -89,9 +83,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function queryCacheHit($regionName, QueryCacheKey $key)
{
$this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName])
? $this->cacheHitCountMap[$regionName] + 1
: 1;
$this->cacheHitCountMap[$regionName]
= ($this->cacheHitCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -99,9 +92,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function queryCacheMiss($regionName, QueryCacheKey $key)
{
$this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName])
? $this->cacheMissCountMap[$regionName] + 1
: 1;
$this->cacheMissCountMap[$regionName]
= ($this->cacheMissCountMap[$regionName] ?? 0) + 1;
}
/**
@@ -109,9 +101,8 @@ class StatisticsCacheLogger implements CacheLogger
*/
public function queryCachePut($regionName, QueryCacheKey $key)
{
$this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName])
? $this->cachePutCountMap[$regionName] + 1
: 1;
$this->cachePutCountMap[$regionName]
= ($this->cachePutCountMap[$regionName] ?? 0) + 1;
}
/**

View File

@@ -8,6 +8,8 @@ namespace Doctrine\ORM\Cache;
* Defines a region that supports multi-get reading.
*
* With one method call we can get multiple items.
*
* @deprecated Implement {@see Region} instead.
*/
interface MultiGetRegion
{

View File

@@ -13,11 +13,15 @@ interface CachedPersister
{
/**
* Perform whatever processing is encapsulated here after completion of the transaction.
*
* @return void
*/
public function afterTransactionComplete();
/**
* Perform whatever processing is encapsulated here after completion of the rolled-back.
*
* @return void
*/
public function afterTransactionRolledBack();

View File

@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Cache\Persister\Collection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Cache\CollectionCacheKey;
use Doctrine\ORM\Cache\CollectionHydrator;
use Doctrine\ORM\Cache\EntityCacheKey;
@@ -108,7 +109,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
}
/**
* @return object[]|null
* {@inheritdoc}
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key)
{
@@ -224,10 +225,18 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
/**
* Clears cache entries related to the current collection
*
* @deprecated This method is not used anymore.
*
* @return void
*/
protected function evictCollectionCache(PersistentCollection $collection)
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9512',
'The method %s() is deprecated and will be removed without replacement.'
);
$key = new CollectionCacheKey(
$this->sourceEntity->rootEntityName,
$this->association['fieldName'],
@@ -242,13 +251,22 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
}
/**
* @deprecated This method is not used anymore.
*
* @param string $targetEntity
* @param object $element
* @psalm-param class-string $targetEntity
*
* @return void
*/
protected function evictElementCache($targetEntity, $element)
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9512',
'The method %s() is deprecated and will be removed without replacement.'
);
$targetPersister = $this->uow->getEntityPersister($targetEntity);
assert($targetPersister instanceof CachedEntityPersister);
$targetRegion = $targetPersister->getCacheRegion();

View File

@@ -29,14 +29,14 @@ interface CachedCollectionPersister extends CachedPersister, CollectionPersister
/**
* Loads a collection from cache
*
* @return PersistentCollection|null
* @return mixed[]|null
*/
public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key);
/**
* Stores a collection into cache
*
* @param array|mixed[]|Collection $elements
* @param mixed[]|Collection $elements
*
* @return void
*/

View File

@@ -15,10 +15,7 @@ use function spl_object_id;
class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister
{
/**
* @param CollectionPersister $persister The collection persister that will be cached.
* @param ConcurrentRegion $region The collection region.
* @param EntityManagerInterface $em The entity manager.
* @param mixed[] $association The association mapping.
* @param mixed[] $association The association mapping.
*/
public function __construct(CollectionPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, array $association)
{

View File

@@ -251,8 +251,8 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
* @param string $query
* @param string[]|Criteria $criteria
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @param int|null $limit
* @param int|null $offset
*
* @return string
*/

View File

@@ -15,12 +15,6 @@ use Doctrine\ORM\Persisters\Entity\EntityPersister;
*/
class ReadWriteCachedEntityPersister extends AbstractEntityPersister
{
/**
* @param EntityPersister $persister The entity persister to cache.
* @param ConcurrentRegion $region The entity cache region.
* @param EntityManagerInterface $em The entity manager.
* @param ClassMetadata $class The entity metadata.
*/
public function __construct(EntityPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, ClassMetadata $class)
{
parent::__construct($persister, $region, $em, $class);

View File

@@ -12,20 +12,24 @@ use function microtime;
class QueryCacheEntry implements CacheEntry
{
/**
* List of entity identifiers
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var array<string, mixed> List of entity identifiers
* @var array<string, mixed>
*/
public $result;
/**
* Time creation of this cache entry
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var float Time creation of this cache entry
* @var float
*/
public $time;
/**
* @param array<string, mixed> $result
* @param float $time
* @param float|null $time
*/
public function __construct($result, $time = null)
{

View File

@@ -12,8 +12,10 @@ use Doctrine\ORM\Cache;
class QueryCacheKey extends CacheKey
{
/**
* Cache key lifetime
*
* @readonly Public only for performance reasons, it should be considered immutable.
* @var int Cache key lifetime
* @var int
*/
public $lifetime;

View File

@@ -18,7 +18,7 @@ class TimestampCacheEntry implements CacheEntry
public $time;
/**
* @param float $time
* @param float|null $time
*/
public function __construct($time = null)
{

View File

@@ -10,9 +10,9 @@ namespace Doctrine\ORM\Cache;
interface TimestampRegion extends Region
{
/**
* Update an specific key into the cache region.
* Update a specific key into the cache region.
*
* @param CacheKey $key The key of the item to update the timestamp.
* @return void
*
* @throws LockException Indicates a problem accessing the region.
*/

View File

@@ -12,6 +12,7 @@ use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\Cache as CacheDriver;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Cache\CacheConfiguration;
@@ -23,6 +24,7 @@ use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache;
use Doctrine\ORM\Exception\InvalidEntityRepository;
use Doctrine\ORM\Exception\NamedNativeQueryNotFound;
use Doctrine\ORM\Exception\NamedQueryNotFound;
use Doctrine\ORM\Exception\NotSupported;
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
use Doctrine\ORM\Exception\UnknownEntityNamespace;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
@@ -34,16 +36,20 @@ use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\EntityListenerResolver;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Query\Filter\SQLFilter;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\ObjectRepository;
use LogicException;
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;
use function class_exists;
use function is_a;
use function method_exists;
use function sprintf;
use function strtolower;
use function trim;
@@ -52,6 +58,8 @@ use function trim;
* It combines all configuration options from DBAL & ORM.
*
* Internal note: When adding a new configuration option just write a getter/setter pair.
*
* @psalm-import-type AutogenerateMode from ProxyFactory
*/
class Configuration extends \Doctrine\DBAL\Configuration
{
@@ -73,10 +81,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the directory where Doctrine generates any necessary proxy class files.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return string|null
*/
public function getProxyDir()
@@ -87,11 +91,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the strategy for automatically generating proxy classes.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return int Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory.
* @psalm-return AutogenerateMode
*/
public function getAutoGenerateProxyClasses()
{
@@ -114,10 +115,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
/**
* Gets the namespace where proxy classes reside.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return string|null
*/
public function getProxyNamespace()
@@ -154,6 +151,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader
* is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported.
*
* @deprecated Use {@see ORMSetup::createDefaultAnnotationDriver()} instead.
*
* @param string|string[] $paths
* @param bool $useSimpleAnnotationReader
* @psalm-param string|list<string> $paths
@@ -162,6 +161,22 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function newDefaultAnnotationDriver($paths = [], $useSimpleAnnotationReader = true)
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9443',
'%s is deprecated, call %s::createDefaultAnnotationDriver() instead.',
__METHOD__,
ORMSetup::class
);
if (! class_exists(AnnotationReader::class)) {
throw new LogicException(sprintf(
'The annotation metadata driver cannot be enabled because the "doctrine/annotations" library'
. ' is not installed. Please run "composer require doctrine/annotations" or choose a different'
. ' metadata driver.'
));
}
AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php');
if ($useSimpleAnnotationReader) {
@@ -183,6 +198,8 @@ class Configuration extends \Doctrine\DBAL\Configuration
}
/**
* @deprecated No replacement planned.
*
* Adds a namespace under a certain alias.
*
* @param string $alias
@@ -192,6 +209,21 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function addEntityNamespace($alias, $namespace)
{
if (class_exists(PersistentObject::class)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/8818',
'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.',
$alias
);
} else {
NotSupported::createForPersistence3(sprintf(
'Using short namespace alias "%s" by calling %s',
$alias,
__METHOD__
));
}
$this->_attributes['entityNamespaces'][$alias] = $namespace;
}
@@ -538,7 +570,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
throw QueryCacheUsesNonPersistentCache::fromDriver($queryCacheImpl);
}
if ($this->getAutoGenerateProxyClasses()) {
if ($this->getAutoGenerateProxyClasses() !== AbstractProxyFactory::AUTOGENERATE_NEVER) {
throw ProxyClassesAlwaysRegenerating::create();
}
@@ -784,6 +816,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @param string $name The name of the filter.
* @param string $className The class name of the filter.
* @psalm-param class-string<SQLFilter> $className
*
* @return void
*/
@@ -799,7 +832,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
*
* @return string|null The class name of the filter, or null if it is not
* defined.
* @psalm-return ?class-string
* @psalm-return class-string<SQLFilter>|null
*/
public function getFilterClassName($name)
{
@@ -810,6 +843,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Sets default repository class.
*
* @param string $className
* @psalm-param class-string<EntityRepository> $className
*
* @return void
*
@@ -817,12 +851,20 @@ class Configuration extends \Doctrine\DBAL\Configuration
*/
public function setDefaultRepositoryClassName($className)
{
$reflectionClass = new ReflectionClass($className);
if (! $reflectionClass->implementsInterface(ObjectRepository::class)) {
if (! class_exists($className) || ! is_a($className, ObjectRepository::class, true)) {
throw InvalidEntityRepository::fromClassName($className);
}
if (! is_a($className, EntityRepository::class, true)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9533',
'Configuring %s as default repository class is deprecated because it does not extend %s.',
$className,
EntityRepository::class
);
}
$this->_attributes['defaultRepositoryClassName'] = $className;
}
@@ -830,7 +872,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
* Get default repository class.
*
* @return string
* @psalm-return class-string
* @psalm-return class-string<EntityRepository>
*/
public function getDefaultRepositoryClassName()
{

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Decorator;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Persistence\ObjectManagerDecorator;
@@ -43,6 +44,28 @@ abstract class EntityManagerDecorator extends ObjectManagerDecorator implements
return $this->wrapped->getExpressionBuilder();
}
/**
* {@inheritdoc}
*
* @psalm-param class-string<T> $className
*
* @psalm-return EntityRepository<T>
*
* @template T of object
*/
public function getRepository($className)
{
return $this->wrapped->getRepository($className);
}
/**
* {@inheritdoc}
*/
public function getClassMetadata($className)
{
return $this->wrapped->getClassMetadata($className);
}
/**
* {@inheritdoc}
*/

View File

@@ -4,9 +4,11 @@ declare(strict_types=1);
namespace Doctrine\ORM;
use BackedEnum;
use BadMethodCallException;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\EventManager;
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
@@ -17,6 +19,8 @@ use Doctrine\ORM\Exception\InvalidHydrationMode;
use Doctrine\ORM\Exception\MismatchedEventManager;
use Doctrine\ORM\Exception\MissingIdentifierField;
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
use Doctrine\ORM\Exception\NotSupported;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
@@ -32,6 +36,7 @@ use Throwable;
use function array_keys;
use function call_user_func;
use function class_exists;
use function get_debug_type;
use function gettype;
use function is_array;
@@ -40,6 +45,7 @@ use function is_object;
use function is_string;
use function ltrim;
use function sprintf;
use function strpos;
/**
* The EntityManager is the central access point to ORM functionality.
@@ -59,7 +65,7 @@ use function sprintf;
* $entityManager = EntityManager::create($dbParams, $config);
*
* For more information see
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
* {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/stable/reference/configuration.html}
*
* You should never attempt to inherit from the EntityManager: Inheritance
* is not a valid extension point for the EntityManager. Instead you
@@ -432,11 +438,14 @@ use function sprintf;
}
foreach ($id as $i => $value) {
if (is_object($value) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($value))) {
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
if (is_object($value)) {
$className = ClassUtils::getClass($value);
if ($this->metadataFactory->hasMetadataFor($className)) {
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
if ($id[$i] === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
if ($id[$i] === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($className);
}
}
}
}
@@ -448,7 +457,12 @@ use function sprintf;
throw MissingIdentifierField::fromFieldAndClass($identifier, $class->name);
}
$sortedId[$identifier] = $id[$identifier];
if ($id[$identifier] instanceof BackedEnum) {
$sortedId[$identifier] = $id[$identifier]->value;
} else {
$sortedId[$identifier] = $id[$identifier];
}
unset($id[$identifier]);
}
@@ -778,11 +792,39 @@ use function sprintf;
* @return ObjectRepository|EntityRepository The repository class.
* @psalm-return EntityRepository<T>
*
* @template T
* @template T of object
*/
public function getRepository($entityName)
{
return $this->repositoryFactory->getRepository($this, $entityName);
if (strpos($entityName, ':') !== false) {
if (class_exists(PersistentObject::class)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/issues/8818',
'Short namespace aliases such as "%s" are deprecated and will be removed in Doctrine ORM 3.0.',
$entityName
);
} else {
NotSupported::createForPersistence3(sprintf(
'Using short namespace alias "%s" when calling %s',
$entityName,
__METHOD__
));
}
}
$repository = $this->repositoryFactory->getRepository($this, $entityName);
if (! $repository instanceof EntityRepository) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9533',
'Not returning an instance of %s from %s::getRepository() is deprecated and will cause a TypeError on 3.0.',
EntityRepository::class,
get_debug_type($this->repositoryFactory)
);
}
return $repository;
}
/**

View File

@@ -9,6 +9,7 @@ use DateTimeInterface;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Query\Expr;
@@ -31,7 +32,7 @@ interface EntityManagerInterface extends ObjectManager
*
* @psalm-return EntityRepository<T>
*
* @template T
* @template T of object
*/
public function getRepository($className);

View File

@@ -5,22 +5,25 @@ declare(strict_types=1);
namespace Doctrine\ORM;
use BadMethodCallException;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\AbstractLazyCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Selectable;
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\DBAL\LockMode;
use Doctrine\Deprecations\Deprecation;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Exception\NotSupported;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
use Doctrine\Persistence\ObjectRepository;
use function array_slice;
use function class_exists;
use function lcfirst;
use function sprintf;
use function strpos;
use function str_starts_with;
use function substr;
/**
@@ -30,28 +33,42 @@ use function substr;
* This class is designed for inheritance and users can subclass this class to
* write their own repositories with business-specific methods to locate entities.
*
* @template T
* @template T of object
* @template-implements Selectable<int,T>
* @template-implements ObjectRepository<T>
*/
class EntityRepository implements ObjectRepository, Selectable
{
/** @var string */
/**
* @internal This property will be private in 3.0, call {@see getEntityName()} instead.
*
* @var string
* @psalm-var class-string<T>
*/
protected $_entityName;
/** @var EntityManagerInterface */
/**
* @internal This property will be private in 3.0, call {@see getEntityManager()} instead.
*
* @var EntityManagerInterface
*/
protected $_em;
/** @var ClassMetadata */
/**
* @internal This property will be private in 3.0, call {@see getClassMetadata()} instead.
*
* @var ClassMetadata
* @psalm-var ClassMetadata<T>
*/
protected $_class;
/** @var Inflector */
/** @var Inflector|null */
private static $inflector;
/**
* Initializes a new <tt>EntityRepository</tt>.
* @psalm-param ClassMetadata<T> $class
*/
public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
public function __construct(EntityManagerInterface $em, ClassMetadata $class)
{
$this->_entityName = $class->name;
$this->_em = $em;
@@ -61,8 +78,8 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
*
* @param string $alias
* @param string $indexBy The index for the from.
* @param string $alias
* @param string|null $indexBy The index for the from.
*
* @return QueryBuilder
*/
@@ -154,6 +171,13 @@ class EntityRepository implements ObjectRepository, Selectable
__METHOD__
);
if (! class_exists(PersistentObject::class)) {
throw NotSupported::createForPersistence3(sprintf(
'Partial clearing of entities for class %s',
$this->_class->rootEntityName
));
}
$this->_em->clear($this->_class->rootEntityName);
}
@@ -246,15 +270,15 @@ class EntityRepository implements ObjectRepository, Selectable
*/
public function __call($method, $arguments)
{
if (strpos($method, 'findBy') === 0) {
if (str_starts_with($method, 'findBy')) {
return $this->resolveMagicCall('findBy', substr($method, 6), $arguments);
}
if (strpos($method, 'findOneBy') === 0) {
if (str_starts_with($method, 'findOneBy')) {
return $this->resolveMagicCall('findOneBy', substr($method, 9), $arguments);
}
if (strpos($method, 'countBy') === 0) {
if (str_starts_with($method, 'countBy')) {
return $this->resolveMagicCall('count', substr($method, 7), $arguments);
}
@@ -267,6 +291,7 @@ class EntityRepository implements ObjectRepository, Selectable
/**
* @return string
* @psalm-return class-string<T>
*/
protected function getEntityName()
{
@@ -274,7 +299,7 @@ class EntityRepository implements ObjectRepository, Selectable
}
/**
* @return string
* {@inheritdoc}
*/
public function getClassName()
{
@@ -290,7 +315,8 @@ class EntityRepository implements ObjectRepository, Selectable
}
/**
* @return Mapping\ClassMetadata
* @return ClassMetadata
* @psalm-return ClassMetadata<T>
*/
protected function getClassMetadata()
{
@@ -301,8 +327,8 @@ class EntityRepository implements ObjectRepository, Selectable
* Select all elements from a selectable that match the expression and
* return a new collection containing these elements.
*
* @return LazyCriteriaCollection
* @psalm-return Collection<int, T>
* @return AbstractLazyCollection
* @psalm-return AbstractLazyCollection<int, T>&Selectable<int, T>
*/
public function matching(Criteria $criteria)
{

View File

@@ -11,7 +11,7 @@ use Doctrine\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs;
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
* of entities.
*
* @link www.doctrine-project.org
* @extends BaseLifecycleEventArgs<EntityManagerInterface>
*/
class LifecycleEventArgs extends BaseLifecycleEventArgs
{

View File

@@ -11,8 +11,7 @@ use Doctrine\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetada
/**
* Class that holds event arguments for a loadMetadata event.
*
* @method __construct(ClassMetadata $classMetadata, EntityManagerInterface $objectManager)
* @method ClassMetadata getClassMetadata()
* @extends BaseLoadClassMetadataEventArgs<ClassMetadata<object>, EntityManagerInterface>
*/
class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs
{

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Event;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\Event\ManagerEventArgs;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\ObjectManager;
@@ -13,6 +14,8 @@ use Doctrine\Persistence\ObjectManager;
*
* This object is mutable by design, allowing callbacks having access to it to set the
* found metadata in it, and therefore "cancelling" a `onClassMetadataNotFound` event
*
* @extends ManagerEventArgs<EntityManagerInterface>
*/
class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs
{
@@ -23,7 +26,8 @@ class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs
private $foundMetadata;
/**
* @param string $className
* @param string $className
* @param EntityManagerInterface $objectManager
*/
public function __construct($className, ObjectManager $objectManager)
{

View File

@@ -42,6 +42,8 @@ class OnClearEventArgs extends EventArgs
/**
* Name of the entity class that is cleared, or empty if all are cleared.
*
* @deprecated Clearing the entity manager partially is deprecated. This method will be removed in 3.0.
*
* @return string|null
*/
public function getEntityClass()
@@ -52,6 +54,8 @@ class OnClearEventArgs extends EventArgs
/**
* Checks if event clears all entities.
*
* @deprecated Clearing the entity manager partially is deprecated. This method will be removed in 3.0.
*
* @return bool
*/
public function clearsAllEntities()

View File

@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace Doctrine\ORM\Exception;
use Doctrine\Persistence\ObjectRepository;
use Doctrine\ORM\EntityRepository;
final class InvalidEntityRepository extends ORMException implements ConfigurationException
{
public static function fromClassName(string $className): self
{
return new self(
"Invalid repository class '" . $className . "'. It must be a " . ObjectRepository::class . '.'
"Invalid repository class '" . $className . "'. It must be a " . EntityRepository::class . '.'
);
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Doctrine\ORM\Exception;
use function sprintf;
final class NotSupported extends ORMException
{
public static function create(): self
@@ -11,8 +13,29 @@ final class NotSupported extends ORMException
return new self('This behaviour is (currently) not supported by Doctrine 2');
}
public static function createForDbal3(): self
public static function createForDbal3(string $context): self
{
return new self('Feature was deprecated in doctrine/dbal 2.x and is not supported by installed doctrine/dbal:3.x, please see the doctrine/deprecations logs for new alternative approaches.');
return new self(sprintf(
<<<'EXCEPTION'
Context: %s
Problem: Feature was deprecated in doctrine/dbal 2.x and is not supported by installed doctrine/dbal:3.x
Solution: See the doctrine/deprecations logs for new alternative approaches.
EXCEPTION
,
$context
));
}
public static function createForPersistence3(string $context): self
{
return new self(sprintf(
<<<'EXCEPTION'
Context: %s
Problem: Feature was deprecated in doctrine/persistence 2.x and is not supported by installed doctrine/persistence:3.x
Solution: See the doctrine/deprecations logs for new alternative approaches.
EXCEPTION
,
$context
));
}
}

View File

@@ -6,6 +6,9 @@ namespace Doctrine\ORM\Exception;
use function sprintf;
/**
* @deprecated No replacement planned.
*/
final class UnknownEntityNamespace extends ORMException implements ConfigurationException
{
public static function fromNamespaceAlias(string $entityNamespaceAlias): self

View File

@@ -62,7 +62,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
$connection->ensureConnectedToPrimary();
}
$this->_nextValue = (int) $connection->executeQuery($sql)->fetchOne();
$this->_nextValue = (int) $connection->fetchOne($sql);
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
}

View File

@@ -11,6 +11,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Exception\NotSupported;
use function method_exists;
use function sprintf;
/**
* Represents an ID generator that uses the database UUID expression
@@ -29,7 +30,10 @@ class UuidGenerator extends AbstractIdGenerator
);
if (! method_exists(AbstractPlatform::class, 'getGuidExpression')) {
throw NotSupported::createForDbal3();
throw NotSupported::createForDbal3(sprintf(
'Using the database to generate a UUID through %s',
self::class
));
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Internal\Hydration;
use BackedEnum;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\ForwardCompatibility\Result as ForwardCompatibilityResult;
use Doctrine\DBAL\Platforms\AbstractPlatform;
@@ -27,6 +28,7 @@ use function count;
use function end;
use function get_debug_type;
use function in_array;
use function is_array;
use function sprintf;
/**
@@ -429,7 +431,20 @@ abstract class AbstractHydrator
$type = $cacheKeyInfo['type'];
$value = $type->convertToPHPValue($value, $this->_platform);
// Reimplement ReflectionEnumProperty code
if ($value !== null && isset($cacheKeyInfo['enumType'])) {
$enumType = $cacheKeyInfo['enumType'];
if (is_array($value)) {
$value = array_map(static function ($value) use ($enumType): BackedEnum {
return $enumType::from($value);
}, $value);
} else {
$value = $enumType::from($value);
}
}
$rowData['scalars'][$fieldName] = $value;
break;
//case (isset($cacheKeyInfo['isMetaColumn'])):
@@ -572,6 +587,7 @@ abstract class AbstractHydrator
'fieldName' => $this->_rsm->scalarMappings[$key],
'type' => Type::getType($this->_rsm->typeMappings[$key]),
'dqlAlias' => '',
'enumType' => $this->_rsm->enumMappings[$key] ?? null,
];
case isset($this->_rsm->scalarMappings[$key]):
@@ -579,6 +595,7 @@ abstract class AbstractHydrator
'isScalar' => true,
'fieldName' => $this->_rsm->scalarMappings[$key],
'type' => Type::getType($this->_rsm->typeMappings[$key]),
'enumType' => $this->_rsm->enumMappings[$key] ?? null,
];
case isset($this->_rsm->metaMappings[$key]):

View File

@@ -84,17 +84,17 @@ class HydrationException extends ORMException
/**
* @param string $discrValue
* @param string[] $discrMap
* @psalm-param array<string, string> $discrMap
* @param string[] $discrValues
* @psalm-param list<string> $discrValues
*
* @return HydrationException
*/
public static function invalidDiscriminatorValue($discrValue, $discrMap)
public static function invalidDiscriminatorValue($discrValue, $discrValues)
{
return new self(sprintf(
'The discriminator value "%s" is invalid. It must be one of "%s".',
$discrValue,
implode('", "', $discrMap)
implode('", "', $discrValues)
));
}
}

View File

@@ -394,8 +394,11 @@ class ObjectHydrator extends AbstractHydrator
$reflFieldValue->hydrateSet($indexValue, $element);
$this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
} else {
$reflFieldValue->hydrateAdd($element);
$reflFieldValue->last();
if (! $reflFieldValue->contains($element)) {
$reflFieldValue->hydrateAdd($element);
$reflFieldValue->last();
}
$this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
}

View File

@@ -7,6 +7,7 @@ namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\ORM\Exception\MultipleSelectorsFoundException;
use function array_column;
use function count;
/**
@@ -26,12 +27,8 @@ final class ScalarColumnHydrator extends AbstractHydrator
throw MultipleSelectorsFoundException::create($this->resultSetMapping()->fieldMappings);
}
$result = [];
$result = $this->statement()->fetchAllNumeric();
while ($row = $this->statement()->fetchOne()) {
$result[] = $row;
}
return $result;
return array_column($result, 0);
}
}

View File

@@ -11,11 +11,18 @@ use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use ReturnTypeWillChange;
use function assert;
/**
* A lazy collection that allows a fast count when using criteria object
* Once count gets executed once without collection being initialized, result
* is cached and returned on subsequent calls until collection gets loaded,
* then returning the number of loaded results.
*
* @template TKey of array-key
* @template TValue of object
* @extends AbstractLazyCollection<TKey, TValue>
* @implements Selectable<TKey, TValue>
*/
class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable
{
@@ -72,6 +79,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
* Do an optimized search of an element
*
* @param object $element
* @psalm-param TValue $element
*
* @return bool
*/
@@ -90,6 +98,7 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
public function matching(Criteria $criteria)
{
$this->initialize();
assert($this->collection instanceof Selectable);
return $this->collection->matching($criteria);
}

View File

@@ -59,9 +59,9 @@ class ClassMetadataBuilder
/**
* Adds and embedded class
*
* @param string $fieldName
* @param string $class
* @param string|null $columnPrefix
* @param string $fieldName
* @param string $class
* @param string|false|null $columnPrefix
*
* @return $this
*/

View File

@@ -35,8 +35,8 @@ use function end;
use function explode;
use function in_array;
use function is_subclass_of;
use function str_contains;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
@@ -193,6 +193,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
$class->containsForeignIdentifier = true;
}
if ($parent->containsEnumIdentifier) {
$class->containsEnumIdentifier = true;
}
if (! empty($parent->namedQueries)) {
$this->addInheritedNamedQueries($class, $parent);
}
@@ -338,7 +342,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
*/
private function getShortName(string $className): string
{
if (strpos($className, '\\') === false) {
if (! str_contains($className, '\\')) {
return strtolower($className);
}
@@ -718,7 +722,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
}
/**
* {@inheritDoc}
* @deprecated This method will be removed in ORM 3.0.
*
* @return class-string
*/
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
{

View File

@@ -49,8 +49,8 @@ use function is_subclass_of;
use function ltrim;
use function method_exists;
use function spl_object_id;
use function str_contains;
use function str_replace;
use function strpos;
use function strtolower;
use function trait_exists;
use function trim;
@@ -519,7 +519,9 @@ class ClassMetadataInfo implements ClassMetadata
*
* @see discriminatorColumn
*
* @var mixed
* @var array<string, string>
*
* @psalm-var array<string, class-string>
*/
public $discriminatorMap = [];
@@ -543,10 +545,10 @@ class ClassMetadataInfo implements ClassMetadata
* @var mixed[]
* @psalm-var array{
* name: string,
* schema: string,
* indexes: array,
* uniqueConstraints: array,
* options: array<string, mixed>,
* schema?: string,
* indexes?: array,
* uniqueConstraints?: array,
* options?: array<string, mixed>,
* quoted?: bool
* }
*/
@@ -639,6 +641,15 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $containsForeignIdentifier = false;
/**
* READ-ONLY: Flag indicating whether the identifier/primary key contains at least one ENUM type.
*
* This flag is necessary because some code blocks require special treatment of this cases.
*
* @var bool
*/
public $containsEnumIdentifier = false;
/**
* READ-ONLY: The ID generator used for generating IDs for this class.
*
@@ -958,6 +969,10 @@ class ClassMetadataInfo implements ClassMetadata
$serialized[] = 'containsForeignIdentifier';
}
if ($this->containsEnumIdentifier) {
$serialized[] = 'containsEnumIdentifier';
}
if ($this->isVersioned) {
$serialized[] = 'isVersioned';
$serialized[] = 'versionField';
@@ -1056,9 +1071,19 @@ class ClassMetadataInfo implements ClassMetadata
foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
$childProperty = $this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']);
assert($childProperty !== null);
if (isset($mapping['enumType'])) {
$childProperty = new ReflectionEnumProperty(
$childProperty,
$mapping['enumType']
);
}
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
$parentReflFields[$mapping['declaredField']],
$this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']),
$childProperty,
$mapping['originalClass']
);
continue;
@@ -1517,7 +1542,7 @@ class ClassMetadataInfo implements ClassMetadata
! isset($mapping['type'])
&& ($type instanceof ReflectionNamedType)
) {
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName(), false)) {
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) {
$mapping['enumType'] = $type->getName();
$reflection = new ReflectionEnum($type->getName());
@@ -1665,6 +1690,10 @@ class ClassMetadataInfo implements ClassMetadata
if (! enum_exists($mapping['enumType'])) {
throw MappingException::nonEnumTypeMapped($this->name, $mapping['fieldName'], $mapping['enumType']);
}
if (! empty($mapping['id'])) {
$this->containsEnumIdentifier = true;
}
}
return $mapping;
@@ -2661,7 +2690,7 @@ class ClassMetadataInfo implements ClassMetadata
{
if (isset($table['name'])) {
// Split schema and table name from a table name like "myschema.mytable"
if (strpos($table['name'], '.') !== false) {
if (str_contains($table['name'], '.')) {
[$this->table['schema'], $table['name']] = explode('.', $table['name'], 2);
}
@@ -2907,7 +2936,7 @@ class ClassMetadataInfo implements ClassMetadata
if (! isset($field['column'])) {
$fieldName = $field['name'];
if (strpos($fieldName, '.')) {
if (str_contains($fieldName, '.')) {
[, $fieldName] = explode('.', $fieldName);
}
@@ -3691,7 +3720,7 @@ class ClassMetadataInfo implements ClassMetadata
return $className;
}
if (strpos($className, '\\') === false && $this->namespace) {
if (! str_contains($className, '\\') && $this->namespace) {
return $this->namespace . '\\' . $className;
}

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use function strpos;
use function str_contains;
use function strrpos;
use function strtolower;
use function substr;
@@ -21,7 +21,7 @@ class DefaultNamingStrategy implements NamingStrategy
*/
public function classToTableName($className)
{
if (strpos($className, '\\') !== false) {
if (str_contains($className, '\\')) {
return substr($className, strrpos($className, '\\') + 1);
}

View File

@@ -15,13 +15,13 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_CLASS)]
final class DiscriminatorColumn implements Annotation
{
/** @var string */
/** @var string|null */
public $name;
/** @var string */
/** @var string|null */
public $type;
/** @var int */
/** @var int|null */
public $length;
/**

View File

@@ -5,13 +5,15 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\Reader;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
@@ -29,8 +31,19 @@ use function is_numeric;
/**
* The AnnotationDriver reads the mapping metadata from docblock annotations.
*/
class AnnotationDriver extends AbstractAnnotationDriver
class AnnotationDriver extends CompatibilityAnnotationDriver
{
use ColocatedMappingDriver;
/**
* The annotation reader.
*
* @internal this property will be private in 3.0
*
* @var Reader
*/
protected $reader;
/**
* @var int[]
* @psalm-var array<class-string, int>
@@ -41,11 +54,29 @@ class AnnotationDriver extends AbstractAnnotationDriver
];
/**
* {@inheritDoc}
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
* docblock annotations.
*
* @param Reader $reader The AnnotationReader to use
* @param string|string[]|null $paths One or multiple paths where mapping classes can be found.
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function __construct($reader, $paths = null)
{
$this->reader = $reader;
$this->addPaths((array) $paths);
}
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
assert($metadata instanceof Mapping\ClassMetadata);
$class = $metadata->getReflectionClass()
// this happens when running annotation driver in combination with
// static reflection services. This is not the nicest fix
@@ -271,7 +302,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
);
if ($metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
$discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class];
@@ -281,7 +312,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
[
'name' => $discrColumnAnnot->name,
'type' => $discrColumnAnnot->type ?: 'string',
'length' => $discrColumnAnnot->length ?: 255,
'length' => $discrColumnAnnot->length ?? 255,
'columnDefinition' => $discrColumnAnnot->columnDefinition,
]
);
@@ -516,7 +547,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
private function loadRelationShipMapping(
ReflectionProperty $property,
array &$mapping,
ClassMetadata $metadata,
PersistenceClassMetadata $metadata,
array $joinColumns,
string $className
): void {
@@ -620,7 +651,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
/**
* Attempts to resolve the fetch mode.
*
* @psalm-return \Doctrine\ORM\Mapping\ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
* @psalm-return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
*
* @throws MappingException If the fetch mode is not valid.
*/
@@ -636,7 +667,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
/**
* Attempts to resolve the generated mode.
*
* @psalm-return ClassMetadataInfo::GENERATED_*
* @psalm-return ClassMetadata::GENERATED_*
*
* @throws MappingException If the fetch mode is not valid.
*/
@@ -736,7 +767,7 @@ class AnnotationDriver extends AbstractAnnotationDriver
* precision: int,
* notInsertable?: bool,
* notUpdateble?: bool,
* generated?: ClassMetadataInfo::GENERATED_*,
* generated?: ClassMetadata::GENERATED_*,
* enumType?: class-string,
* options?: mixed[],
* columnName?: string,
@@ -786,6 +817,39 @@ class AnnotationDriver extends AbstractAnnotationDriver
return $mapping;
}
/**
* Retrieve the current annotation reader
*
* @return Reader
*/
public function getReader()
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9587',
'%s is deprecated with no replacement',
__METHOD__
);
return $this->reader;
}
/**
* {@inheritDoc}
*/
public function isTransient($className)
{
$classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
foreach ($classAnnotations as $annot) {
if (isset($this->entityAnnotationClasses[get_class($annot)])) {
return false;
}
}
return true;
}
/**
* Factory method for the Annotation Driver.
*

View File

@@ -4,13 +4,15 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
use LogicException;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
@@ -20,9 +22,14 @@ use function class_exists;
use function constant;
use function defined;
use function get_class;
use function sprintf;
class AttributeDriver extends AnnotationDriver
use const PHP_VERSION_ID;
class AttributeDriver extends CompatibilityAnnotationDriver
{
use ColocatedMappingDriver;
/** @var array<string,int> */
// @phpcs:ignore
protected $entityAnnotationClasses = [
@@ -30,12 +37,48 @@ class AttributeDriver extends AnnotationDriver
Mapping\MappedSuperclass::class => 2,
];
/**
* The annotation reader.
*
* @internal this property will be private in 3.0
*
* @var AttributeReader
*/
protected $reader;
/**
* @param array<string> $paths
*/
public function __construct(array $paths)
{
parent::__construct(new AttributeReader(), $paths);
if (PHP_VERSION_ID < 80000) {
throw new LogicException(sprintf(
'The attribute metadata driver cannot be enabled on PHP 7. Please upgrade to PHP 8 or choose a different'
. ' metadata driver.'
));
}
$this->reader = new AttributeReader();
$this->addPaths($paths);
}
/**
* Retrieve the current annotation reader
*
* @deprecated no replacement planned.
*
* @return AttributeReader
*/
public function getReader()
{
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9587',
'%s is deprecated with no replacement',
__METHOD__
);
return $this->reader;
}
/**
@@ -55,10 +98,16 @@ class AttributeDriver extends AnnotationDriver
return true;
}
public function loadMetadataForClass($className, ClassMetadata $metadata): void
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata): void
{
assert($metadata instanceof ClassMetadataInfo);
$reflectionClass = $metadata->getReflectionClass();
$classAttributes = $this->reader->getClassAnnotations($reflectionClass);
@@ -195,7 +244,7 @@ class AttributeDriver extends AnnotationDriver
constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAttribute->value)
);
if ($metadata->inheritanceType !== Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate DiscriminatorColumn annotation
if (isset($classAttributes[Mapping\DiscriminatorColumn::class])) {
$discrColumnAttribute = $classAttributes[Mapping\DiscriminatorColumn::class];
@@ -260,7 +309,7 @@ class AttributeDriver extends AnnotationDriver
// Check for JoinColumn/JoinColumns annotations
$joinColumns = [];
$joinColumnAttributes = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class);
$joinColumnAttributes = $this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class);
foreach ($joinColumnAttributes as $joinColumnAttribute) {
$joinColumns[] = $this->joinColumnToArray($joinColumnAttribute);
@@ -365,11 +414,11 @@ class AttributeDriver extends AnnotationDriver
];
}
foreach ($this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class) as $joinColumn) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
}
foreach ($this->reader->getPropertyAnnotation($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
}
@@ -448,7 +497,7 @@ class AttributeDriver extends AnnotationDriver
// Check for `fetch`
if ($associationOverride->fetch) {
$override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
$override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
}
$metadata->setAssociationOverride($fieldName, $override);
@@ -515,7 +564,7 @@ class AttributeDriver extends AnnotationDriver
* @param string $className The class name.
* @param string $fetchMode The fetch mode.
*
* @return int The fetch mode as defined in ClassMetadata.
* @return ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
*
* @throws MappingException If the fetch mode is not valid.
*/

View File

@@ -6,6 +6,7 @@ namespace Doctrine\ORM\Mapping\Driver;
use Attribute;
use Doctrine\ORM\Mapping\Annotation;
use LogicException;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
@@ -14,58 +15,93 @@ use ReflectionProperty;
use function assert;
use function is_string;
use function is_subclass_of;
use function sprintf;
/**
* @internal
*/
final class AttributeReader
{
/** @var array<string,bool> */
/** @var array<class-string<Annotation>,bool> */
private array $isRepeatableAttribute = [];
/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @psalm-return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getClassAnnotations(ReflectionClass $class): array
{
return $this->convertToAttributeInstances($class->getAttributes());
}
/** @return Annotation|RepeatableAttributeCollection|null */
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getMethodAnnotations(ReflectionMethod $method): array
{
return $this->convertToAttributeInstances($method->getAttributes());
}
/** @return Annotation|RepeatableAttributeCollection|null */
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
return $this->getMethodAnnotations($method)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getPropertyAnnotations(ReflectionProperty $property): array
{
return $this->convertToAttributeInstances($property->getAttributes());
}
/** @return Annotation|RepeatableAttributeCollection|null */
/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return T|null
*
* @template T of Annotation
*/
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
if ($this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is repeatable. Call getPropertyAnnotationCollection() instead.',
$annotationName
));
}
return $this->getPropertyAnnotations($property)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}
/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return RepeatableAttributeCollection<T>
*
* @template T of Annotation
*/
public function getPropertyAnnotationCollection(
ReflectionProperty $property,
string $annotationName
): RepeatableAttributeCollection {
if (! $this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is not repeatable. Call getPropertyAnnotation() instead.',
$annotationName
));
}
return $this->getPropertyAnnotations($property)[$annotationName] ?? new RepeatableAttributeCollection();
}
/**
* @param array<ReflectionAttribute> $attributes
*
* @return array<Annotation|RepeatableAttributeCollection>
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
private function convertToAttributeInstances(array $attributes): array
{
@@ -98,6 +134,9 @@ final class AttributeReader
return $instances;
}
/**
* @param class-string<Annotation> $attributeClassName
*/
private function isRepeatable(string $attributeClassName): bool
{
if (isset($this->isRepeatableAttribute[$attributeClassName])) {

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as PersistenceAnnotationDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use function class_exists;
if (class_exists(PersistenceAnnotationDriver::class)) {
/**
* @internal This class will be removed in ORM 3.0.
*/
abstract class CompatibilityAnnotationDriver extends PersistenceAnnotationDriver
{
}
} else {
/**
* @internal This class will be removed in ORM 3.0.
*/
abstract class CompatibilityAnnotationDriver implements MappingDriver
{
}
}

View File

@@ -12,9 +12,10 @@ use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Types;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use InvalidArgumentException;
@@ -166,8 +167,13 @@ class DatabaseDriver implements MappingDriver
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$this->reverseEngineerMappingFromDatabase();

View File

@@ -8,7 +8,8 @@ use ArrayObject;
use Doctrine\ORM\Mapping\Annotation;
/**
* @template-extends ArrayObject<int,Annotation>
* @template-extends ArrayObject<int, T>
* @template T of Annotation
*/
final class RepeatableAttributeCollection extends ArrayObject
{

View File

@@ -6,11 +6,12 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
use InvalidArgumentException;
use LogicException;
use SimpleXMLElement;
use function assert;
@@ -18,6 +19,7 @@ use function constant;
use function count;
use function defined;
use function explode;
use function extension_loaded;
use function file_get_contents;
use function in_array;
use function simplexml_load_string;
@@ -39,13 +41,25 @@ class XmlDriver extends FileDriver
*/
public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
{
if (! extension_loaded('simplexml')) {
throw new LogicException(sprintf(
'The XML metadata driver cannot be enabled because the SimpleXML PHP extension is missing.'
. ' Please configure PHP with SimpleXML or choose a different metadata driver.'
));
}
parent::__construct($locator, $fileExtension);
}
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$xmlRoot = $this->getElement($className);
assert($xmlRoot instanceof SimpleXMLElement);
@@ -159,7 +173,7 @@ class XmlDriver extends FileDriver
$inheritanceType = (string) $xmlRoot['inheritance-type'];
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
if ($metadata->inheritanceType !== Metadata::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate <discriminator-column...>
if (isset($xmlRoot->{'discriminator-column'})) {
$discrColumn = $xmlRoot->{'discriminator-column'};
@@ -676,7 +690,7 @@ class XmlDriver extends FileDriver
// Check for `fetch`
if (isset($overrideElement['fetch'])) {
$override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
$override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
}
$metadata->setAssociationOverride($fieldName, $override);

View File

@@ -6,14 +6,16 @@ namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\FileDriver;
use InvalidArgumentException;
use LogicException;
use Symfony\Component\Yaml\Yaml;
use function array_map;
use function class_exists;
use function constant;
use function defined;
use function explode;
@@ -45,13 +47,26 @@ class YamlDriver extends FileDriver
'YAML mapping driver is deprecated and will be removed in Doctrine ORM 3.0, please migrate to attribute or XML driver.'
);
if (! class_exists(Yaml::class)) {
throw new LogicException(sprintf(
'The YAML metadata driver cannot be enabled because the "symfony/yaml" library'
. ' is not installed. Please run "composer require symfony/yaml" or choose a different'
. ' metadata driver.'
));
}
parent::__construct($locator, $fileExtension);
}
/**
* {@inheritDoc}
*
* @psalm-param class-string<T> $className
* @psalm-param ClassMetadata<T> $metadata
*
* @template T of object
*/
public function loadMetadataForClass($className, ClassMetadata $metadata)
public function loadMetadataForClass($className, PersistenceClassMetadata $metadata)
{
$element = $this->getElement($className);
@@ -176,7 +191,7 @@ class YamlDriver extends FileDriver
if (isset($element['inheritanceType'])) {
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
if ($metadata->inheritanceType !== Metadata::INHERITANCE_TYPE_NONE) {
if ($metadata->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// Evaluate discriminatorColumn
if (isset($element['discriminatorColumn'])) {
$discrColumn = $element['discriminatorColumn'];
@@ -673,7 +688,7 @@ class YamlDriver extends FileDriver
// Check for `fetch`
if (isset($associationOverrideElement['fetch'])) {
$override['fetch'] = constant(Metadata::class . '::FETCH_' . $associationOverrideElement['fetch']);
$override['fetch'] = constant(ClassMetadata::class . '::FETCH_' . $associationOverrideElement['fetch']);
}
$metadata->setAssociationOverride($fieldName, $override);

View File

@@ -12,13 +12,14 @@ use Doctrine\ORM\EntityRepository;
* @Annotation
* @NamedArgumentConstructor()
* @Target("CLASS")
* @template T of object
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class Entity implements Annotation
{
/**
* @var string|null
* @psalm-var class-string<EntityRepository>|null
* @psalm-var class-string<EntityRepository<T>>|null
*/
public $repositoryClass;
@@ -26,7 +27,7 @@ final class Entity implements Annotation
public $readOnly = false;
/**
* @psalm-param class-string<EntityRepository>|null $repositoryClass
* @psalm-param class-string<EntityRepository<T>>|null $repositoryClass
*/
public function __construct(?string $repositoryClass = null, bool $readOnly = false)
{

View File

@@ -31,10 +31,10 @@ final class Index implements Annotation
public $options;
/**
* @param array<string>|null $columns
* @param array<string>|null $fields
* @param array<string>|null $flags
* @param array<string>|null $options
* @param array<string>|null $columns
* @param array<string>|null $fields
* @param array<string>|null $flags
* @param array<string,mixed>|null $options
*/
public function __construct(
?array $columns = null,

View File

@@ -16,7 +16,7 @@ use Doctrine\Deprecations\Deprecation;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ManyToMany implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string|null */
@@ -43,7 +43,8 @@ final class ManyToMany implements Annotation
public $indexBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $targetEntity = null,

View File

@@ -15,7 +15,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ManyToOne implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string[]|null */
@@ -33,7 +33,8 @@ final class ManyToOne implements Annotation
public $inversedBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $targetEntity = null,

View File

@@ -18,7 +18,7 @@ final class OneToMany implements Annotation
/** @var string */
public $mappedBy;
/** @var string */
/** @var class-string|null */
public $targetEntity;
/** @var array<string> */
@@ -39,7 +39,8 @@ final class OneToMany implements Annotation
public $indexBy;
/**
* @param string[]|null $cascade
* @param class-string|null $targetEntity
* @param string[]|null $cascade
*/
public function __construct(
?string $mappedBy = null,

View File

@@ -15,7 +15,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
#[Attribute(Attribute::TARGET_PROPERTY)]
final class OneToOne implements Annotation
{
/** @var string|null */
/** @var class-string|null */
public $targetEntity;
/** @var string|null */
@@ -39,6 +39,7 @@ final class OneToOne implements Annotation
public $orphanRemoval = false;
/**
* @param class-string|null $targetEntity
* @param array<string>|null $cascade
*/
public function __construct(

View File

@@ -9,10 +9,9 @@ use ReflectionProperty;
use ReturnTypeWillChange;
use ValueError;
use function assert;
use function array_map;
use function get_class;
use function is_int;
use function is_string;
use function is_array;
class ReflectionEnumProperty extends ReflectionProperty
{
@@ -29,6 +28,11 @@ class ReflectionEnumProperty extends ReflectionProperty
{
$this->originalReflectionProperty = $originalReflectionProperty;
$this->enumType = $enumType;
parent::__construct(
$originalReflectionProperty->getDeclaringClass()->getName(),
$originalReflectionProperty->getName()
);
}
/**
@@ -36,7 +40,7 @@ class ReflectionEnumProperty extends ReflectionProperty
*
* @param object|null $object
*
* @return int|string|null
* @return int|string|int[]|string[]|null
*/
#[ReturnTypeWillChange]
public function getValue($object = null)
@@ -51,32 +55,52 @@ class ReflectionEnumProperty extends ReflectionProperty
return null;
}
if (is_array($enum)) {
return array_map(static function (BackedEnum $item): mixed {
return $item->value;
}, $enum);
}
return $enum->value;
}
/**
* @param object $object
* @param mixed $value
* @param object $object
* @param int|string|int[]|string[]|null $value
*/
public function setValue($object, $value = null): void
{
if ($value !== null) {
$enumType = $this->enumType;
try {
$value = $enumType::from($value);
} catch (ValueError $e) {
assert(is_string($value) || is_int($value));
throw MappingException::invalidEnumValue(
get_class($object),
$this->originalReflectionProperty->getName(),
(string) $value,
$enumType,
$e
);
if (is_array($value)) {
$value = array_map(function ($item) use ($object): BackedEnum {
return $this->initializeEnumValue($object, $item);
}, $value);
} else {
$value = $this->initializeEnumValue($object, $value);
}
}
$this->originalReflectionProperty->setValue($object, $value);
}
/**
* @param object $object
* @param int|string $value
*/
private function initializeEnumValue($object, $value): BackedEnum
{
$enumType = $this->enumType;
try {
return $enumType::from($value);
} catch (ValueError $e) {
throw MappingException::invalidEnumValue(
get_class($object),
$this->originalReflectionProperty->getName(),
(string) $value,
$enumType,
$e
);
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Doctrine\ORM\Mapping;
use Doctrine\Deprecations\Deprecation;
use function preg_replace;
use function strpos;
use function str_contains;
use function strrpos;
use function strtolower;
use function strtoupper;
@@ -79,7 +79,7 @@ class UnderscoreNamingStrategy implements NamingStrategy
*/
public function classToTableName($className)
{
if (strpos($className, '\\') !== false) {
if (str_contains($className, '\\')) {
$className = substr($className, strrpos($className, '\\') + 1);
}

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